Build XCEditor viewport slot shell foundation
This commit is contained in:
@@ -15,6 +15,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_ui_editor_panel_frame.cpp
|
||||
test_ui_editor_status_bar.cpp
|
||||
test_ui_editor_tab_strip.cpp
|
||||
test_ui_editor_viewport_slot.cpp
|
||||
test_ui_editor_shortcut_manager.cpp
|
||||
test_ui_editor_workspace_controller.cpp
|
||||
test_ui_editor_workspace_layout_persistence.cpp
|
||||
|
||||
281
tests/UI/Editor/unit/test_ui_editor_viewport_slot.cpp
Normal file
281
tests/UI/Editor/unit/test_ui_editor_viewport_slot.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
#include <XCEditor/Widgets/UIEditorViewportSlot.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawCommand;
|
||||
using XCEngine::UI::UIDrawCommandType;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UISize;
|
||||
using XCEngine::UI::UITextureHandle;
|
||||
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::ResolveUIEditorViewportSlotDesiredToolWidth;
|
||||
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::UIEditorViewportSlotHitTargetKind;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotInvalidIndex;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotLayout;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotPalette;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotState;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotToolItem;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorViewportSlotToolSlot;
|
||||
|
||||
void ExpectColorEq(const UIColor& actual, const UIColor& expected) {
|
||||
EXPECT_FLOAT_EQ(actual.r, expected.r);
|
||||
EXPECT_FLOAT_EQ(actual.g, expected.g);
|
||||
EXPECT_FLOAT_EQ(actual.b, expected.b);
|
||||
EXPECT_FLOAT_EQ(actual.a, expected.a);
|
||||
}
|
||||
|
||||
bool RectEq(const UIRect& actual, const UIRect& expected) {
|
||||
return actual.x == expected.x &&
|
||||
actual.y == expected.y &&
|
||||
actual.width == expected.width &&
|
||||
actual.height == expected.height;
|
||||
}
|
||||
|
||||
const UIDrawCommand* FindCommand(
|
||||
const UIDrawList& drawList,
|
||||
UIDrawCommandType type,
|
||||
const UIRect& rect) {
|
||||
for (const UIDrawCommand& command : drawList.GetCommands()) {
|
||||
if (command.type == type && RectEq(command.rect, rect)) {
|
||||
return &command;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ContainsText(const UIDrawList& drawList, std::string_view text) {
|
||||
for (const UIDrawCommand& command : drawList.GetCommands()) {
|
||||
if (command.type == UIDrawCommandType::Text && command.text == text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<UIEditorViewportSlotToolItem> BuildToolItems() {
|
||||
return {
|
||||
{ "mode", "Perspective", UIEditorViewportSlotToolSlot::Leading, true, true, 96.0f },
|
||||
{ "lit", "Lit", UIEditorViewportSlotToolSlot::Trailing, true, false, 48.0f },
|
||||
{ "gizmos", "Gizmos", UIEditorViewportSlotToolSlot::Trailing, true, false, 72.0f }
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<UIEditorStatusBarSegment> BuildStatusSegments() {
|
||||
return {
|
||||
{ "scene", "Scene", UIEditorStatusBarSlot::Leading, {}, true, true, 72.0f },
|
||||
{ "resolution", "1280x720", UIEditorStatusBarSlot::Leading, {}, true, false, 92.0f },
|
||||
{ "frame", "16.7 ms", UIEditorStatusBarSlot::Trailing, {}, true, false, 72.0f }
|
||||
};
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, DesiredToolWidthUsesExplicitValueBeforeEstimatedLabelWidth) {
|
||||
UIEditorViewportSlotToolItem explicitWidth = {};
|
||||
explicitWidth.label = "Scene";
|
||||
explicitWidth.desiredWidth = 88.0f;
|
||||
|
||||
UIEditorViewportSlotToolItem inferredWidth = {};
|
||||
inferredWidth.label = "Scene";
|
||||
|
||||
EXPECT_FLOAT_EQ(ResolveUIEditorViewportSlotDesiredToolWidth(explicitWidth), 88.0f);
|
||||
EXPECT_FLOAT_EQ(ResolveUIEditorViewportSlotDesiredToolWidth(inferredWidth), 55.0f);
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, LayoutBuildsTopBarSurfaceBottomBarAndAspectFittedTexture) {
|
||||
UIEditorViewportSlotChrome chrome = {};
|
||||
chrome.topBarHeight = 40.0f;
|
||||
chrome.bottomBarHeight = 28.0f;
|
||||
|
||||
UIEditorViewportSlotFrame frame = {};
|
||||
frame.hasTexture = true;
|
||||
frame.presentedSize = UISize(1600.0f, 900.0f);
|
||||
|
||||
const UIEditorViewportSlotLayout layout =
|
||||
BuildUIEditorViewportSlotLayout(
|
||||
UIRect(10.0f, 20.0f, 800.0f, 600.0f),
|
||||
chrome,
|
||||
frame,
|
||||
BuildToolItems(),
|
||||
BuildStatusSegments());
|
||||
|
||||
EXPECT_TRUE(layout.hasTopBar);
|
||||
EXPECT_TRUE(layout.hasBottomBar);
|
||||
EXPECT_FLOAT_EQ(layout.topBarRect.y, 20.0f);
|
||||
EXPECT_FLOAT_EQ(layout.topBarRect.height, 40.0f);
|
||||
EXPECT_FLOAT_EQ(layout.bottomBarRect.y, 592.0f);
|
||||
EXPECT_FLOAT_EQ(layout.bottomBarRect.height, 28.0f);
|
||||
EXPECT_FLOAT_EQ(layout.surfaceRect.y, 60.0f);
|
||||
EXPECT_FLOAT_EQ(layout.surfaceRect.height, 532.0f);
|
||||
EXPECT_FLOAT_EQ(layout.requestedSurfaceSize.width, 800.0f);
|
||||
EXPECT_FLOAT_EQ(layout.requestedSurfaceSize.height, 532.0f);
|
||||
EXPECT_FLOAT_EQ(layout.textureRect.x, 10.0f);
|
||||
EXPECT_FLOAT_EQ(layout.textureRect.y, 101.0f);
|
||||
EXPECT_FLOAT_EQ(layout.textureRect.width, 800.0f);
|
||||
EXPECT_FLOAT_EQ(layout.textureRect.height, 450.0f);
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, ToolItemsAlignToEdgesAndTitleRectClampsBetweenToolBands) {
|
||||
UIEditorViewportSlotChrome chrome = {};
|
||||
chrome.topBarHeight = 40.0f;
|
||||
chrome.bottomBarHeight = 0.0f;
|
||||
chrome.showBottomBar = false;
|
||||
|
||||
const UIEditorViewportSlotLayout layout =
|
||||
BuildUIEditorViewportSlotLayout(
|
||||
UIRect(0.0f, 0.0f, 900.0f, 520.0f),
|
||||
chrome,
|
||||
UIEditorViewportSlotFrame{},
|
||||
BuildToolItems(),
|
||||
{});
|
||||
|
||||
EXPECT_FLOAT_EQ(layout.toolItemRects[0].x, 12.0f);
|
||||
EXPECT_FLOAT_EQ(layout.toolItemRects[0].width, 96.0f);
|
||||
EXPECT_FLOAT_EQ(layout.toolItemRects[1].x, 762.0f);
|
||||
EXPECT_FLOAT_EQ(layout.toolItemRects[2].x, 816.0f);
|
||||
EXPECT_FLOAT_EQ(layout.titleRect.x, 118.0f);
|
||||
EXPECT_FLOAT_EQ(layout.titleRect.width, 634.0f);
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, HitTestPrioritizesToolThenStatusThenSurface) {
|
||||
UIEditorViewportSlotChrome chrome = {};
|
||||
chrome.topBarHeight = 40.0f;
|
||||
chrome.bottomBarHeight = 28.0f;
|
||||
|
||||
const UIEditorViewportSlotLayout layout =
|
||||
BuildUIEditorViewportSlotLayout(
|
||||
UIRect(0.0f, 0.0f, 900.0f, 520.0f),
|
||||
chrome,
|
||||
UIEditorViewportSlotFrame{},
|
||||
BuildToolItems(),
|
||||
BuildStatusSegments());
|
||||
|
||||
auto hit = HitTestUIEditorViewportSlot(layout, UIPoint(30.0f, 16.0f));
|
||||
EXPECT_EQ(hit.kind, UIEditorViewportSlotHitTargetKind::ToolItem);
|
||||
EXPECT_EQ(hit.index, 0u);
|
||||
|
||||
hit = HitTestUIEditorViewportSlot(layout, UIPoint(22.0f, 505.0f));
|
||||
EXPECT_EQ(hit.kind, UIEditorViewportSlotHitTargetKind::StatusSegment);
|
||||
EXPECT_EQ(hit.index, 0u);
|
||||
|
||||
hit = HitTestUIEditorViewportSlot(layout, UIPoint(450.0f, 240.0f));
|
||||
EXPECT_EQ(hit.kind, UIEditorViewportSlotHitTargetKind::Surface);
|
||||
EXPECT_EQ(hit.index, UIEditorViewportSlotInvalidIndex);
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, BackgroundAndForegroundEmitChromeAndImageBranchCommands) {
|
||||
UIEditorViewportSlotChrome chrome = {};
|
||||
chrome.title = "Scene View";
|
||||
chrome.subtitle = "ViewportSlot shell";
|
||||
|
||||
UIEditorViewportSlotFrame frame = {};
|
||||
frame.hasTexture = true;
|
||||
frame.texture = UITextureHandle{ 1u, 1280u, 720u };
|
||||
frame.presentedSize = UISize(1280.0f, 720.0f);
|
||||
|
||||
UIEditorViewportSlotState state = {};
|
||||
state.focused = true;
|
||||
state.surfaceHovered = true;
|
||||
state.surfaceActive = true;
|
||||
state.inputCaptured = true;
|
||||
state.hoveredToolIndex = 0u;
|
||||
state.activeToolIndex = 1u;
|
||||
state.statusBarState.focused = true;
|
||||
|
||||
const auto toolItems = BuildToolItems();
|
||||
const auto statusSegments = BuildStatusSegments();
|
||||
const UIEditorViewportSlotLayout layout =
|
||||
BuildUIEditorViewportSlotLayout(
|
||||
UIRect(12.0f, 16.0f, 900.0f, 520.0f),
|
||||
chrome,
|
||||
frame,
|
||||
toolItems,
|
||||
statusSegments);
|
||||
|
||||
const UIEditorViewportSlotPalette palette = {};
|
||||
|
||||
UIDrawList background("ViewportSlotBackground");
|
||||
AppendUIEditorViewportSlotBackground(
|
||||
background,
|
||||
layout,
|
||||
toolItems,
|
||||
statusSegments,
|
||||
state,
|
||||
palette);
|
||||
|
||||
const UIDrawCommand* surfaceBorder =
|
||||
FindCommand(background, UIDrawCommandType::RectOutline, layout.inputRect);
|
||||
ASSERT_NE(surfaceBorder, nullptr);
|
||||
ExpectColorEq(surfaceBorder->color, palette.surfaceCapturedBorderColor);
|
||||
|
||||
UIDrawList foreground("ViewportSlotForeground");
|
||||
AppendUIEditorViewportSlotForeground(
|
||||
foreground,
|
||||
layout,
|
||||
chrome,
|
||||
frame,
|
||||
toolItems,
|
||||
statusSegments,
|
||||
state,
|
||||
palette);
|
||||
|
||||
EXPECT_TRUE(ContainsText(foreground, "Scene View"));
|
||||
EXPECT_TRUE(ContainsText(foreground, "Perspective"));
|
||||
EXPECT_TRUE(ContainsText(foreground, "Texture 1280x720"));
|
||||
|
||||
bool foundImage = false;
|
||||
for (const UIDrawCommand& command : foreground.GetCommands()) {
|
||||
if (command.type == UIDrawCommandType::Image &&
|
||||
RectEq(command.rect, layout.textureRect)) {
|
||||
foundImage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(foundImage);
|
||||
}
|
||||
|
||||
TEST(UIEditorViewportSlotTest, ForegroundFallsBackToStatusTextWhenTextureIsUnavailable) {
|
||||
UIEditorViewportSlotChrome chrome = {};
|
||||
chrome.title = "Game View";
|
||||
|
||||
UIEditorViewportSlotFrame frame = {};
|
||||
frame.statusText = "Viewport is waiting for frame";
|
||||
|
||||
const auto toolItems = BuildToolItems();
|
||||
const auto statusSegments = BuildStatusSegments();
|
||||
const UIEditorViewportSlotLayout layout =
|
||||
BuildUIEditorViewportSlotLayout(
|
||||
UIRect(0.0f, 0.0f, 720.0f, 420.0f),
|
||||
chrome,
|
||||
frame,
|
||||
toolItems,
|
||||
statusSegments);
|
||||
|
||||
UIDrawList foreground("ViewportSlotForegroundFallback");
|
||||
AppendUIEditorViewportSlotForeground(
|
||||
foreground,
|
||||
layout,
|
||||
chrome,
|
||||
frame,
|
||||
toolItems,
|
||||
statusSegments,
|
||||
UIEditorViewportSlotState{});
|
||||
|
||||
EXPECT_TRUE(ContainsText(foreground, "Viewport is waiting for frame"));
|
||||
EXPECT_FALSE(ContainsText(foreground, "Texture 1280x720"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user