From 3cf4adf181074485549d69e559bc01d770b26448 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 7 Apr 2026 12:47:16 +0800 Subject: [PATCH] Unify editor shell asset contracts --- new_editor/app/Application.h | 3 +- new_editor/src/Core/EditorShellAsset.cpp | 28 +++++++++ new_editor/src/Core/EditorShellAsset.h | 11 ++++ .../test_editor_shell_asset_validation.cpp | 63 +++++++++++++++++++ .../unit/test_structured_editor_shell.cpp | 37 +++++++++++ 5 files changed, 140 insertions(+), 2 deletions(-) diff --git a/new_editor/app/Application.h b/new_editor/app/Application.h index 9a4008eb..7f76caef 100644 --- a/new_editor/app/Application.h +++ b/new_editor/app/Application.h @@ -42,8 +42,7 @@ struct StructuredEditorShellBinding { inline UIEditorShortcutManager BuildStructuredEditorShortcutManager( const EditorShellAsset& asset) { - (void)asset; - return UIEditorShortcutManager(UIEditorCommandRegistry{}); + return BuildEditorShellShortcutManager(asset); } inline StructuredEditorShellBinding BuildStructuredEditorShellBinding( diff --git a/new_editor/src/Core/EditorShellAsset.cpp b/new_editor/src/Core/EditorShellAsset.cpp index d7012acc..76fbc005 100644 --- a/new_editor/src/Core/EditorShellAsset.cpp +++ b/new_editor/src/Core/EditorShellAsset.cpp @@ -148,6 +148,14 @@ EditorShellAsset BuildDefaultEditorShellAsset(const std::filesystem::path& repoR return asset; } +UIEditorShortcutManager BuildEditorShellShortcutManager(const EditorShellAsset& asset) { + UIEditorShortcutManager manager(asset.shortcutAsset.commandRegistry); + for (const XCEngine::UI::UIShortcutBinding& binding : asset.shortcutAsset.bindings) { + manager.RegisterBinding(binding); + } + return manager; +} + EditorShellAssetValidationResult ValidateEditorShellAsset(const EditorShellAsset& asset) { const UIEditorPanelRegistryValidationResult registryValidation = ValidateUIEditorPanelRegistry(asset.panelRegistry); @@ -188,6 +196,26 @@ EditorShellAssetValidationResult ValidateEditorShellAsset(const EditorShellAsset return shellDefinitionValidation; } + const UIEditorMenuModelValidationResult shellMenuValidation = + ValidateUIEditorMenuModel( + asset.shellDefinition.menuModel, + asset.shortcutAsset.commandRegistry); + if (!shellMenuValidation.IsValid()) { + return MakeValidationError( + EditorShellAssetValidationCode::InvalidShellMenuModel, + shellMenuValidation.message); + } + + const UIEditorShortcutManager shortcutManager = + BuildEditorShellShortcutManager(asset); + const UIEditorShortcutManagerValidationResult shortcutValidation = + shortcutManager.ValidateConfiguration(); + if (!shortcutValidation.IsValid()) { + return MakeValidationError( + EditorShellAssetValidationCode::InvalidShortcutConfiguration, + shortcutValidation.message); + } + return {}; } diff --git a/new_editor/src/Core/EditorShellAsset.h b/new_editor/src/Core/EditorShellAsset.h index 2e519e09..f8f02f1b 100644 --- a/new_editor/src/Core/EditorShellAsset.h +++ b/new_editor/src/Core/EditorShellAsset.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,9 +9,15 @@ #include #include #include +#include namespace XCEngine::UI::Editor { +struct EditorShellShortcutAsset { + UIEditorCommandRegistry commandRegistry = {}; + std::vector bindings = {}; +}; + struct EditorShellAsset { std::string screenId = "editor.shell"; std::filesystem::path documentPath = {}; @@ -20,6 +27,7 @@ struct EditorShellAsset { UIEditorWorkspaceModel workspace = {}; UIEditorWorkspaceSession workspaceSession = {}; UIEditorShellInteractionDefinition shellDefinition = {}; + EditorShellShortcutAsset shortcutAsset = {}; }; enum class EditorShellAssetValidationCode : std::uint8_t { @@ -27,6 +35,8 @@ enum class EditorShellAssetValidationCode : std::uint8_t { InvalidPanelRegistry, InvalidWorkspace, InvalidWorkspaceSession, + InvalidShellMenuModel, + InvalidShortcutConfiguration, MissingPanelDescriptor, PanelTitleMismatch, PanelPlaceholderMismatch, @@ -46,6 +56,7 @@ struct EditorShellAssetValidationResult { }; EditorShellAsset BuildDefaultEditorShellAsset(const std::filesystem::path& repoRoot); +UIEditorShortcutManager BuildEditorShellShortcutManager(const EditorShellAsset& asset); EditorShellAssetValidationResult ValidateEditorShellAsset(const EditorShellAsset& asset); } // namespace XCEngine::UI::Editor diff --git a/tests/UI/Editor/unit/test_editor_shell_asset_validation.cpp b/tests/UI/Editor/unit/test_editor_shell_asset_validation.cpp index b588e7e4..9449573f 100644 --- a/tests/UI/Editor/unit/test_editor_shell_asset_validation.cpp +++ b/tests/UI/Editor/unit/test_editor_shell_asset_validation.cpp @@ -4,12 +4,31 @@ #include +#include + namespace { +using XCEngine::Input::KeyCode; using XCEngine::UI::Editor::BuildDefaultEditorShellAsset; using XCEngine::UI::Editor::EditorShellAssetValidationCode; using XCEngine::UI::Editor::FindUIEditorPanelDescriptor; +using XCEngine::UI::Editor::UIEditorCommandPanelSource; +using XCEngine::UI::Editor::UIEditorMenuItemKind; +using XCEngine::UI::Editor::UIEditorWorkspaceCommandKind; using XCEngine::UI::Editor::ValidateEditorShellAsset; +using XCEngine::UI::UIInputEventType; +using XCEngine::UI::UIShortcutBinding; +using XCEngine::UI::UIShortcutScope; + +UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) { + UIShortcutBinding binding = {}; + binding.commandId = std::move(commandId); + binding.scope = UIShortcutScope::Global; + binding.triggerEventType = UIInputEventType::KeyDown; + binding.chord.keyCode = static_cast(keyCode); + binding.chord.modifiers.control = true; + return binding; +} TEST(EditorShellAssetValidationTest, DefaultShellAssetPassesValidation) { const auto shellAsset = BuildDefaultEditorShellAsset("."); @@ -99,6 +118,50 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsMissingRequiredShellPresen EditorShellAssetValidationCode::MissingRequiredShellPresentation); } +TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShellMenuModel) { + auto shellAsset = BuildDefaultEditorShellAsset("."); + shellAsset.shellDefinition.menuModel.menus = { + { + "window", + "Window", + { + { + UIEditorMenuItemKind::Command, + "reset-layout", + {}, + "workspace.reset_layout", + {}, + {} + } + } + } + }; + + const auto validation = ValidateEditorShellAsset(shellAsset); + EXPECT_EQ( + validation.code, + EditorShellAssetValidationCode::InvalidShellMenuModel); +} + +TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShortcutConfiguration) { + auto shellAsset = BuildDefaultEditorShellAsset("."); + shellAsset.shortcutAsset.commandRegistry.commands = { + { + "workspace.reset_layout", + "Reset Layout", + { UIEditorWorkspaceCommandKind::ResetWorkspace, UIEditorCommandPanelSource::None, {} } + } + }; + shellAsset.shortcutAsset.bindings = { + MakeBinding("missing.command", KeyCode::R) + }; + + const auto validation = ValidateEditorShellAsset(shellAsset); + EXPECT_EQ( + validation.code, + EditorShellAssetValidationCode::InvalidShortcutConfiguration); +} + TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationKindMismatch) { auto shellAsset = BuildDefaultEditorShellAsset("."); ASSERT_EQ( diff --git a/tests/UI/Editor/unit/test_structured_editor_shell.cpp b/tests/UI/Editor/unit/test_structured_editor_shell.cpp index 92056661..12cf17c7 100644 --- a/tests/UI/Editor/unit/test_structured_editor_shell.cpp +++ b/tests/UI/Editor/unit/test_structured_editor_shell.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -18,15 +19,22 @@ namespace { +using XCEngine::Input::KeyCode; using XCEngine::UI::Editor::BuildDefaultEditorShellAsset; using XCEngine::UI::Editor::BuildStructuredEditorShellBinding; using XCEngine::UI::Editor::BuildStructuredEditorShellServices; using XCEngine::UI::Editor::ResolveUIEditorShellInteractionModel; using XCEngine::UI::Editor::AreUIEditorWorkspaceModelsEquivalent; using XCEngine::UI::Editor::AreUIEditorWorkspaceSessionsEquivalent; +using XCEngine::UI::Editor::UIEditorCommandPanelSource; +using XCEngine::UI::Editor::UIEditorMenuItemKind; +using XCEngine::UI::Editor::UIEditorWorkspaceCommandKind; using XCEngine::UI::UIDrawCommand; using XCEngine::UI::UIDrawCommandType; using XCEngine::UI::UIDrawData; +using XCEngine::UI::UIInputEventType; +using XCEngine::UI::UIShortcutBinding; +using XCEngine::UI::UIShortcutScope; using XCEngine::UI::Runtime::UIScreenAsset; using XCEngine::UI::Runtime::UIScreenFrameInput; using XCEngine::UI::Runtime::UIScreenPlayer; @@ -137,6 +145,16 @@ bool ShellDefinitionsMatch( return true; } +UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) { + UIShortcutBinding binding = {}; + binding.commandId = std::move(commandId); + binding.scope = UIShortcutScope::Global; + binding.triggerEventType = UIInputEventType::KeyDown; + binding.chord.keyCode = static_cast(keyCode); + binding.chord.modifiers.control = true; + return binding; +} + } // namespace TEST(EditorUIStructuredShellTest, AuthoredEditorShellLoadsFromRepositoryResources) { @@ -173,9 +191,24 @@ TEST(EditorUIStructuredShellTest, AuthoredEditorShellLoadsFromRepositoryResource TEST(EditorUIStructuredShellTest, StructuredShellBindingUsesEditorShellAssetAsSingleSource) { auto shell = BuildDefaultEditorShellAsset(RepoRootPath()); shell.shellDefinition.statusSegments.front().label = "Asset Contract"; + shell.shortcutAsset.commandRegistry.commands = { + { + "workspace.reset_layout", + "Reset Layout", + { UIEditorWorkspaceCommandKind::ResetWorkspace, UIEditorCommandPanelSource::None, {} } + } + }; + shell.shortcutAsset.bindings = { + MakeBinding("workspace.reset_layout", KeyCode::R) + }; + XCEngine::UI::Editor::UIEditorMenuItemDescriptor assetCommand = {}; + assetCommand.kind = UIEditorMenuItemKind::Command; + assetCommand.itemId = "asset-reset-layout"; + assetCommand.commandId = "workspace.reset_layout"; XCEngine::UI::Editor::UIEditorMenuDescriptor assetMenu = {}; assetMenu.menuId = "asset"; assetMenu.label = "Asset"; + assetMenu.items = { assetCommand }; shell.shellDefinition.menuModel.menus = { assetMenu }; const auto binding = BuildStructuredEditorShellBinding(shell); @@ -203,6 +236,10 @@ TEST(EditorUIStructuredShellTest, StructuredShellBindingUsesEditorShellAssetAsSi ASSERT_EQ(model.resolvedMenuModel.menus.size(), 1u); EXPECT_EQ(model.resolvedMenuModel.menus.front().menuId, "asset"); EXPECT_EQ(model.resolvedMenuModel.menus.front().label, "Asset"); + ASSERT_EQ(model.resolvedMenuModel.menus.front().items.size(), 1u); + EXPECT_EQ(model.resolvedMenuModel.menus.front().items.front().commandId, "workspace.reset_layout"); + EXPECT_EQ(model.resolvedMenuModel.menus.front().items.front().label, "Reset Layout"); + EXPECT_EQ(model.resolvedMenuModel.menus.front().items.front().shortcutText, "Ctrl+R"); ASSERT_EQ(model.statusSegments.size(), shell.shellDefinition.statusSegments.size()); EXPECT_EQ(model.statusSegments.front().label, shell.shellDefinition.statusSegments.front().label); ASSERT_EQ(