Unify editor shell asset contracts

This commit is contained in:
2026-04-07 12:47:16 +08:00
parent 49e9b63a2d
commit 3cf4adf181
5 changed files with 140 additions and 2 deletions

View File

@@ -4,12 +4,31 @@
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEngine/Input/InputTypes.h>
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<std::int32_t>(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(

View File

@@ -5,6 +5,7 @@
#include <XCEditor/Core/UIEditorShellInteraction.h>
#include <XCEditor/Core/UIEditorWorkspaceModel.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Runtime/UIScreenDocumentHost.h>
#include <XCEngine/UI/Runtime/UIScreenPlayer.h>
@@ -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<std::int32_t>(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(