#include "Composition/EditorShellRuntimeInternal.h" #include "Features/PanelInputContext.h" #include "Composition/EditorContext.h" #include "Composition/EditorPanelIds.h" #include namespace XCEngine::UI::Editor::App::Internal { 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; } } 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 FilterShellInputEventsForHostedContentCapture( const std::vector& inputEvents) { std::vector 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 FilterHostedContentInputEventsForShellOwnership( const std::vector& inputEvents, bool shellOwnsPointerStream) { if (!shellOwnsPointerStream) { return inputEvents; } std::vector 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::Internal namespace XCEngine::UI::Editor::App { using namespace Internal; 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) { UIEditorShellInteractionMetrics metrics = ResolveUIEditorShellInteractionMetrics(); 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); 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_inspectorPanel.Update( context.GetSession(), context.GetSceneRuntime(), m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame, hostedContentEvents, inspectorInputContext); context.SyncSessionFromCommandFocusService(); m_consolePanel.Update( context.GetSession(), m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame); } } // namespace XCEngine::UI::Editor::App