Files
XCEngine/new_editor/app/Shell/EditorShellAssetBuilder.cpp

520 lines
20 KiB
C++

#include "ProductShellAsset.h"
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Input/UIShortcutRegistry.h>
#include <utility>
namespace XCEngine::UI::Editor::App {
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIShortcutBinding;
using XCEngine::UI::UIShortcutScope;
using Widgets::UIEditorStatusBarSegment;
using Widgets::UIEditorStatusBarSlot;
using Widgets::UIEditorStatusBarTextTone;
UIEditorPanelRegistry BuildPanelRegistry() {
UIEditorPanelRegistry registry = {};
registry.panels = {
{ "hierarchy", "Hierarchy", UIEditorPanelPresentationKind::HostedContent, true, false, false },
{ "scene", "Scene", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
{ "game", "Game", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
{ "inspector", "Inspector", UIEditorPanelPresentationKind::HostedContent, true, false, false },
{ "console", "Console", UIEditorPanelPresentationKind::HostedContent, true, false, false },
{ "project", "Project", UIEditorPanelPresentationKind::HostedContent, false, false, false }
};
return registry;
}
UIEditorWorkspaceModel BuildWorkspace() {
UIEditorWorkspaceModel workspace = {};
workspace.root = BuildUIEditorWorkspaceSplit(
"workspace-root",
UIEditorWorkspaceSplitAxis::Vertical,
0.75f,
BuildUIEditorWorkspaceSplit(
"workspace-top",
UIEditorWorkspaceSplitAxis::Horizontal,
0.7875f,
BuildUIEditorWorkspaceSplit(
"workspace-main",
UIEditorWorkspaceSplitAxis::Horizontal,
0.19047619f,
BuildUIEditorWorkspaceSingleTabStack(
"hierarchy-panel",
"hierarchy",
"Hierarchy",
true),
BuildUIEditorWorkspaceTabStack(
"center-tabs",
{
BuildUIEditorWorkspacePanel(
"scene-panel",
"scene",
"Scene",
false),
BuildUIEditorWorkspacePanel(
"game-panel",
"game",
"Game",
false)
},
0u)),
BuildUIEditorWorkspaceSingleTabStack(
"inspector-panel",
"inspector",
"Inspector",
true)),
BuildUIEditorWorkspaceTabStack(
"bottom-tabs",
{
BuildUIEditorWorkspacePanel(
"console-panel",
"console",
"Console",
true),
BuildUIEditorWorkspacePanel(
"project-panel",
"project",
"Project",
false)
},
1u));
workspace.activePanelId = "scene";
return workspace;
}
UIEditorCommandDescriptor BuildHostCommand(
std::string commandId,
std::string displayName) {
UIEditorCommandDescriptor command = {};
command.commandId = std::move(commandId);
command.displayName = std::move(displayName);
command.kind = UIEditorCommandKind::Host;
command.workspaceCommand.panelSource = UIEditorCommandPanelSource::None;
command.workspaceCommand.panelId.clear();
return command;
}
UIEditorCommandDescriptor BuildWorkspaceCommand(
std::string commandId,
std::string displayName,
UIEditorWorkspaceCommandKind kind,
std::string panelId = {}) {
UIEditorCommandDescriptor command = {};
command.commandId = std::move(commandId);
command.displayName = std::move(displayName);
command.workspaceCommand.kind = kind;
if (kind == UIEditorWorkspaceCommandKind::ResetWorkspace) {
command.workspaceCommand.panelSource = UIEditorCommandPanelSource::None;
} else {
command.workspaceCommand.panelSource = UIEditorCommandPanelSource::FixedPanelId;
command.workspaceCommand.panelId = std::move(panelId);
}
return command;
}
UIEditorCommandRegistry BuildCommandRegistry() {
UIEditorCommandRegistry registry = {};
registry.commands = {
BuildHostCommand("file.new_project", "New Project..."),
BuildHostCommand("file.open_project", "Open Project..."),
BuildHostCommand("file.save_project", "Save Project"),
BuildHostCommand("file.new_scene", "New Scene"),
BuildHostCommand("file.open_scene", "Open Scene"),
BuildHostCommand("file.save_scene", "Save Scene"),
BuildHostCommand("file.save_scene_as", "Save Scene As..."),
BuildHostCommand("file.exit", "Exit"),
BuildHostCommand("edit.undo", "Undo"),
BuildHostCommand("edit.redo", "Redo"),
BuildHostCommand("edit.cut", "Cut"),
BuildHostCommand("edit.copy", "Copy"),
BuildHostCommand("edit.paste", "Paste"),
BuildHostCommand("edit.duplicate", "Duplicate"),
BuildHostCommand("edit.delete", "Delete"),
BuildHostCommand("edit.rename", "Rename"),
BuildHostCommand("assets.reimport_selected", "Reimport Selected Asset"),
BuildHostCommand("assets.reimport_all", "Reimport All Assets"),
BuildHostCommand("assets.clear_library", "Clear Library"),
BuildHostCommand("run.play", "Play"),
BuildHostCommand("run.pause", "Pause"),
BuildHostCommand("run.step", "Step"),
BuildHostCommand("scripts.rebuild", "Rebuild Script Assemblies"),
BuildHostCommand("help.about", "About"),
BuildWorkspaceCommand(
"view.reset_layout",
"Reset Layout",
UIEditorWorkspaceCommandKind::ResetWorkspace),
BuildWorkspaceCommand(
"view.activate_hierarchy",
"Hierarchy",
UIEditorWorkspaceCommandKind::ActivatePanel,
"hierarchy"),
BuildWorkspaceCommand(
"view.activate_scene",
"Scene",
UIEditorWorkspaceCommandKind::ActivatePanel,
"scene"),
BuildWorkspaceCommand(
"view.activate_game",
"Game",
UIEditorWorkspaceCommandKind::ActivatePanel,
"game"),
BuildWorkspaceCommand(
"view.activate_inspector",
"Inspector",
UIEditorWorkspaceCommandKind::ActivatePanel,
"inspector"),
BuildWorkspaceCommand(
"view.activate_console",
"Console",
UIEditorWorkspaceCommandKind::ActivatePanel,
"console"),
BuildWorkspaceCommand(
"view.activate_project",
"Project",
UIEditorWorkspaceCommandKind::ActivatePanel,
"project")
};
return registry;
}
UIEditorMenuItemDescriptor BuildCommandItem(
std::string itemId,
std::string label,
std::string commandId,
UIEditorMenuCheckedStateBinding checkedState = {}) {
UIEditorMenuItemDescriptor item = {};
item.kind = UIEditorMenuItemKind::Command;
item.itemId = std::move(itemId);
item.label = std::move(label);
item.commandId = std::move(commandId);
item.checkedState = std::move(checkedState);
return item;
}
UIEditorMenuItemDescriptor BuildSeparatorItem(std::string itemId) {
UIEditorMenuItemDescriptor item = {};
item.kind = UIEditorMenuItemKind::Separator;
item.itemId = std::move(itemId);
return item;
}
UIEditorMenuItemDescriptor BuildSubmenuItem(
std::string itemId,
std::string label,
std::vector<UIEditorMenuItemDescriptor> children) {
UIEditorMenuItemDescriptor item = {};
item.kind = UIEditorMenuItemKind::Submenu;
item.itemId = std::move(itemId);
item.label = std::move(label);
item.children = std::move(children);
return item;
}
UIEditorMenuModel BuildMenuModel() {
UIEditorMenuModel model = {};
UIEditorMenuDescriptor fileMenu = {};
fileMenu.menuId = "file";
fileMenu.label = "File";
fileMenu.items = {
BuildCommandItem("file-new-project", "New Project...", "file.new_project"),
BuildCommandItem("file-open-project", "Open Project...", "file.open_project"),
BuildCommandItem("file-save-project", "Save Project", "file.save_project"),
BuildSeparatorItem("file-separator-project"),
BuildCommandItem("file-new-scene", "New Scene", "file.new_scene"),
BuildCommandItem("file-open-scene", "Open Scene", "file.open_scene"),
BuildCommandItem("file-save-scene", "Save Scene", "file.save_scene"),
BuildCommandItem("file-save-scene-as", "Save Scene As...", "file.save_scene_as"),
BuildSeparatorItem("file-separator-exit"),
BuildCommandItem("file-exit", "Exit", "file.exit")
};
UIEditorMenuDescriptor editMenu = {};
editMenu.menuId = "edit";
editMenu.label = "Edit";
editMenu.items = {
BuildCommandItem("edit-undo", "Undo", "edit.undo"),
BuildCommandItem("edit-redo", "Redo", "edit.redo"),
BuildSeparatorItem("edit-separator-history"),
BuildCommandItem("edit-cut", "Cut", "edit.cut"),
BuildCommandItem("edit-copy", "Copy", "edit.copy"),
BuildCommandItem("edit-paste", "Paste", "edit.paste"),
BuildCommandItem("edit-duplicate", "Duplicate", "edit.duplicate"),
BuildCommandItem("edit-delete", "Delete", "edit.delete"),
BuildCommandItem("edit-rename", "Rename", "edit.rename")
};
UIEditorMenuDescriptor assetsMenu = {};
assetsMenu.menuId = "assets";
assetsMenu.label = "Assets";
assetsMenu.items = {
BuildCommandItem("assets-reimport-selected", "Reimport Selected Asset", "assets.reimport_selected"),
BuildCommandItem("assets-reimport-all", "Reimport All Assets", "assets.reimport_all"),
BuildSeparatorItem("assets-separator-clear"),
BuildCommandItem("assets-clear-library", "Clear Library", "assets.clear_library")
};
UIEditorMenuDescriptor runMenu = {};
runMenu.menuId = "run";
runMenu.label = "Run";
runMenu.items = {
BuildCommandItem("run-play", "Play", "run.play"),
BuildCommandItem("run-pause", "Pause", "run.pause"),
BuildCommandItem("run-step", "Step", "run.step")
};
UIEditorMenuDescriptor scriptsMenu = {};
scriptsMenu.menuId = "scripts";
scriptsMenu.label = "Scripts";
scriptsMenu.items = {
BuildCommandItem("scripts-rebuild", "Rebuild Script Assemblies", "scripts.rebuild")
};
UIEditorMenuCheckedStateBinding hierarchyActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"hierarchy"
};
UIEditorMenuCheckedStateBinding sceneActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"scene"
};
UIEditorMenuCheckedStateBinding gameActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"game"
};
UIEditorMenuCheckedStateBinding inspectorActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"inspector"
};
UIEditorMenuCheckedStateBinding consoleActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"console"
};
UIEditorMenuCheckedStateBinding projectActive = {
UIEditorMenuCheckedStateSource::PanelActive,
"project"
};
UIEditorMenuDescriptor viewMenu = {};
viewMenu.menuId = "view";
viewMenu.label = "View";
viewMenu.items = {
BuildCommandItem("view-reset-layout", "Reset Layout", "view.reset_layout"),
BuildSeparatorItem("view-separator-panels"),
BuildSubmenuItem(
"view-panels",
"Panels",
{
BuildCommandItem("view-panel-hierarchy", "Hierarchy", "view.activate_hierarchy", hierarchyActive),
BuildCommandItem("view-panel-scene", "Scene", "view.activate_scene", sceneActive),
BuildCommandItem("view-panel-game", "Game", "view.activate_game", gameActive),
BuildCommandItem("view-panel-inspector", "Inspector", "view.activate_inspector", inspectorActive),
BuildCommandItem("view-panel-console", "Console", "view.activate_console", consoleActive),
BuildCommandItem("view-panel-project", "Project", "view.activate_project", projectActive)
})
};
UIEditorMenuDescriptor helpMenu = {};
helpMenu.menuId = "help";
helpMenu.label = "Help";
helpMenu.items = {
BuildCommandItem("help-about", "About", "help.about")
};
model.menus = {
std::move(fileMenu),
std::move(editMenu),
std::move(assetsMenu),
std::move(runMenu),
std::move(scriptsMenu),
std::move(viewMenu),
std::move(helpMenu)
};
return model;
}
UIShortcutBinding BuildBinding(
std::string commandId,
std::int32_t keyCode,
bool control = false,
bool shift = false,
bool alt = false) {
UIShortcutBinding binding = {};
binding.scope = UIShortcutScope::Global;
binding.triggerEventType = UIInputEventType::KeyDown;
binding.commandId = std::move(commandId);
binding.chord.keyCode = keyCode;
binding.chord.modifiers.control = control;
binding.chord.modifiers.shift = shift;
binding.chord.modifiers.alt = alt;
return binding;
}
std::vector<UIShortcutBinding> BuildShortcutBindings() {
return {
BuildBinding("file.new_scene", static_cast<std::int32_t>(KeyCode::N), true),
BuildBinding("file.open_scene", static_cast<std::int32_t>(KeyCode::O), true),
BuildBinding("file.save_scene", static_cast<std::int32_t>(KeyCode::S), true),
BuildBinding("file.save_scene_as", static_cast<std::int32_t>(KeyCode::S), true, true),
BuildBinding("edit.undo", static_cast<std::int32_t>(KeyCode::Z), true),
BuildBinding("edit.redo", static_cast<std::int32_t>(KeyCode::Y), true),
BuildBinding("edit.cut", static_cast<std::int32_t>(KeyCode::X), true),
BuildBinding("edit.copy", static_cast<std::int32_t>(KeyCode::C), true),
BuildBinding("edit.paste", static_cast<std::int32_t>(KeyCode::V), true),
BuildBinding("edit.duplicate", static_cast<std::int32_t>(KeyCode::D), true),
BuildBinding("edit.delete", static_cast<std::int32_t>(KeyCode::Delete)),
BuildBinding("edit.rename", static_cast<std::int32_t>(KeyCode::F2)),
BuildBinding("run.play", static_cast<std::int32_t>(KeyCode::F5)),
BuildBinding("run.pause", static_cast<std::int32_t>(KeyCode::F6)),
BuildBinding("run.step", static_cast<std::int32_t>(KeyCode::F7)),
BuildBinding("file.exit", static_cast<std::int32_t>(KeyCode::F4), false, false, true)
};
}
UIEditorStatusBarSegment BuildStatusSegment(
std::string segmentId,
std::string label,
UIEditorStatusBarSlot slot,
UIEditorStatusBarTextTone tone,
float desiredWidth,
bool showSeparator) {
UIEditorStatusBarSegment segment = {};
segment.segmentId = std::move(segmentId);
segment.label = std::move(label);
segment.slot = slot;
segment.tone = tone;
segment.interactive = false;
segment.desiredWidth = desiredWidth;
segment.showSeparator = showSeparator;
return segment;
}
UIEditorShellToolbarButton BuildToolbarButton(
std::string buttonId,
UIEditorShellToolbarGlyph glyph) {
UIEditorShellToolbarButton button = {};
button.buttonId = std::move(buttonId);
button.glyph = glyph;
button.enabled = true;
return button;
}
UIEditorWorkspacePanelPresentationModel BuildPlaceholderPresentation(
std::string panelId) {
UIEditorWorkspacePanelPresentationModel presentation = {};
presentation.panelId = std::move(panelId);
presentation.kind = UIEditorPanelPresentationKind::Placeholder;
return presentation;
}
UIEditorWorkspacePanelPresentationModel BuildHostedContentPresentation(
std::string panelId) {
UIEditorWorkspacePanelPresentationModel presentation = {};
presentation.panelId = std::move(panelId);
presentation.kind = UIEditorPanelPresentationKind::HostedContent;
return presentation;
}
UIEditorWorkspacePanelPresentationModel BuildViewportPresentation(
std::string panelId,
std::string title,
std::string subtitle) {
UIEditorWorkspacePanelPresentationModel presentation = {};
presentation.panelId = std::move(panelId);
presentation.kind = UIEditorPanelPresentationKind::ViewportShell;
presentation.viewportShellModel.spec.chrome.title = std::move(title);
presentation.viewportShellModel.spec.chrome.subtitle = {};
presentation.viewportShellModel.spec.chrome.showTopBar = false;
presentation.viewportShellModel.spec.chrome.showBottomBar = false;
presentation.viewportShellModel.frame.statusText.clear();
return presentation;
}
UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
UIEditorShellInteractionDefinition definition = {};
definition.menuModel = BuildMenuModel();
definition.toolbarButtons = {
BuildToolbarButton("run.play", UIEditorShellToolbarGlyph::Play),
BuildToolbarButton("run.pause", UIEditorShellToolbarGlyph::Pause),
BuildToolbarButton("run.step", UIEditorShellToolbarGlyph::Step)
};
definition.statusSegments = {};
definition.workspacePresentations = {
BuildHostedContentPresentation("hierarchy"),
BuildViewportPresentation("scene", "Scene", {}),
BuildViewportPresentation("game", "Game", {}),
BuildHostedContentPresentation("inspector"),
BuildHostedContentPresentation("console"),
BuildHostedContentPresentation("project")
};
return definition;
}
std::string ResolvePanelTitle(
const UIEditorPanelRegistry& registry,
std::string_view panelId) {
if (const UIEditorPanelDescriptor* descriptor =
FindUIEditorPanelDescriptor(registry, panelId);
descriptor != nullptr) {
return descriptor->defaultTitle;
}
return panelId.empty() ? std::string("(none)") : std::string(panelId);
}
} // namespace
EditorShellAsset BuildProductShellAsset(const std::filesystem::path& repoRoot) {
EditorShellAsset asset = {};
asset.screenId = "editor.product.shell";
asset.captureRootPath = (repoRoot / "new_editor/captures").lexically_normal();
asset.panelRegistry = BuildPanelRegistry();
asset.workspace = BuildWorkspace();
asset.workspaceSession =
BuildDefaultUIEditorWorkspaceSession(asset.panelRegistry, asset.workspace);
asset.shellDefinition = BuildBaseShellDefinition();
asset.shortcutAsset.commandRegistry = BuildCommandRegistry();
asset.shortcutAsset.bindings = BuildShortcutBindings();
return asset;
}
UIEditorShellInteractionDefinition BuildProductShellInteractionDefinition(
const EditorShellAsset& asset,
const UIEditorWorkspaceController& controller,
std::string_view statusText,
std::string_view captureText,
ProductEditorShellVariant variant) {
UIEditorShellInteractionDefinition definition = asset.shellDefinition;
if (variant == ProductEditorShellVariant::DetachedWindow) {
definition.menuModel = {};
definition.toolbarButtons.clear();
definition.statusSegments.clear();
}
const std::string activeTitle =
ResolvePanelTitle(asset.panelRegistry, controller.GetWorkspace().activePanelId);
const std::string resolvedStatus =
statusText.empty() ? std::string("Shell ready") : std::string(statusText);
const std::string resolvedCapture =
captureText.empty() ? std::string("F12 -> Screenshot") : std::string(captureText);
for (UIEditorStatusBarSegment& segment : definition.statusSegments) {
if (segment.segmentId == "editor-status") {
segment.label = resolvedStatus;
} else if (segment.segmentId == "capture") {
segment.label = resolvedCapture;
} else if (segment.segmentId == "active-panel") {
segment.label = activeTitle;
}
}
return definition;
}
} // namespace XCEngine::UI::Editor::App