@@ -0,0 +1,680 @@
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <XCEditor/Shell/UIEditorDockHost.h>
# include <XCEditor/Shell/UIEditorDockHostInteraction.h>
# include <XCEditor/Shell/UIEditorWorkspaceController.h>
# include <XCEditor/Shell/UIEditorWorkspaceModel.h>
# include "Host/AutoScreenshot.h"
# include "Host/NativeRenderer.h"
# include <XCEngine/Input/InputTypes.h>
# include <XCEngine/UI/DrawData.h>
# include <windows.h>
# include <windowsx.h>
# include <algorithm>
# include <filesystem>
# include <sstream>
# include <string>
# include <string_view>
# include <vector>
# ifndef XCENGINE_EDITOR_UI_TESTS_REPO_ROOT
# define XCENGINE_EDITOR_UI_TESTS_REPO_ROOT "."
# endif
namespace {
using XCEngine : : Input : : KeyCode ;
using XCEngine : : UI : : UIColor ;
using XCEngine : : UI : : UIDrawData ;
using XCEngine : : UI : : UIDrawList ;
using XCEngine : : UI : : UIInputEvent ;
using XCEngine : : UI : : UIInputEventType ;
using XCEngine : : UI : : UIPoint ;
using XCEngine : : UI : : UIPointerButton ;
using XCEngine : : UI : : UIRect ;
using XCEngine : : UI : : Editor : : BuildDefaultUIEditorWorkspaceController ;
using XCEngine : : UI : : Editor : : BuildUIEditorWorkspacePanel ;
using XCEngine : : UI : : Editor : : BuildUIEditorWorkspaceSingleTabStack ;
using XCEngine : : UI : : Editor : : BuildUIEditorWorkspaceSplit ;
using XCEngine : : UI : : Editor : : BuildUIEditorWorkspaceTabStack ;
using XCEngine : : UI : : Editor : : FindUIEditorWorkspaceNode ;
using XCEngine : : UI : : Editor : : Host : : AutoScreenshotController ;
using XCEngine : : UI : : Editor : : Host : : NativeRenderer ;
using XCEngine : : UI : : Editor : : UIEditorDockHostInteractionFrame ;
using XCEngine : : UI : : Editor : : UIEditorDockHostInteractionResult ;
using XCEngine : : UI : : Editor : : UIEditorDockHostInteractionState ;
using XCEngine : : UI : : Editor : : UIEditorDockHostTabStripInteractionEntry ;
using XCEngine : : UI : : Editor : : UIEditorPanelRegistry ;
using XCEngine : : UI : : Editor : : UIEditorWorkspaceController ;
using XCEngine : : UI : : Editor : : UIEditorWorkspaceLayoutOperationStatus ;
using XCEngine : : UI : : Editor : : UIEditorWorkspaceModel ;
using XCEngine : : UI : : Editor : : UIEditorWorkspaceSplitAxis ;
using XCEngine : : UI : : Editor : : UpdateUIEditorDockHostInteraction ;
using XCEngine : : UI : : Editor : : Widgets : : AppendUIEditorDockHostBackground ;
using XCEngine : : UI : : Editor : : Widgets : : AppendUIEditorDockHostForeground ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorDockHostHitTarget ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorDockHostHitTargetKind ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorDockHostTabStackLayout ;
constexpr const wchar_t * kWindowClassName = L " XCUIEditorDockTabReorderSameStackValidation " ;
constexpr const wchar_t * kWindowTitle = L " XCUI Editor | Dock Tab Reorder Same Stack " ;
constexpr UIColor kWindowBg ( 0.10f , 0.10f , 0.10f , 1.0f ) ;
constexpr UIColor kCardBg ( 0.16f , 0.16f , 0.16f , 1.0f ) ;
constexpr UIColor kCardBorder ( 0.28f , 0.28f , 0.28f , 1.0f ) ;
constexpr UIColor kPreviewBg ( 0.12f , 0.12f , 0.12f , 1.0f ) ;
constexpr UIColor kTextPrimary ( 0.92f , 0.92f , 0.92f , 1.0f ) ;
constexpr UIColor kTextMuted ( 0.74f , 0.74f , 0.74f , 1.0f ) ;
constexpr UIColor kTextWeak ( 0.57f , 0.57f , 0.57f , 1.0f ) ;
constexpr UIColor kButtonBg ( 0.24f , 0.24f , 0.24f , 1.0f ) ;
constexpr UIColor kButtonHover ( 0.31f , 0.31f , 0.31f , 1.0f ) ;
constexpr UIColor kButtonBorder ( 0.44f , 0.44f , 0.44f , 1.0f ) ;
constexpr UIColor kOk ( 0.47f , 0.71f , 0.50f , 1.0f ) ;
constexpr UIColor kWarn ( 0.85f , 0.68f , 0.36f , 1.0f ) ;
enum class ActionId : unsigned char {
Reset = 0 ,
Capture
} ;
struct ButtonState {
ActionId action = ActionId : : Reset ;
std : : string label = { } ;
UIRect rect = { } ;
bool hovered = false ;
} ;
std : : filesystem : : path ResolveRepoRootPath ( ) {
std : : string root = XCENGINE_EDITOR_UI_TESTS_REPO_ROOT ;
if ( root . size ( ) > = 2u & & root . front ( ) = = ' " ' & & root . back ( ) = = ' " ' ) {
root = root . substr ( 1u , root . size ( ) - 2u ) ;
}
return std : : filesystem : : path ( root ) . lexically_normal ( ) ;
}
bool ContainsPoint ( const UIRect & rect , float x , float y ) {
return x > = rect . x & &
x < = rect . x + rect . width & &
y > = rect . y & &
y < = rect . y + rect . height ;
}
std : : string FormatBool ( bool value ) {
return value ? " 是 " : " 否 " ;
}
std : : string FormatOptionalIndex ( std : : size_t index ) {
if ( index = = XCEngine : : UI : : Editor : : Widgets : : UIEditorTabStripInvalidIndex ) {
return " 无 " ;
}
return std : : to_string ( index ) ;
}
std : : string DescribeHitTarget ( const UIEditorDockHostHitTarget & target ) {
switch ( target . kind ) {
case UIEditorDockHostHitTargetKind : : Tab :
return " 标签: " + target . panelId ;
case UIEditorDockHostHitTargetKind : : TabStripBackground :
return " 标签栏空白 " ;
case UIEditorDockHostHitTargetKind : : PanelBody :
return " 面板主体: " + target . panelId ;
case UIEditorDockHostHitTargetKind : : SplitterHandle :
return " 分割条: " + target . nodeId ;
case UIEditorDockHostHitTargetKind : : None :
default :
return " 无 " ;
}
}
void DrawCard (
UIDrawList & drawList ,
const UIRect & rect ,
std : : string_view title ,
std : : string_view subtitle = { } ) {
drawList . AddFilledRect ( rect , kCardBg , 10.0f ) ;
drawList . AddRectOutline ( rect , kCardBorder , 1.0f , 10.0f ) ;
drawList . AddText ( UIPoint ( rect . x + 16.0f , rect . y + 14.0f ) , std : : string ( title ) , kTextPrimary , 17.0f ) ;
if ( ! subtitle . empty ( ) ) {
drawList . AddText ( UIPoint ( rect . x + 16.0f , rect . y + 38.0f ) , std : : string ( subtitle ) , kTextMuted , 12.0f ) ;
}
}
void DrawButton ( UIDrawList & drawList , const ButtonState & button ) {
drawList . AddFilledRect ( button . rect , button . hovered ? kButtonHover : kButtonBg , 8.0f ) ;
drawList . AddRectOutline ( button . rect , kButtonBorder , 1.0f , 8.0f ) ;
drawList . AddText ( UIPoint ( button . rect . x + 14.0f , button . rect . y + 10.0f ) , button . label , kTextPrimary , 12.0f ) ;
}
UIEditorPanelRegistry BuildPanelRegistry ( ) {
UIEditorPanelRegistry registry = { } ;
registry . panels = {
{ " doc-a " , " Document A " , { } , true , true , true } ,
{ " doc-b " , " Document B " , { } , true , true , true } ,
{ " doc-c " , " Document C " , { } , true , true , true } ,
{ " details " , " Details " , { } , true , true , true }
} ;
return registry ;
}
UIEditorWorkspaceModel BuildWorkspace ( ) {
UIEditorWorkspaceModel workspace = { } ;
workspace . root = BuildUIEditorWorkspaceSplit (
" root-split " ,
UIEditorWorkspaceSplitAxis : : Horizontal ,
0.72f ,
BuildUIEditorWorkspaceTabStack (
" document-tabs " ,
{
BuildUIEditorWorkspacePanel ( " doc-a-node " , " doc-a " , " Document A " , true ) ,
BuildUIEditorWorkspacePanel ( " doc-b-node " , " doc-b " , " Document B " , true ) ,
BuildUIEditorWorkspacePanel ( " doc-c-node " , " doc-c " , " Document C " , true )
} ,
0u ) ,
BuildUIEditorWorkspaceSingleTabStack ( " details-node " , " details " , " Details " , true ) ) ;
workspace . activePanelId = " doc-a " ;
return workspace ;
}
std : : string CollectDocumentTabOrder ( const UIEditorWorkspaceController & controller ) {
const auto * node = FindUIEditorWorkspaceNode ( controller . GetWorkspace ( ) , " document-tabs " ) ;
if ( node = = nullptr ) {
return " 缺失 " ;
}
std : : ostringstream stream = { } ;
for ( std : : size_t index = 0 ; index < node - > children . size ( ) ; + + index ) {
if ( index > 0u ) {
stream < < " | " ;
}
stream < < node - > children [ index ] . panel . panelId ;
}
return stream . str ( ) ;
}
const XCEngine : : UI : : Editor : : Widgets : : UIEditorDockHostTabStackLayout * FindDocumentTabStackLayout (
const UIEditorDockHostInteractionFrame & frame ) {
for ( const auto & tabStack : frame . layout . tabStacks ) {
if ( tabStack . nodeId = = " document-tabs " ) {
return & tabStack ;
}
}
return nullptr ;
}
const UIEditorDockHostTabStripInteractionEntry * FindDocumentTabStripInteractionEntry (
const UIEditorDockHostInteractionState & state ) {
for ( const UIEditorDockHostTabStripInteractionEntry & entry : state . tabStripInteractions ) {
if ( entry . nodeId = = " document-tabs " ) {
return & entry ;
}
}
return nullptr ;
}
class ScenarioApp {
public :
int Run ( HINSTANCE hInstance , int nCmdShow ) {
if ( ! Initialize ( hInstance , nCmdShow ) ) {
Shutdown ( ) ;
return 1 ;
}
MSG message = { } ;
while ( message . message ! = WM_QUIT ) {
if ( PeekMessageW ( & message , nullptr , 0U , 0U , PM_REMOVE ) ) {
TranslateMessage ( & message ) ;
DispatchMessageW ( & message ) ;
continue ;
}
RenderFrame ( ) ;
Sleep ( 8 ) ;
}
Shutdown ( ) ;
return static_cast < int > ( message . wParam ) ;
}
private :
static LRESULT CALLBACK WndProc ( HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam ) {
if ( message = = WM_NCCREATE ) {
const auto * createStruct = reinterpret_cast < CREATESTRUCTW * > ( lParam ) ;
auto * app = reinterpret_cast < ScenarioApp * > ( createStruct - > lpCreateParams ) ;
SetWindowLongPtrW ( hwnd , GWLP_USERDATA , reinterpret_cast < LONG_PTR > ( app ) ) ;
return TRUE ;
}
auto * app = reinterpret_cast < ScenarioApp * > ( GetWindowLongPtrW ( hwnd , GWLP_USERDATA ) ) ;
switch ( message ) {
case WM_SIZE :
if ( app ! = nullptr & & wParam ! = SIZE_MINIMIZED ) {
app - > m_renderer . Resize ( static_cast < UINT > ( LOWORD ( lParam ) ) , static_cast < UINT > ( HIWORD ( lParam ) ) ) ;
}
return 0 ;
case WM_PAINT :
if ( app ! = nullptr ) {
PAINTSTRUCT paintStruct = { } ;
BeginPaint ( hwnd , & paintStruct ) ;
app - > RenderFrame ( ) ;
EndPaint ( hwnd , & paintStruct ) ;
return 0 ;
}
break ;
case WM_MOUSEMOVE :
if ( app ! = nullptr ) {
if ( ! app - > m_trackingMouseLeave ) {
TRACKMOUSEEVENT trackMouseEvent = { } ;
trackMouseEvent . cbSize = sizeof ( trackMouseEvent ) ;
trackMouseEvent . dwFlags = TME_LEAVE ;
trackMouseEvent . hwndTrack = hwnd ;
if ( TrackMouseEvent ( & trackMouseEvent ) ) {
app - > m_trackingMouseLeave = true ;
}
}
app - > HandleMouseMove (
static_cast < float > ( GET_X_LPARAM ( lParam ) ) ,
static_cast < float > ( GET_Y_LPARAM ( lParam ) ) ) ;
return 0 ;
}
break ;
case WM_MOUSELEAVE :
if ( app ! = nullptr ) {
app - > m_trackingMouseLeave = false ;
UIInputEvent event = { } ;
event . type = UIInputEventType : : PointerLeave ;
app - > m_pendingInputEvents . push_back ( event ) ;
return 0 ;
}
break ;
case WM_LBUTTONDOWN :
if ( app ! = nullptr ) {
SetFocus ( hwnd ) ;
app - > HandleLeftButtonDown (
static_cast < float > ( GET_X_LPARAM ( lParam ) ) ,
static_cast < float > ( GET_Y_LPARAM ( lParam ) ) ) ;
return 0 ;
}
break ;
case WM_LBUTTONUP :
if ( app ! = nullptr ) {
app - > HandleLeftButtonUp (
static_cast < float > ( GET_X_LPARAM ( lParam ) ) ,
static_cast < float > ( GET_Y_LPARAM ( lParam ) ) ) ;
return 0 ;
}
break ;
case WM_SETFOCUS :
if ( app ! = nullptr ) {
UIInputEvent event = { } ;
event . type = UIInputEventType : : FocusGained ;
app - > m_pendingInputEvents . push_back ( event ) ;
return 0 ;
}
break ;
case WM_KILLFOCUS :
if ( app ! = nullptr ) {
UIInputEvent event = { } ;
event . type = UIInputEventType : : FocusLost ;
app - > m_pendingInputEvents . push_back ( event ) ;
app - > m_lastInputCause = " focus_lost " ;
return 0 ;
}
break ;
case WM_CAPTURECHANGED :
if ( app ! = nullptr & &
! app - > m_interactionState . activeTabDragNodeId . empty ( ) & &
reinterpret_cast < HWND > ( lParam ) ! = hwnd ) {
UIInputEvent event = { } ;
event . type = UIInputEventType : : FocusLost ;
app - > m_pendingInputEvents . push_back ( event ) ;
app - > m_lastInputCause = " focus_lost " ;
return 0 ;
}
break ;
case WM_KEYDOWN :
case WM_SYSKEYDOWN :
if ( app ! = nullptr ) {
if ( wParam = = VK_F12 ) {
app - > m_autoScreenshot . RequestCapture ( " manual_f12 " ) ;
return 0 ;
}
UIInputEvent event = { } ;
event . type = UIInputEventType : : KeyDown ;
event . keyCode = static_cast < std : : int32_t > ( wParam = = VK_ESCAPE ? KeyCode : : Escape : KeyCode : : None ) ;
app - > m_pendingInputEvents . push_back ( event ) ;
return 0 ;
}
break ;
case WM_ERASEBKGND :
return 1 ;
case WM_DESTROY :
PostQuitMessage ( 0 ) ;
return 0 ;
default :
break ;
}
return DefWindowProcW ( hwnd , message , wParam , lParam ) ;
}
bool Initialize ( HINSTANCE hInstance , int nCmdShow ) {
m_captureRoot =
ResolveRepoRootPath ( ) / " tests/UI/Editor/integration/shell/dock_tab_reorder_same_stack/captures " ;
m_autoScreenshot . Initialize ( m_captureRoot ) ;
WNDCLASSEXW windowClass = { } ;
windowClass . cbSize = sizeof ( windowClass ) ;
windowClass . style = CS_HREDRAW | CS_VREDRAW ;
windowClass . lpfnWndProc = & ScenarioApp : : WndProc ;
windowClass . hInstance = hInstance ;
windowClass . hCursor = LoadCursorW ( nullptr , IDC_ARROW ) ;
windowClass . lpszClassName = kWindowClassName ;
m_windowClassAtom = RegisterClassExW ( & windowClass ) ;
if ( m_windowClassAtom = = 0 ) {
return false ;
}
m_hwnd = CreateWindowExW (
0 ,
kWindowClassName ,
kWindowTitle ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
1500 ,
920 ,
nullptr ,
nullptr ,
hInstance ,
this ) ;
if ( m_hwnd = = nullptr ) {
return false ;
}
ShowWindow ( m_hwnd , nCmdShow ) ;
if ( ! m_renderer . Initialize ( m_hwnd ) ) {
return false ;
}
ResetScenario ( ) ;
return true ;
}
void Shutdown ( ) {
if ( GetCapture ( ) = = m_hwnd ) {
ReleaseCapture ( ) ;
}
m_autoScreenshot . Shutdown ( ) ;
m_renderer . Shutdown ( ) ;
if ( m_hwnd ! = nullptr & & IsWindow ( m_hwnd ) ) {
DestroyWindow ( m_hwnd ) ;
}
if ( m_windowClassAtom ! = 0 ) {
UnregisterClassW ( kWindowClassName , GetModuleHandleW ( nullptr ) ) ;
}
}
void ResetScenario ( ) {
if ( GetCapture ( ) = = m_hwnd ) {
ReleaseCapture ( ) ;
}
m_controller = BuildDefaultUIEditorWorkspaceController ( BuildPanelRegistry ( ) , BuildWorkspace ( ) ) ;
m_interactionState = { } ;
m_cachedFrame = { } ;
m_pendingInputEvents . clear ( ) ;
m_lastInputCause . clear ( ) ;
m_hoverText = " 无 " ;
m_lastResult = " 等待验证:请在同一标签栏内拖拽标签。 " ;
m_lastResultColor = kWarn ;
}
void UpdateLayout ( ) {
RECT clientRect = { } ;
GetClientRect ( m_hwnd , & clientRect ) ;
const float width = static_cast < float > ( ( std : : max ) ( clientRect . right - clientRect . left , 1L ) ) ;
const float height = static_cast < float > ( ( std : : max ) ( clientRect . bottom - clientRect . top , 1L ) ) ;
constexpr float padding = 20.0f ;
constexpr float sidebarWidth = 360.0f ;
m_introRect = UIRect ( padding , padding , width - padding * 2.0f , 168.0f ) ;
m_previewRect = UIRect (
padding ,
m_introRect . y + m_introRect . height + 16.0f ,
width - sidebarWidth - padding * 3.0f ,
height - m_introRect . height - padding * 3.0f ) ;
m_stateRect = UIRect (
m_previewRect . x + m_previewRect . width + 20.0f ,
m_previewRect . y ,
sidebarWidth ,
m_previewRect . height ) ;
m_dockHostRect = UIRect (
m_previewRect . x + 16.0f ,
m_previewRect . y + 64.0f ,
m_previewRect . width - 32.0f ,
m_previewRect . height - 80.0f ) ;
const float buttonWidth = ( m_stateRect . width - 44.0f ) * 0.5f ;
const float buttonY = m_stateRect . y + m_stateRect . height - 52.0f ;
m_buttons = {
{ ActionId : : Reset , " 重置 " , UIRect ( m_stateRect . x + 16.0f , buttonY , buttonWidth , 36.0f ) , false } ,
{ ActionId : : Capture , " 截图(F12) " , UIRect ( m_stateRect . x + 28.0f + buttonWidth , buttonY , buttonWidth , 36.0f ) , false }
} ;
}
void HandleMouseMove ( float x , float y ) {
UpdateLayout ( ) ;
for ( ButtonState & button : m_buttons ) {
button . hovered = ContainsPoint ( button . rect , x , y ) ;
}
UIInputEvent event = { } ;
event . type = UIInputEventType : : PointerMove ;
event . position = UIPoint ( x , y ) ;
m_pendingInputEvents . push_back ( event ) ;
}
void HandleLeftButtonDown ( float x , float y ) {
UpdateLayout ( ) ;
for ( const ButtonState & button : m_buttons ) {
if ( ContainsPoint ( button . rect , x , y ) ) {
ExecuteAction ( button . action ) ;
return ;
}
}
UIInputEvent event = { } ;
event . type = UIInputEventType : : PointerButtonDown ;
event . position = UIPoint ( x , y ) ;
event . pointerButton = UIPointerButton : : Left ;
m_pendingInputEvents . push_back ( event ) ;
}
void HandleLeftButtonUp ( float x , float y ) {
UIInputEvent event = { } ;
event . type = UIInputEventType : : PointerButtonUp ;
event . position = UIPoint ( x , y ) ;
event . pointerButton = UIPointerButton : : Left ;
m_pendingInputEvents . push_back ( event ) ;
}
void ExecuteAction ( ActionId action ) {
if ( action = = ActionId : : Reset ) {
ResetScenario ( ) ;
m_lastResult = " 已重置:请重新检查提交、取消和失焦取消。 " ;
m_lastResultColor = kWarn ;
return ;
}
m_autoScreenshot . RequestCapture ( " manual_button " ) ;
m_lastResult = " 截图已排队:输出到 captures/。 " ;
m_lastResultColor = kWarn ;
}
void ApplyHostCaptureRequests ( const UIEditorDockHostInteractionResult & result ) {
if ( result . requestPointerCapture & & GetCapture ( ) ! = m_hwnd ) {
SetCapture ( m_hwnd ) ;
}
if ( result . releasePointerCapture & & GetCapture ( ) = = m_hwnd ) {
ReleaseCapture ( ) ;
}
}
void UpdateLastResult ( const UIEditorDockHostInteractionResult & result ) {
if ( result . layoutResult . status = = UIEditorWorkspaceLayoutOperationStatus : : Changed ) {
m_lastResult = " 结果:同一标签栏内的标签重排已提交。 " ;
m_lastResultColor = kOk ;
return ;
}
if ( result . requestPointerCapture ) {
m_lastResult = " 结果:开始拖拽,宿主已拿到 pointer capture。 " ;
m_lastResultColor = kOk ;
return ;
}
if ( result . releasePointerCapture & & ! result . layoutChanged & & ! result . commandExecuted ) {
if ( m_lastInputCause = = " focus_lost " ) {
m_lastResult = " 结果:窗口失焦,本次重排已取消。 " ;
} else {
m_lastResult = " 结果:拖拽已取消。拖到 body 区再松开时,标签顺序应保持不变。 " ;
}
m_lastResultColor = kWarn ;
return ;
}
}
void RenderFrame ( ) {
UpdateLayout ( ) ;
m_cachedFrame = UpdateUIEditorDockHostInteraction (
m_interactionState ,
m_controller ,
m_dockHostRect ,
m_pendingInputEvents ) ;
m_pendingInputEvents . clear ( ) ;
ApplyHostCaptureRequests ( m_cachedFrame . result ) ;
UpdateLastResult ( m_cachedFrame . result ) ;
m_lastInputCause . clear ( ) ;
RECT clientRect = { } ;
GetClientRect ( m_hwnd , & clientRect ) ;
const float width = static_cast < float > ( ( std : : max ) ( clientRect . right - clientRect . left , 1L ) ) ;
const float height = static_cast < float > ( ( std : : max ) ( clientRect . bottom - clientRect . top , 1L ) ) ;
UIDrawData drawData = { } ;
UIDrawList & drawList = drawData . EmplaceDrawList ( " DockTabReorderSameStack " ) ;
drawList . AddFilledRect ( UIRect ( 0.0f , 0.0f , width , height ) , kWindowBg ) ;
DrawCard ( drawList , m_introRect , " 这个测试验证什么功能? " , " 只验证同一标签栏内 tab 重排的基础 contract, 不做 editor 业务。 " ) ;
drawList . AddText ( UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 70.0f ) , " 1. 拖动 Document A / B / C 的 tab header 到同一行其他位置。 " , kTextPrimary , 12.0f ) ;
drawList . AddText ( UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 92.0f ) , " 2. 在 header 区松开:应提交重排,并更新右侧标签顺序。 " , kTextPrimary , 12.0f ) ;
drawList . AddText ( UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 114.0f ) , " 3. 拖到 body 区再松开:应取消,本次标签顺序不能变化。 " , kTextPrimary , 12.0f ) ;
drawList . AddText ( UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 136.0f ) , " 4. 拖拽中切走窗口或按 Esc: 应释放 capture, 并取消这次重排。 " , kTextWeak , 11.0f ) ;
DrawCard ( drawList , m_previewRect , " 预览区 " , " 这里只保留同栏重排所需的最小工作区。 " ) ;
drawList . AddFilledRect ( m_dockHostRect , kPreviewBg , 8.0f ) ;
AppendUIEditorDockHostBackground ( drawList , m_cachedFrame . layout ) ;
AppendUIEditorDockHostForeground ( drawList , m_cachedFrame . layout ) ;
DrawCard ( drawList , m_stateRect , " 状态 " , " 拖拽时重点观察右侧状态是否和画面一致。 " ) ;
float y = m_stateRect . y + 68.0f ;
auto addLine = [ & ] ( std : : string text , const UIColor & color = kTextPrimary , float fontSize = 12.0f ) mutable {
drawList . AddText ( UIPoint ( m_stateRect . x + 16.0f , y ) , std : : move ( text ) , color , fontSize ) ;
y + = 22.0f ;
} ;
const auto * documentTabStack = FindDocumentTabStackLayout ( m_cachedFrame ) ;
const bool previewVisible =
documentTabStack ! = nullptr & &
documentTabStack - > tabStripLayout . insertionPreview . visible ;
const std : : size_t previewInsertionIndex =
documentTabStack ! = nullptr
? documentTabStack - > tabStripLayout . insertionPreview . insertionIndex
: XCEngine : : UI : : Editor : : Widgets : : UIEditorTabStripInvalidIndex ;
addLine ( " 当前标签顺序: " + CollectDocumentTabOrder ( m_controller ) , kTextPrimary , 11.0f ) ;
addLine ( " 活动面板: " + m_controller . GetWorkspace ( ) . activePanelId , kTextPrimary , 11.0f ) ;
addLine ( " 已聚焦: " + FormatBool ( m_cachedFrame . focused ) , m_cachedFrame . focused ? kOk : kTextMuted , 11.0f ) ;
addLine ( " 宿主捕获: " + FormatBool ( GetCapture ( ) = = m_hwnd ) , GetCapture ( ) = = m_hwnd ? kOk : kTextMuted , 11.0f ) ;
addLine (
" 拖拽标签栈: " +
( m_interactionState . activeTabDragNodeId . empty ( )
? std : : string ( " 无 " )
: m_interactionState . activeTabDragNodeId ) ,
kTextWeak ,
11.0f ) ;
addLine ( " 预览插入位激活: " + FormatBool ( previewVisible ) , previewVisible ? kOk : kTextWeak , 11.0f ) ;
addLine ( " 预览插入索引: " + FormatOptionalIndex ( previewInsertionIndex ) , kTextWeak , 11.0f ) ;
addLine ( " 当前 Hover: " + m_hoverText , kTextWeak , 11.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , y + 8.0f ) ,
" 最近结果 " ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , y + 32.0f ) ,
m_lastResult ,
m_lastResultColor ,
12.0f ) ;
const std : : string captureSummary =
m_autoScreenshot . HasPendingCapture ( )
? " 截图排队中... "
: ( m_autoScreenshot . GetLastCaptureSummary ( ) . empty ( )
? " F12 或按钮 -> captures/ "
: m_autoScreenshot . GetLastCaptureSummary ( ) ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + m_stateRect . height - 86.0f ) ,
captureSummary ,
kTextWeak ,
11.0f ) ;
for ( const ButtonState & button : m_buttons ) {
DrawButton ( drawList , button ) ;
}
const bool framePresented = m_renderer . Render ( drawData ) ;
m_autoScreenshot . CaptureIfRequested (
m_renderer ,
drawData ,
static_cast < unsigned int > ( width ) ,
static_cast < unsigned int > ( height ) ,
framePresented ) ;
}
HWND m_hwnd = nullptr ;
ATOM m_windowClassAtom = 0 ;
NativeRenderer m_renderer = { } ;
AutoScreenshotController m_autoScreenshot = { } ;
std : : filesystem : : path m_captureRoot = { } ;
UIEditorWorkspaceController m_controller = { } ;
UIEditorDockHostInteractionState m_interactionState = { } ;
UIEditorDockHostInteractionFrame m_cachedFrame = { } ;
std : : vector < UIInputEvent > m_pendingInputEvents = { } ;
std : : vector < ButtonState > m_buttons = { } ;
UIRect m_introRect = { } ;
UIRect m_previewRect = { } ;
UIRect m_stateRect = { } ;
UIRect m_dockHostRect = { } ;
bool m_trackingMouseLeave = false ;
std : : string m_lastInputCause = { } ;
std : : string m_hoverText = { } ;
std : : string m_lastResult = { } ;
UIColor m_lastResultColor = kTextMuted ;
} ;
} // namespace
int WINAPI wWinMain ( HINSTANCE hInstance , HINSTANCE , LPWSTR , int nCmdShow ) {
return ScenarioApp ( ) . Run ( hInstance , nCmdShow ) ;
}