@@ -0,0 +1,741 @@
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <XCEditor/Widgets/UIEditorViewportSlot.h>
# include "Host/AutoScreenshot.h"
# include "Host/NativeRenderer.h"
# include <XCEngine/UI/DrawData.h>
# include <windows.h>
# include <windowsx.h>
# include <algorithm>
# include <filesystem>
# 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 : : UI : : UIColor ;
using XCEngine : : UI : : UIDrawData ;
using XCEngine : : UI : : UIDrawList ;
using XCEngine : : UI : : UIPoint ;
using XCEngine : : UI : : UIRect ;
using XCEngine : : UI : : UISize ;
using XCEngine : : UI : : Editor : : Host : : AutoScreenshotController ;
using XCEngine : : UI : : Editor : : Host : : NativeRenderer ;
using XCEngine : : UI : : Editor : : Widgets : : AppendUIEditorViewportSlotBackground ;
using XCEngine : : UI : : Editor : : Widgets : : AppendUIEditorViewportSlotForeground ;
using XCEngine : : UI : : Editor : : Widgets : : BuildUIEditorViewportSlotLayout ;
using XCEngine : : UI : : Editor : : Widgets : : HitTestUIEditorViewportSlot ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorStatusBarSegment ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorStatusBarSlot ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotChrome ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotFrame ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotHitTarget ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotHitTargetKind ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotInvalidIndex ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotLayout ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotState ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotToolItem ;
using XCEngine : : UI : : Editor : : Widgets : : UIEditorViewportSlotToolSlot ;
constexpr const wchar_t * kWindowClassName = L " XCUIEditorViewportSlotBasicValidation " ;
constexpr const wchar_t * kWindowTitle = L " XCUI Editor | ViewportSlot Basic " ;
constexpr UIColor kWindowBg ( 0.12f , 0.12f , 0.12f , 1.0f ) ;
constexpr UIColor kCardBg ( 0.18f , 0.18f , 0.18f , 1.0f ) ;
constexpr UIColor kCardBorder ( 0.29f , 0.29f , 0.29f , 1.0f ) ;
constexpr UIColor kTextPrimary ( 0.94f , 0.94f , 0.94f , 1.0f ) ;
constexpr UIColor kTextMuted ( 0.72f , 0.72f , 0.72f , 1.0f ) ;
constexpr UIColor kTextWeak ( 0.56f , 0.56f , 0.56f , 1.0f ) ;
constexpr UIColor kButtonBg ( 0.25f , 0.25f , 0.25f , 1.0f ) ;
constexpr UIColor kButtonOnBg ( 0.39f , 0.39f , 0.39f , 1.0f ) ;
constexpr UIColor kButtonBorder ( 0.47f , 0.47f , 0.47f , 1.0f ) ;
constexpr UIColor kPreviewBg ( 0.15f , 0.15f , 0.15f , 1.0f ) ;
enum class ActionId : unsigned char {
ToggleTopBar = 0 ,
ToggleBottomBar ,
ToggleTexture ,
ToggleSquareAspect ,
Reset ,
Capture
} ;
struct ButtonState {
ActionId action = ActionId : : ToggleTopBar ;
std : : string label = { } ;
UIRect rect = { } ;
bool selected = 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 BoolText ( bool value ) {
return value ? " On " : " Off " ;
}
std : : string DescribeHitTarget ( const UIEditorViewportSlotHitTarget & hit ) {
switch ( hit . kind ) {
case UIEditorViewportSlotHitTargetKind : : ToolItem :
return " ToolItem[ " + std : : to_string ( hit . index ) + " ] " ;
case UIEditorViewportSlotHitTargetKind : : Surface :
return " Surface " ;
case UIEditorViewportSlotHitTargetKind : : StatusSegment :
return " StatusSegment[ " + std : : to_string ( hit . index ) + " ] " ;
case UIEditorViewportSlotHitTargetKind : : StatusSeparator :
return " StatusSeparator[ " + std : : to_string ( hit . index ) + " ] " ;
case UIEditorViewportSlotHitTargetKind : : TopBar :
return " TopBar " ;
case UIEditorViewportSlotHitTargetKind : : Title :
return " Title " ;
case UIEditorViewportSlotHitTargetKind : : BottomBar :
return " BottomBar " ;
case UIEditorViewportSlotHitTargetKind : : None :
default :
return " None " ;
}
}
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 . selected ? kButtonOnBg : kButtonBg , 8.0f ) ;
drawList . AddRectOutline ( button . rect , kButtonBorder , 1.0f , 8.0f ) ;
drawList . AddText (
UIPoint ( button . rect . x + 12.0f , button . rect . y + 10.0f ) ,
button . label ,
kTextPrimary ,
12.0f ) ;
}
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 - > OnResize ( static_cast < UINT > ( LOWORD ( lParam ) ) , static_cast < UINT > ( HIWORD ( lParam ) ) ) ;
}
return 0 ;
case WM_MOUSEMOVE :
if ( app ! = nullptr ) {
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 - > HandleMouseLeave ( ) ;
return 0 ;
}
break ;
case WM_LBUTTONDOWN :
if ( app ! = nullptr ) {
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_CAPTURECHANGED :
if ( app ! = nullptr ) {
app - > m_slotState . inputCaptured = false ;
return 0 ;
}
break ;
case WM_KEYDOWN :
case WM_SYSKEYDOWN :
if ( app ! = nullptr & & wParam = = VK_F12 ) {
app - > m_autoScreenshot . RequestCapture ( " manual_f12 " ) ;
InvalidateRect ( hwnd , nullptr , FALSE ) ;
UpdateWindow ( hwnd ) ;
return 0 ;
}
break ;
case WM_PAINT :
if ( app ! = nullptr ) {
PAINTSTRUCT paintStruct = { } ;
BeginPaint ( hwnd , & paintStruct ) ;
app - > RenderFrame ( ) ;
EndPaint ( hwnd , & paintStruct ) ;
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/viewport_slot_basic/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 ,
1520 ,
940 ,
nullptr ,
nullptr ,
hInstance ,
this ) ;
if ( m_hwnd = = nullptr ) {
return false ;
}
if ( ! m_renderer . Initialize ( m_hwnd ) ) {
return false ;
}
ShowWindow ( m_hwnd , nCmdShow ) ;
ResetState ( ) ;
return true ;
}
void Shutdown ( ) {
m_autoScreenshot . Shutdown ( ) ;
m_renderer . Shutdown ( ) ;
if ( m_hwnd ! = nullptr & & IsWindow ( m_hwnd ) ) {
DestroyWindow ( m_hwnd ) ;
}
m_hwnd = nullptr ;
if ( m_windowClassAtom ! = 0 ) {
UnregisterClassW ( kWindowClassName , GetModuleHandleW ( nullptr ) ) ;
m_windowClassAtom = 0 ;
}
}
void ResetState ( ) {
m_showTopBar = true ;
m_showBottomBar = true ;
m_textureEnabled = true ;
m_squareAspect = false ;
m_slotState = { } ;
m_slotState . focused = true ;
m_slotState . surfaceActive = true ;
m_lastHover = { } ;
m_lastResult = " Ready " ;
}
UIEditorViewportSlotChrome BuildChrome ( ) const {
UIEditorViewportSlotChrome chrome = { } ;
chrome . title = " Scene View " ;
chrome . subtitle = " ViewportSlot 基础壳层 " ;
chrome . showTopBar = m_showTopBar ;
chrome . showBottomBar = m_showBottomBar ;
chrome . topBarHeight = 40.0f ;
chrome . bottomBarHeight = 28.0f ;
return chrome ;
}
UIEditorViewportSlotFrame BuildFrame ( ) const {
UIEditorViewportSlotFrame frame = { } ;
if ( m_textureEnabled ) {
const unsigned int width = m_squareAspect ? 1024u : 1280u ;
const unsigned int height = m_squareAspect ? 1024u : 720u ;
frame . hasTexture = true ;
frame . texture = { 1u , width , height } ;
frame . presentedSize = UISize ( static_cast < float > ( width ) , static_cast < float > ( height ) ) ;
frame . statusText = " Fake viewport frame " ;
} else {
frame . hasTexture = false ;
frame . statusText = " 当前无纹理,检查 fallback message 与 input 边界。 " ;
}
return frame ;
}
std : : vector < UIEditorViewportSlotToolItem > BuildToolItems ( ) const {
return {
{ " mode " , " Perspective " , UIEditorViewportSlotToolSlot : : Leading , true , true , 98.0f } ,
{ " lit " , " Lit " , UIEditorViewportSlotToolSlot : : Trailing , true , m_textureEnabled , 48.0f } ,
{ " gizmos " , " Gizmos " , UIEditorViewportSlotToolSlot : : Trailing , true , true , 72.0f }
} ;
}
std : : vector < UIEditorStatusBarSegment > BuildStatusSegments ( ) const {
return {
{ " scene " , " Scene " , UIEditorStatusBarSlot : : Leading , { } , true , true , 72.0f } ,
{ " resolution " , m_squareAspect ? " 1024x1024 " : " 1280x720 " , UIEditorStatusBarSlot : : Leading , { } , true , false , 92.0f } ,
{ " capture " , std : : string ( " Capture " ) + BoolText ( m_slotState . inputCaptured ) , UIEditorStatusBarSlot : : Trailing , { } , true , true , 104.0f } ,
{ " focus " , std : : string ( " Focus " ) + BoolText ( m_slotState . focused ) , UIEditorStatusBarSlot : : Trailing , { } , true , false , 88.0f }
} ;
}
void OnResize ( UINT width , UINT height ) {
m_renderer . Resize ( width , height ) ;
if ( width > 0u & & height > 0u ) {
UpdateLayoutForCurrentWindow ( ) ;
}
}
void UpdateLayoutForCurrentWindow ( ) {
if ( m_hwnd = = nullptr ) {
return ;
}
RECT clientRect = { } ;
GetClientRect ( m_hwnd , & clientRect ) ;
const float width = static_cast < float > ( ( std : : max ) ( 1L , clientRect . right - clientRect . left ) ) ;
const float height = static_cast < float > ( ( std : : max ) ( 1L , clientRect . bottom - clientRect . top ) ) ;
const float leftColumnWidth = 420.0f ;
const float outerPadding = 20.0f ;
m_introRect = UIRect ( outerPadding , outerPadding , leftColumnWidth , 224.0f ) ;
m_controlsRect = UIRect ( outerPadding , 264.0f , leftColumnWidth , 324.0f ) ;
m_stateRect = UIRect ( outerPadding , 608.0f , leftColumnWidth , height - 628.0f ) ;
m_previewRect = UIRect (
leftColumnWidth + outerPadding * 2.0f ,
outerPadding ,
width - leftColumnWidth - outerPadding * 3.0f ,
height - outerPadding * 2.0f ) ;
m_slotRect = UIRect (
m_previewRect . x + 18.0f ,
m_previewRect . y + 18.0f ,
m_previewRect . width - 36.0f ,
m_previewRect . height - 36.0f ) ;
BuildButtons ( ) ;
m_layout = BuildUIEditorViewportSlotLayout (
m_slotRect ,
BuildChrome ( ) ,
BuildFrame ( ) ,
BuildToolItems ( ) ,
BuildStatusSegments ( ) ) ;
UpdateHoverState ( ) ;
}
void BuildButtons ( ) {
const float buttonHeight = 34.0f ;
const float gap = 10.0f ;
const float left = m_controlsRect . x + 16.0f ;
const float top = m_controlsRect . y + 54.0f ;
const float width = m_controlsRect . width - 32.0f ;
m_buttons = {
{ ActionId : : ToggleTopBar , " TopBar " , UIRect ( left , top , width , buttonHeight ) , m_showTopBar } ,
{ ActionId : : ToggleBottomBar , " 状态条 " , UIRect ( left , top + ( buttonHeight + gap ) , width , buttonHeight ) , m_showBottomBar } ,
{ ActionId : : ToggleTexture , " Texture " , UIRect ( left , top + ( buttonHeight + gap ) * 2.0f , width , buttonHeight ) , m_textureEnabled } ,
{ ActionId : : ToggleSquareAspect , " 方形比例 " , UIRect ( left , top + ( buttonHeight + gap ) * 3.0f , width , buttonHeight ) , m_squareAspect } ,
{ ActionId : : Reset , " Reset " , UIRect ( left , top + ( buttonHeight + gap ) * 4.0f , width , buttonHeight ) , false } ,
{ ActionId : : Capture , " 截图 " , UIRect ( left , top + ( buttonHeight + gap ) * 5.0f , width , buttonHeight ) , false }
} ;
}
void UpdateHoverState ( ) {
m_lastHover = HitTestUIEditorViewportSlot ( m_layout , m_mousePosition ) ;
m_slotState . hoveredToolIndex = UIEditorViewportSlotInvalidIndex ;
m_slotState . statusBarState . hoveredIndex = UIEditorViewportSlotInvalidIndex ;
m_slotState . surfaceHovered = false ;
switch ( m_lastHover . kind ) {
case UIEditorViewportSlotHitTargetKind : : ToolItem :
m_slotState . hoveredToolIndex = m_lastHover . index ;
break ;
case UIEditorViewportSlotHitTargetKind : : StatusSegment :
m_slotState . statusBarState . hoveredIndex = m_lastHover . index ;
break ;
case UIEditorViewportSlotHitTargetKind : : Surface :
m_slotState . surfaceHovered = true ;
break ;
default :
break ;
}
m_slotState . statusBarState . focused = m_slotState . focused ;
}
void InvalidateScenario ( ) {
if ( m_hwnd ! = nullptr ) {
InvalidateRect ( m_hwnd , nullptr , FALSE ) ;
}
}
void HandleMouseMove ( float x , float y ) {
m_mousePosition = UIPoint ( x , y ) ;
TRACKMOUSEEVENT event = { } ;
event . cbSize = sizeof ( event ) ;
event . dwFlags = TME_LEAVE ;
event . hwndTrack = m_hwnd ;
TrackMouseEvent ( & event ) ;
UpdateHoverState ( ) ;
InvalidateScenario ( ) ;
}
void HandleMouseLeave ( ) {
m_mousePosition = UIPoint ( - 1000.0f , - 1000.0f ) ;
UpdateHoverState ( ) ;
InvalidateScenario ( ) ;
}
void HandleLeftButtonDown ( float x , float y ) {
SetFocus ( m_hwnd ) ;
m_mousePosition = UIPoint ( x , y ) ;
UpdateHoverState ( ) ;
const UIEditorViewportSlotHitTarget hit =
HitTestUIEditorViewportSlot ( m_layout , UIPoint ( x , y ) ) ;
if ( hit . kind = = UIEditorViewportSlotHitTargetKind : : Surface ) {
SetCapture ( m_hwnd ) ;
m_slotState . focused = true ;
m_slotState . surfaceActive = true ;
m_slotState . inputCaptured = true ;
m_lastResult = " Surface 按下: focus + active + capture " ;
}
InvalidateScenario ( ) ;
}
void ExecuteAction ( ActionId action ) {
switch ( action ) {
case ActionId : : ToggleTopBar :
m_showTopBar = ! m_showTopBar ;
m_lastResult = m_showTopBar ? " TopBar 已打开 " : " TopBar 已关闭 " ;
break ;
case ActionId : : ToggleBottomBar :
m_showBottomBar = ! m_showBottomBar ;
m_lastResult = m_showBottomBar ? " 状态条已打开 " : " 状态条已关闭 " ;
break ;
case ActionId : : ToggleTexture :
m_textureEnabled = ! m_textureEnabled ;
m_lastResult = m_textureEnabled ? " Texture 分支已打开 " : " Fallback 分支已打开 " ;
break ;
case ActionId : : ToggleSquareAspect :
m_squareAspect = ! m_squareAspect ;
m_lastResult = m_squareAspect ? " 切到 1:1 比例 " : " 切到 16:9 比例 " ;
break ;
case ActionId : : Reset :
ResetState ( ) ;
m_lastResult = " 状态已重置 " ;
break ;
case ActionId : : Capture :
m_autoScreenshot . RequestCapture ( " manual_button " ) ;
InvalidateRect ( m_hwnd , nullptr , FALSE ) ;
UpdateWindow ( m_hwnd ) ;
m_lastResult = " 截图已排队 " ;
break ;
}
UpdateLayoutForCurrentWindow ( ) ;
}
void HandleLeftButtonUp ( float x , float y ) {
if ( GetCapture ( ) = = m_hwnd ) {
ReleaseCapture ( ) ;
}
m_slotState . inputCaptured = false ;
m_mousePosition = UIPoint ( x , y ) ;
for ( const ButtonState & button : m_buttons ) {
if ( ContainsPoint ( button . rect , x , y ) ) {
ExecuteAction ( button . action ) ;
return ;
}
}
const UIEditorViewportSlotHitTarget hit =
HitTestUIEditorViewportSlot ( m_layout , UIPoint ( x , y ) ) ;
switch ( hit . kind ) {
case UIEditorViewportSlotHitTargetKind : : ToolItem :
m_slotState . activeToolIndex = hit . index ;
m_lastResult = " ToolItem 命中: " + std : : to_string ( hit . index ) ;
break ;
case UIEditorViewportSlotHitTargetKind : : StatusSegment :
m_slotState . statusBarState . activeIndex = hit . index ;
m_lastResult = " StatusSegment 命中: " + std : : to_string ( hit . index ) ;
break ;
case UIEditorViewportSlotHitTargetKind : : Surface :
m_slotState . focused = true ;
m_slotState . surfaceActive = true ;
m_lastResult = " Surface 激活, capture 已释放 " ;
break ;
default :
m_slotState . focused = false ;
m_slotState . surfaceActive = false ;
m_slotState . activeToolIndex = UIEditorViewportSlotInvalidIndex ;
m_slotState . statusBarState . activeIndex = UIEditorViewportSlotInvalidIndex ;
m_lastResult = " 点击外部, focus / active 已清除 " ;
break ;
}
UpdateHoverState ( ) ;
InvalidateScenario ( ) ;
}
void RenderFrame ( ) {
if ( m_hwnd = = nullptr ) {
return ;
}
RECT clientRect = { } ;
GetClientRect ( m_hwnd , & clientRect ) ;
const float width = static_cast < float > ( ( std : : max ) ( 1L , clientRect . right - clientRect . left ) ) ;
const float height = static_cast < float > ( ( std : : max ) ( 1L , clientRect . bottom - clientRect . top ) ) ;
UpdateLayoutForCurrentWindow ( ) ;
const auto chrome = BuildChrome ( ) ;
const auto frame = BuildFrame ( ) ;
const auto toolItems = BuildToolItems ( ) ;
const auto statusSegments = BuildStatusSegments ( ) ;
UIDrawData drawData = { } ;
UIDrawList & drawList = drawData . EmplaceDrawList ( " ViewportSlotBasic " ) ;
drawList . AddFilledRect ( UIRect ( 0.0f , 0.0f , width , height ) , kWindowBg ) ;
DrawCard (
drawList ,
m_introRect ,
" 测试功能: ViewportSlot 基础壳层 " ,
" 只验证 Editor 基础层。 " ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 66.0f ) ,
" 检查1: TopBar / Surface / 状态条布局。 " ,
kTextMuted ,
12.0f ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 88.0f ) ,
" 检查2: hover / focus / active / capture。 " ,
kTextMuted ,
12.0f ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 110.0f ) ,
" 操作1: hover toolbar / surface / status bar。 " ,
kTextMuted ,
12.0f ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 132.0f ) ,
" 操作2: click surface; 按住左键观察 capture。 " ,
kTextMuted ,
12.0f ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 154.0f ) ,
" 操作3: 切换 TopBar / 状态条 / Texture / 比例; F12 或“截图”。 " ,
kTextMuted ,
12.0f ) ;
drawList . AddText (
UIPoint ( m_introRect . x + 16.0f , m_introRect . y + 180.0f ) ,
" 结果: chrome 开关后 surface 扩张; Texture/Fallback 正确。 " ,
kTextWeak ,
12.0f ) ;
DrawCard ( drawList , m_controlsRect , " 开关 " , " 只保留和 ViewportSlot contract 直接相关的操作。 " ) ;
for ( const ButtonState & button : m_buttons ) {
DrawButton ( drawList , button ) ;
}
DrawCard ( drawList , m_stateRect , " 状态 " , " 实时显示 hover / focus / active / capture 与截图状态。 " ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 66.0f ) ,
" Hover: " + DescribeHitTarget ( m_lastHover ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 92.0f ) ,
" Focus: " + BoolText ( m_slotState . focused ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 118.0f ) ,
" Active: " + BoolText ( m_slotState . surfaceActive ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 144.0f ) ,
" Capture: " + BoolText ( m_slotState . inputCaptured ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 170.0f ) ,
" Chrome: TopBar " + BoolText ( m_showTopBar ) + " | 状态条 " + BoolText ( m_showBottomBar ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 196.0f ) ,
" Frame: " + std : : string ( m_textureEnabled ? ( m_squareAspect ? " 1024x1024 " : " 1280x720 " ) : " Fallback " ) ,
kTextPrimary ,
13.0f ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 222.0f ) ,
" Result: " + m_lastResult ,
kTextMuted ,
12.0f ) ;
const std : : string captureSummary =
m_autoScreenshot . HasPendingCapture ( )
? " 截图排队中... "
: ( m_autoScreenshot . GetLastCaptureSummary ( ) . empty ( )
? std : : string ( " 截图: F12 或按钮 -> viewport_slot_basic/captures/ " )
: m_autoScreenshot . GetLastCaptureSummary ( ) ) ;
drawList . AddText (
UIPoint ( m_stateRect . x + 16.0f , m_stateRect . y + 248.0f ) ,
captureSummary ,
kTextWeak ,
12.0f ) ;
DrawCard ( drawList , m_previewRect , " Preview " , " 这里只放一个 ViewportSlot, 专门检查基础壳层。 " ) ;
drawList . AddFilledRect (
UIRect (
m_previewRect . x + 12.0f ,
m_previewRect . y + 44.0f ,
m_previewRect . width - 24.0f ,
m_previewRect . height - 56.0f ) ,
kPreviewBg ,
10.0f ) ;
m_layout = BuildUIEditorViewportSlotLayout (
m_slotRect ,
chrome ,
frame ,
toolItems ,
statusSegments ) ;
UpdateHoverState ( ) ;
AppendUIEditorViewportSlotBackground (
drawList ,
m_layout ,
toolItems ,
statusSegments ,
m_slotState ) ;
AppendUIEditorViewportSlotForeground (
drawList ,
m_layout ,
chrome ,
frame ,
toolItems ,
statusSegments ,
m_slotState ) ;
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 = { } ;
std : : vector < ButtonState > m_buttons = { } ;
UIPoint m_mousePosition = UIPoint ( - 1000.0f , - 1000.0f ) ;
UIEditorViewportSlotState m_slotState = { } ;
UIEditorViewportSlotHitTarget m_lastHover = { } ;
UIEditorViewportSlotLayout m_layout = { } ;
UIRect m_introRect = { } ;
UIRect m_controlsRect = { } ;
UIRect m_stateRect = { } ;
UIRect m_previewRect = { } ;
UIRect m_slotRect = { } ;
bool m_showTopBar = true ;
bool m_showBottomBar = true ;
bool m_textureEnabled = true ;
bool m_squareAspect = false ;
std : : string m_lastResult = { } ;
} ;
} // namespace
int WINAPI wWinMain ( HINSTANCE hInstance , HINSTANCE , LPWSTR , int nCmdShow ) {
return ScenarioApp ( ) . Run ( hInstance , nCmdShow ) ;
}