diff --git a/editor/AGENTS.md b/editor/AGENTS.md index 2cccfe0b..2f61838a 100644 --- a/editor/AGENTS.md +++ b/editor/AGENTS.md @@ -48,6 +48,10 @@ change. passes. - `app/Features/` contains user-facing panels and editor tools: Hierarchy, Scene viewport, Inspector, Project, Console, Color Picker, and component UI. +- `app/Features/EditorWorkspacePanelRegistry.*` is the single composition + entry point for workspace panel runtimes. It owns the concrete workspace + panel adapters and keeps `app/Composition/**` from depending on individual + feature-panel classes. - `app/Project/`, `app/Scene/`, and `app/State/` hold application services that panels should use instead of owning global state themselves. @@ -104,10 +108,9 @@ change. through to the authoritative window workspace state; a copied controller is only a preview. - `EditorShellRuntime` owns per-window interactive runtime state: viewport host - service, built-in icons, hosted panels, scene viewport feature, shell - interaction state/frame, splitter correction state, trace entries, draw - composer, interaction engine, hosted panel coordinator, and session - coordinator. + service, built-in icons, the workspace panel runtime set, shell interaction + state/frame, splitter correction state, trace entries, draw composer, + interaction engine, hosted panel coordinator, and session coordinator. - `EditorWindowRuntimeController` owns the content controller, render runtime, screenshot controller, title-bar logo texture, text measurer access, DPI scale, and frame-rate display. @@ -178,17 +181,27 @@ inside pure shell/widget code. capture root, and status-bar definitions. - Panel IDs are centralized in `EditorPanelIds.h`; prefer these constants over raw string literals in app code. +- Workspace panels are adapted behind `EditorWorkspacePanelRuntimeSet`. + Composition code should ask that runtime set to initialize, prepare command + routes, update, draw, expose cursor/capture state, and collect frame events + instead of directly naming `HierarchyPanel`, `ProjectPanel`, `InspectorPanel`, + `ConsolePanel`, or `SceneViewportFeature`. - Hosted panels receive mounted bounds and filtered input through - `UIEditorHostedPanelDispatchFrame`. + `UIEditorHostedPanelDispatchFrame`; concrete workspace-panel adapters resolve + their own dispatch entries from that frame. - `EditorShellHostedPanelCoordinator` filters hosted-panel input when shell - capture owns the pointer stream, updates scene viewport state, wires app - services into panels, dispatches hosted panels, and syncs command focus. + capture owns the pointer stream, then updates `EditorWorkspacePanelRuntimeSet`. + Concrete adapters wire app services into their panels and the runtime set + syncs command focus before after-focus panels such as Console read session + state. - `EditorHostCommandBridge` routes host commands to focused edit routes, project asset routes, scene routes, inspector routes, workspace commands, or the app exit handler. -- `WorkspaceEventSync` is the current place where hierarchy/project frame - events update app status, selection-backed session state, scene-open requests, - and runtime trace entries. +- `WorkspaceEventSync` consumes generic `EditorWorkspacePanelFrameEvent` + entries from the workspace panel runtime set. Concrete hierarchy/project + event formatting lives behind the feature registry, while this sync layer + owns selection-backed session state, scene-open requests, status updates, and + runtime trace entries. ## Viewport Rendering @@ -212,13 +225,14 @@ inside pure shell/widget code. - Add reusable widgets, docking/workspace behavior, shell interaction, or field logic to `include/XCEditor/**` and `src/**`, with unit coverage in `tests/UI/Editor/unit`. -- Add product editor behavior to `app/Features/**`, then wire it through - `EditorShellAssetBuilder`, `EditorShellRuntime`, - `EditorShellHostedPanelCoordinator`, `EditorShellDrawComposer`, command - routing, and app state services as needed. +- Add product editor behavior to `app/Features/**`. Workspace panels must + enter composition through `EditorWorkspacePanelRegistry.*` and the + `EditorWorkspacePanel` adapter interface; do not include concrete feature + panel headers from `EditorShellRuntime`, `EditorShellHostedPanelCoordinator`, + `EditorShellDrawComposer`, or `EditorShellSessionCoordinator`. - Add new workspace panels by updating the panel ID constants, panel registry, - default workspace model, shell presentation, content dispatch, drawing, and - command/menu entries together. + default workspace model, shell presentation, workspace panel runtime adapter, + and command/menu entries together. - Add new utility windows through `EditorUtilityWindowKind`, `EditorUtilityWindowRegistry`, an `EditorUtilityWindowPanel`, and the context/request path. Do not model utility windows as workspace panels unless @@ -321,3 +335,7 @@ ownership rule. remains a single `XCEditor` target with output name `XCEngine`, and manual-validation hosts use the shared Core UI validation host instead of an editor app host target. +- Workspace panel runtimes are now behind `EditorWorkspacePanelRuntimeSet` and + `EditorWorkspacePanelRegistry.*`. `app/Composition/**` no longer directly + owns or dispatches concrete Console, Hierarchy, Inspector, Project, or Scene + panel classes. diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index cc417e6f..1355323d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -240,6 +240,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) set(XCUI_EDITOR_APP_FEATURE_SOURCES app/Features/Console/ConsolePanel.cpp app/Features/ColorPicker/ColorPickerPanel.cpp + app/Features/EditorWorkspacePanelRegistry.cpp app/Features/Hierarchy/HierarchyModel.cpp app/Features/Hierarchy/HierarchyPanel.cpp app/Features/Inspector/AddComponentPanel.cpp diff --git a/editor/app/Composition/EditorShellDrawComposer.cpp b/editor/app/Composition/EditorShellDrawComposer.cpp index 724548d2..db56a610 100644 --- a/editor/app/Composition/EditorShellDrawComposer.cpp +++ b/editor/app/Composition/EditorShellDrawComposer.cpp @@ -1,10 +1,6 @@ #include "Composition/EditorShellDrawComposer.h" -#include "Features/Console/ConsolePanel.h" -#include "Features/Hierarchy/HierarchyPanel.h" -#include "Features/Inspector/InspectorPanel.h" -#include "Features/Project/ProjectPanel.h" -#include "Features/Scene/SceneViewportFeature.h" +#include "Features/EditorWorkspacePanelRegistry.h" #include "Rendering/Assets/BuiltInIcons.h" #include @@ -146,26 +142,7 @@ void EditorShellDrawComposer::Append( palette.shellPalette, metrics.shellMetrics); }); - AppendDrawPacket( - drawData, - "XCEditorPanel.Console", - [&](UIDrawList& drawList) { context.consolePanel.Append(drawList); }); - AppendDrawPacket( - drawData, - "XCEditorPanel.Hierarchy", - [&](UIDrawList& drawList) { context.hierarchyPanel.Append(drawList); }); - AppendDrawPacket( - drawData, - "XCEditorPanel.Inspector", - [&](UIDrawList& drawList) { context.inspectorPanel.Append(drawList); }); - AppendDrawPacket( - drawData, - "XCEditorPanel.Project", - [&](UIDrawList& drawList) { context.projectPanel.Append(drawList); }); - AppendDrawPacket( - drawData, - "XCEditorPanel.SceneOverlay", - [&](UIDrawList& drawList) { context.sceneViewportFeature.Append(drawList); }); + context.workspacePanels.AppendDrawPackets(drawData); AppendDrawPacket( drawData, "XCEditorShell.Compose.Overlay", diff --git a/editor/app/Composition/EditorShellDrawComposer.h b/editor/app/Composition/EditorShellDrawComposer.h index 86f72da1..25313aeb 100644 --- a/editor/app/Composition/EditorShellDrawComposer.h +++ b/editor/app/Composition/EditorShellDrawComposer.h @@ -12,21 +12,13 @@ struct UIEditorShellInteractionState; namespace XCEngine::UI::Editor::App { class BuiltInIcons; -class ConsolePanel; -class HierarchyPanel; -class InspectorPanel; -class ProjectPanel; -class SceneViewportFeature; +class EditorWorkspacePanelRuntimeSet; struct EditorShellDrawComposerContext { const UIEditorShellInteractionFrame& shellFrame; const UIEditorShellInteractionState& shellInteractionState; const BuiltInIcons& builtInIcons; - const ConsolePanel& consolePanel; - const HierarchyPanel& hierarchyPanel; - const InspectorPanel& inspectorPanel; - const ProjectPanel& projectPanel; - const SceneViewportFeature& sceneViewportFeature; + const EditorWorkspacePanelRuntimeSet& workspacePanels; }; class EditorShellDrawComposer final { diff --git a/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp b/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp index d5f26469..a7538cf3 100644 --- a/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp +++ b/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp @@ -1,12 +1,6 @@ #include "Composition/EditorShellHostedPanelCoordinator.h" -#include "Composition/EditorContext.h" -#include "Composition/EditorPanelIds.h" -#include "Features/Console/ConsolePanel.h" -#include "Features/Hierarchy/HierarchyPanel.h" -#include "Features/Inspector/InspectorPanel.h" -#include "Features/Project/ProjectPanel.h" -#include "Features/Scene/SceneViewportFeature.h" +#include "Features/EditorWorkspacePanelRegistry.h" #include #include @@ -14,23 +8,6 @@ namespace XCEngine::UI::Editor::App { -namespace { - -const UIEditorHostedPanelDispatchEntry& ResolveHostedPanelDispatchEntry( - const UIEditorHostedPanelDispatchFrame& dispatchFrame, - std::string_view panelId) { - static const UIEditorHostedPanelDispatchEntry kEmptyEntry = {}; - if (const UIEditorHostedPanelDispatchEntry* entry = - FindUIEditorHostedPanelDispatchEntry(dispatchFrame, panelId); - entry != nullptr) { - return *entry; - } - - return kEmptyEntry; -} - -} // namespace - void EditorShellHostedPanelCoordinator::Update( const EditorShellHostedPanelCoordinatorContext& context) const { const bool shellOwnsHostedContentPointerStream = @@ -42,39 +19,13 @@ void EditorShellHostedPanelCoordinator::Update( context.inputEvents, shellOwnsHostedContentPointerStream); - const UIEditorHostedPanelDispatchFrame& hostedPanelDispatchFrame = - context.shellFrame.hostedPanelDispatchFrame; - const UIEditorHostedPanelDispatchEntry& hierarchyDispatchEntry = - ResolveHostedPanelDispatchEntry(hostedPanelDispatchFrame, kHierarchyPanelId); - const UIEditorHostedPanelDispatchEntry& projectDispatchEntry = - ResolveHostedPanelDispatchEntry(hostedPanelDispatchFrame, kProjectPanelId); - const UIEditorHostedPanelDispatchEntry& inspectorDispatchEntry = - ResolveHostedPanelDispatchEntry(hostedPanelDispatchFrame, kInspectorPanelId); - const UIEditorHostedPanelDispatchEntry& consoleDispatchEntry = - ResolveHostedPanelDispatchEntry(hostedPanelDispatchFrame, kConsolePanelId); - - context.sceneViewportFeature.Update( - context.context.GetSceneRuntime(), - context.shellInteractionState.workspaceInteractionState.composeState, - context.shellFrame.workspaceInteractionFrame.composeFrame); - context.projectPanel.SetProjectRuntime(&context.context.GetProjectRuntime()); - context.projectPanel.SetCommandFocusService(&context.context.GetCommandFocusService()); - context.projectPanel.SetSystemInteractionHost(context.context.GetSystemInteractionHost()); - context.inspectorPanel.SetCommandFocusService(&context.context.GetCommandFocusService()); - context.hierarchyPanel.Update( - hierarchyDispatchEntry, - hostedContentEvents); - context.projectPanel.Update( - projectDispatchEntry, - hostedContentEvents); - context.inspectorPanel.Update( - context.context, - inspectorDispatchEntry, - hostedContentEvents); - context.context.SyncSessionFromCommandFocusService(); - context.consolePanel.Update( - context.context.GetSession(), - consoleDispatchEntry); + context.workspacePanels.Update( + EditorWorkspacePanelUpdateContext{ + .context = context.context, + .shellFrame = context.shellFrame, + .shellInteractionState = context.shellInteractionState, + .hostedContentEvents = hostedContentEvents, + }); } } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellHostedPanelCoordinator.h b/editor/app/Composition/EditorShellHostedPanelCoordinator.h index 03354c4c..d5c48bb2 100644 --- a/editor/app/Composition/EditorShellHostedPanelCoordinator.h +++ b/editor/app/Composition/EditorShellHostedPanelCoordinator.h @@ -17,12 +17,8 @@ struct UIEditorShellInteractionState; namespace XCEngine::UI::Editor::App { -class ConsolePanel; class EditorContext; -class HierarchyPanel; -class InspectorPanel; -class ProjectPanel; -class SceneViewportFeature; +class EditorWorkspacePanelRuntimeSet; struct EditorShellHostedPanelCoordinatorContext { EditorContext& context; @@ -30,11 +26,7 @@ struct EditorShellHostedPanelCoordinatorContext { UIEditorShellInteractionState& shellInteractionState; const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents; bool shellInteractiveCaptureActive = false; - ConsolePanel& consolePanel; - HierarchyPanel& hierarchyPanel; - InspectorPanel& inspectorPanel; - ProjectPanel& projectPanel; - SceneViewportFeature& sceneViewportFeature; + EditorWorkspacePanelRuntimeSet& workspacePanels; }; class EditorShellHostedPanelCoordinator final { diff --git a/editor/app/Composition/EditorShellRuntime.cpp b/editor/app/Composition/EditorShellRuntime.cpp index 1504459f..616171ba 100644 --- a/editor/app/Composition/EditorShellRuntime.cpp +++ b/editor/app/Composition/EditorShellRuntime.cpp @@ -2,7 +2,6 @@ #include "Rendering/Host/UiTextureHost.h" #include "Rendering/Host/ViewportRenderHost.h" #include "Composition/EditorContext.h" -#include "Composition/EditorPanelIds.h" #include #include @@ -14,16 +13,15 @@ void EditorShellRuntime::Initialize( UIEditorTextMeasurer& textMeasurer) { m_textureHost = &textureHost; m_builtInIcons.Initialize(textureHost); - m_sceneViewportFeature.Initialize( - repoRoot, - textureHost, - &m_builtInIcons, - m_viewportHostService); - m_hierarchyPanel.SetBuiltInIcons(&m_builtInIcons); - m_hierarchyPanel.SetTextMeasurer(&textMeasurer); - m_projectPanel.SetBuiltInIcons(&m_builtInIcons); - m_projectPanel.SetTextMeasurer(&textMeasurer); - m_hierarchyPanel.Initialize(); + m_workspacePanels = CreateEditorWorkspacePanelRuntimeSet(); + m_workspacePanels.Initialize( + EditorWorkspacePanelInitializationContext{ + .repoRoot = repoRoot, + .textureHost = textureHost, + .textMeasurer = textMeasurer, + .builtInIcons = m_builtInIcons, + .viewportHostService = m_viewportHostService, + }); } void EditorShellRuntime::AttachViewportWindowRenderer(Rendering::Host::ViewportRenderHost& renderer) { @@ -43,12 +41,17 @@ void EditorShellRuntime::Shutdown() { m_shellInteractionState = {}; m_splitterDragCorrectionState = {}; m_traceEntries.clear(); - m_sceneEditCommandRoute = {}; if (m_textureHost != nullptr) { - m_sceneViewportFeature.Shutdown(*m_textureHost, m_viewportHostService); + m_workspacePanels.Shutdown( + EditorWorkspacePanelShutdownContext{ + .textureHost = *m_textureHost, + .viewportHostService = m_viewportHostService, + }); + m_workspacePanels = {}; m_builtInIcons.Shutdown(); m_textureHost = nullptr; } else { + m_workspacePanels = {}; m_builtInIcons.Shutdown(); } m_viewportHostService.Shutdown(); @@ -59,9 +62,7 @@ void EditorShellRuntime::ResetInteractionState() { m_shellInteractionState = {}; m_splitterDragCorrectionState = {}; m_traceEntries.clear(); - m_sceneViewportFeature.ResetInteractionState(); - m_hierarchyPanel.ResetInteractionState(); - m_projectPanel.ResetInteractionState(); + m_workspacePanels.ResetInteractionState(); } const UIEditorShellInteractionFrame& EditorShellRuntime::GetShellFrame() const { @@ -91,8 +92,8 @@ void EditorShellRuntime::ClearExternalDockHostDropPreview() { .dockHostState.dropPreview = {}; } -ProjectPanel::CursorKind EditorShellRuntime::GetHostedContentCursorKind() const { - return m_projectPanel.GetCursorKind(); +EditorWorkspacePanelCursorKind EditorShellRuntime::GetHostedContentCursorKind() const { + return m_workspacePanels.GetHostedContentCursorKind(); } Widgets::UIEditorDockHostCursorKind EditorShellRuntime::GetDockCursorKind() const { @@ -121,8 +122,7 @@ UIEditorDockHostTabDropTarget EditorShellRuntime::ResolveDockTabDropTarget( } bool EditorShellRuntime::HasHostedContentCapture() const { - return m_hierarchyPanel.HasActivePointerCapture() || - m_projectPanel.HasActivePointerCapture(); + return m_workspacePanels.HasActivePointerCapture(); } bool EditorShellRuntime::HasShellInteractiveCapture() const { @@ -148,11 +148,7 @@ void EditorShellRuntime::Append(::XCEngine::UI::UIDrawData& drawData) const { .shellFrame = m_shellFrame, .shellInteractionState = m_shellInteractionState, .builtInIcons = m_builtInIcons, - .consolePanel = m_consolePanel, - .hierarchyPanel = m_hierarchyPanel, - .inspectorPanel = m_inspectorPanel, - .projectPanel = m_projectPanel, - .sceneViewportFeature = m_sceneViewportFeature, + .workspacePanels = m_workspacePanels, }, drawData); } @@ -178,11 +174,7 @@ void EditorShellRuntime::Update( .workspaceController = workspaceController, .captureText = captureText, .shellVariant = shellVariant, - .hierarchyPanel = m_hierarchyPanel, - .inspectorPanel = m_inspectorPanel, - .projectPanel = m_projectPanel, - .sceneEditCommandRoute = m_sceneEditCommandRoute, - .sceneViewportFeature = m_sceneViewportFeature, + .workspacePanels = m_workspacePanels, }); }; m_interactionEngine.Update( @@ -210,16 +202,11 @@ void EditorShellRuntime::Update( .shellInteractionState = m_shellInteractionState, .inputEvents = inputEvents, .shellInteractiveCaptureActive = HasShellInteractiveCapture(), - .consolePanel = m_consolePanel, - .hierarchyPanel = m_hierarchyPanel, - .inspectorPanel = m_inspectorPanel, - .projectPanel = m_projectPanel, - .sceneViewportFeature = m_sceneViewportFeature, + .workspacePanels = m_workspacePanels, }); m_traceEntries = SyncWorkspaceEvents( context, - m_hierarchyPanel.GetFrameEvents(), - m_projectPanel.GetFrameEvents()); + m_workspacePanels.CollectFrameEvents()); } } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellRuntime.h b/editor/app/Composition/EditorShellRuntime.h index 2f198fbe..17b6ed7d 100644 --- a/editor/app/Composition/EditorShellRuntime.h +++ b/editor/app/Composition/EditorShellRuntime.h @@ -5,12 +5,7 @@ #include "Composition/EditorShellInteractionEngine.h" #include "Composition/EditorShellSessionCoordinator.h" #include "Composition/EditorShellVariant.h" -#include "Features/Console/ConsolePanel.h" -#include "Features/Hierarchy/HierarchyPanel.h" -#include "Features/Inspector/InspectorPanel.h" -#include "Features/Project/ProjectPanel.h" -#include "Features/Scene/SceneEditCommandRoute.h" -#include "Features/Scene/SceneViewportFeature.h" +#include "Features/EditorWorkspacePanelRegistry.h" #include "Rendering/Assets/BuiltInIcons.h" #include "Rendering/Viewport/ViewportHostService.h" #include "Composition/WorkspaceEventSync.h" @@ -82,7 +77,7 @@ public: const Widgets::UIEditorDockHostDropPreviewState& preview); void ClearExternalDockHostDropPreview(); - ProjectPanel::CursorKind GetHostedContentCursorKind() const; + EditorWorkspacePanelCursorKind GetHostedContentCursorKind() const; Widgets::UIEditorDockHostCursorKind GetDockCursorKind() const; bool TryResolveDockTabDragHotspot( std::string_view nodeId, @@ -97,14 +92,9 @@ public: private: ViewportHostService m_viewportHostService = {}; - SceneViewportFeature m_sceneViewportFeature = {}; BuiltInIcons m_builtInIcons = {}; Rendering::Host::UiTextureHost* m_textureHost = nullptr; - ConsolePanel m_consolePanel = {}; - HierarchyPanel m_hierarchyPanel = {}; - InspectorPanel m_inspectorPanel = {}; - ProjectPanel m_projectPanel = {}; - SceneEditCommandRoute m_sceneEditCommandRoute = {}; + EditorWorkspacePanelRuntimeSet m_workspacePanels = {}; UIEditorShellInteractionState m_shellInteractionState = {}; UIEditorShellInteractionFrame m_shellFrame = {}; std::vector m_traceEntries = {}; diff --git a/editor/app/Composition/EditorShellSessionCoordinator.cpp b/editor/app/Composition/EditorShellSessionCoordinator.cpp index 711944a2..9179f542 100644 --- a/editor/app/Composition/EditorShellSessionCoordinator.cpp +++ b/editor/app/Composition/EditorShellSessionCoordinator.cpp @@ -1,29 +1,20 @@ #include "Composition/EditorShellSessionCoordinator.h" #include "Composition/EditorContext.h" -#include "Features/Hierarchy/HierarchyPanel.h" -#include "Features/Inspector/InspectorPanel.h" -#include "Features/Project/ProjectPanel.h" -#include "Features/Scene/SceneEditCommandRoute.h" -#include "Features/Scene/SceneViewportFeature.h" +#include "Features/EditorWorkspacePanelRegistry.h" namespace XCEngine::UI::Editor::App { UIEditorShellInteractionDefinition EditorShellSessionCoordinator::PrepareShellDefinition( const EditorShellSessionCoordinatorContext& context) const { - context.hierarchyPanel.SetSceneRuntime(&context.context.GetSceneRuntime()); - context.hierarchyPanel.SetCommandFocusService(&context.context.GetCommandFocusService()); - context.sceneEditCommandRoute.BindSceneRuntime(&context.context.GetSceneRuntime()); - context.sceneViewportFeature.SetCommandFocusService( - &context.context.GetCommandFocusService()); - // Keep the previous render request available for readback-based picking during - // this update, then refresh it again after camera/navigation state changes. - context.sceneViewportFeature.SyncRenderRequest(context.context.GetSceneRuntime()); + context.workspacePanels.PrepareForShellDefinition( + context.context, + context.workspaceController); context.context.BindEditCommandRoutes( - &context.hierarchyPanel, - &context.projectPanel, - &context.sceneEditCommandRoute, - &context.inspectorPanel); + context.workspacePanels.FindCommandRoute(EditorActionRoute::Hierarchy), + context.workspacePanels.FindCommandRoute(EditorActionRoute::Project), + context.workspacePanels.FindCommandRoute(EditorActionRoute::Scene), + context.workspacePanels.FindCommandRoute(EditorActionRoute::Inspector)); context.context.SyncSessionFromWorkspace(context.workspaceController); return context.context.BuildShellDefinition( context.workspaceController, diff --git a/editor/app/Composition/EditorShellSessionCoordinator.h b/editor/app/Composition/EditorShellSessionCoordinator.h index e917782f..3bf01cbc 100644 --- a/editor/app/Composition/EditorShellSessionCoordinator.h +++ b/editor/app/Composition/EditorShellSessionCoordinator.h @@ -10,22 +10,14 @@ namespace XCEngine::UI::Editor::App { class EditorContext; -class HierarchyPanel; -class InspectorPanel; -class ProjectPanel; -class SceneEditCommandRoute; -class SceneViewportFeature; +class EditorWorkspacePanelRuntimeSet; struct EditorShellSessionCoordinatorContext { EditorContext& context; UIEditorWorkspaceController& workspaceController; std::string_view captureText; EditorShellVariant shellVariant = EditorShellVariant::Primary; - HierarchyPanel& hierarchyPanel; - InspectorPanel& inspectorPanel; - ProjectPanel& projectPanel; - SceneEditCommandRoute& sceneEditCommandRoute; - SceneViewportFeature& sceneViewportFeature; + EditorWorkspacePanelRuntimeSet& workspacePanels; }; class EditorShellSessionCoordinator final { diff --git a/editor/app/Composition/WorkspaceEventSync.cpp b/editor/app/Composition/WorkspaceEventSync.cpp index fb1f3a68..2f74b877 100644 --- a/editor/app/Composition/WorkspaceEventSync.cpp +++ b/editor/app/Composition/WorkspaceEventSync.cpp @@ -2,145 +2,13 @@ #include "Composition/EditorContext.h" -#include "Composition/EditorPanelIds.h" - -#include #include namespace XCEngine::UI::Editor::App { -namespace { - -std::string_view DescribeProjectItemKind(ProjectBrowserModel::ItemKind kind) { - switch (kind) { - case ProjectBrowserModel::ItemKind::Folder: - return "Folder"; - case ProjectBrowserModel::ItemKind::Scene: - return "Scene"; - case ProjectBrowserModel::ItemKind::Model: - return "Model"; - case ProjectBrowserModel::ItemKind::Material: - return "Material"; - case ProjectBrowserModel::ItemKind::Texture: - return "Texture"; - case ProjectBrowserModel::ItemKind::Script: - return "Script"; - case ProjectBrowserModel::ItemKind::File: - default: - return "File"; - } -} - -std::string DescribeProjectPanelEvent(const ProjectPanel::Event& event) { - std::ostringstream stream = {}; - switch (event.kind) { - case ProjectPanel::EventKind::AssetSelected: - stream << "AssetSelected"; - break; - case ProjectPanel::EventKind::AssetSelectionCleared: - stream << "AssetSelectionCleared"; - break; - case ProjectPanel::EventKind::FolderNavigated: - stream << "FolderNavigated"; - break; - case ProjectPanel::EventKind::AssetOpened: - stream << "AssetOpened"; - break; - case ProjectPanel::EventKind::RenameRequested: - stream << "RenameRequested"; - break; - case ProjectPanel::EventKind::ContextMenuRequested: - stream << "ContextMenuRequested"; - break; - case ProjectPanel::EventKind::None: - default: - stream << "None"; - break; - } - - stream << " source="; - switch (event.source) { - case ProjectPanel::EventSource::Tree: - stream << "Tree"; - break; - case ProjectPanel::EventSource::Breadcrumb: - stream << "Breadcrumb"; - break; - case ProjectPanel::EventSource::GridPrimary: - stream << "GridPrimary"; - break; - case ProjectPanel::EventSource::GridDoubleClick: - stream << "GridDoubleClick"; - break; - case ProjectPanel::EventSource::GridSecondary: - stream << "GridSecondary"; - break; - case ProjectPanel::EventSource::GridDrag: - stream << "GridDrag"; - break; - case ProjectPanel::EventSource::Command: - stream << "Command"; - break; - case ProjectPanel::EventSource::Background: - stream << "Background"; - break; - case ProjectPanel::EventSource::None: - default: - stream << "None"; - break; - } - - if (!event.itemId.empty()) { - stream << " item=" << event.itemId; - } - if (!event.displayName.empty()) { - stream << " label=" << event.displayName; - } - if (!event.itemId.empty()) { - stream << " kind=" << DescribeProjectItemKind(event.itemKind); - } - return stream.str(); -} - -std::string DescribeHierarchyPanelEvent(const HierarchyPanel::Event& event) { - std::ostringstream stream = {}; - switch (event.kind) { - case HierarchyPanel::EventKind::SelectionChanged: - stream << "SelectionChanged"; - break; - case HierarchyPanel::EventKind::Reparented: - stream << "Reparented"; - break; - case HierarchyPanel::EventKind::MovedToRoot: - stream << "MovedToRoot"; - break; - case HierarchyPanel::EventKind::RenameRequested: - stream << "RenameRequested"; - break; - case HierarchyPanel::EventKind::None: - default: - stream << "None"; - break; - } - - if (!event.itemId.empty()) { - stream << " item=" << event.itemId; - } - if (!event.targetItemId.empty()) { - stream << " target=" << event.targetItemId; - } - if (!event.label.empty()) { - stream << " label=" << event.label; - } - return stream.str(); -} - -} // namespace - std::vector SyncWorkspaceEvents( EditorContext& context, - const std::vector& hierarchyPanelEvents, - const std::vector& projectPanelEvents) { + const std::vector& panelEvents) { std::vector entries = {}; context.SyncSessionFromSelectionService(); if (const std::optional scenePath = @@ -150,16 +18,9 @@ std::vector SyncWorkspaceEvents( context.SyncSessionFromSelectionService(); } - for (const HierarchyPanel::Event& event : hierarchyPanelEvents) { - const std::string message = DescribeHierarchyPanelEvent(event); - context.SetStatus("Hierarchy", message); - entries.push_back(WorkspaceTraceEntry{ std::string(kHierarchyPanelId), std::move(message) }); - } - - for (const ProjectPanel::Event& event : projectPanelEvents) { - const std::string message = DescribeProjectPanelEvent(event); - context.SetStatus("Project", message); - entries.push_back(WorkspaceTraceEntry{ std::string(kProjectPanelId), std::move(message) }); + for (const EditorWorkspacePanelFrameEvent& event : panelEvents) { + context.SetStatus(event.status, event.message); + entries.push_back(WorkspaceTraceEntry{ event.traceChannel, event.message }); } return entries; diff --git a/editor/app/Composition/WorkspaceEventSync.h b/editor/app/Composition/WorkspaceEventSync.h index 49160d4a..bca44b03 100644 --- a/editor/app/Composition/WorkspaceEventSync.h +++ b/editor/app/Composition/WorkspaceEventSync.h @@ -1,7 +1,6 @@ #pragma once -#include "Features/Hierarchy/HierarchyPanel.h" -#include "Features/Project/ProjectPanel.h" +#include "Features/EditorWorkspacePanelRegistry.h" #include #include @@ -17,8 +16,7 @@ struct WorkspaceTraceEntry { std::vector SyncWorkspaceEvents( EditorContext& context, - const std::vector& hierarchyPanelEvents, - const std::vector& projectPanelEvents); + const std::vector& panelEvents); } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/EditorWorkspacePanelRegistry.cpp b/editor/app/Features/EditorWorkspacePanelRegistry.cpp new file mode 100644 index 00000000..a54eb86f --- /dev/null +++ b/editor/app/Features/EditorWorkspacePanelRegistry.cpp @@ -0,0 +1,610 @@ +#include "Features/EditorWorkspacePanelRegistry.h" + +#include "Composition/EditorPanelIds.h" +#include "Features/Console/ConsolePanel.h" +#include "Features/Hierarchy/HierarchyPanel.h" +#include "Features/Inspector/InspectorPanel.h" +#include "Features/Project/ProjectPanel.h" +#include "Features/Scene/SceneEditCommandRoute.h" +#include "Features/Scene/SceneViewportFeature.h" +#include "Rendering/Assets/BuiltInIcons.h" +#include "Rendering/Viewport/ViewportHostService.h" +#include "Composition/EditorContext.h" + +#include + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kSceneUpdatePriority = 0; +constexpr int kHierarchyUpdatePriority = 10; +constexpr int kProjectUpdatePriority = 20; +constexpr int kInspectorUpdatePriority = 30; +constexpr int kConsoleUpdatePriority = 40; + +const UIEditorHostedPanelDispatchEntry& ResolveHostedPanelDispatchEntry( + const UIEditorHostedPanelDispatchFrame& dispatchFrame, + std::string_view panelId) { + static const UIEditorHostedPanelDispatchEntry kEmptyEntry = {}; + if (const UIEditorHostedPanelDispatchEntry* entry = + FindUIEditorHostedPanelDispatchEntry(dispatchFrame, panelId); + entry != nullptr) { + return *entry; + } + + return kEmptyEntry; +} + +std::string_view DescribeProjectItemKind(ProjectBrowserModel::ItemKind kind) { + switch (kind) { + case ProjectBrowserModel::ItemKind::Folder: + return "Folder"; + case ProjectBrowserModel::ItemKind::Scene: + return "Scene"; + case ProjectBrowserModel::ItemKind::Model: + return "Model"; + case ProjectBrowserModel::ItemKind::Material: + return "Material"; + case ProjectBrowserModel::ItemKind::Texture: + return "Texture"; + case ProjectBrowserModel::ItemKind::Script: + return "Script"; + case ProjectBrowserModel::ItemKind::File: + default: + return "File"; + } +} + +std::string DescribeProjectPanelEvent(const ProjectPanel::Event& event) { + std::ostringstream stream = {}; + switch (event.kind) { + case ProjectPanel::EventKind::AssetSelected: + stream << "AssetSelected"; + break; + case ProjectPanel::EventKind::AssetSelectionCleared: + stream << "AssetSelectionCleared"; + break; + case ProjectPanel::EventKind::FolderNavigated: + stream << "FolderNavigated"; + break; + case ProjectPanel::EventKind::AssetOpened: + stream << "AssetOpened"; + break; + case ProjectPanel::EventKind::RenameRequested: + stream << "RenameRequested"; + break; + case ProjectPanel::EventKind::ContextMenuRequested: + stream << "ContextMenuRequested"; + break; + case ProjectPanel::EventKind::None: + default: + stream << "None"; + break; + } + + stream << " source="; + switch (event.source) { + case ProjectPanel::EventSource::Tree: + stream << "Tree"; + break; + case ProjectPanel::EventSource::Breadcrumb: + stream << "Breadcrumb"; + break; + case ProjectPanel::EventSource::GridPrimary: + stream << "GridPrimary"; + break; + case ProjectPanel::EventSource::GridDoubleClick: + stream << "GridDoubleClick"; + break; + case ProjectPanel::EventSource::GridSecondary: + stream << "GridSecondary"; + break; + case ProjectPanel::EventSource::GridDrag: + stream << "GridDrag"; + break; + case ProjectPanel::EventSource::Command: + stream << "Command"; + break; + case ProjectPanel::EventSource::Background: + stream << "Background"; + break; + case ProjectPanel::EventSource::None: + default: + stream << "None"; + break; + } + + if (!event.itemId.empty()) { + stream << " item=" << event.itemId; + } + if (!event.displayName.empty()) { + stream << " label=" << event.displayName; + } + if (!event.itemId.empty()) { + stream << " kind=" << DescribeProjectItemKind(event.itemKind); + } + return stream.str(); +} + +std::string DescribeHierarchyPanelEvent(const HierarchyPanel::Event& event) { + std::ostringstream stream = {}; + switch (event.kind) { + case HierarchyPanel::EventKind::SelectionChanged: + stream << "SelectionChanged"; + break; + case HierarchyPanel::EventKind::Reparented: + stream << "Reparented"; + break; + case HierarchyPanel::EventKind::MovedToRoot: + stream << "MovedToRoot"; + break; + case HierarchyPanel::EventKind::RenameRequested: + stream << "RenameRequested"; + break; + case HierarchyPanel::EventKind::None: + default: + stream << "None"; + break; + } + + if (!event.itemId.empty()) { + stream << " item=" << event.itemId; + } + if (!event.targetItemId.empty()) { + stream << " target=" << event.targetItemId; + } + if (!event.label.empty()) { + stream << " label=" << event.label; + } + return stream.str(); +} + +template +void AppendDrawPacket( + ::XCEngine::UI::UIDrawData& drawData, + std::string debugName, + AppendFn&& appendFn) { + ::XCEngine::UI::UIDrawList drawList(std::move(debugName)); + appendFn(drawList); + if (!drawList.Empty()) { + drawData.AddDrawList(std::move(drawList)); + } +} + +class ConsoleWorkspacePanel final : public EditorWorkspacePanel { +public: + std::string_view GetPanelId() const override { + return kConsolePanelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Console"; + } + + EditorActionRoute GetActionRoute() const override { + return EditorActionRoute::Console; + } + + EditorWorkspacePanelUpdatePhase GetUpdatePhase() const override { + return EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync; + } + + int GetUpdatePriority() const override { + return kConsoleUpdatePriority; + } + + void Update(const EditorWorkspacePanelUpdateContext& context) override { + m_panel.Update( + context.context.GetSession(), + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId())); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + +private: + ConsolePanel m_panel = {}; +}; + +class HierarchyWorkspacePanel final : public EditorWorkspacePanel { +public: + std::string_view GetPanelId() const override { + return kHierarchyPanelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Hierarchy"; + } + + EditorActionRoute GetActionRoute() const override { + return EditorActionRoute::Hierarchy; + } + + int GetUpdatePriority() const override { + return kHierarchyUpdatePriority; + } + + void Initialize(const EditorWorkspacePanelInitializationContext& context) override { + m_panel.SetBuiltInIcons(&context.builtInIcons); + m_panel.SetTextMeasurer(&context.textMeasurer); + m_panel.Initialize(); + } + + void ResetInteractionState() override { + m_panel.ResetInteractionState(); + } + + void PrepareForShellDefinition( + EditorContext& context, + UIEditorWorkspaceController&) override { + m_panel.SetSceneRuntime(&context.GetSceneRuntime()); + m_panel.SetCommandFocusService(&context.GetCommandFocusService()); + } + + void Update(const EditorWorkspacePanelUpdateContext& context) override { + m_panel.Update( + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + bool HasActivePointerCapture() const override { + return m_panel.HasActivePointerCapture(); + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + + std::vector CollectFrameEvents() const override { + std::vector events = {}; + for (const HierarchyPanel::Event& event : m_panel.GetFrameEvents()) { + events.push_back(EditorWorkspacePanelFrameEvent{ + .status = std::string(kHierarchyPanelTitle), + .traceChannel = std::string(kHierarchyPanelId), + .message = DescribeHierarchyPanelEvent(event), + }); + } + return events; + } + +private: + HierarchyPanel m_panel = {}; +}; + +class InspectorWorkspacePanel final : public EditorWorkspacePanel { +public: + std::string_view GetPanelId() const override { + return kInspectorPanelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Inspector"; + } + + EditorActionRoute GetActionRoute() const override { + return EditorActionRoute::Inspector; + } + + int GetUpdatePriority() const override { + return kInspectorUpdatePriority; + } + + void Update(const EditorWorkspacePanelUpdateContext& context) override { + m_panel.SetCommandFocusService(&context.context.GetCommandFocusService()); + m_panel.Update( + context.context, + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + +private: + InspectorPanel m_panel = {}; +}; + +class ProjectWorkspacePanel final : public EditorWorkspacePanel { +public: + std::string_view GetPanelId() const override { + return kProjectPanelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Project"; + } + + EditorActionRoute GetActionRoute() const override { + return EditorActionRoute::Project; + } + + int GetUpdatePriority() const override { + return kProjectUpdatePriority; + } + + void Initialize(const EditorWorkspacePanelInitializationContext& context) override { + m_panel.SetBuiltInIcons(&context.builtInIcons); + m_panel.SetTextMeasurer(&context.textMeasurer); + } + + void ResetInteractionState() override { + m_panel.ResetInteractionState(); + } + + void Update(const EditorWorkspacePanelUpdateContext& context) override { + m_panel.SetProjectRuntime(&context.context.GetProjectRuntime()); + m_panel.SetCommandFocusService(&context.context.GetCommandFocusService()); + m_panel.SetSystemInteractionHost(context.context.GetSystemInteractionHost()); + m_panel.Update( + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + bool HasActivePointerCapture() const override { + return m_panel.HasActivePointerCapture(); + } + + EditorWorkspacePanelCursorKind GetCursorKind() const override { + switch (m_panel.GetCursorKind()) { + case ProjectPanel::CursorKind::ResizeEW: + return EditorWorkspacePanelCursorKind::ResizeEW; + case ProjectPanel::CursorKind::Arrow: + default: + return EditorWorkspacePanelCursorKind::Arrow; + } + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + + std::vector CollectFrameEvents() const override { + std::vector events = {}; + for (const ProjectPanel::Event& event : m_panel.GetFrameEvents()) { + events.push_back(EditorWorkspacePanelFrameEvent{ + .status = std::string(kProjectPanelTitle), + .traceChannel = std::string(kProjectPanelId), + .message = DescribeProjectPanelEvent(event), + }); + } + return events; + } + +private: + ProjectPanel m_panel = {}; +}; + +class SceneWorkspacePanel final : public EditorWorkspacePanel { +public: + std::string_view GetPanelId() const override { + return kScenePanelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.SceneOverlay"; + } + + EditorActionRoute GetActionRoute() const override { + return EditorActionRoute::Scene; + } + + int GetUpdatePriority() const override { + return kSceneUpdatePriority; + } + + void Initialize(const EditorWorkspacePanelInitializationContext& context) override { + m_feature.Initialize( + context.repoRoot, + context.textureHost, + &context.builtInIcons, + context.viewportHostService); + } + + void Shutdown(const EditorWorkspacePanelShutdownContext& context) override { + m_feature.Shutdown(context.textureHost, context.viewportHostService); + m_commandRoute.BindSceneRuntime(nullptr); + } + + void ResetInteractionState() override { + m_feature.ResetInteractionState(); + } + + void PrepareForShellDefinition( + EditorContext& context, + UIEditorWorkspaceController&) override { + m_commandRoute.BindSceneRuntime(&context.GetSceneRuntime()); + m_feature.SetCommandFocusService(&context.GetCommandFocusService()); + m_feature.SyncRenderRequest(context.GetSceneRuntime()); + } + + void Update(const EditorWorkspacePanelUpdateContext& context) override { + m_feature.Update( + context.context.GetSceneRuntime(), + context.shellInteractionState.workspaceInteractionState.composeState, + context.shellFrame.workspaceInteractionFrame.composeFrame); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_feature.Append(drawList); + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_commandRoute; + } + +private: + SceneViewportFeature m_feature = {}; + SceneEditCommandRoute m_commandRoute = {}; +}; + +} // namespace + +void EditorWorkspacePanelRuntimeSet::AddPanel( + std::unique_ptr panel) { + if (panel != nullptr) { + m_panels.push_back(std::move(panel)); + } +} + +void EditorWorkspacePanelRuntimeSet::Initialize( + const EditorWorkspacePanelInitializationContext& context) { + for (const std::unique_ptr& panel : m_panels) { + panel->Initialize(context); + } +} + +void EditorWorkspacePanelRuntimeSet::Shutdown( + const EditorWorkspacePanelShutdownContext& context) { + for (const std::unique_ptr& panel : m_panels) { + panel->Shutdown(context); + } +} + +void EditorWorkspacePanelRuntimeSet::ResetInteractionState() { + for (const std::unique_ptr& panel : m_panels) { + panel->ResetInteractionState(); + } +} + +void EditorWorkspacePanelRuntimeSet::PrepareForShellDefinition( + EditorContext& context, + UIEditorWorkspaceController& workspaceController) { + for (const std::unique_ptr& panel : m_panels) { + panel->PrepareForShellDefinition(context, workspaceController); + } +} + +void EditorWorkspacePanelRuntimeSet::Update( + const EditorWorkspacePanelUpdateContext& context) { + const std::vector mainPanels = + BuildUpdateOrder(EditorWorkspacePanelUpdatePhase::Main); + for (EditorWorkspacePanel* panel : mainPanels) { + panel->Update(context); + } + + context.context.SyncSessionFromCommandFocusService(); + + const std::vector afterFocusSyncPanels = + BuildUpdateOrder(EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync); + for (EditorWorkspacePanel* panel : afterFocusSyncPanels) { + panel->Update(context); + } +} + +void EditorWorkspacePanelRuntimeSet::AppendDrawPackets( + ::XCEngine::UI::UIDrawData& drawData) const { + for (const std::unique_ptr& panel : m_panels) { + AppendDrawPacket( + drawData, + std::string(panel->GetDrawListId()), + [&](::XCEngine::UI::UIDrawList& drawList) { + panel->Append(drawList); + }); + } +} + +bool EditorWorkspacePanelRuntimeSet::HasActivePointerCapture() const { + for (const std::unique_ptr& panel : m_panels) { + if (panel->HasActivePointerCapture()) { + return true; + } + } + + return false; +} + +EditorWorkspacePanelCursorKind +EditorWorkspacePanelRuntimeSet::GetHostedContentCursorKind() const { + for (const std::unique_ptr& panel : m_panels) { + const EditorWorkspacePanelCursorKind cursorKind = panel->GetCursorKind(); + if (cursorKind != EditorWorkspacePanelCursorKind::Arrow) { + return cursorKind; + } + } + + return EditorWorkspacePanelCursorKind::Arrow; +} + +EditorEditCommandRoute* EditorWorkspacePanelRuntimeSet::FindCommandRoute( + EditorActionRoute route) { + for (const std::unique_ptr& panel : m_panels) { + if (panel->GetActionRoute() == route) { + return panel->GetEditCommandRoute(); + } + } + + return nullptr; +} + +std::vector +EditorWorkspacePanelRuntimeSet::CollectFrameEvents() const { + std::vector events = {}; + for (const std::unique_ptr& panel : m_panels) { + std::vector panelEvents = + panel->CollectFrameEvents(); + events.insert( + events.end(), + std::make_move_iterator(panelEvents.begin()), + std::make_move_iterator(panelEvents.end())); + } + return events; +} + +std::vector EditorWorkspacePanelRuntimeSet::BuildUpdateOrder( + EditorWorkspacePanelUpdatePhase phase) const { + std::vector panels = {}; + panels.reserve(m_panels.size()); + for (const std::unique_ptr& panel : m_panels) { + if (panel->GetUpdatePhase() == phase) { + panels.push_back(panel.get()); + } + } + + std::sort( + panels.begin(), + panels.end(), + [](const EditorWorkspacePanel* left, const EditorWorkspacePanel* right) { + return left->GetUpdatePriority() < right->GetUpdatePriority(); + }); + return panels; +} + +EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet() { + EditorWorkspacePanelRuntimeSet panels = {}; + panels.AddPanel(std::make_unique()); + panels.AddPanel(std::make_unique()); + panels.AddPanel(std::make_unique()); + panels.AddPanel(std::make_unique()); + panels.AddPanel(std::make_unique()); + return panels; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/EditorWorkspacePanelRegistry.h b/editor/app/Features/EditorWorkspacePanelRegistry.h new file mode 100644 index 00000000..11748c62 --- /dev/null +++ b/editor/app/Features/EditorWorkspacePanelRegistry.h @@ -0,0 +1,145 @@ +#pragma once + +#include "Commands/EditorEditCommandRoute.h" +#include "State/EditorSession.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace XCEngine::UI { + +struct UIInputEvent; + +} // namespace XCEngine::UI + +namespace XCEngine::UI::Editor::Rendering::Host { + +class UiTextureHost; + +} // namespace XCEngine::UI::Editor::Rendering::Host + +namespace XCEngine::UI::Editor::App { + +class BuiltInIcons; +class EditorContext; +class ViewportHostService; + +enum class EditorWorkspacePanelCursorKind : std::uint8_t { + Arrow = 0, + ResizeEW +}; + +enum class EditorWorkspacePanelUpdatePhase : std::uint8_t { + Main = 0, + AfterCommandFocusSync +}; + +struct EditorWorkspacePanelFrameEvent { + std::string status = {}; + std::string traceChannel = {}; + std::string message = {}; +}; + +struct EditorWorkspacePanelInitializationContext { + const std::filesystem::path& repoRoot; + Rendering::Host::UiTextureHost& textureHost; + UIEditorTextMeasurer& textMeasurer; + BuiltInIcons& builtInIcons; + ViewportHostService& viewportHostService; +}; + +struct EditorWorkspacePanelShutdownContext { + Rendering::Host::UiTextureHost& textureHost; + ViewportHostService& viewportHostService; +}; + +struct EditorWorkspacePanelUpdateContext { + EditorContext& context; + UIEditorShellInteractionFrame& shellFrame; + UIEditorShellInteractionState& shellInteractionState; + const std::vector<::XCEngine::UI::UIInputEvent>& hostedContentEvents; +}; + +class EditorWorkspacePanel { +public: + virtual ~EditorWorkspacePanel() = default; + + virtual std::string_view GetPanelId() const = 0; + virtual std::string_view GetDrawListId() const = 0; + virtual EditorActionRoute GetActionRoute() const = 0; + + virtual void Initialize(const EditorWorkspacePanelInitializationContext&) {} + virtual void Shutdown(const EditorWorkspacePanelShutdownContext&) {} + virtual void ResetInteractionState() {} + virtual void PrepareForShellDefinition( + EditorContext& context, + UIEditorWorkspaceController& workspaceController) { + (void)context; + (void)workspaceController; + } + virtual EditorWorkspacePanelUpdatePhase GetUpdatePhase() const { + return EditorWorkspacePanelUpdatePhase::Main; + } + virtual int GetUpdatePriority() const { + return 0; + } + virtual void Update(const EditorWorkspacePanelUpdateContext& context) = 0; + virtual void Append(::XCEngine::UI::UIDrawList& drawList) const = 0; + virtual bool HasActivePointerCapture() const { + return false; + } + virtual EditorWorkspacePanelCursorKind GetCursorKind() const { + return EditorWorkspacePanelCursorKind::Arrow; + } + virtual EditorEditCommandRoute* GetEditCommandRoute() { + return nullptr; + } + virtual std::vector CollectFrameEvents() const { + return {}; + } +}; + +class EditorWorkspacePanelRuntimeSet final { +public: + EditorWorkspacePanelRuntimeSet() = default; + EditorWorkspacePanelRuntimeSet(const EditorWorkspacePanelRuntimeSet&) = delete; + EditorWorkspacePanelRuntimeSet& operator=(const EditorWorkspacePanelRuntimeSet&) = delete; + EditorWorkspacePanelRuntimeSet(EditorWorkspacePanelRuntimeSet&&) noexcept = default; + EditorWorkspacePanelRuntimeSet& operator=(EditorWorkspacePanelRuntimeSet&&) noexcept = default; + + void AddPanel(std::unique_ptr panel); + void Initialize(const EditorWorkspacePanelInitializationContext& context); + void Shutdown(const EditorWorkspacePanelShutdownContext& context); + void ResetInteractionState(); + void PrepareForShellDefinition( + EditorContext& context, + UIEditorWorkspaceController& workspaceController); + void Update(const EditorWorkspacePanelUpdateContext& context); + void AppendDrawPackets(::XCEngine::UI::UIDrawData& drawData) const; + + bool HasActivePointerCapture() const; + EditorWorkspacePanelCursorKind GetHostedContentCursorKind() const; + EditorEditCommandRoute* FindCommandRoute(EditorActionRoute route); + std::vector CollectFrameEvents() const; + +private: + std::vector BuildUpdateOrder( + EditorWorkspacePanelUpdatePhase phase) const; + + std::vector> m_panels = {}; +}; + +EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp index 723f968b..856256d8 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp +++ b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp @@ -9,11 +9,12 @@ namespace XCEngine::UI::Editor::App { namespace { -EditorWindowContentCursorKind ToContentCursorKind(ProjectPanel::CursorKind cursorKind) { +EditorWindowContentCursorKind ToContentCursorKind( + EditorWorkspacePanelCursorKind cursorKind) { switch (cursorKind) { - case ProjectPanel::CursorKind::ResizeEW: + case EditorWorkspacePanelCursorKind::ResizeEW: return EditorWindowContentCursorKind::ResizeEW; - case ProjectPanel::CursorKind::Arrow: + case EditorWorkspacePanelCursorKind::Arrow: default: return EditorWindowContentCursorKind::Arrow; }