From 7ad4bfbb1cecfbc74b9ed6c1e6f44202a6f8dac4 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 12 Apr 2026 01:49:08 +0800 Subject: [PATCH] Extract new editor host command session bridge --- new_editor/CMakeLists.txt | 2 + new_editor/app/Application.cpp | 70 +------- new_editor/app/Application.h | 7 +- .../ProductEditorHostCommandBridge.cpp | 156 ++++++++++++++++++ .../Commands/ProductEditorHostCommandBridge.h | 36 ++++ new_editor/app/Core/ProductEditorContext.cpp | 23 ++- new_editor/app/Core/ProductEditorContext.h | 20 +-- new_editor/app/Core/ProductEditorSession.cpp | 67 ++++++++ new_editor/app/Core/ProductEditorSession.h | 45 +++++ .../app/Workspace/ProductEditorWorkspace.cpp | 2 + 10 files changed, 344 insertions(+), 84 deletions(-) create mode 100644 new_editor/app/Commands/ProductEditorHostCommandBridge.cpp create mode 100644 new_editor/app/Commands/ProductEditorHostCommandBridge.h create mode 100644 new_editor/app/Core/ProductEditorSession.cpp create mode 100644 new_editor/app/Core/ProductEditorSession.h diff --git a/new_editor/CMakeLists.txt b/new_editor/CMakeLists.txt index 1fca5c3f..083c3af1 100644 --- a/new_editor/CMakeLists.txt +++ b/new_editor/CMakeLists.txt @@ -147,7 +147,9 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) add_executable(XCUIEditorApp WIN32 app/main.cpp app/Application.cpp + app/Commands/ProductEditorHostCommandBridge.cpp app/Core/ProductEditorContext.cpp + app/Core/ProductEditorSession.cpp app/Icons/ProductBuiltInIcons.cpp app/Panels/ProductHierarchyPanel.cpp app/Panels/ProductProjectPanel.cpp diff --git a/new_editor/app/Application.cpp b/new_editor/app/Application.cpp index 58654718..06bb1af7 100644 --- a/new_editor/app/Application.cpp +++ b/new_editor/app/Application.cpp @@ -377,7 +377,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { InitializeUIEditorRuntimeTrace(logRoot); SetUnhandledExceptionFilter(&Application::HandleUnhandledException); LogRuntimeTrace("app", "initialize begin"); - if (!m_editorContext.Initialize(repoRoot, *this)) { + if (!m_editorContext.Initialize(repoRoot)) { LogRuntimeTrace( "app", "shell asset validation failed: " + m_editorContext.GetValidationMessage()); @@ -417,6 +417,11 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { m_windowDpi = QueryWindowDpi(m_hwnd); m_dpiScale = GetDpiScale(); m_renderer.SetDpiScale(m_dpiScale); + m_editorContext.SetExitRequestHandler([this]() { + if (m_hwnd != nullptr) { + PostMessageW(m_hwnd, WM_CLOSE, 0, 0); + } + }); std::ostringstream dpiTrace = {}; dpiTrace << "initial dpi=" << m_windowDpi << " scale=" << m_dpiScale; @@ -811,69 +816,6 @@ LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionI return EXCEPTION_EXECUTE_HANDLER; } -UIEditorHostCommandEvaluationResult Application::EvaluateHostCommand( - std::string_view commandId) const { - UIEditorHostCommandEvaluationResult result = {}; - if (commandId == "file.exit") { - result.executable = true; - result.message = "Exit command is ready."; - return result; - } - - if (commandId == "help.about") { - result.executable = true; - result.message = "About placeholder is ready."; - return result; - } - - if (commandId.rfind("file.", 0u) == 0u) { - result.message = "Document command bridge is not attached yet."; - return result; - } - if (commandId.rfind("edit.", 0u) == 0u) { - result.message = "Edit route bridge is not attached yet."; - return result; - } - if (commandId.rfind("assets.", 0u) == 0u) { - result.message = "Asset pipeline bridge is not attached yet."; - return result; - } - if (commandId.rfind("run.", 0u) == 0u) { - result.message = "Runtime bridge is not attached yet."; - return result; - } - if (commandId.rfind("scripts.", 0u) == 0u) { - result.message = "Script pipeline bridge is not attached yet."; - return result; - } - - result.message = "Host command is not attached yet."; - return result; -} - -UIEditorHostCommandDispatchResult Application::DispatchHostCommand( - std::string_view commandId) { - UIEditorHostCommandDispatchResult result = {}; - if (commandId == "file.exit") { - result.commandExecuted = true; - result.message = "Exit requested."; - if (m_hwnd != nullptr) { - PostMessageW(m_hwnd, WM_CLOSE, 0, 0); - } - return result; - } - - if (commandId == "help.about") { - result.commandExecuted = true; - result.message = "About dialog will be wired after modal layer lands."; - m_editorContext.SetStatus("About", result.message); - return result; - } - - result.message = "Host command dispatch rejected."; - return result; -} - LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_NCCREATE) { TryEnableNonClientDpiScaling(hwnd); diff --git a/new_editor/app/Application.h b/new_editor/app/Application.h index 46367fa9..0dd45634 100644 --- a/new_editor/app/Application.h +++ b/new_editor/app/Application.h @@ -24,17 +24,12 @@ namespace XCEngine::UI::Editor { -class Application : public UIEditorHostCommandHandler { +class Application { public: Application() = default; int Run(HINSTANCE hInstance, int nCmdShow); - UIEditorHostCommandEvaluationResult EvaluateHostCommand( - std::string_view commandId) const override; - UIEditorHostCommandDispatchResult DispatchHostCommand( - std::string_view commandId) override; - private: static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); diff --git a/new_editor/app/Commands/ProductEditorHostCommandBridge.cpp b/new_editor/app/Commands/ProductEditorHostCommandBridge.cpp new file mode 100644 index 00000000..362b9e03 --- /dev/null +++ b/new_editor/app/Commands/ProductEditorHostCommandBridge.cpp @@ -0,0 +1,156 @@ +#include "ProductEditorHostCommandBridge.h" + +#include + +namespace XCEngine::UI::Editor::App { + +void ProductEditorHostCommandBridge::BindSession(ProductEditorSession& session) { + m_session = &session; +} + +void ProductEditorHostCommandBridge::SetExitRequestHandler(std::function handler) { + m_requestExit = std::move(handler); +} + +UIEditorHostCommandEvaluationResult ProductEditorHostCommandBridge::EvaluateHostCommand( + std::string_view commandId) const { + UIEditorHostCommandEvaluationResult result = {}; + + if (commandId == "file.exit") { + result.executable = true; + result.message = "Exit command is ready."; + return result; + } + + if (commandId == "help.about") { + result.executable = true; + result.message = "About placeholder is ready."; + return result; + } + + if (commandId.rfind("edit.", 0u) == 0u) { + return EvaluateEditCommand(commandId); + } + + if (commandId.rfind("file.", 0u) == 0u) { + return BuildDisabledResult("Document command bridge is not attached yet."); + } + if (commandId.rfind("assets.", 0u) == 0u) { + return BuildDisabledResult("Asset pipeline bridge is not attached yet."); + } + if (commandId.rfind("run.", 0u) == 0u) { + return BuildDisabledResult("Runtime bridge is not attached yet."); + } + if (commandId.rfind("scripts.", 0u) == 0u) { + return BuildDisabledResult("Script pipeline bridge is not attached yet."); + } + + return BuildDisabledResult("Host command is not attached yet."); +} + +UIEditorHostCommandDispatchResult ProductEditorHostCommandBridge::DispatchHostCommand( + std::string_view commandId) { + UIEditorHostCommandDispatchResult result = {}; + + if (commandId == "file.exit") { + result.commandExecuted = true; + result.message = "Exit requested."; + if (m_requestExit) { + m_requestExit(); + } + return result; + } + + if (commandId == "help.about") { + result.commandExecuted = true; + result.message = "About dialog will be wired after modal layer lands."; + return result; + } + + if (commandId.rfind("edit.", 0u) == 0u) { + return DispatchEditCommand(commandId); + } + + result.message = EvaluateHostCommand(commandId).message; + return result; +} + +UIEditorHostCommandEvaluationResult ProductEditorHostCommandBridge::BuildDisabledResult( + std::string_view message) const { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandEvaluationResult ProductEditorHostCommandBridge::EvaluateEditCommand( + std::string_view commandId) const { + if (m_session == nullptr) { + return BuildDisabledResult("Editor session is not attached yet."); + } + + switch (m_session->activeRoute) { + case ProductEditorActionRoute::Hierarchy: + if (SupportsHierarchyEditCommands(commandId)) { + return UIEditorHostCommandEvaluationResult{ + true, + "Hierarchy edit route placeholder is active." + }; + } + return BuildDisabledResult("Current hierarchy route does not expose this command yet."); + case ProductEditorActionRoute::Project: + if (SupportsProjectEditCommands(commandId)) { + return UIEditorHostCommandEvaluationResult{ + true, + "Project edit route placeholder is active." + }; + } + return BuildDisabledResult("Current project route does not expose this command yet."); + case ProductEditorActionRoute::None: + return BuildDisabledResult("No active edit route."); + default: + return BuildDisabledResult("Current panel does not expose edit commands yet."); + } +} + +UIEditorHostCommandDispatchResult ProductEditorHostCommandBridge::DispatchEditCommand( + std::string_view commandId) { + UIEditorHostCommandDispatchResult result = {}; + const UIEditorHostCommandEvaluationResult evaluation = EvaluateEditCommand(commandId); + if (!evaluation.executable) { + result.message = evaluation.message; + return result; + } + + result.commandExecuted = true; + switch (m_session != nullptr ? m_session->activeRoute : ProductEditorActionRoute::None) { + case ProductEditorActionRoute::Hierarchy: + result.message = "Hierarchy edit command route reached."; + break; + case ProductEditorActionRoute::Project: + result.message = "Project edit command route reached."; + break; + default: + result.message = "Edit command route reached."; + break; + } + return result; +} + +bool ProductEditorHostCommandBridge::SupportsHierarchyEditCommands( + std::string_view commandId) const { + return commandId == "edit.cut" || + commandId == "edit.copy" || + commandId == "edit.paste" || + commandId == "edit.duplicate" || + commandId == "edit.delete" || + commandId == "edit.rename"; +} + +bool ProductEditorHostCommandBridge::SupportsProjectEditCommands( + std::string_view commandId) const { + return commandId == "edit.delete" || + commandId == "edit.rename"; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Commands/ProductEditorHostCommandBridge.h b/new_editor/app/Commands/ProductEditorHostCommandBridge.h new file mode 100644 index 00000000..7271fec4 --- /dev/null +++ b/new_editor/app/Commands/ProductEditorHostCommandBridge.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Core/ProductEditorSession.h" + +#include + +#include +#include + +namespace XCEngine::UI::Editor::App { + +class ProductEditorHostCommandBridge : public UIEditorHostCommandHandler { +public: + void BindSession(ProductEditorSession& session); + void SetExitRequestHandler(std::function handler); + + UIEditorHostCommandEvaluationResult EvaluateHostCommand( + std::string_view commandId) const override; + UIEditorHostCommandDispatchResult DispatchHostCommand( + std::string_view commandId) override; + +private: + UIEditorHostCommandEvaluationResult BuildDisabledResult( + std::string_view message) const; + UIEditorHostCommandEvaluationResult EvaluateEditCommand( + std::string_view commandId) const; + UIEditorHostCommandDispatchResult DispatchEditCommand( + std::string_view commandId); + bool SupportsHierarchyEditCommands(std::string_view commandId) const; + bool SupportsProjectEditCommands(std::string_view commandId) const; + + ProductEditorSession* m_session = nullptr; + std::function m_requestExit = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Core/ProductEditorContext.cpp b/new_editor/app/Core/ProductEditorContext.cpp index 9dc2420a..d136a110 100644 --- a/new_editor/app/Core/ProductEditorContext.cpp +++ b/new_editor/app/Core/ProductEditorContext.cpp @@ -28,21 +28,24 @@ std::string ComposeStatusText( } // namespace -bool ProductEditorContext::Initialize( - const std::filesystem::path& repoRoot, - UIEditorHostCommandHandler& hostCommandHandler) { +bool ProductEditorContext::Initialize(const std::filesystem::path& repoRoot) { m_shellAsset = BuildProductShellAsset(repoRoot); m_shellValidation = ValidateEditorShellAsset(m_shellAsset); if (!m_shellValidation.IsValid()) { return false; } + m_session = {}; + m_session.repoRoot = repoRoot; + m_session.projectRoot = (repoRoot / "project").lexically_normal(); m_workspaceController = UIEditorWorkspaceController( m_shellAsset.panelRegistry, m_shellAsset.workspace, m_shellAsset.workspaceSession); + SyncSessionFromWorkspace(); + m_hostCommandBridge.BindSession(m_session); m_shortcutManager = BuildEditorShellShortcutManager(m_shellAsset); - m_shortcutManager.SetHostCommandHandler(&hostCommandHandler); + m_shortcutManager.SetHostCommandHandler(&m_hostCommandBridge); m_shellServices = {}; m_shellServices.commandDispatcher = &m_shortcutManager.GetCommandDispatcher(); m_shellServices.shortcutManager = &m_shortcutManager; @@ -55,6 +58,14 @@ void ProductEditorContext::AttachTextMeasurer( m_shellServices.textMeasurer = &textMeasurer; } +void ProductEditorContext::SetExitRequestHandler(std::function handler) { + m_hostCommandBridge.SetExitRequestHandler(std::move(handler)); +} + +void ProductEditorContext::SyncSessionFromWorkspace() { + SyncProductEditorSessionFromWorkspace(m_session, m_workspaceController); +} + bool ProductEditorContext::IsValid() const { return m_shellValidation.IsValid(); } @@ -67,6 +78,10 @@ const EditorShellAsset& ProductEditorContext::GetShellAsset() const { return m_shellAsset; } +const ProductEditorSession& ProductEditorContext::GetSession() const { + return m_session; +} + UIEditorWorkspaceController& ProductEditorContext::GetWorkspaceController() { return m_workspaceController; } diff --git a/new_editor/app/Core/ProductEditorContext.h b/new_editor/app/Core/ProductEditorContext.h index f80794ef..31d1bd80 100644 --- a/new_editor/app/Core/ProductEditorContext.h +++ b/new_editor/app/Core/ProductEditorContext.h @@ -1,33 +1,31 @@ #pragma once +#include "Commands/ProductEditorHostCommandBridge.h" +#include "Core/ProductEditorSession.h" + #include #include #include #include #include +#include #include #include -namespace XCEngine::UI::Editor { - -class UIEditorHostCommandHandler; -struct UIEditorTextMeasurer; - -} // namespace XCEngine::UI::Editor - namespace XCEngine::UI::Editor::App { class ProductEditorContext { public: - bool Initialize( - const std::filesystem::path& repoRoot, - UIEditorHostCommandHandler& hostCommandHandler); + bool Initialize(const std::filesystem::path& repoRoot); void AttachTextMeasurer(const UIEditorTextMeasurer& textMeasurer); + void SetExitRequestHandler(std::function handler); + void SyncSessionFromWorkspace(); bool IsValid() const; const std::string& GetValidationMessage() const; const EditorShellAsset& GetShellAsset() const; + const ProductEditorSession& GetSession() const; UIEditorWorkspaceController& GetWorkspaceController(); const UIEditorWorkspaceController& GetWorkspaceController() const; @@ -48,6 +46,8 @@ private: UIEditorWorkspaceController m_workspaceController = {}; UIEditorShortcutManager m_shortcutManager = {}; UIEditorShellInteractionServices m_shellServices = {}; + ProductEditorSession m_session = {}; + ProductEditorHostCommandBridge m_hostCommandBridge = {}; std::string m_lastStatus = {}; std::string m_lastMessage = {}; }; diff --git a/new_editor/app/Core/ProductEditorSession.cpp b/new_editor/app/Core/ProductEditorSession.cpp new file mode 100644 index 00000000..9d8b7db7 --- /dev/null +++ b/new_editor/app/Core/ProductEditorSession.cpp @@ -0,0 +1,67 @@ +#include "ProductEditorSession.h" + +namespace XCEngine::UI::Editor::App { + +std::string_view GetProductEditorRuntimeModeName(ProductEditorRuntimeMode mode) { + switch (mode) { + case ProductEditorRuntimeMode::Edit: + return "Edit"; + case ProductEditorRuntimeMode::Play: + return "Play"; + case ProductEditorRuntimeMode::Paused: + return "Paused"; + default: + return "Unknown"; + } +} + +std::string_view GetProductEditorActionRouteName(ProductEditorActionRoute route) { + switch (route) { + case ProductEditorActionRoute::Hierarchy: + return "Hierarchy"; + case ProductEditorActionRoute::Project: + return "Project"; + case ProductEditorActionRoute::Inspector: + return "Inspector"; + case ProductEditorActionRoute::Console: + return "Console"; + case ProductEditorActionRoute::Scene: + return "Scene"; + case ProductEditorActionRoute::Game: + return "Game"; + case ProductEditorActionRoute::None: + default: + return "None"; + } +} + +ProductEditorActionRoute ResolveProductEditorActionRoute(std::string_view panelId) { + if (panelId == "hierarchy") { + return ProductEditorActionRoute::Hierarchy; + } + if (panelId == "project") { + return ProductEditorActionRoute::Project; + } + if (panelId == "inspector") { + return ProductEditorActionRoute::Inspector; + } + if (panelId == "console") { + return ProductEditorActionRoute::Console; + } + if (panelId == "scene") { + return ProductEditorActionRoute::Scene; + } + if (panelId == "game") { + return ProductEditorActionRoute::Game; + } + return ProductEditorActionRoute::None; +} + +void SyncProductEditorSessionFromWorkspace( + ProductEditorSession& session, + const UIEditorWorkspaceController& controller) { + session.activePanelId = controller.GetWorkspace().activePanelId; + session.activeRoute = ResolveProductEditorActionRoute(session.activePanelId); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Core/ProductEditorSession.h b/new_editor/app/Core/ProductEditorSession.h new file mode 100644 index 00000000..e7fea5e8 --- /dev/null +++ b/new_editor/app/Core/ProductEditorSession.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +enum class ProductEditorRuntimeMode : std::uint8_t { + Edit = 0, + Play, + Paused +}; + +enum class ProductEditorActionRoute : std::uint8_t { + None = 0, + Hierarchy, + Project, + Inspector, + Console, + Scene, + Game +}; + +struct ProductEditorSession { + std::filesystem::path repoRoot = {}; + std::filesystem::path projectRoot = {}; + std::string activePanelId = {}; + ProductEditorRuntimeMode runtimeMode = ProductEditorRuntimeMode::Edit; + ProductEditorActionRoute activeRoute = ProductEditorActionRoute::None; +}; + +std::string_view GetProductEditorRuntimeModeName(ProductEditorRuntimeMode mode); +std::string_view GetProductEditorActionRouteName(ProductEditorActionRoute route); + +ProductEditorActionRoute ResolveProductEditorActionRoute(std::string_view panelId); + +void SyncProductEditorSessionFromWorkspace( + ProductEditorSession& session, + const UIEditorWorkspaceController& controller); + +} // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Workspace/ProductEditorWorkspace.cpp b/new_editor/app/Workspace/ProductEditorWorkspace.cpp index f7946fba..c3f3f1bb 100644 --- a/new_editor/app/Workspace/ProductEditorWorkspace.cpp +++ b/new_editor/app/Workspace/ProductEditorWorkspace.cpp @@ -99,6 +99,7 @@ void ProductEditorWorkspace::Update( const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, std::string_view captureText) { const auto& metrics = ResolveUIEditorShellInteractionMetrics(); + context.SyncSessionFromWorkspace(); const UIEditorShellInteractionDefinition definition = context.BuildShellDefinition(captureText); const std::vector hostedContentEvents = inputEvents; @@ -115,6 +116,7 @@ void ProductEditorWorkspace::Update( shellEvents, context.GetShellServices(), metrics); + context.SyncSessionFromWorkspace(); context.UpdateStatusFromShellResult(m_shellFrame.result); const std::string& activePanelId =