diff --git a/docs/plan/Editor重构3.26.md b/docs/plan/Editor重构3.26.md index 704e5c11..c90fd53c 100644 --- a/docs/plan/Editor重构3.26.md +++ b/docs/plan/Editor重构3.26.md @@ -102,6 +102,7 @@ - 已补 active action route - `MenuBar Edit` 已开始跟随 `Hierarchy / Project` 切换动作目标 - `Rename` 这类依赖 panel 内联状态的动作,开始通过 `EventBus` 请求而不是直接耦合 panel 实现 +- `Edit` 动作解析与菜单绘制 / shortcut 分发已开始从 `MenuBar` 抽成共享 router ### 5. Dock / Layout 层 @@ -159,6 +160,7 @@ - `About` 已接入真实 modal popup - `Exit` 已通过事件驱动关闭 editor - `Edit` 菜单已开始跟随 active action route 在 `Hierarchy / Project` 之间切换 +- `Edit` 菜单与上下文快捷键开始共享同一套 edit action router 仍待完成: @@ -217,6 +219,7 @@ - 工具栏与内容区布局已共享化 - 日志行 hover 表现已统一 - `Clear / Filter` 已接 action 层 +- console filter 状态已从 panel 裸布尔字段收成独立 state object 仍待完成: diff --git a/editor/src/Actions/ActionRouting.h b/editor/src/Actions/ActionRouting.h index 082597df..86b45329 100644 --- a/editor/src/Actions/ActionRouting.h +++ b/editor/src/Actions/ActionRouting.h @@ -15,6 +15,10 @@ inline void ObserveFocusedActionRoute(IEditorContext& context, EditorActionRoute } } +inline void ObserveInactiveActionRoute(IEditorContext& context) { + ObserveFocusedActionRoute(context, EditorActionRoute::None); +} + inline bool IsActionRouteActive(const IEditorContext& context, EditorActionRoute route) { return context.GetActiveActionRoute() == route; } diff --git a/editor/src/Actions/EditActionRouter.h b/editor/src/Actions/EditActionRouter.h new file mode 100644 index 00000000..d3719c24 --- /dev/null +++ b/editor/src/Actions/EditActionRouter.h @@ -0,0 +1,144 @@ +#pragma once + +#include "ActionBinding.h" +#include "EditorActions.h" +#include "Commands/EntityCommands.h" +#include "Commands/ProjectCommands.h" +#include "Core/EventBus.h" +#include "Core/EditorEvents.h" +#include "Core/IEditorContext.h" +#include "UI/UI.h" + +namespace XCEngine { +namespace Editor { +namespace Actions { + +struct EditActionTarget { + EditorActionRoute route = EditorActionRoute::None; + ::XCEngine::Components::GameObject* selectedGameObject = nullptr; + AssetItemPtr selectedAssetItem; +}; + +inline EditActionTarget ResolveEditActionTarget(IEditorContext& context) { + EditActionTarget target; + target.route = context.GetActiveActionRoute(); + target.selectedGameObject = GetSelectedGameObject(context); + target.selectedAssetItem = GetSelectedAssetItem(context); + return target; +} + +inline ActionBinding MakeDisabledPasteAction() { + return MakeAction("Paste", "Ctrl+V", false, false); +} + +inline void HandleProjectEditShortcuts( + IEditorContext& context, + const EditActionTarget& target, + const ShortcutContext& shortcutContext) { + auto& projectManager = context.GetProjectManager(); + + HandleShortcut(MakeNavigateBackAction(projectManager.CanNavigateBack()), shortcutContext, [&]() { + projectManager.NavigateBack(); + }); + HandleShortcut(MakeOpenAssetAction(Commands::CanOpenAsset(target.selectedAssetItem)), shortcutContext, [&]() { + Commands::OpenAsset(context, target.selectedAssetItem); + }); + HandleShortcut(MakeDeleteAssetAction(target.selectedAssetItem != nullptr), shortcutContext, [&]() { + Commands::DeleteAsset(projectManager, projectManager.GetSelectedIndex()); + }); +} + +inline void HandleHierarchyEditShortcuts( + IEditorContext& context, + const EditActionTarget& target, + const ShortcutContext& shortcutContext) { + HandleShortcut(MakeDeleteEntityAction(target.selectedGameObject), shortcutContext, [&]() { + Commands::DeleteEntity(context, target.selectedGameObject->GetID()); + }); + HandleShortcut(MakeRenameEntityAction(target.selectedGameObject), shortcutContext, [&]() { + context.GetEventBus().Publish(EntityRenameRequestedEvent{ target.selectedGameObject->GetID() }); + }); + HandleShortcut(MakeCopyEntityAction(target.selectedGameObject), shortcutContext, [&]() { + Commands::CopyEntity(context, target.selectedGameObject->GetID()); + }); + HandleShortcut(MakePasteEntityAction(context), shortcutContext, [&]() { + Commands::PasteEntity(context, target.selectedGameObject ? target.selectedGameObject->GetID() : 0); + }); + HandleShortcut(MakeDuplicateEntityAction(target.selectedGameObject), shortcutContext, [&]() { + Commands::DuplicateEntity(context, target.selectedGameObject->GetID()); + }); +} + +inline void HandleEditShortcuts(IEditorContext& context, const ShortcutContext& shortcutContext) { + const EditActionTarget target = ResolveEditActionTarget(context); + + switch (target.route) { + case EditorActionRoute::Project: + HandleProjectEditShortcuts(context, target, shortcutContext); + return; + case EditorActionRoute::Hierarchy: + HandleHierarchyEditShortcuts(context, target, shortcutContext); + return; + case EditorActionRoute::None: + return; + } +} + +inline void DrawProjectEditActions(IEditorContext& context, const EditActionTarget& target) { + auto& projectManager = context.GetProjectManager(); + + DrawMenuAction(MakeOpenAssetAction(Commands::CanOpenAsset(target.selectedAssetItem)), [&]() { + Commands::OpenAsset(context, target.selectedAssetItem); + }); + DrawMenuAction(MakeDeleteAssetAction(target.selectedAssetItem != nullptr), [&]() { + Commands::DeleteAsset(projectManager, projectManager.GetSelectedIndex()); + }); + DrawMenuSeparator(); + DrawMenuAction(MakeNavigateBackAction(projectManager.CanNavigateBack()), [&]() { + projectManager.NavigateBack(); + }); +} + +inline void DrawHierarchyEditActions(IEditorContext& context, const EditActionTarget& target) { + const bool hierarchyRouteActive = target.route == EditorActionRoute::Hierarchy; + const ::XCEngine::Components::GameObject* activeObject = + hierarchyRouteActive ? target.selectedGameObject : nullptr; + + DrawMenuAction(MakeCutAction(false), []() {}); + DrawMenuAction(MakeCopyEntityAction(const_cast<::XCEngine::Components::GameObject*>(activeObject)), [&]() { + Commands::CopyEntity(context, target.selectedGameObject->GetID()); + }); + DrawMenuAction( + hierarchyRouteActive ? MakePasteEntityAction(context) : MakeDisabledPasteAction(), + [&]() { + Commands::PasteEntity(context, target.selectedGameObject ? target.selectedGameObject->GetID() : 0); + }); + DrawMenuAction(MakeDuplicateEntityAction(const_cast<::XCEngine::Components::GameObject*>(activeObject)), [&]() { + Commands::DuplicateEntity(context, target.selectedGameObject->GetID()); + }); + DrawMenuAction(MakeDeleteEntityAction(const_cast<::XCEngine::Components::GameObject*>(activeObject)), [&]() { + Commands::DeleteEntity(context, target.selectedGameObject->GetID()); + }); + DrawMenuAction(MakeRenameEntityAction(const_cast<::XCEngine::Components::GameObject*>(activeObject)), [&]() { + context.GetEventBus().Publish(EntityRenameRequestedEvent{ target.selectedGameObject->GetID() }); + }); +} + +inline void DrawEditActions(IEditorContext& context) { + const EditActionTarget target = ResolveEditActionTarget(context); + + DrawMenuAction(MakeUndoAction(context), [&]() { context.GetUndoManager().Undo(); }); + DrawMenuAction(MakeRedoAction(context), [&]() { context.GetUndoManager().Redo(); }); + DrawMenuSeparator(); + + if (target.route == EditorActionRoute::Project) { + DrawProjectEditActions(context, target); + return; + } + + DrawHierarchyEditActions(context, target); +} + +} // namespace Actions +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/UI/ConsoleFilterState.h b/editor/src/UI/ConsoleFilterState.h new file mode 100644 index 00000000..7b084316 --- /dev/null +++ b/editor/src/UI/ConsoleFilterState.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Editor { +namespace UI { + +class ConsoleFilterState { +public: + bool& ShowInfo() { + return m_showInfo; + } + + bool& ShowWarning() { + return m_showWarning; + } + + bool& ShowError() { + return m_showError; + } + + bool Allows(::XCEngine::Debug::LogLevel level) const { + switch (level) { + case ::XCEngine::Debug::LogLevel::Verbose: + case ::XCEngine::Debug::LogLevel::Debug: + case ::XCEngine::Debug::LogLevel::Info: + return m_showInfo; + case ::XCEngine::Debug::LogLevel::Warning: + return m_showWarning; + case ::XCEngine::Debug::LogLevel::Error: + case ::XCEngine::Debug::LogLevel::Fatal: + return m_showError; + } + + return false; + } + +private: + bool m_showInfo = true; + bool m_showWarning = true; + bool m_showError = true; +}; + +} // namespace UI +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/UI/UI.h b/editor/src/UI/UI.h index 47a845a7..de7258e6 100644 --- a/editor/src/UI/UI.h +++ b/editor/src/UI/UI.h @@ -1,6 +1,7 @@ #pragma once #include "BaseTheme.h" +#include "ConsoleFilterState.h" #include "Core.h" #include "DockHostStyle.h" #include "PanelChrome.h" diff --git a/editor/src/panels/ConsolePanel.cpp b/editor/src/panels/ConsolePanel.cpp index 8d5436b2..55506764 100644 --- a/editor/src/panels/ConsolePanel.cpp +++ b/editor/src/panels/ConsolePanel.cpp @@ -1,3 +1,4 @@ +#include "Actions/ActionRouting.h" #include "Actions/EditorActions.h" #include "ConsolePanel.h" #include "Core/EditorConsoleSink.h" @@ -18,6 +19,8 @@ void ConsolePanel::Render() { return; } + Actions::ObserveInactiveActionRoute(*m_context); + auto* sink = Debug::EditorConsoleSink::GetInstance(); { @@ -30,11 +33,11 @@ void ConsolePanel::Render() { UI::DrawToolbarLabel("Filter"); ImGui::SameLine(); - Actions::DrawToolbarToggleAction(Actions::MakeConsoleInfoFilterAction(m_showInfo), m_showInfo); + Actions::DrawToolbarToggleAction(Actions::MakeConsoleInfoFilterAction(m_filterState.ShowInfo()), m_filterState.ShowInfo()); ImGui::SameLine(); - Actions::DrawToolbarToggleAction(Actions::MakeConsoleWarningFilterAction(m_showWarning), m_showWarning); + Actions::DrawToolbarToggleAction(Actions::MakeConsoleWarningFilterAction(m_filterState.ShowWarning()), m_filterState.ShowWarning()); ImGui::SameLine(); - Actions::DrawToolbarToggleAction(Actions::MakeConsoleErrorFilterAction(m_showError), m_showError); + Actions::DrawToolbarToggleAction(Actions::MakeConsoleErrorFilterAction(m_filterState.ShowError()), m_filterState.ShowError()); } } @@ -46,23 +49,7 @@ void ConsolePanel::Render() { const auto logs = sink->GetLogs(); size_t logIndex = 0; for (const auto& log : logs) { - bool shouldShow = false; - switch (log.level) { - case ::XCEngine::Debug::LogLevel::Info: - case ::XCEngine::Debug::LogLevel::Verbose: - case ::XCEngine::Debug::LogLevel::Debug: - shouldShow = m_showInfo; - break; - case ::XCEngine::Debug::LogLevel::Warning: - shouldShow = m_showWarning; - break; - case ::XCEngine::Debug::LogLevel::Error: - case ::XCEngine::Debug::LogLevel::Fatal: - shouldShow = m_showError; - break; - } - - if (!shouldShow) { + if (!m_filterState.Allows(log.level)) { continue; } diff --git a/editor/src/panels/ConsolePanel.h b/editor/src/panels/ConsolePanel.h index 7b351f19..852284aa 100644 --- a/editor/src/panels/ConsolePanel.h +++ b/editor/src/panels/ConsolePanel.h @@ -1,6 +1,7 @@ #pragma once #include "Panel.h" +#include "UI/ConsoleFilterState.h" namespace XCEngine { namespace Editor { @@ -11,11 +12,8 @@ public: void Render() override; private: - bool m_showInfo = true; - bool m_showWarning = true; - bool m_showError = true; - bool m_scrollToBottom = false; + UI::ConsoleFilterState m_filterState; }; } -} \ No newline at end of file +} diff --git a/editor/src/panels/GameViewPanel.cpp b/editor/src/panels/GameViewPanel.cpp index e4f92b3b..df1359bb 100644 --- a/editor/src/panels/GameViewPanel.cpp +++ b/editor/src/panels/GameViewPanel.cpp @@ -1,3 +1,4 @@ +#include "Actions/ActionRouting.h" #include "GameViewPanel.h" #include "UI/UI.h" #include @@ -9,7 +10,11 @@ GameViewPanel::GameViewPanel() : Panel("Game") {} void GameViewPanel::Render() { UI::PanelWindowScope panel(m_name.c_str()); - (void)panel; + if (!panel.IsOpen()) { + return; + } + + Actions::ObserveInactiveActionRoute(*m_context); } } diff --git a/editor/src/panels/HierarchyPanel.cpp b/editor/src/panels/HierarchyPanel.cpp index a7c6515e..10f0d8cd 100644 --- a/editor/src/panels/HierarchyPanel.cpp +++ b/editor/src/panels/HierarchyPanel.cpp @@ -63,9 +63,6 @@ void HierarchyPanel::Render() { Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Hierarchy); RenderSearchBar(); - - HandleKeyboardShortcuts(); - std::string filter = m_searchBuffer; UI::PanelContentScope content("EntityList"); @@ -315,29 +312,6 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObje } } -void HierarchyPanel::HandleKeyboardShortcuts() { - auto& sceneManager = m_context->GetSceneManager(); - auto& selectionManager = m_context->GetSelectionManager(); - ::XCEngine::Components::GameObject* selectedGameObject = sceneManager.GetEntity(selectionManager.GetSelectedEntity()); - const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext(); - - Actions::HandleShortcut(Actions::MakeDeleteEntityAction(selectedGameObject), shortcutContext, [&]() { - Commands::DeleteEntity(*m_context, selectedGameObject->GetID()); - }); - Actions::HandleShortcut(Actions::MakeRenameEntityAction(selectedGameObject), shortcutContext, [&]() { - BeginRename(selectedGameObject); - }); - Actions::HandleShortcut(Actions::MakeCopyEntityAction(selectedGameObject), shortcutContext, [&]() { - Commands::CopyEntity(*m_context, selectedGameObject->GetID()); - }); - Actions::HandleShortcut(Actions::MakePasteEntityAction(*m_context), shortcutContext, [&]() { - Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0); - }); - Actions::HandleShortcut(Actions::MakeDuplicateEntityAction(selectedGameObject), shortcutContext, [&]() { - Commands::DuplicateEntity(*m_context, selectedGameObject->GetID()); - }); -} - bool HierarchyPanel::PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter) { if (!gameObject) return false; diff --git a/editor/src/panels/HierarchyPanel.h b/editor/src/panels/HierarchyPanel.h index 505f3b18..5085e6e8 100644 --- a/editor/src/panels/HierarchyPanel.h +++ b/editor/src/panels/HierarchyPanel.h @@ -29,7 +29,6 @@ private: void CommitRename(); void CancelRename(); void HandleDragDrop(::XCEngine::Components::GameObject* gameObject); - void HandleKeyboardShortcuts(); bool PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter); void SortEntities(std::vector<::XCEngine::Components::GameObject*>& entities); diff --git a/editor/src/panels/InspectorPanel.cpp b/editor/src/panels/InspectorPanel.cpp index c8b2ff1f..99a1e6ff 100644 --- a/editor/src/panels/InspectorPanel.cpp +++ b/editor/src/panels/InspectorPanel.cpp @@ -1,3 +1,4 @@ +#include "Actions/ActionRouting.h" #include "Actions/EditorActions.h" #include "Commands/ComponentCommands.h" #include "InspectorPanel.h" @@ -36,6 +37,8 @@ void InspectorPanel::Render() { if (!panel.IsOpen()) { return; } + + Actions::ObserveInactiveActionRoute(*m_context); if (!m_selectionHandlerId && m_context) { m_selectionHandlerId = m_context->GetEventBus().Subscribe( diff --git a/editor/src/panels/MenuBar.cpp b/editor/src/panels/MenuBar.cpp index 43b00486..537e4e71 100644 --- a/editor/src/panels/MenuBar.cpp +++ b/editor/src/panels/MenuBar.cpp @@ -1,16 +1,12 @@ #include "Actions/ActionRouting.h" +#include "Actions/EditActionRouter.h" #include "Actions/EditorActions.h" -#include "Commands/EntityCommands.h" -#include "Commands/ProjectCommands.h" #include "Commands/SceneCommands.h" #include "MenuBar.h" #include "Core/EditorEvents.h" #include "Core/EventBus.h" #include "Core/IEditorContext.h" -#include "Core/IProjectManager.h" #include "Core/ISceneManager.h" -#include "Core/IUndoManager.h" -#include "Core/ISelectionManager.h" #include "UI/UI.h" #include #include @@ -48,6 +44,7 @@ void MenuBar::HandleShortcuts() { Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), shortcutContext, [&]() { Commands::SaveSceneAsWithDialog(*m_context); }); Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Undo(); }); Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Redo(); }); + Actions::HandleEditShortcuts(*m_context, shortcutContext); } void MenuBar::ShowFileMenu() { @@ -64,46 +61,8 @@ void MenuBar::ShowFileMenu() { } void MenuBar::ShowEditMenu() { - ::XCEngine::Components::GameObject* selectedGameObject = Actions::GetSelectedGameObject(*m_context); - const AssetItemPtr selectedAssetItem = Actions::GetSelectedAssetItem(*m_context); - auto& projectManager = m_context->GetProjectManager(); - const EditorActionRoute actionRoute = m_context->GetActiveActionRoute(); - UI::DrawMenuScope("Edit", [&]() { - Actions::DrawMenuAction(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); }); - Actions::DrawMenuAction(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); }); - Actions::DrawMenuSeparator(); - - if (actionRoute == EditorActionRoute::Project) { - Actions::DrawMenuAction(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedAssetItem)), [&]() { - Commands::OpenAsset(*m_context, selectedAssetItem); - }); - Actions::DrawMenuAction(Actions::MakeDeleteAssetAction(selectedAssetItem != nullptr), [&]() { - Commands::DeleteAsset(projectManager, projectManager.GetSelectedIndex()); - }); - Actions::DrawMenuSeparator(); - Actions::DrawMenuAction(Actions::MakeNavigateBackAction(projectManager.CanNavigateBack()), [&]() { - projectManager.NavigateBack(); - }); - return; - } - - Actions::DrawMenuAction(Actions::MakeCutAction(false), []() {}); - Actions::DrawMenuAction(Actions::MakeCopyEntityAction(selectedGameObject), [&]() { - Commands::CopyEntity(*m_context, selectedGameObject->GetID()); - }); - Actions::DrawMenuAction(Actions::MakePasteEntityAction(*m_context), [&]() { - Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0); - }); - Actions::DrawMenuAction(Actions::MakeDuplicateEntityAction(selectedGameObject), [&]() { - Commands::DuplicateEntity(*m_context, selectedGameObject->GetID()); - }); - Actions::DrawMenuAction(Actions::MakeDeleteEntityAction(selectedGameObject), [&]() { - Commands::DeleteEntity(*m_context, selectedGameObject->GetID()); - }); - Actions::DrawMenuAction(Actions::MakeRenameEntityAction(selectedGameObject), [&]() { - m_context->GetEventBus().Publish(EntityRenameRequestedEvent{ selectedGameObject->GetID() }); - }); + Actions::DrawEditActions(*m_context); }); } diff --git a/editor/src/panels/ProjectPanel.cpp b/editor/src/panels/ProjectPanel.cpp index 46cdeb4f..0ce4b1c6 100644 --- a/editor/src/panels/ProjectPanel.cpp +++ b/editor/src/panels/ProjectPanel.cpp @@ -35,7 +35,6 @@ void ProjectPanel::Render() { } Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Project); - HandleKeyboardShortcuts(); auto& manager = m_context->GetProjectManager(); @@ -150,30 +149,6 @@ void ProjectPanel::Render() { } } -AssetItemPtr ProjectPanel::GetSelectedItem() const { - return m_context ? Actions::GetSelectedAssetItem(*m_context) : nullptr; -} - -void ProjectPanel::HandleKeyboardShortcuts() { - if (!m_context) { - return; - } - - auto& manager = m_context->GetProjectManager(); - const AssetItemPtr selectedItem = GetSelectedItem(); - const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext(); - - Actions::HandleShortcut(Actions::MakeNavigateBackAction(manager.CanNavigateBack()), shortcutContext, [&]() { - manager.NavigateBack(); - }); - Actions::HandleShortcut(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedItem)), shortcutContext, [&]() { - Commands::OpenAsset(*m_context, selectedItem); - }); - Actions::HandleShortcut(Actions::MakeDeleteAssetAction(selectedItem != nullptr), shortcutContext, [&]() { - Commands::DeleteAsset(manager, manager.GetSelectedIndex()); - }); -} - void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) { auto& manager = m_context->GetProjectManager(); bool isSelected = (manager.GetSelectedIndex() == index); diff --git a/editor/src/panels/ProjectPanel.h b/editor/src/panels/ProjectPanel.h index 828634d6..3e55b81b 100644 --- a/editor/src/panels/ProjectPanel.h +++ b/editor/src/panels/ProjectPanel.h @@ -14,8 +14,6 @@ public: void Initialize(const std::string& projectPath); private: - AssetItemPtr GetSelectedItem() const; - void HandleKeyboardShortcuts(); void RenderAssetItem(const AssetItemPtr& item, int index); char m_searchBuffer[256] = ""; diff --git a/editor/src/panels/SceneViewPanel.cpp b/editor/src/panels/SceneViewPanel.cpp index a4f5e79a..2457f05b 100644 --- a/editor/src/panels/SceneViewPanel.cpp +++ b/editor/src/panels/SceneViewPanel.cpp @@ -1,3 +1,4 @@ +#include "Actions/ActionRouting.h" #include "SceneViewPanel.h" #include "UI/UI.h" #include @@ -9,7 +10,11 @@ SceneViewPanel::SceneViewPanel() : Panel("Scene") {} void SceneViewPanel::Render() { UI::PanelWindowScope panel(m_name.c_str()); - (void)panel; + if (!panel.IsOpen()) { + return; + } + + Actions::ObserveInactiveActionRoute(*m_context); } }