From cda4a57756bb8e69cdf467a36e3ea049038a066d Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 5 Apr 2026 16:38:00 +0800 Subject: [PATCH] Apply shared native shell layout in new editor --- docs/plan/XCUI_Phase_Status_2026-04-05.md | 2 + new_editor/src/Application.cpp | 224 +++++++--------------- 2 files changed, 67 insertions(+), 159 deletions(-) diff --git a/docs/plan/XCUI_Phase_Status_2026-04-05.md b/docs/plan/XCUI_Phase_Status_2026-04-05.md index 4b2d584b..e27f0d3b 100644 --- a/docs/plan/XCUI_Phase_Status_2026-04-05.md +++ b/docs/plan/XCUI_Phase_Status_2026-04-05.md @@ -128,6 +128,7 @@ Current gap: - routes shell shortcuts through the same command router without reading ImGui capture state in the default host path - drives both sandbox runtimes directly from Win32/XCUI input snapshots instead of routing through ImGui panel hosts - composes one native `UIDrawData` packet and submits it through `NativeWindowUICompositor` +- The native shell layout policy now also lives behind `XCUINativeShellLayout`, so top-bar/footer/workspace geometry, panel split rules, and active-panel transfer on pointer press are no longer hard-coded inline inside `Application.cpp`. - `NativeXCUIPanelCanvasHost` now backs that direct shell path as an externally driven canvas/session host for native cards instead of assuming an ImGui child-window model, and it now emits native `Image` draw commands for hosted surface-image previews while preserving per-surface UVs. - `NativeWindowUICompositor` now creates and frees SRV-backed texture registrations for hosted preview surfaces, so native publication no longer depends on ImGui descriptor handles. - `Application` now runs the hosted-preview lifecycle in both legacy and native frame paths, treats published textures as XCUI-owned `UITextureHandle` state, queues native preview frames from `BuildNativeShellDrawData(...)`, and drains them during native rendering before shell chrome overlays. @@ -138,6 +139,7 @@ Current gap: - The default shell host is now native, and the legacy ImGui shell/panel path has been split out of the default executable into the standalone `XCNewEditorImGuiCompat` compatibility slice. - The default native shell path is now split away from direct `ImGui::*` calls at the main-target header/include level and no longer links the compatibility slice by default. +- The default native shell now also consumes shared `XCUINativeShellLayout` and `UIEditorPanelChrome` helpers for panel split/chrome policy instead of duplicating that card layout logic entirely inside `Application.cpp`. - The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document. - Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules. - The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem. diff --git a/new_editor/src/Application.cpp b/new_editor/src/Application.cpp index 336049fc..2f13c113 100644 --- a/new_editor/src/Application.cpp +++ b/new_editor/src/Application.cpp @@ -1,8 +1,10 @@ #include "Application.h" +#include "XCUIBackend/XCUINativeShellLayout.h" #include "XCUIBackend/NativeWindowUICompositor.h" #include #include +#include #include #include @@ -20,14 +22,6 @@ constexpr wchar_t kWindowClassName[] = L"XCNewEditorWindowClass"; constexpr wchar_t kWindowTitle[] = L"XCNewEditor"; constexpr float kClearColor[4] = { 0.08f, 0.09f, 0.11f, 1.0f }; constexpr float kHostedPreviewClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; -constexpr float kNativeShellOuterMargin = 22.0f; -constexpr float kNativeShellInnerGap = 18.0f; -constexpr float kNativeShellHeaderHeight = 58.0f; -constexpr float kNativeShellFooterHeight = 34.0f; -constexpr float kNativePanelHeaderHeight = 42.0f; -constexpr float kNativePanelPadding = 14.0f; -constexpr float kNativePanelMinWidth = 260.0f; -constexpr float kNativePanelMinHeight = 180.0f; template void ShutdownAndDelete(ResourceType*& resource) { @@ -40,13 +34,6 @@ void ShutdownAndDelete(ResourceType*& resource) { resource = nullptr; } -bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) { - return point.x >= rect.x && - point.y >= rect.y && - point.x <= rect.x + rect.width && - point.y <= rect.y + rect.height; -} - std::uint64_t MakeFrameTimestampNanoseconds() { return static_cast( std::chrono::duration_cast( @@ -102,37 +89,6 @@ void PopulateKeyboardNavigationInput( input.navigateExpand = pressedThisFrame(KeyCode::Right); } -struct NativeShellPanelLayout { - Application::ShellPanelId panelId = Application::ShellPanelId::XCUIDemo; - std::string title = {}; - UI::UIRect panelRect = {}; - UI::UIRect canvasRect = {}; - bool visible = false; - bool hovered = false; - bool active = false; -}; - -NativeShellPanelLayout MakePanelLayout( - Application::ShellPanelId panelId, - std::string title, - const UI::UIRect& panelRect, - const UI::UIPoint& pointerPosition, - bool windowFocused, - bool active) { - NativeShellPanelLayout layout = {}; - layout.panelId = panelId; - layout.title = std::move(title); - layout.panelRect = panelRect; - layout.canvasRect = UI::UIRect( - panelRect.x + kNativePanelPadding, - panelRect.y + kNativePanelHeaderHeight, - (std::max)(0.0f, panelRect.width - kNativePanelPadding * 2.0f), - (std::max)(0.0f, panelRect.height - kNativePanelHeaderHeight - kNativePanelPadding)); - layout.visible = panelRect.width >= kNativePanelMinWidth && panelRect.height >= kNativePanelMinHeight; - layout.hovered = windowFocused && ContainsPoint(layout.canvasRect, pointerPosition); - layout.active = active; - return layout; -} } // namespace bool Application::IsNativeWindowHostEnabled() const { @@ -583,6 +539,15 @@ bool Application::RenderHostedPreviewOffscreenSurface( ::XCEngine::UI::UIDrawData Application::BuildNativeShellDrawData( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& shellSnapshot, const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta& shellFrameDelta) { + using NativeShellLayout = ::XCEngine::Editor::XCUIBackend::XCUINativeShellLayoutResult; + using NativeShellMetrics = ::XCEngine::Editor::XCUIBackend::XCUINativeShellMetrics; + using NativeShellPanelLayout = ::XCEngine::Editor::XCUIBackend::XCUINativeShellPanelLayout; + using NativeShellPanelSpec = ::XCEngine::Editor::XCUIBackend::XCUINativeShellPanelSpec; + using PanelChromeMetrics = ::XCEngine::UI::Widgets::UIEditorPanelChromeMetrics; + using PanelChromePalette = ::XCEngine::UI::Widgets::UIEditorPanelChromePalette; + using PanelChromeState = ::XCEngine::UI::Widgets::UIEditorPanelChromeState; + using PanelChromeText = ::XCEngine::UI::Widgets::UIEditorPanelChromeText; + ::XCEngine::UI::UIDrawData composedDrawData = {}; RECT clientRect = {}; @@ -597,96 +562,42 @@ bool Application::RenderHostedPreviewOffscreenSurface( } const UI::UIRect shellRect(0.0f, 0.0f, windowWidth, windowHeight); + const NativeShellMetrics nativeShellMetrics = {}; + const PanelChromeMetrics panelChromeMetrics = {}; + const PanelChromePalette panelChromePalette = {}; const UI::UIColor shellBorderColor(40.0f / 255.0f, 54.0f / 255.0f, 74.0f / 255.0f, 1.0f); const UI::UIColor shellSurfaceColor(11.0f / 255.0f, 15.0f / 255.0f, 22.0f / 255.0f, 180.0f / 255.0f); - const UI::UIColor panelSurfaceColor(9.0f / 255.0f, 13.0f / 255.0f, 18.0f / 255.0f, 212.0f / 255.0f); - const UI::UIColor panelBorderColor(53.0f / 255.0f, 72.0f / 255.0f, 96.0f / 255.0f, 1.0f); - const UI::UIColor panelAccentColor(84.0f / 255.0f, 176.0f / 255.0f, 244.0f / 255.0f, 1.0f); - const UI::UIColor hoveredAccentColor(1.0f, 206.0f / 255.0f, 112.0f / 255.0f, 1.0f); - const UI::UIColor textPrimary(232.0f / 255.0f, 238.0f / 255.0f, 246.0f / 255.0f, 1.0f); - const UI::UIColor textSecondary(150.0f / 255.0f, 164.0f / 255.0f, 184.0f / 255.0f, 1.0f); - const UI::UIColor textMuted(108.0f / 255.0f, 123.0f / 255.0f, 145.0f / 255.0f, 1.0f); + const UI::UIColor& panelBorderColor = panelChromePalette.borderColor; + const UI::UIColor& panelAccentColor = panelChromePalette.accentColor; + const UI::UIColor& textPrimary = panelChromePalette.textPrimary; + const UI::UIColor& textSecondary = panelChromePalette.textSecondary; + const UI::UIColor& textMuted = panelChromePalette.textMuted; - const float topBarY = kNativeShellOuterMargin; - const UI::UIRect topBarRect( - kNativeShellOuterMargin, - topBarY, - (std::max)(0.0f, windowWidth - kNativeShellOuterMargin * 2.0f), - kNativeShellHeaderHeight); - const UI::UIRect footerRect( - kNativeShellOuterMargin, - (std::max)(topBarRect.y + topBarRect.height + kNativeShellInnerGap, windowHeight - kNativeShellOuterMargin - kNativeShellFooterHeight), - (std::max)(0.0f, windowWidth - kNativeShellOuterMargin * 2.0f), - kNativeShellFooterHeight); + const NativeShellLayout shellLayout = ::XCEngine::Editor::XCUIBackend::BuildXCUINativeShellLayout( + shellRect, + { + NativeShellPanelSpec{ + ShellPanelId::XCUIDemo, + "XCUI Demo", + m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo), + }, + NativeShellPanelSpec{ + ShellPanelId::XCUILayoutLab, + "XCUI Layout Lab", + m_shellChromeState.IsPanelVisible(ShellPanelId::XCUILayoutLab), + }, + }, + m_nativeActivePanel, + shellSnapshot.pointerPosition, + shellSnapshot.windowFocused, + shellFrameDelta.pointer.pressed[0], + nativeShellMetrics); + m_nativeActivePanel = shellLayout.activePanel; - const float workspaceTop = topBarRect.y + topBarRect.height + kNativeShellInnerGap; - const float workspaceBottom = footerRect.y - kNativeShellInnerGap; - const UI::UIRect workspaceRect( - kNativeShellOuterMargin, - workspaceTop, - (std::max)(0.0f, windowWidth - kNativeShellOuterMargin * 2.0f), - (std::max)(0.0f, workspaceBottom - workspaceTop)); - - bool demoVisible = m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo); - bool layoutVisible = m_shellChromeState.IsPanelVisible(ShellPanelId::XCUILayoutLab); - if (m_nativeActivePanel == ShellPanelId::XCUIDemo && !demoVisible && layoutVisible) { - m_nativeActivePanel = ShellPanelId::XCUILayoutLab; - } else if (m_nativeActivePanel == ShellPanelId::XCUILayoutLab && !layoutVisible && demoVisible) { - m_nativeActivePanel = ShellPanelId::XCUIDemo; - } - - std::vector panelLayouts = {}; - panelLayouts.reserve(2u); - if (demoVisible && layoutVisible) { - const float leftWidth = (std::max)( - kNativePanelMinWidth, - (std::min)(workspaceRect.width * 0.60f, workspaceRect.width - kNativePanelMinWidth - kNativeShellInnerGap)); - const float rightWidth = (std::max)(0.0f, workspaceRect.width - leftWidth - kNativeShellInnerGap); - panelLayouts.push_back(MakePanelLayout( - ShellPanelId::XCUIDemo, - "XCUI Demo", - UI::UIRect(workspaceRect.x, workspaceRect.y, leftWidth, workspaceRect.height), - shellSnapshot.pointerPosition, - shellSnapshot.windowFocused, - m_nativeActivePanel == ShellPanelId::XCUIDemo)); - panelLayouts.push_back(MakePanelLayout( - ShellPanelId::XCUILayoutLab, - "XCUI Layout Lab", - UI::UIRect(workspaceRect.x + leftWidth + kNativeShellInnerGap, workspaceRect.y, rightWidth, workspaceRect.height), - shellSnapshot.pointerPosition, - shellSnapshot.windowFocused, - m_nativeActivePanel == ShellPanelId::XCUILayoutLab)); - } else if (demoVisible) { - panelLayouts.push_back(MakePanelLayout( - ShellPanelId::XCUIDemo, - "XCUI Demo", - workspaceRect, - shellSnapshot.pointerPosition, - shellSnapshot.windowFocused, - true)); - m_nativeActivePanel = ShellPanelId::XCUIDemo; - } else if (layoutVisible) { - panelLayouts.push_back(MakePanelLayout( - ShellPanelId::XCUILayoutLab, - "XCUI Layout Lab", - workspaceRect, - shellSnapshot.pointerPosition, - shellSnapshot.windowFocused, - true)); - m_nativeActivePanel = ShellPanelId::XCUILayoutLab; - } - - if (shellFrameDelta.pointer.pressed[0]) { - for (const NativeShellPanelLayout& panelLayout : panelLayouts) { - if (panelLayout.hovered) { - m_nativeActivePanel = panelLayout.panelId; - break; - } - } - for (NativeShellPanelLayout& panelLayout : panelLayouts) { - panelLayout.active = panelLayout.panelId == m_nativeActivePanel; - } - } + const UI::UIRect& topBarRect = shellLayout.topBarRect; + const UI::UIRect& footerRect = shellLayout.footerRect; + const UI::UIRect& workspaceRect = shellLayout.workspaceRect; + const std::vector& panelLayouts = shellLayout.panelLayouts; ::XCEngine::UI::UIDrawList& chromeBackground = composedDrawData.EmplaceDrawList("XCUI.NativeShell.Background"); @@ -702,8 +613,12 @@ bool Application::RenderHostedPreviewOffscreenSurface( workspaceRect.y, workspaceRect.width, (std::max)(0.0f, workspaceRect.height)); - chromeBackground.AddFilledRect(emptyStateRect, panelSurfaceColor, 18.0f); - chromeBackground.AddRectOutline(emptyStateRect, panelBorderColor, 1.0f, 18.0f); + ::XCEngine::UI::Widgets::AppendUIEditorPanelChromeBackground( + chromeBackground, + emptyStateRect, + {}, + panelChromePalette, + panelChromeMetrics); ::XCEngine::UI::UIDrawList& emptyForeground = composedDrawData.EmplaceDrawList("XCUI.NativeShell.EmptyState"); @@ -719,19 +634,12 @@ bool Application::RenderHostedPreviewOffscreenSurface( } for (const NativeShellPanelLayout& panelLayout : panelLayouts) { - const UI::UIColor borderColor = panelLayout.active - ? panelAccentColor - : (panelLayout.hovered ? hoveredAccentColor : panelBorderColor); - chromeBackground.AddFilledRect(panelLayout.panelRect, panelSurfaceColor, 18.0f); - chromeBackground.AddRectOutline(panelLayout.panelRect, borderColor, panelLayout.active ? 2.0f : 1.0f, 18.0f); - chromeBackground.AddFilledRect( - UI::UIRect( - panelLayout.panelRect.x, - panelLayout.panelRect.y, - panelLayout.panelRect.width, - kNativePanelHeaderHeight), - UI::UIColor(13.0f / 255.0f, 20.0f / 255.0f, 28.0f / 255.0f, 242.0f / 255.0f), - 18.0f); + ::XCEngine::UI::Widgets::AppendUIEditorPanelChromeBackground( + chromeBackground, + panelLayout.panelRect, + PanelChromeState{ panelLayout.active, panelLayout.hovered }, + panelChromePalette, + panelChromeMetrics); } struct NativePanelFrameSummary { @@ -1065,18 +973,16 @@ bool Application::RenderHostedPreviewOffscreenSurface( textSecondary); for (const NativePanelFrameSummary& summary : panelSummaries) { - chromeForeground.AddText( - UI::UIPoint(summary.layout.panelRect.x + 16.0f, summary.layout.panelRect.y + 12.0f), - summary.layout.title, - textPrimary); - chromeForeground.AddText( - UI::UIPoint(summary.layout.panelRect.x + 16.0f, summary.layout.panelRect.y + 28.0f), - summary.lineA, - textSecondary); - chromeForeground.AddText( - UI::UIPoint(summary.layout.panelRect.x + 16.0f, summary.layout.panelRect.y + summary.layout.panelRect.height - 18.0f), - summary.lineB, - textMuted); + ::XCEngine::UI::Widgets::AppendUIEditorPanelChromeForeground( + chromeForeground, + summary.layout.panelRect, + PanelChromeText{ + summary.layout.title, + summary.lineA, + summary.lineB + }, + panelChromePalette, + panelChromeMetrics); } if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) {