refactor(new_editor): snapshot hosted editor restructuring

This commit is contained in:
2026-04-21 00:57:14 +08:00
parent e123e584c8
commit 9b7b369007
248 changed files with 21152 additions and 14397 deletions

View File

@@ -1,22 +1,50 @@
#include "Composition/EditorShellRuntime.h"
#include "Composition/EditorShellRuntime.h"
#include "Ports/TexturePort.h"
#include "Ports/ViewportRenderPort.h"
#include "Composition/EditorContext.h"
#include "Composition/EditorPanelIds.h"
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEditor/Foundation/UIEditorTheme.h>
#include <algorithm>
#include "Features/PanelInputContext.h"
namespace XCEngine::UI::Editor::App {
void ApplyViewportFramesToShellFrame(
UIEditorShellInteractionFrame& shellFrame,
ViewportHostService& viewportHostService);
std::vector<::XCEngine::UI::UIInputEvent> FilterShellInputEventsForHostedContentCapture(
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents);
bool ShouldHostedContentYieldPointerStream(
const UIEditorShellInteractionFrame& shellFrame,
bool shellInteractiveCaptureActive);
std::vector<::XCEngine::UI::UIInputEvent> FilterHostedContentInputEventsForShellOwnership(
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
bool shellOwnsPointerStream);
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
const UIEditorShellInteractionFrame& frame);
void AppendShellPopups(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellInteractionFrame& frame,
const UIEditorShellInteractionPalette& palette,
const UIEditorShellInteractionMetrics& metrics);
void EditorShellRuntime::Initialize(
const std::filesystem::path& repoRoot,
Ports::TexturePort& textureHost,
UIEditorTextMeasurer& textMeasurer) {
m_textureHost = &textureHost;
m_builtInIcons.Initialize(textureHost);
m_sceneViewportFeature.Initialize(repoRoot, textureHost, m_viewportHostService);
m_sceneViewportFeature.Initialize(
repoRoot,
textureHost,
&m_builtInIcons,
m_viewportHostService);
m_hierarchyPanel.SetBuiltInIcons(&m_builtInIcons);
m_projectPanel.SetBuiltInIcons(&m_builtInIcons);
m_projectPanel.SetTextMeasurer(&textMeasurer);
@@ -59,6 +87,7 @@ void EditorShellRuntime::ResetInteractionState() {
m_traceEntries.clear();
m_sceneViewportFeature.ResetInteractionState();
m_hierarchyPanel.ResetInteractionState();
m_colorPickerPanel.ResetInteractionState();
m_projectPanel.ResetInteractionState();
}
@@ -106,6 +135,26 @@ Widgets::UIEditorDockHostCursorKind EditorShellRuntime::GetDockCursorKind() cons
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout);
}
bool EditorShellRuntime::TryResolveDockTabDragHotspot(
std::string_view nodeId,
std::string_view panelId,
const ::XCEngine::UI::UIPoint& point,
::XCEngine::UI::UIPoint& outHotspot) const {
return TryResolveUIEditorDockHostTabDragHotspot(
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout,
nodeId,
panelId,
point,
outHotspot);
}
UIEditorDockHostTabDropTarget EditorShellRuntime::ResolveDockTabDropTarget(
const ::XCEngine::UI::UIPoint& point) const {
return ResolveUIEditorDockHostTabDropTarget(
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout,
point);
}
EditorShellPointerOwner EditorShellRuntime::GetPointerOwner() const {
const auto& dockHostInteractionState =
m_shellInteractionState.workspaceInteractionState.dockHostInteractionState;
@@ -159,3 +208,465 @@ bool EditorShellRuntime::HasInteractiveCapture() const {
}
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::App {
using ::XCEngine::UI::UIDrawList;
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
const UIEditorShellInteractionFrame& frame) {
UIEditorShellComposeModel model = {};
model.menuBarItems = frame.request.menuBarItems;
model.toolbarButtons = frame.model.toolbarButtons;
model.statusSegments = frame.model.statusSegments;
model.workspacePresentations = frame.model.workspacePresentations;
return model;
}
void AppendShellPopups(
UIDrawList& drawList,
const UIEditorShellInteractionFrame& frame,
const UIEditorShellInteractionPalette& palette,
const UIEditorShellInteractionMetrics& metrics) {
const std::size_t popupCount =
(std::min)(frame.request.popupRequests.size(), frame.popupFrames.size());
for (std::size_t index = 0; index < popupCount; ++index) {
const UIEditorShellInteractionPopupRequest& popupRequest =
frame.request.popupRequests[index];
const UIEditorShellInteractionPopupFrame& popupFrame =
frame.popupFrames[index];
Widgets::AppendUIEditorMenuPopupBackground(
drawList,
popupRequest.layout,
popupRequest.widgetItems,
popupFrame.popupState,
palette.popupPalette,
metrics.popupMetrics);
Widgets::AppendUIEditorMenuPopupForeground(
drawList,
popupRequest.layout,
popupRequest.widgetItems,
popupFrame.popupState,
palette.popupPalette,
metrics.popupMetrics);
}
}
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::App {
using ::XCEngine::UI::UIDrawList;
void EditorShellRuntime::RenderRequestedViewports(
const ::XCEngine::Rendering::RenderContext& renderContext) {
m_viewportHostService.RenderRequestedViewports(renderContext);
}
void EditorShellRuntime::Append(UIDrawList& drawList) const {
const auto& metrics = ResolveUIEditorShellInteractionMetrics();
const auto& palette = ResolveUIEditorShellInteractionPalette();
const UIEditorShellComposeModel shellComposeModel =
BuildShellComposeModelFromFrame(m_shellFrame);
AppendUIEditorShellCompose(
drawList,
m_shellFrame.shellFrame,
shellComposeModel,
m_shellInteractionState.composeState,
palette.shellPalette,
metrics.shellMetrics,
&m_builtInIcons);
m_consolePanel.Append(drawList);
m_colorPickerPanel.Append(drawList);
m_hierarchyPanel.Append(drawList);
m_inspectorPanel.Append(drawList);
m_projectPanel.Append(drawList);
m_sceneViewportFeature.Append(drawList);
AppendUIEditorShellComposeOverlay(
drawList,
m_shellFrame.shellFrame,
palette.shellPalette,
metrics.shellMetrics);
AppendShellPopups(drawList, m_shellFrame, palette, metrics);
}
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::App {
namespace {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using Widgets::UIEditorDockHostHitTargetKind;
bool IsPointerInputEventType(UIInputEventType type) {
switch (type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
return true;
default:
return false;
}
}
const UIEditorPanelDescriptor* ResolveSingleVisibleToolWindowPanelDescriptor(
const UIEditorWorkspaceController& workspaceController) {
const UIEditorWorkspaceNode& root = workspaceController.GetWorkspace().root;
if (root.kind != UIEditorWorkspaceNodeKind::TabStack) {
return nullptr;
}
const UIEditorWorkspaceSession& session = workspaceController.GetSession();
const UIEditorPanelRegistry& panelRegistry = workspaceController.GetPanelRegistry();
const UIEditorPanelDescriptor* visibleDescriptor = nullptr;
std::size_t visibleCount = 0u;
for (const UIEditorWorkspaceNode& child : root.children) {
if (child.kind != UIEditorWorkspaceNodeKind::Panel) {
continue;
}
const UIEditorPanelSessionState* panelState =
FindUIEditorPanelSessionState(session, child.panel.panelId);
if (panelState == nullptr || !panelState->open || !panelState->visible) {
continue;
}
const UIEditorPanelDescriptor* descriptor =
FindUIEditorPanelDescriptor(panelRegistry, child.panel.panelId);
if (descriptor == nullptr) {
return nullptr;
}
++visibleCount;
visibleDescriptor = descriptor;
if (visibleCount > 1u) {
return nullptr;
}
}
return visibleDescriptor != nullptr && visibleDescriptor->toolWindow
? visibleDescriptor
: nullptr;
}
PanelInputContext BuildHostedPanelInputContext(
const UIEditorWorkspaceInteractionFrame& workspaceFrame,
bool allowInteraction,
std::string_view panelId) {
PanelInputContext inputContext = {};
inputContext.allowInteraction = allowInteraction;
inputContext.hasInputFocus =
IsUIEditorWorkspaceHostedPanelInputOwner(workspaceFrame.inputOwner, panelId);
inputContext.focusGained =
!IsUIEditorWorkspaceHostedPanelInputOwner(workspaceFrame.previousInputOwner, panelId) &&
inputContext.hasInputFocus;
inputContext.focusLost =
IsUIEditorWorkspaceHostedPanelInputOwner(workspaceFrame.previousInputOwner, panelId) &&
!inputContext.hasInputFocus;
return inputContext;
}
} // namespace
std::vector<UIInputEvent> FilterShellInputEventsForHostedContentCapture(
const std::vector<UIInputEvent>& inputEvents) {
std::vector<UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size());
for (const UIInputEvent& event : inputEvents) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
break;
default:
filteredEvents.push_back(event);
break;
}
}
return filteredEvents;
}
bool ShouldHostedContentYieldPointerStream(
const UIEditorShellInteractionFrame& shellFrame,
bool shellInteractiveCaptureActive) {
if (shellInteractiveCaptureActive ||
shellFrame.result.requestPointerCapture ||
shellFrame.result.releasePointerCapture) {
return true;
}
return shellFrame.result.workspaceResult.dockHostResult.hitTarget.kind ==
UIEditorDockHostHitTargetKind::SplitterHandle;
}
std::vector<UIInputEvent> FilterHostedContentInputEventsForShellOwnership(
const std::vector<UIInputEvent>& inputEvents,
bool shellOwnsPointerStream) {
if (!shellOwnsPointerStream) {
return inputEvents;
}
std::vector<UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size() + 1u);
bool strippedPointerInput = false;
UIInputEvent lastPointerEvent = {};
for (const UIInputEvent& event : inputEvents) {
if (IsPointerInputEventType(event.type)) {
strippedPointerInput = true;
lastPointerEvent = event;
continue;
}
filteredEvents.push_back(event);
}
if (strippedPointerInput) {
UIInputEvent leaveEvent = {};
leaveEvent.type = UIInputEventType::PointerLeave;
leaveEvent.position = lastPointerEvent.position;
leaveEvent.modifiers = lastPointerEvent.modifiers;
filteredEvents.push_back(leaveEvent);
}
return filteredEvents;
}
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::App {
void EditorShellRuntime::Update(
EditorContext& context,
UIEditorWorkspaceController& workspaceController,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
std::string_view captureText,
EditorShellVariant shellVariant,
bool useDetachedTitleBarTabStrip,
float detachedTitleBarTabHeight,
float detachedWindowChromeHeight) {
UIEditorShellInteractionMetrics metrics = ResolveUIEditorShellInteractionMetrics();
if (const UIEditorPanelDescriptor* toolWindowDescriptor =
ResolveSingleVisibleToolWindowPanelDescriptor(workspaceController);
toolWindowDescriptor != nullptr) {
metrics.shellMetrics.dockHostMetrics.tabStripMetrics.layoutMetrics.headerHeight = 0.0f;
const float minimumContentWidth =
toolWindowDescriptor->minimumDetachedWindowSize.width > 0.0f
? toolWindowDescriptor->minimumDetachedWindowSize.width
: metrics.shellMetrics.dockHostMetrics.minimumStandalonePanelBodySize.width;
const float minimumContentHeight =
toolWindowDescriptor->minimumDetachedWindowSize.height > detachedWindowChromeHeight
? toolWindowDescriptor->minimumDetachedWindowSize.height -
detachedWindowChromeHeight
: metrics.shellMetrics.dockHostMetrics.minimumStandalonePanelBodySize.height;
metrics.shellMetrics.dockHostMetrics.minimumStandalonePanelBodySize =
::XCEngine::UI::UISize(minimumContentWidth, minimumContentHeight);
metrics.shellMetrics.dockHostMetrics.minimumTabContentBodySize =
metrics.shellMetrics.dockHostMetrics.minimumStandalonePanelBodySize;
}
if (useDetachedTitleBarTabStrip && detachedTitleBarTabHeight > 0.0f) {
metrics.shellMetrics.dockHostMetrics.tabStripMetrics.layoutMetrics.headerHeight =
detachedTitleBarTabHeight;
}
const UIEditorWorkspaceLayoutSnapshot preUpdateSnapshot =
workspaceController.CaptureLayoutSnapshot();
const Widgets::UIEditorDockHostLayout preUpdateDockLayout =
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout;
m_hierarchyPanel.SetSceneRuntime(&context.GetSceneRuntime());
m_hierarchyPanel.SetCommandFocusService(&context.GetCommandFocusService());
m_sceneEditCommandRoute.BindSceneRuntime(&context.GetSceneRuntime());
m_sceneViewportFeature.SetCommandFocusService(&context.GetCommandFocusService());
// Keep the previous render request available for readback-based picking during
// this update, then refresh it again after camera/navigation state changes.
m_sceneViewportFeature.SyncRenderRequest(context.GetSceneRuntime());
context.BindEditCommandRoutes(
&m_hierarchyPanel,
&m_projectPanel,
&m_sceneEditCommandRoute,
&m_inspectorPanel);
context.SyncSessionFromWorkspace(workspaceController);
UIEditorShellInteractionDefinition definition =
context.BuildShellDefinition(workspaceController, captureText, shellVariant);
m_viewportHostService.BeginFrame();
const std::vector<::XCEngine::UI::UIInputEvent> shellEvents =
HasHostedContentCapture()
? FilterShellInputEventsForHostedContentCapture(inputEvents)
: inputEvents;
m_shellFrame = UpdateUIEditorShellInteraction(
m_shellInteractionState,
workspaceController,
bounds,
definition,
shellEvents,
context.GetShellServices(),
metrics);
if (TryApplyUIEditorWorkspaceSplitterDragCorrection(
m_splitterDragCorrectionState,
m_shellInteractionState.workspaceInteractionState.dockHostInteractionState,
preUpdateSnapshot,
preUpdateDockLayout,
workspaceController,
metrics.shellMetrics.dockHostMetrics)) {
context.SyncSessionFromWorkspace(workspaceController);
definition =
context.BuildShellDefinition(workspaceController, captureText, shellVariant);
m_shellFrame = UpdateUIEditorShellInteraction(
m_shellInteractionState,
workspaceController,
bounds,
definition,
{},
context.GetShellServices(),
metrics);
}
const bool shellOwnsHostedContentPointerStream =
ShouldHostedContentYieldPointerStream(m_shellFrame, HasShellInteractiveCapture());
const std::vector<::XCEngine::UI::UIInputEvent> hostedContentEvents =
FilterHostedContentInputEventsForShellOwnership(
inputEvents,
shellOwnsHostedContentPointerStream);
m_sceneViewportFeature.Update(
context.GetSceneRuntime(),
m_shellInteractionState.workspaceInteractionState.composeState,
m_shellFrame.workspaceInteractionFrame.composeFrame);
ApplyViewportFramesToShellFrame(m_shellFrame, m_viewportHostService);
context.SyncSessionFromWorkspace(workspaceController);
context.UpdateStatusFromShellResult(workspaceController, m_shellFrame.result);
const bool allowHostedInteraction = !m_shellFrame.result.workspaceInputSuppressed;
const PanelInputContext hierarchyInputContext = BuildHostedPanelInputContext(
m_shellFrame.workspaceInteractionFrame,
allowHostedInteraction,
kHierarchyPanelId);
const PanelInputContext projectInputContext = BuildHostedPanelInputContext(
m_shellFrame.workspaceInteractionFrame,
allowHostedInteraction,
kProjectPanelId);
const PanelInputContext inspectorInputContext = BuildHostedPanelInputContext(
m_shellFrame.workspaceInteractionFrame,
allowHostedInteraction,
kInspectorPanelId);
const PanelInputContext colorPickerInputContext = BuildHostedPanelInputContext(
m_shellFrame.workspaceInteractionFrame,
allowHostedInteraction,
kColorPickerPanelId);
m_projectPanel.SetProjectRuntime(&context.GetProjectRuntime());
m_projectPanel.SetCommandFocusService(&context.GetCommandFocusService());
m_projectPanel.SetSystemInteractionHost(context.GetSystemInteractionHost());
m_inspectorPanel.SetCommandFocusService(&context.GetCommandFocusService());
m_hierarchyPanel.Update(
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
hierarchyInputContext);
m_projectPanel.Update(
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
projectInputContext);
m_traceEntries = SyncWorkspaceEvents(context, *this);
m_colorPickerPanel.Update(
context,
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
colorPickerInputContext);
m_inspectorPanel.Update(
context,
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
inspectorInputContext);
context.SyncSessionFromCommandFocusService();
m_consolePanel.Update(
context.GetSession(),
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame);
}
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::App {
namespace {
bool IsViewportPanel(std::string_view panelId) {
return IsEditorViewportPanelId(panelId);
}
void ApplyViewportFrameToPresentation(
const ViewportFrame& viewportFrame,
UIEditorWorkspacePanelPresentationModel& presentation) {
presentation.viewportShellModel.frame.texture = viewportFrame.texture;
presentation.viewportShellModel.frame.requestedSize = viewportFrame.requestedSize;
presentation.viewportShellModel.frame.presentedSize = viewportFrame.renderSize;
presentation.viewportShellModel.frame.hasTexture = viewportFrame.hasTexture;
presentation.viewportShellModel.frame.statusText = viewportFrame.statusText;
}
void ApplyViewportFrameToShellModel(
const ViewportFrame& viewportFrame,
UIEditorViewportShellModel& shellModel) {
shellModel.frame.texture = viewportFrame.texture;
shellModel.frame.requestedSize = viewportFrame.requestedSize;
shellModel.frame.presentedSize = viewportFrame.renderSize;
shellModel.frame.hasTexture = viewportFrame.hasTexture;
shellModel.frame.statusText = viewportFrame.statusText;
}
UIEditorWorkspacePanelPresentationModel* FindMutableWorkspacePresentation(
std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
std::string_view panelId) {
for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) {
if (presentation.panelId == panelId) {
return &presentation;
}
}
return nullptr;
}
} // namespace
void ApplyViewportFramesToShellFrame(
UIEditorShellInteractionFrame& shellFrame,
ViewportHostService& viewportHostService) {
auto applyToViewportFrames =
[&](std::vector<UIEditorWorkspaceViewportComposeFrame>& viewportFrames) {
for (UIEditorWorkspaceViewportComposeFrame& viewportComposeFrame : viewportFrames) {
if (!IsViewportPanel(viewportComposeFrame.panelId)) {
continue;
}
const ViewportFrame viewportFrame =
viewportHostService.RequestViewport(
viewportComposeFrame.panelId,
viewportComposeFrame.viewportShellFrame.requestedViewportSize);
ApplyViewportFrameToShellModel(
viewportFrame,
viewportComposeFrame.viewportShellModel);
if (UIEditorWorkspacePanelPresentationModel* presentation =
FindMutableWorkspacePresentation(
shellFrame.model.workspacePresentations,
viewportComposeFrame.panelId);
presentation != nullptr) {
ApplyViewportFrameToPresentation(viewportFrame, *presentation);
}
}
};
applyToViewportFrames(shellFrame.workspaceInteractionFrame.composeFrame.viewportFrames);
applyToViewportFrames(shellFrame.shellFrame.workspaceFrame.viewportFrames);
}
} // namespace XCEngine::UI::Editor::App