关键节点
This commit is contained in:
244
editor/src/Foundation/UIEditorCommandDispatcher.cpp
Normal file
244
editor/src/Foundation/UIEditorCommandDispatcher.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
UIEditorCommandEvaluationResult MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode code,
|
||||
bool executable,
|
||||
std::string commandId,
|
||||
std::string displayName,
|
||||
UIEditorWorkspaceCommand workspaceCommand,
|
||||
UIEditorWorkspaceCommandResult previewResult,
|
||||
std::string message,
|
||||
UIEditorCommandKind kind) {
|
||||
UIEditorCommandEvaluationResult result = {};
|
||||
result.code = code;
|
||||
result.executable = executable;
|
||||
result.commandId = std::move(commandId);
|
||||
result.displayName = std::move(displayName);
|
||||
result.workspaceCommand = std::move(workspaceCommand);
|
||||
result.previewResult = std::move(previewResult);
|
||||
result.message = std::move(message);
|
||||
result.kind = kind;
|
||||
return result;
|
||||
}
|
||||
|
||||
UIEditorCommandDispatchResult BuildDispatchResult(
|
||||
UIEditorCommandDispatchStatus status,
|
||||
bool commandExecuted,
|
||||
std::string commandId,
|
||||
std::string displayName,
|
||||
UIEditorWorkspaceCommand workspaceCommand,
|
||||
UIEditorWorkspaceCommandResult commandResult,
|
||||
std::string message,
|
||||
UIEditorCommandKind kind) {
|
||||
UIEditorCommandDispatchResult result = {};
|
||||
result.status = status;
|
||||
result.commandExecuted = commandExecuted;
|
||||
result.commandId = std::move(commandId);
|
||||
result.displayName = std::move(displayName);
|
||||
result.workspaceCommand = std::move(workspaceCommand);
|
||||
result.commandResult = std::move(commandResult);
|
||||
result.message = std::move(message);
|
||||
result.kind = kind;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string_view GetUIEditorCommandDispatchStatusName(
|
||||
UIEditorCommandDispatchStatus status) {
|
||||
switch (status) {
|
||||
case UIEditorCommandDispatchStatus::Dispatched:
|
||||
return "Dispatched";
|
||||
case UIEditorCommandDispatchStatus::Rejected:
|
||||
return "Rejected";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
UIEditorCommandDispatcher::UIEditorCommandDispatcher(
|
||||
UIEditorCommandRegistry commandRegistry)
|
||||
: m_commandRegistry(std::move(commandRegistry)) {
|
||||
}
|
||||
|
||||
UIEditorCommandRegistryValidationResult
|
||||
UIEditorCommandDispatcher::ValidateConfiguration() const {
|
||||
return ValidateUIEditorCommandRegistry(m_commandRegistry);
|
||||
}
|
||||
|
||||
UIEditorCommandEvaluationResult UIEditorCommandDispatcher::Evaluate(
|
||||
std::string_view commandId,
|
||||
const UIEditorWorkspaceController& controller) const {
|
||||
const auto validation = ValidateConfiguration();
|
||||
if (!validation.IsValid()) {
|
||||
return MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode::InvalidCommandRegistry,
|
||||
false,
|
||||
std::string(commandId),
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
"Command registry invalid: " + validation.message,
|
||||
UIEditorCommandKind::Workspace);
|
||||
}
|
||||
|
||||
const UIEditorCommandDescriptor* descriptor =
|
||||
FindUIEditorCommandDescriptor(m_commandRegistry, commandId);
|
||||
if (descriptor == nullptr) {
|
||||
return MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode::UnknownCommandId,
|
||||
false,
|
||||
std::string(commandId),
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
"Editor command '" + std::string(commandId) + "' is not registered.",
|
||||
UIEditorCommandKind::Workspace);
|
||||
}
|
||||
|
||||
if (descriptor->kind == UIEditorCommandKind::Host) {
|
||||
if (m_hostCommandHandler == nullptr) {
|
||||
return MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode::MissingHostCommandHandler,
|
||||
false,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
{},
|
||||
{},
|
||||
"Host command handler is not attached for '" + descriptor->commandId + "'.",
|
||||
descriptor->kind);
|
||||
}
|
||||
|
||||
const UIEditorHostCommandEvaluationResult hostEvaluation =
|
||||
m_hostCommandHandler->EvaluateHostCommand(descriptor->commandId);
|
||||
return MakeEvaluationResult(
|
||||
hostEvaluation.executable
|
||||
? UIEditorCommandEvaluationCode::None
|
||||
: UIEditorCommandEvaluationCode::HostCommandDisabled,
|
||||
hostEvaluation.executable,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
{},
|
||||
{},
|
||||
hostEvaluation.message.empty()
|
||||
? std::string("Host command evaluated.")
|
||||
: hostEvaluation.message,
|
||||
descriptor->kind);
|
||||
}
|
||||
|
||||
UIEditorWorkspaceCommand workspaceCommand = {};
|
||||
workspaceCommand.kind = descriptor->workspaceCommand.kind;
|
||||
|
||||
switch (descriptor->workspaceCommand.panelSource) {
|
||||
case UIEditorCommandPanelSource::None:
|
||||
break;
|
||||
|
||||
case UIEditorCommandPanelSource::FixedPanelId:
|
||||
workspaceCommand.panelId = descriptor->workspaceCommand.panelId;
|
||||
break;
|
||||
|
||||
case UIEditorCommandPanelSource::ActivePanel:
|
||||
if (controller.GetWorkspace().activePanelId.empty()) {
|
||||
return MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode::MissingActivePanel,
|
||||
false,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
{},
|
||||
{},
|
||||
"Editor command '" + descriptor->commandId + "' requires an active panel.",
|
||||
descriptor->kind);
|
||||
}
|
||||
workspaceCommand.panelId = controller.GetWorkspace().activePanelId;
|
||||
break;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceController previewController = controller;
|
||||
UIEditorWorkspaceCommandResult previewResult =
|
||||
previewController.Dispatch(workspaceCommand);
|
||||
|
||||
return MakeEvaluationResult(
|
||||
UIEditorCommandEvaluationCode::None,
|
||||
previewResult.status != UIEditorWorkspaceCommandStatus::Rejected,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
std::move(workspaceCommand),
|
||||
std::move(previewResult),
|
||||
"Editor command resolved.",
|
||||
descriptor->kind);
|
||||
}
|
||||
|
||||
UIEditorCommandDispatchResult UIEditorCommandDispatcher::Dispatch(
|
||||
std::string_view commandId,
|
||||
UIEditorWorkspaceController& controller) const {
|
||||
const UIEditorCommandEvaluationResult evaluation =
|
||||
Evaluate(commandId, controller);
|
||||
if (!evaluation.IsExecutable()) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorCommandDispatchStatus::Rejected,
|
||||
false,
|
||||
evaluation.commandId,
|
||||
evaluation.displayName,
|
||||
evaluation.workspaceCommand,
|
||||
evaluation.previewResult,
|
||||
evaluation.message,
|
||||
evaluation.kind);
|
||||
}
|
||||
|
||||
if (evaluation.kind == UIEditorCommandKind::Host) {
|
||||
if (m_hostCommandHandler == nullptr) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorCommandDispatchStatus::Rejected,
|
||||
false,
|
||||
evaluation.commandId,
|
||||
evaluation.displayName,
|
||||
{},
|
||||
{},
|
||||
"Host command handler is not attached.",
|
||||
evaluation.kind);
|
||||
}
|
||||
|
||||
const UIEditorHostCommandDispatchResult hostDispatch =
|
||||
m_hostCommandHandler->DispatchHostCommand(evaluation.commandId);
|
||||
return BuildDispatchResult(
|
||||
hostDispatch.commandExecuted
|
||||
? UIEditorCommandDispatchStatus::Dispatched
|
||||
: UIEditorCommandDispatchStatus::Rejected,
|
||||
hostDispatch.commandExecuted,
|
||||
evaluation.commandId,
|
||||
evaluation.displayName,
|
||||
{},
|
||||
{},
|
||||
hostDispatch.message.empty()
|
||||
? std::string("Host command dispatched.")
|
||||
: hostDispatch.message,
|
||||
evaluation.kind);
|
||||
}
|
||||
|
||||
UIEditorWorkspaceCommandResult commandResult =
|
||||
controller.Dispatch(evaluation.workspaceCommand);
|
||||
const bool commandExecuted =
|
||||
commandResult.status != UIEditorWorkspaceCommandStatus::Rejected;
|
||||
|
||||
return BuildDispatchResult(
|
||||
commandExecuted
|
||||
? UIEditorCommandDispatchStatus::Dispatched
|
||||
: UIEditorCommandDispatchStatus::Rejected,
|
||||
commandExecuted,
|
||||
evaluation.commandId,
|
||||
evaluation.displayName,
|
||||
evaluation.workspaceCommand,
|
||||
std::move(commandResult),
|
||||
commandExecuted
|
||||
? "Editor command dispatched."
|
||||
: "Editor command dispatch was rejected.",
|
||||
evaluation.kind);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
152
editor/src/Foundation/UIEditorCommandRegistry.cpp
Normal file
152
editor/src/Foundation/UIEditorCommandRegistry.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <XCEditor/Foundation/UIEditorCommandRegistry.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
UIEditorCommandRegistryValidationResult MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode code,
|
||||
std::string message) {
|
||||
UIEditorCommandRegistryValidationResult result = {};
|
||||
result.code = code;
|
||||
result.message = std::move(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CommandKindRequiresPanelId(UIEditorWorkspaceCommandKind kind) {
|
||||
switch (kind) {
|
||||
case UIEditorWorkspaceCommandKind::OpenPanel:
|
||||
case UIEditorWorkspaceCommandKind::ClosePanel:
|
||||
case UIEditorWorkspaceCommandKind::ShowPanel:
|
||||
case UIEditorWorkspaceCommandKind::HidePanel:
|
||||
case UIEditorWorkspaceCommandKind::ActivatePanel:
|
||||
return true;
|
||||
case UIEditorWorkspaceCommandKind::ResetWorkspace:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string_view GetUIEditorCommandPanelSourceName(UIEditorCommandPanelSource source) {
|
||||
switch (source) {
|
||||
case UIEditorCommandPanelSource::None:
|
||||
return "None";
|
||||
case UIEditorCommandPanelSource::FixedPanelId:
|
||||
return "FixedPanelId";
|
||||
case UIEditorCommandPanelSource::ActivePanel:
|
||||
return "ActivePanel";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::string_view GetUIEditorCommandKindName(UIEditorCommandKind kind) {
|
||||
switch (kind) {
|
||||
case UIEditorCommandKind::Workspace:
|
||||
return "Workspace";
|
||||
case UIEditorCommandKind::Host:
|
||||
return "Host";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const UIEditorCommandDescriptor* FindUIEditorCommandDescriptor(
|
||||
const UIEditorCommandRegistry& registry,
|
||||
std::string_view commandId) {
|
||||
for (const UIEditorCommandDescriptor& command : registry.commands) {
|
||||
if (command.commandId == commandId) {
|
||||
return &command;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIEditorCommandRegistryValidationResult ValidateUIEditorCommandRegistry(
|
||||
const UIEditorCommandRegistry& registry) {
|
||||
std::unordered_set<std::string> seenCommandIds = {};
|
||||
for (const UIEditorCommandDescriptor& command : registry.commands) {
|
||||
if (command.commandId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::EmptyCommandId,
|
||||
"Editor command id must not be empty.");
|
||||
}
|
||||
|
||||
if (command.displayName.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::EmptyDisplayName,
|
||||
"Editor command '" + command.commandId + "' must define a displayName.");
|
||||
}
|
||||
|
||||
if (!seenCommandIds.insert(command.commandId).second) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::DuplicateCommandId,
|
||||
"Editor command id '" + command.commandId + "' is duplicated.");
|
||||
}
|
||||
|
||||
if (command.kind == UIEditorCommandKind::Host) {
|
||||
if (command.workspaceCommand.panelSource != UIEditorCommandPanelSource::None ||
|
||||
!command.workspaceCommand.panelId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::UnexpectedPanelSource,
|
||||
"Host editor command '" + command.commandId +
|
||||
"' must not define workspace panel routing.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool requiresPanelId =
|
||||
CommandKindRequiresPanelId(command.workspaceCommand.kind);
|
||||
switch (command.workspaceCommand.panelSource) {
|
||||
case UIEditorCommandPanelSource::None:
|
||||
if (requiresPanelId) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::MissingPanelSource,
|
||||
"Editor command '" + command.commandId + "' requires a panel source.");
|
||||
}
|
||||
if (!command.workspaceCommand.panelId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::UnexpectedPanelSource,
|
||||
"Editor command '" + command.commandId + "' must not carry a fixed panel id.");
|
||||
}
|
||||
break;
|
||||
|
||||
case UIEditorCommandPanelSource::FixedPanelId:
|
||||
if (!requiresPanelId) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::UnexpectedPanelSource,
|
||||
"Editor command '" + command.commandId + "' must not use a fixed panel id.");
|
||||
}
|
||||
if (command.workspaceCommand.panelId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::MissingFixedPanelId,
|
||||
"Editor command '" + command.commandId + "' fixed panel source requires a panel id.");
|
||||
}
|
||||
break;
|
||||
|
||||
case UIEditorCommandPanelSource::ActivePanel:
|
||||
if (!requiresPanelId) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::UnexpectedPanelSource,
|
||||
"Editor command '" + command.commandId + "' must not use the active panel source.");
|
||||
}
|
||||
if (!command.workspaceCommand.panelId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorCommandRegistryValidationCode::UnexpectedPanelSource,
|
||||
"Editor command '" + command.commandId + "' active panel source must not carry a fixed panel id.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
356
editor/src/Foundation/UIEditorRuntimeTrace.cpp
Normal file
356
editor/src/Foundation/UIEditorRuntimeTrace.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex g_traceMutex = {};
|
||||
std::filesystem::path g_logRoot = {};
|
||||
std::filesystem::path g_runtimeTracePath = {};
|
||||
std::filesystem::path g_crashTracePath = {};
|
||||
std::ofstream g_runtimeTraceStream = {};
|
||||
std::ofstream g_crashTraceStream = {};
|
||||
bool g_traceInitialized = false;
|
||||
bool g_symbolHandlerInitialized = false;
|
||||
|
||||
struct CrashStackFrame {
|
||||
DWORD64 address = 0u;
|
||||
DWORD64 displacement = 0u;
|
||||
DWORD line = 0u;
|
||||
DWORD lineDisplacement = 0u;
|
||||
std::string moduleName = {};
|
||||
std::string symbolName = {};
|
||||
std::string sourceFile = {};
|
||||
};
|
||||
|
||||
std::string BuildTimestampString() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm localTime = {};
|
||||
localtime_s(&localTime, ¤tTime);
|
||||
|
||||
const auto milliseconds =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()) %
|
||||
1000;
|
||||
|
||||
std::ostringstream stream = {};
|
||||
stream << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S")
|
||||
<< '.'
|
||||
<< std::setw(3)
|
||||
<< std::setfill('0')
|
||||
<< milliseconds.count();
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void AppendTraceLine(
|
||||
std::ofstream& stream,
|
||||
std::string_view channel,
|
||||
std::string_view message) {
|
||||
if (!stream.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream << '['
|
||||
<< BuildTimestampString()
|
||||
<< "] ["
|
||||
<< channel
|
||||
<< "] "
|
||||
<< message
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
std::string NarrowWideString(std::wstring_view text) {
|
||||
if (text.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int sizeNeeded = WideCharToMultiByte(
|
||||
CP_UTF8,
|
||||
0,
|
||||
text.data(),
|
||||
static_cast<int>(text.size()),
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (sizeNeeded <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result(static_cast<std::size_t>(sizeNeeded), '\0');
|
||||
WideCharToMultiByte(
|
||||
CP_UTF8,
|
||||
0,
|
||||
text.data(),
|
||||
static_cast<int>(text.size()),
|
||||
result.data(),
|
||||
sizeNeeded,
|
||||
nullptr,
|
||||
nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FormatPointer(const void* address) {
|
||||
char buffer[32] = {};
|
||||
std::snprintf(buffer, sizeof(buffer), "%p", address);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string ResolveModulePathFromAddress(DWORD64 address) {
|
||||
if (address == 0u) {
|
||||
return {};
|
||||
}
|
||||
|
||||
HMODULE moduleHandle = nullptr;
|
||||
if (!GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
reinterpret_cast<LPCWSTR>(address),
|
||||
&moduleHandle) ||
|
||||
moduleHandle == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
wchar_t modulePath[MAX_PATH] = {};
|
||||
const DWORD length = GetModuleFileNameW(moduleHandle, modulePath, MAX_PATH);
|
||||
if (length == 0u) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return NarrowWideString(std::wstring_view(modulePath, length));
|
||||
}
|
||||
|
||||
void EnsureSymbolHandlerInitialized(HANDLE process) {
|
||||
if (g_symbolHandlerInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
|
||||
g_symbolHandlerInitialized = SymInitialize(process, nullptr, TRUE) == TRUE;
|
||||
}
|
||||
|
||||
std::vector<CrashStackFrame> CaptureExceptionStack(const _EXCEPTION_POINTERS* exceptionPointers) {
|
||||
std::vector<CrashStackFrame> frames = {};
|
||||
if (exceptionPointers == nullptr || exceptionPointers->ContextRecord == nullptr) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
EnsureSymbolHandlerInitialized(process);
|
||||
|
||||
CONTEXT context = *exceptionPointers->ContextRecord;
|
||||
STACKFRAME64 stackFrame = {};
|
||||
DWORD machineType = 0u;
|
||||
#if defined(_M_X64)
|
||||
machineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackFrame.AddrPC.Offset = context.Rip;
|
||||
stackFrame.AddrFrame.Offset = context.Rbp;
|
||||
stackFrame.AddrStack.Offset = context.Rsp;
|
||||
#elif defined(_M_IX86)
|
||||
machineType = IMAGE_FILE_MACHINE_I386;
|
||||
stackFrame.AddrPC.Offset = context.Eip;
|
||||
stackFrame.AddrFrame.Offset = context.Ebp;
|
||||
stackFrame.AddrStack.Offset = context.Esp;
|
||||
#else
|
||||
return frames;
|
||||
#endif
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
constexpr std::size_t kMaxFrames = 32u;
|
||||
frames.reserve(kMaxFrames);
|
||||
for (std::size_t frameIndex = 0u; frameIndex < kMaxFrames; ++frameIndex) {
|
||||
const BOOL advanced = StackWalk64(
|
||||
machineType,
|
||||
process,
|
||||
thread,
|
||||
&stackFrame,
|
||||
&context,
|
||||
nullptr,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
nullptr);
|
||||
if (advanced == FALSE || stackFrame.AddrPC.Offset == 0u) {
|
||||
break;
|
||||
}
|
||||
|
||||
CrashStackFrame frame = {};
|
||||
frame.address = stackFrame.AddrPC.Offset;
|
||||
frame.moduleName = ResolveModulePathFromAddress(frame.address);
|
||||
|
||||
std::byte symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME] = {};
|
||||
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbolBuffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
if (g_symbolHandlerInitialized &&
|
||||
SymFromAddr(process, frame.address, &frame.displacement, symbol) == TRUE) {
|
||||
frame.symbolName = symbol->Name;
|
||||
}
|
||||
|
||||
IMAGEHLP_LINE64 line = {};
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
if (g_symbolHandlerInitialized &&
|
||||
SymGetLineFromAddr64(process, frame.address, &frame.lineDisplacement, &line) == TRUE) {
|
||||
frame.line = line.LineNumber;
|
||||
if (line.FileName != nullptr) {
|
||||
frame.sourceFile = line.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
frames.push_back(std::move(frame));
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
std::string FormatCrashStackFrame(const CrashStackFrame& frame, std::size_t index) {
|
||||
std::ostringstream stream = {};
|
||||
stream << "stack[" << index << "] pc=0x"
|
||||
<< std::hex << std::uppercase << frame.address << std::dec;
|
||||
if (!frame.moduleName.empty()) {
|
||||
stream << " module=" << frame.moduleName;
|
||||
}
|
||||
if (!frame.symbolName.empty()) {
|
||||
stream << " symbol=" << frame.symbolName;
|
||||
if (frame.displacement != 0u) {
|
||||
stream << "+0x" << std::hex << std::uppercase << frame.displacement << std::dec;
|
||||
}
|
||||
}
|
||||
if (!frame.sourceFile.empty()) {
|
||||
stream << " source=" << frame.sourceFile;
|
||||
if (frame.line != 0u) {
|
||||
stream << ':' << frame.line;
|
||||
if (frame.lineDisplacement != 0u) {
|
||||
stream << "+0x" << std::hex << std::uppercase
|
||||
<< frame.lineDisplacement << std::dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeUIEditorRuntimeTrace(const std::filesystem::path& logRoot) {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
g_logRoot = logRoot.lexically_normal();
|
||||
g_runtimeTracePath = (g_logRoot / "runtime.log").lexically_normal();
|
||||
g_crashTracePath = (g_logRoot / "crash.log").lexically_normal();
|
||||
|
||||
std::error_code errorCode = {};
|
||||
std::filesystem::create_directories(g_logRoot, errorCode);
|
||||
g_runtimeTraceStream.close();
|
||||
g_crashTraceStream.close();
|
||||
g_runtimeTraceStream.open(g_runtimeTracePath, std::ios::out | std::ios::app);
|
||||
g_crashTraceStream.open(g_crashTracePath, std::ios::out | std::ios::app);
|
||||
g_traceInitialized = g_runtimeTraceStream.is_open() && g_crashTraceStream.is_open();
|
||||
if (!g_traceInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendTraceLine(g_runtimeTraceStream, "trace", "trace session started");
|
||||
g_runtimeTraceStream.flush();
|
||||
}
|
||||
|
||||
void ShutdownUIEditorRuntimeTrace() {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
if (!g_traceInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendTraceLine(g_runtimeTraceStream, "trace", "trace session ended");
|
||||
g_runtimeTraceStream.flush();
|
||||
g_crashTraceStream.flush();
|
||||
g_runtimeTraceStream.close();
|
||||
g_crashTraceStream.close();
|
||||
g_traceInitialized = false;
|
||||
}
|
||||
|
||||
void AppendUIEditorRuntimeTrace(
|
||||
std::string_view channel,
|
||||
std::string_view message) {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
if (!g_traceInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendTraceLine(g_runtimeTraceStream, channel, message);
|
||||
g_runtimeTraceStream.flush();
|
||||
}
|
||||
|
||||
void AppendUIEditorCrashTrace(
|
||||
std::uint32_t exceptionCode,
|
||||
const void* exceptionAddress) {
|
||||
AppendUIEditorCrashTrace(exceptionCode, exceptionAddress, nullptr);
|
||||
}
|
||||
|
||||
void AppendUIEditorCrashTrace(
|
||||
std::uint32_t exceptionCode,
|
||||
const void* exceptionAddress,
|
||||
const _EXCEPTION_POINTERS* exceptionPointers) {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
if (!g_traceInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ostringstream header = {};
|
||||
header << "Unhandled exception code=0x"
|
||||
<< std::hex << std::uppercase << std::setw(8) << std::setfill('0')
|
||||
<< exceptionCode
|
||||
<< std::dec << std::setfill(' ')
|
||||
<< " address=" << FormatPointer(exceptionAddress);
|
||||
const std::string exceptionModule =
|
||||
ResolveModulePathFromAddress(reinterpret_cast<DWORD64>(exceptionAddress));
|
||||
if (!exceptionModule.empty()) {
|
||||
header << " module=" << exceptionModule;
|
||||
}
|
||||
header << " threadId=" << GetCurrentThreadId();
|
||||
|
||||
const std::string headerText = header.str();
|
||||
AppendTraceLine(g_crashTraceStream, "crash", headerText);
|
||||
AppendTraceLine(g_runtimeTraceStream, "crash", headerText);
|
||||
|
||||
const std::vector<CrashStackFrame> frames = CaptureExceptionStack(exceptionPointers);
|
||||
for (std::size_t index = 0u; index < frames.size(); ++index) {
|
||||
const std::string line = FormatCrashStackFrame(frames[index], index);
|
||||
AppendTraceLine(g_crashTraceStream, "crash", line);
|
||||
AppendTraceLine(g_runtimeTraceStream, "crash", line);
|
||||
}
|
||||
|
||||
g_crashTraceStream.flush();
|
||||
g_runtimeTraceStream.flush();
|
||||
}
|
||||
|
||||
std::filesystem::path GetUIEditorRuntimeTracePath() {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
return g_runtimeTracePath;
|
||||
}
|
||||
|
||||
std::filesystem::path GetUIEditorCrashTracePath() {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
return g_crashTracePath;
|
||||
}
|
||||
|
||||
bool IsUIEditorRuntimeTraceInitialized() {
|
||||
std::lock_guard lock(g_traceMutex);
|
||||
return g_traceInitialized;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
344
editor/src/Foundation/UIEditorShortcutManager.cpp
Normal file
344
editor/src/Foundation/UIEditorShortcutManager.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::UIShortcutBinding;
|
||||
using XCEngine::UI::UIShortcutMatch;
|
||||
using XCEngine::UI::UIShortcutScope;
|
||||
using XCEngine::Input::KeyCode;
|
||||
|
||||
UIEditorShortcutManagerValidationResult MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode code,
|
||||
std::string message) {
|
||||
UIEditorShortcutManagerValidationResult result = {};
|
||||
result.code = code;
|
||||
result.message = std::move(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModifiersEqual(
|
||||
const XCEngine::UI::UIInputModifiers& lhs,
|
||||
const XCEngine::UI::UIInputModifiers& rhs) {
|
||||
return lhs.shift == rhs.shift &&
|
||||
lhs.control == rhs.control &&
|
||||
lhs.alt == rhs.alt &&
|
||||
lhs.super == rhs.super;
|
||||
}
|
||||
|
||||
bool HaveConflictingChord(
|
||||
const UIShortcutBinding& lhs,
|
||||
const UIShortcutBinding& rhs) {
|
||||
return lhs.triggerEventType == rhs.triggerEventType &&
|
||||
lhs.scope == rhs.scope &&
|
||||
lhs.ownerId == rhs.ownerId &&
|
||||
lhs.chord.keyCode == rhs.chord.keyCode &&
|
||||
ModifiersEqual(lhs.chord.modifiers, rhs.chord.modifiers);
|
||||
}
|
||||
|
||||
int ShortcutDisplayPriority(UIShortcutScope scope) {
|
||||
switch (scope) {
|
||||
case UIShortcutScope::Global:
|
||||
return 0;
|
||||
case UIShortcutScope::Window:
|
||||
return 1;
|
||||
case UIShortcutScope::Panel:
|
||||
return 2;
|
||||
case UIShortcutScope::Widget:
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::string GetKeyCodeDisplayName(std::int32_t keyCode) {
|
||||
if (keyCode >= static_cast<std::int32_t>(KeyCode::A) &&
|
||||
keyCode <= static_cast<std::int32_t>(KeyCode::Z)) {
|
||||
return std::string(1, static_cast<char>('A' +
|
||||
(keyCode - static_cast<std::int32_t>(KeyCode::A))));
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Zero)) return "0";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::One)) return "1";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Two)) return "2";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Three)) return "3";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Four)) return "4";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Five)) return "5";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Six)) return "6";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Seven)) return "7";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Eight)) return "8";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Nine)) return "9";
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F1)) return "F1";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F2)) return "F2";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F3)) return "F3";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F4)) return "F4";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F5)) return "F5";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F6)) return "F6";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F7)) return "F7";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F8)) return "F8";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F9)) return "F9";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F10)) return "F10";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F11)) return "F11";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::F12)) return "F12";
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Tab)) return "Tab";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Enter)) return "Enter";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Escape)) return "Esc";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Space)) return "Space";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Delete)) return "Delete";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Backspace)) return "Backspace";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Up)) return "Up";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Down)) return "Down";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Left)) return "Left";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Right)) return "Right";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Home)) return "Home";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::End)) return "End";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::PageUp)) return "PageUp";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::PageDown)) return "PageDown";
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Minus)) return "-";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Equals)) return "=";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::BracketLeft)) return "[";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Semicolon)) return ";";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Period)) return ".";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Slash)) return "/";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Backslash)) return "\\";
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Backtick)) return "`";
|
||||
|
||||
return keyCode == 0
|
||||
? std::string()
|
||||
: "Key(" + std::to_string(keyCode) + ")";
|
||||
}
|
||||
|
||||
std::string FormatShortcutChord(const XCEngine::UI::UIShortcutChord& chord) {
|
||||
std::string result = {};
|
||||
if (chord.modifiers.control) {
|
||||
result += "Ctrl+";
|
||||
}
|
||||
if (chord.modifiers.shift) {
|
||||
result += "Shift+";
|
||||
}
|
||||
if (chord.modifiers.alt) {
|
||||
result += "Alt+";
|
||||
}
|
||||
if (chord.modifiers.super) {
|
||||
result += "Super+";
|
||||
}
|
||||
|
||||
result += GetKeyCodeDisplayName(chord.keyCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string_view GetUIEditorShortcutDispatchStatusName(
|
||||
UIEditorShortcutDispatchStatus status) {
|
||||
switch (status) {
|
||||
case UIEditorShortcutDispatchStatus::NoMatch:
|
||||
return "NoMatch";
|
||||
case UIEditorShortcutDispatchStatus::Suppressed:
|
||||
return "Suppressed";
|
||||
case UIEditorShortcutDispatchStatus::Dispatched:
|
||||
return "Dispatched";
|
||||
case UIEditorShortcutDispatchStatus::Rejected:
|
||||
return "Rejected";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
UIEditorShortcutManager::UIEditorShortcutManager(UIEditorCommandRegistry commandRegistry)
|
||||
: m_commandDispatcher(std::move(commandRegistry)) {
|
||||
}
|
||||
|
||||
std::uint64_t UIEditorShortcutManager::RegisterBinding(
|
||||
const XCEngine::UI::UIShortcutBinding& binding) {
|
||||
return m_shortcutRegistry.RegisterBinding(binding);
|
||||
}
|
||||
|
||||
bool UIEditorShortcutManager::UnregisterBinding(std::uint64_t bindingId) {
|
||||
return m_shortcutRegistry.UnregisterBinding(bindingId);
|
||||
}
|
||||
|
||||
void UIEditorShortcutManager::ClearBindings() {
|
||||
m_shortcutRegistry.Clear();
|
||||
}
|
||||
|
||||
UIEditorShortcutManagerValidationResult UIEditorShortcutManager::ValidateConfiguration() const {
|
||||
const UIEditorCommandRegistryValidationResult commandValidation =
|
||||
m_commandDispatcher.ValidateConfiguration();
|
||||
if (!commandValidation.IsValid()) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::InvalidCommandRegistry,
|
||||
commandValidation.message);
|
||||
}
|
||||
|
||||
const std::vector<UIShortcutBinding>& bindings = m_shortcutRegistry.GetBindings();
|
||||
for (std::size_t index = 0; index < bindings.size(); ++index) {
|
||||
const UIShortcutBinding& binding = bindings[index];
|
||||
if (binding.commandId.empty()) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::EmptyBindingCommandId,
|
||||
"Editor shortcut binding commandId must not be empty.");
|
||||
}
|
||||
|
||||
if (FindUIEditorCommandDescriptor(
|
||||
m_commandDispatcher.GetCommandRegistry(),
|
||||
binding.commandId) == nullptr) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::UnknownCommandId,
|
||||
"Editor shortcut binding references unknown command '" + binding.commandId + "'.");
|
||||
}
|
||||
|
||||
if (binding.chord.keyCode == 0) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::EmptyShortcutKey,
|
||||
"Editor shortcut binding '" + binding.commandId + "' must define a keyCode.");
|
||||
}
|
||||
|
||||
if (binding.scope != UIShortcutScope::Global && binding.ownerId == 0) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::MissingScopedOwnerId,
|
||||
"Editor shortcut binding '" + binding.commandId + "' must define ownerId for non-global scope.");
|
||||
}
|
||||
|
||||
for (std::size_t candidateIndex = index + 1u; candidateIndex < bindings.size(); ++candidateIndex) {
|
||||
const UIShortcutBinding& candidate = bindings[candidateIndex];
|
||||
if (HaveConflictingChord(binding, candidate)) {
|
||||
return MakeValidationError(
|
||||
UIEditorShortcutManagerValidationCode::ConflictingBinding,
|
||||
"Editor shortcut bindings '" + binding.commandId +
|
||||
"' and '" + candidate.commandId +
|
||||
"' conflict on the same chord/scope/owner.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const XCEngine::UI::UIShortcutBinding* UIEditorShortcutManager::FindPreferredBinding(
|
||||
std::string_view commandId) const {
|
||||
const UIShortcutBinding* preferred = nullptr;
|
||||
for (const UIShortcutBinding& binding : m_shortcutRegistry.GetBindings()) {
|
||||
if (binding.commandId != commandId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preferred == nullptr) {
|
||||
preferred = &binding;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int currentPriority = ShortcutDisplayPriority(binding.scope);
|
||||
const int preferredPriority = ShortcutDisplayPriority(preferred->scope);
|
||||
if (currentPriority < preferredPriority ||
|
||||
(currentPriority == preferredPriority &&
|
||||
(binding.ownerId < preferred->ownerId ||
|
||||
(binding.ownerId == preferred->ownerId &&
|
||||
binding.bindingId < preferred->bindingId)))) {
|
||||
preferred = &binding;
|
||||
}
|
||||
}
|
||||
|
||||
return preferred;
|
||||
}
|
||||
|
||||
std::string UIEditorShortcutManager::GetPreferredShortcutText(
|
||||
std::string_view commandId) const {
|
||||
const UIShortcutBinding* binding = FindPreferredBinding(commandId);
|
||||
return binding != nullptr ? FormatShortcutChord(binding->chord) : std::string();
|
||||
}
|
||||
|
||||
UIEditorShortcutDispatchResult UIEditorShortcutManager::BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus status,
|
||||
std::string commandId,
|
||||
std::string commandDisplayName,
|
||||
std::string message,
|
||||
const UIShortcutMatch* match) const {
|
||||
UIEditorShortcutDispatchResult result = {};
|
||||
result.status = status;
|
||||
result.matched = match != nullptr && match->matched;
|
||||
result.commandId = std::move(commandId);
|
||||
result.commandDisplayName = std::move(commandDisplayName);
|
||||
result.message = std::move(message);
|
||||
if (match != nullptr && match->matched) {
|
||||
result.shortcutScope = match->binding.scope;
|
||||
result.shortcutOwnerId = match->binding.ownerId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UIEditorShortcutDispatchResult UIEditorShortcutManager::Dispatch(
|
||||
const XCEngine::UI::UIInputEvent& event,
|
||||
const XCEngine::UI::UIShortcutContext& shortcutContext,
|
||||
UIEditorWorkspaceController& controller) const {
|
||||
const UIEditorShortcutManagerValidationResult validation = ValidateConfiguration();
|
||||
if (!validation.IsValid()) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::Rejected,
|
||||
{},
|
||||
{},
|
||||
"Shortcut manager configuration invalid: " + validation.message);
|
||||
}
|
||||
|
||||
const UIShortcutMatch match = m_shortcutRegistry.Match(event, shortcutContext);
|
||||
if (!match.matched) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::NoMatch,
|
||||
{},
|
||||
{},
|
||||
"No shortcut binding matched the input event.");
|
||||
}
|
||||
|
||||
const UIEditorCommandDescriptor* descriptor =
|
||||
FindUIEditorCommandDescriptor(
|
||||
m_commandDispatcher.GetCommandRegistry(),
|
||||
match.binding.commandId);
|
||||
if (descriptor == nullptr) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::Rejected,
|
||||
match.binding.commandId,
|
||||
{},
|
||||
"Matched shortcut references an unknown editor command.",
|
||||
&match);
|
||||
}
|
||||
|
||||
if (shortcutContext.textInputActive) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::Suppressed,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
"Shortcut matched but was suppressed by active text input.",
|
||||
&match);
|
||||
}
|
||||
|
||||
const UIEditorCommandDispatchResult dispatchResult =
|
||||
m_commandDispatcher.Dispatch(descriptor->commandId, controller);
|
||||
if (!dispatchResult.commandExecuted) {
|
||||
return BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::Rejected,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
dispatchResult.message,
|
||||
&match);
|
||||
}
|
||||
|
||||
UIEditorShortcutDispatchResult result = BuildDispatchResult(
|
||||
UIEditorShortcutDispatchStatus::Dispatched,
|
||||
descriptor->commandId,
|
||||
descriptor->displayName,
|
||||
"Shortcut matched and command dispatched.",
|
||||
&match);
|
||||
result.commandExecuted = true;
|
||||
result.commandResult = dispatchResult.commandResult;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
385
editor/src/Foundation/UIEditorTheme.cpp
Normal file
385
editor/src/Foundation/UIEditorTheme.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
#include <XCEditor/Foundation/UIEditorTheme.h>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::UI::UIColor;
|
||||
|
||||
const UIColor kSurfaceLower(0.09f, 0.09f, 0.09f, 1.0f);
|
||||
const UIColor kSurfaceBase(0.10f, 0.10f, 0.10f, 1.0f);
|
||||
const UIColor kSurfaceRaised(0.11f, 0.11f, 0.11f, 1.0f);
|
||||
const UIColor kSurfacePanel(0.12f, 0.12f, 0.12f, 1.0f);
|
||||
const UIColor kSurfaceHover(0.14f, 0.14f, 0.14f, 1.0f);
|
||||
const UIColor kSurfaceActive(0.17f, 0.17f, 0.17f, 1.0f);
|
||||
const UIColor kSurfaceActiveStrong(0.19f, 0.19f, 0.19f, 1.0f);
|
||||
const UIColor kBorderSubtle(0.14f, 0.14f, 0.14f, 1.0f);
|
||||
const UIColor kBorderDefault(0.15f, 0.15f, 0.15f, 1.0f);
|
||||
const UIColor kBorderFocused(0.19f, 0.19f, 0.19f, 1.0f);
|
||||
const UIColor kTextPrimary(0.92f, 0.92f, 0.92f, 1.0f);
|
||||
const UIColor kTextStrong(0.94f, 0.94f, 0.94f, 1.0f);
|
||||
const UIColor kTextSecondary(0.72f, 0.72f, 0.72f, 1.0f);
|
||||
const UIColor kTextMuted(0.62f, 0.62f, 0.62f, 1.0f);
|
||||
const UIColor kTextDisabled(0.46f, 0.46f, 0.46f, 1.0f);
|
||||
|
||||
template <typename TValue>
|
||||
const TValue& GetDefaultValue() {
|
||||
static const TValue value = {};
|
||||
return value;
|
||||
}
|
||||
|
||||
Widgets::UIEditorMenuBarPalette BuildMenuBarPalette() {
|
||||
Widgets::UIEditorMenuBarPalette palette = {};
|
||||
palette.barColor = kSurfaceBase;
|
||||
palette.buttonColor = UIColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
palette.buttonHoveredColor = kSurfaceHover;
|
||||
palette.buttonOpenColor = kSurfaceActive;
|
||||
palette.borderColor = UIColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
palette.focusedBorderColor = UIColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
palette.openBorderColor = UIColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
palette.textPrimary = kTextStrong;
|
||||
palette.textMuted = kTextSecondary;
|
||||
palette.textDisabled = kTextDisabled;
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorMenuPopupPalette BuildMenuPopupPalette() {
|
||||
Widgets::UIEditorMenuPopupPalette palette = {};
|
||||
palette.popupColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.itemHoverColor = kSurfaceHover;
|
||||
palette.itemOpenColor = kSurfaceActive;
|
||||
palette.separatorColor = kBorderSubtle;
|
||||
palette.textPrimary = kTextStrong;
|
||||
palette.textMuted = kTextSecondary;
|
||||
palette.textDisabled = kTextDisabled;
|
||||
palette.glyphColor = kTextPrimary;
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorListViewPalette BuildListViewPalette() {
|
||||
Widgets::UIEditorListViewPalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.rowHoverColor = kSurfaceHover;
|
||||
palette.rowSelectedColor = kSurfaceActive;
|
||||
palette.rowSelectedFocusedColor = kSurfaceActiveStrong;
|
||||
palette.primaryTextColor = kTextStrong;
|
||||
palette.secondaryTextColor = kTextMuted;
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorTreeViewPalette BuildTreeViewPalette() {
|
||||
Widgets::UIEditorTreeViewPalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.borderColor = kSurfaceBase;
|
||||
palette.focusedBorderColor = kSurfaceBase;
|
||||
palette.rowHoverColor = UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||
palette.rowSelectedColor = kSurfaceActive;
|
||||
palette.rowSelectedFocusedColor = kSurfaceActive;
|
||||
palette.disclosureColor = kTextMuted;
|
||||
palette.textColor = kTextPrimary;
|
||||
palette.scrollViewPalette.surfaceColor = palette.surfaceColor;
|
||||
palette.scrollViewPalette.borderColor = palette.borderColor;
|
||||
palette.scrollViewPalette.focusedBorderColor = palette.focusedBorderColor;
|
||||
palette.scrollViewPalette.scrollbarTrackColor = kSurfaceLower;
|
||||
palette.scrollViewPalette.scrollbarThumbColor = kSurfaceHover;
|
||||
palette.scrollViewPalette.scrollbarThumbHoverColor = kSurfaceActive;
|
||||
palette.scrollViewPalette.scrollbarThumbActiveColor = UIColor(0.20f, 0.20f, 0.20f, 1.0f);
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorTreeViewMetrics BuildTreeViewMetrics() {
|
||||
Widgets::UIEditorTreeViewMetrics metrics = {};
|
||||
metrics.rowHeight = 20.0f;
|
||||
metrics.rowGap = 0.0f;
|
||||
metrics.horizontalPadding = 6.0f;
|
||||
metrics.indentWidth = 14.0f;
|
||||
metrics.disclosureExtent = 18.0f;
|
||||
metrics.disclosureLabelGap = 2.0f;
|
||||
metrics.iconExtent = 18.0f;
|
||||
metrics.iconLabelGap = 2.0f;
|
||||
metrics.iconInsetY = -1.0f;
|
||||
metrics.labelInsetY = 0.0f;
|
||||
metrics.cornerRounding = 0.0f;
|
||||
metrics.borderThickness = 0.0f;
|
||||
metrics.focusedBorderThickness = 0.0f;
|
||||
metrics.scrollViewMetrics.scrollbarWidth = 8.0f;
|
||||
metrics.scrollViewMetrics.scrollbarInset = 3.0f;
|
||||
metrics.scrollViewMetrics.minThumbHeight = 28.0f;
|
||||
metrics.scrollViewMetrics.cornerRounding = metrics.cornerRounding;
|
||||
metrics.scrollViewMetrics.borderThickness = metrics.borderThickness;
|
||||
metrics.scrollViewMetrics.focusedBorderThickness = metrics.focusedBorderThickness;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
Widgets::UIEditorScrollViewPalette BuildScrollViewPalette() {
|
||||
Widgets::UIEditorScrollViewPalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.scrollbarTrackColor = kSurfaceLower;
|
||||
palette.scrollbarThumbColor = kSurfaceHover;
|
||||
palette.scrollbarThumbHoverColor = kSurfaceActive;
|
||||
palette.scrollbarThumbActiveColor = UIColor(0.20f, 0.20f, 0.20f, 1.0f);
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorTabStripPalette BuildTabStripPalette() {
|
||||
Widgets::UIEditorTabStripPalette palette = {};
|
||||
palette.stripBackgroundColor = kSurfaceBase;
|
||||
palette.headerBackgroundColor = kSurfaceRaised;
|
||||
palette.contentBackgroundColor = kSurfaceBase;
|
||||
palette.headerContentSeparatorColor = kBorderDefault;
|
||||
palette.tabColor = kSurfaceRaised;
|
||||
palette.tabHoveredColor = kSurfaceHover;
|
||||
palette.tabSelectedColor = kSurfaceRaised;
|
||||
palette.tabBorderColor = kBorderDefault;
|
||||
palette.tabHoveredBorderColor = kBorderFocused;
|
||||
palette.tabSelectedBorderColor = kBorderDefault;
|
||||
palette.textPrimary = kTextPrimary;
|
||||
palette.textSecondary = kTextSecondary;
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorStatusBarPalette BuildStatusBarPalette() {
|
||||
Widgets::UIEditorStatusBarPalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.segmentColor = kSurfacePanel;
|
||||
palette.segmentHoveredColor = kSurfaceHover;
|
||||
palette.segmentActiveColor = kSurfaceActive;
|
||||
palette.segmentBorderColor = kBorderDefault;
|
||||
palette.separatorColor = kBorderDefault;
|
||||
palette.textPrimary = kTextPrimary;
|
||||
palette.textMuted = kTextMuted;
|
||||
palette.textAccent = kTextStrong;
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorPanelFramePalette BuildPanelFramePalette() {
|
||||
Widgets::UIEditorPanelFramePalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.headerColor = kSurfaceRaised;
|
||||
palette.footerColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.hoveredBorderColor = kSurfaceActive;
|
||||
palette.activeBorderColor = kBorderFocused;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.textPrimary = kTextPrimary;
|
||||
palette.textSecondary = kTextSecondary;
|
||||
palette.textMuted = kTextMuted;
|
||||
palette.actionButtonColor = kSurfaceRaised;
|
||||
palette.actionButtonHoveredColor = kSurfaceHover;
|
||||
palette.actionButtonSelectedColor = kSurfaceActive;
|
||||
palette.actionButtonBorderColor = kBorderDefault;
|
||||
palette.actionGlyphColor = UIColor(0.86f, 0.86f, 0.86f, 1.0f);
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorDockHostPalette BuildDockHostPalette() {
|
||||
Widgets::UIEditorDockHostPalette palette = {};
|
||||
palette.tabStripPalette = BuildTabStripPalette();
|
||||
palette.panelFramePalette = BuildPanelFramePalette();
|
||||
palette.splitterColor = kBorderSubtle;
|
||||
palette.splitterHoveredColor = UIColor(0.16f, 0.16f, 0.16f, 1.0f);
|
||||
palette.splitterActiveColor = UIColor(0.18f, 0.18f, 0.18f, 1.0f);
|
||||
palette.placeholderTitleColor = kTextPrimary;
|
||||
palette.placeholderTextColor = kTextSecondary;
|
||||
palette.placeholderMutedColor = kTextMuted;
|
||||
palette.dropPreviewFillColor = UIColor(0.92f, 0.92f, 0.92f, 0.06f);
|
||||
palette.dropPreviewBorderColor = UIColor(0.95f, 0.95f, 0.95f, 0.55f);
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorViewportSlotPalette BuildViewportSlotPalette() {
|
||||
Widgets::UIEditorViewportSlotPalette palette = {};
|
||||
palette.frameColor = kSurfaceLower;
|
||||
palette.topBarColor = kSurfaceBase;
|
||||
palette.surfaceColor = kSurfaceLower;
|
||||
palette.surfaceHoverOverlayColor = UIColor(0.18f, 0.18f, 0.18f, 0.08f);
|
||||
palette.surfaceActiveOverlayColor = UIColor(0.22f, 0.22f, 0.22f, 0.08f);
|
||||
palette.captureOverlayColor = UIColor(0.92f, 0.92f, 0.92f, 0.05f);
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.surfaceBorderColor = kBorderDefault;
|
||||
palette.surfaceHoveredBorderColor = kSurfaceActive;
|
||||
palette.surfaceActiveBorderColor = kBorderFocused;
|
||||
palette.surfaceCapturedBorderColor = kBorderFocused;
|
||||
palette.toolColor = kSurfacePanel;
|
||||
palette.toolHoveredColor = kSurfaceHover;
|
||||
palette.toolSelectedColor = kSurfaceActive;
|
||||
palette.toolDisabledColor = kSurfaceLower;
|
||||
palette.toolBorderColor = kBorderDefault;
|
||||
palette.textPrimary = kTextPrimary;
|
||||
palette.textSecondary = kTextSecondary;
|
||||
palette.textMuted = kTextMuted;
|
||||
palette.imageTint = UIColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
palette.statusBarPalette = BuildStatusBarPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
UIEditorShellComposePalette BuildShellComposePalette() {
|
||||
UIEditorShellComposePalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.surfaceBorderColor = kBorderDefault;
|
||||
palette.menuBarPalette = BuildMenuBarPalette();
|
||||
palette.toolbarPalette.barColor = kSurfaceBase;
|
||||
palette.toolbarPalette.groupColor = kSurfaceRaised;
|
||||
palette.toolbarPalette.groupBorderColor = kBorderDefault;
|
||||
palette.toolbarPalette.buttonColor = kSurfacePanel;
|
||||
palette.toolbarPalette.buttonBorderColor = kBorderDefault;
|
||||
palette.toolbarPalette.iconColor = UIColor(0.88f, 0.88f, 0.88f, 1.0f);
|
||||
palette.dockHostPalette = BuildDockHostPalette();
|
||||
palette.viewportPalette = BuildViewportSlotPalette();
|
||||
palette.statusBarPalette = BuildStatusBarPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
UIEditorShellInteractionPalette BuildShellInteractionPalette() {
|
||||
UIEditorShellInteractionPalette palette = {};
|
||||
palette.shellPalette = BuildShellComposePalette();
|
||||
palette.popupPalette = BuildMenuPopupPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
Widgets::UIEditorPropertyGridPalette BuildPropertyGridPalette() {
|
||||
Widgets::UIEditorPropertyGridPalette palette = {};
|
||||
palette.surfaceColor = kSurfaceBase;
|
||||
palette.borderColor = kBorderDefault;
|
||||
palette.focusedBorderColor = kBorderFocused;
|
||||
palette.sectionHeaderColor = kSurfaceRaised;
|
||||
palette.sectionHeaderHoverColor = kSurfaceHover;
|
||||
palette.fieldHoverColor = kSurfaceHover;
|
||||
palette.fieldSelectedColor = kSurfaceActive;
|
||||
palette.fieldSelectedFocusedColor = kSurfaceActiveStrong;
|
||||
palette.valueBoxColor = kSurfacePanel;
|
||||
palette.valueBoxHoverColor = kSurfaceHover;
|
||||
palette.valueBoxEditingColor = kSurfaceActive;
|
||||
palette.valueBoxReadOnlyColor = kSurfaceBase;
|
||||
palette.valueBoxBorderColor = kBorderDefault;
|
||||
palette.valueBoxEditingBorderColor = kBorderFocused;
|
||||
palette.disclosureColor = kTextMuted;
|
||||
palette.sectionTextColor = kTextPrimary;
|
||||
palette.labelTextColor = kTextSecondary;
|
||||
palette.valueTextColor = kTextPrimary;
|
||||
palette.readOnlyValueTextColor = UIColor(0.60f, 0.60f, 0.60f, 1.0f);
|
||||
palette.editTagColor = UIColor(0.80f, 0.80f, 0.80f, 1.0f);
|
||||
return palette;
|
||||
}
|
||||
|
||||
#define XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Name, Type) \
|
||||
const Type& ResolveUIEditor##Name() { \
|
||||
return GetDefaultValue<Type>(); \
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(BoolFieldMetrics, Widgets::UIEditorBoolFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(BoolFieldPalette, Widgets::UIEditorBoolFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(NumberFieldMetrics, Widgets::UIEditorNumberFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(NumberFieldPalette, Widgets::UIEditorNumberFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(TextFieldMetrics, Widgets::UIEditorTextFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(TextFieldPalette, Widgets::UIEditorTextFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector2FieldMetrics, Widgets::UIEditorVector2FieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector2FieldPalette, Widgets::UIEditorVector2FieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector3FieldMetrics, Widgets::UIEditorVector3FieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector3FieldPalette, Widgets::UIEditorVector3FieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector4FieldMetrics, Widgets::UIEditorVector4FieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(Vector4FieldPalette, Widgets::UIEditorVector4FieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(EnumFieldMetrics, Widgets::UIEditorEnumFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(EnumFieldPalette, Widgets::UIEditorEnumFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ColorFieldMetrics, Widgets::UIEditorColorFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ColorFieldPalette, Widgets::UIEditorColorFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ObjectFieldMetrics, Widgets::UIEditorObjectFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ObjectFieldPalette, Widgets::UIEditorObjectFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(AssetFieldMetrics, Widgets::UIEditorAssetFieldMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(AssetFieldPalette, Widgets::UIEditorAssetFieldPalette)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(MenuPopupMetrics, Widgets::UIEditorMenuPopupMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ListViewMetrics, Widgets::UIEditorListViewMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ScrollViewMetrics, Widgets::UIEditorScrollViewMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(TabStripMetrics, Widgets::UIEditorTabStripMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(MenuBarMetrics, Widgets::UIEditorMenuBarMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(StatusBarMetrics, Widgets::UIEditorStatusBarMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(PanelFrameMetrics, Widgets::UIEditorPanelFrameMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(DockHostMetrics, Widgets::UIEditorDockHostMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ViewportSlotMetrics, Widgets::UIEditorViewportSlotMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ShellComposeMetrics, UIEditorShellComposeMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(ShellInteractionMetrics, UIEditorShellInteractionMetrics)
|
||||
XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS(PropertyGridMetrics, Widgets::UIEditorPropertyGridMetrics)
|
||||
|
||||
const Widgets::UIEditorMenuPopupPalette& ResolveUIEditorMenuPopupPalette() {
|
||||
static const Widgets::UIEditorMenuPopupPalette palette = BuildMenuPopupPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorListViewPalette& ResolveUIEditorListViewPalette() {
|
||||
static const Widgets::UIEditorListViewPalette palette = BuildListViewPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorTreeViewPalette& ResolveUIEditorTreeViewPalette() {
|
||||
static const Widgets::UIEditorTreeViewPalette palette = BuildTreeViewPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorTreeViewMetrics& ResolveUIEditorTreeViewMetrics() {
|
||||
static const Widgets::UIEditorTreeViewMetrics metrics = BuildTreeViewMetrics();
|
||||
return metrics;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorScrollViewPalette& ResolveUIEditorScrollViewPalette() {
|
||||
static const Widgets::UIEditorScrollViewPalette palette = BuildScrollViewPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorTabStripPalette& ResolveUIEditorTabStripPalette() {
|
||||
static const Widgets::UIEditorTabStripPalette palette = BuildTabStripPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorMenuBarPalette& ResolveUIEditorMenuBarPalette() {
|
||||
static const Widgets::UIEditorMenuBarPalette palette = BuildMenuBarPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorStatusBarPalette& ResolveUIEditorStatusBarPalette() {
|
||||
static const Widgets::UIEditorStatusBarPalette palette = BuildStatusBarPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorPanelFramePalette& ResolveUIEditorPanelFramePalette() {
|
||||
static const Widgets::UIEditorPanelFramePalette palette = BuildPanelFramePalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorDockHostPalette& ResolveUIEditorDockHostPalette() {
|
||||
static const Widgets::UIEditorDockHostPalette palette = BuildDockHostPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorViewportSlotPalette& ResolveUIEditorViewportSlotPalette() {
|
||||
static const Widgets::UIEditorViewportSlotPalette palette = BuildViewportSlotPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const UIEditorShellComposePalette& ResolveUIEditorShellComposePalette() {
|
||||
static const UIEditorShellComposePalette palette = BuildShellComposePalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const UIEditorShellInteractionPalette& ResolveUIEditorShellInteractionPalette() {
|
||||
static const UIEditorShellInteractionPalette palette = BuildShellInteractionPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorPropertyGridPalette& ResolveUIEditorPropertyGridPalette() {
|
||||
static const Widgets::UIEditorPropertyGridPalette palette = BuildPropertyGridPalette();
|
||||
return palette;
|
||||
}
|
||||
|
||||
#undef XCUIEDITOR_DEFINE_DEFAULT_ACCESSORS
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
Reference in New Issue
Block a user