ui: add typed editor field foundations

This commit is contained in:
2026-04-08 02:52:28 +08:00
parent 805e07bf90
commit 0a392e1311
69 changed files with 11676 additions and 1169 deletions

View File

@@ -17,6 +17,8 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
test_ui_editor_shell_compose.cpp
test_ui_editor_shell_interaction.cpp
test_ui_editor_collection_primitives.cpp
test_ui_editor_field_row_layout.cpp
test_ui_editor_theme.cpp
test_ui_editor_bool_field.cpp
test_ui_editor_bool_field_interaction.cpp
test_ui_editor_dock_host.cpp
@@ -28,6 +30,12 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
test_ui_editor_enum_field_interaction.cpp
test_ui_editor_number_field.cpp
test_ui_editor_number_field_interaction.cpp
test_ui_editor_text_field.cpp
test_ui_editor_text_field_interaction.cpp
test_ui_editor_vector2_field.cpp
test_ui_editor_vector2_field_interaction.cpp
test_ui_editor_vector3_field.cpp
test_ui_editor_vector3_field_interaction.cpp
test_ui_editor_scroll_view.cpp
test_ui_editor_scroll_view_interaction.cpp
test_ui_editor_status_bar.cpp

View File

@@ -1,9 +1,12 @@
#include <gtest/gtest.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEditor/Widgets/UIEditorBoolField.h>
namespace {
using XCEngine::UI::UIDrawCommandType;
using XCEngine::UI::UIColor;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::AppendUIEditorBoolFieldBackground;
@@ -16,39 +19,54 @@ using XCEngine::UI::Editor::Widgets::UIEditorBoolFieldState;
TEST(UIEditorBoolFieldTest, LayoutBuildsLabelAndToggleRects) {
UIEditorBoolFieldSpec spec = { "visible", "Visible", true, false };
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
EXPECT_GT(layout.labelRect.width, 0.0f);
EXPECT_FLOAT_EQ(layout.toggleRect.width, 42.0f);
EXPECT_GT(layout.knobRect.x, layout.toggleRect.x);
EXPECT_FLOAT_EQ(layout.controlRect.x, 236.0f);
EXPECT_FLOAT_EQ(layout.checkboxRect.width, 18.0f);
EXPECT_FLOAT_EQ(layout.checkmarkRect.width, layout.checkboxRect.width);
EXPECT_FLOAT_EQ(layout.checkboxRect.y, 2.0f);
}
TEST(UIEditorBoolFieldTest, HitTestResolvesToggleAndRow) {
UIEditorBoolFieldSpec spec = { "visible", "Visible", false, false };
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
const auto toggleHit = HitTestUIEditorBoolField(
const auto checkboxHit = HitTestUIEditorBoolField(
layout,
UIPoint(layout.toggleRect.x + 4.0f, layout.toggleRect.y + 4.0f));
EXPECT_EQ(toggleHit.kind, UIEditorBoolFieldHitTargetKind::Toggle);
UIPoint(layout.controlRect.x + layout.controlRect.width - 2.0f, layout.controlRect.y + 2.0f));
EXPECT_EQ(checkboxHit.kind, UIEditorBoolFieldHitTargetKind::Checkbox);
const auto rowHit = HitTestUIEditorBoolField(layout, UIPoint(20.0f, 16.0f));
const auto rowHit = HitTestUIEditorBoolField(layout, UIPoint(20.0f, 11.0f));
EXPECT_EQ(rowHit.kind, UIEditorBoolFieldHitTargetKind::Row);
}
TEST(UIEditorBoolFieldTest, BackgroundAndForegroundEmitStableCommands) {
TEST(UIEditorBoolFieldTest, BackgroundAndForegroundEmitCheckboxOnlyChromeAndCenteredText) {
UIEditorBoolFieldSpec spec = { "visible", "Visible", true, false };
UIEditorBoolFieldState state = {};
state.focused = true;
state.hoveredTarget = UIEditorBoolFieldHitTargetKind::Toggle;
state.hoveredTarget = UIEditorBoolFieldHitTargetKind::Checkbox;
XCEngine::UI::UIDrawData drawData = {};
auto& drawList = drawData.EmplaceDrawList("BoolField");
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
const auto layout = BuildUIEditorBoolFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
AppendUIEditorBoolFieldBackground(drawList, layout, spec, state);
AppendUIEditorBoolFieldForeground(drawList, layout, spec);
ASSERT_GE(drawList.GetCommands().size(), 6u);
const auto& commands = drawList.GetCommands();
ASSERT_EQ(commands.size(), 6u);
EXPECT_EQ(commands[0].type, UIDrawCommandType::FilledRect);
EXPECT_EQ(commands[0].rect.x, layout.checkboxRect.x);
EXPECT_EQ(commands[0].rect.y, layout.checkboxRect.y);
EXPECT_EQ(commands[1].type, UIDrawCommandType::RectOutline);
EXPECT_EQ(commands[1].color.r, 0.14f);
EXPECT_EQ(commands[2].type, UIDrawCommandType::PushClipRect);
EXPECT_EQ(commands[3].type, UIDrawCommandType::Text);
EXPECT_FLOAT_EQ(commands[3].position.y, 2.0f);
EXPECT_EQ(commands[4].type, UIDrawCommandType::PopClipRect);
EXPECT_EQ(commands[5].type, UIDrawCommandType::Text);
EXPECT_EQ(commands[5].text, "V");
EXPECT_FLOAT_EQ(commands[5].position.y, 2.0f);
}
} // namespace

View File

@@ -45,7 +45,7 @@ TEST(UIEditorBoolFieldInteractionTest, ClickToggleFlipsValue) {
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{});
const auto toggle = frame.layout.toggleRect;
const auto checkbox = frame.layout.checkboxRect;
frame = UpdateUIEditorBoolFieldInteraction(
state,
@@ -53,8 +53,8 @@ TEST(UIEditorBoolFieldInteractionTest, ClickToggleFlipsValue) {
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{
MakePointer(UIInputEventType::PointerButtonDown, toggle.x + 4.0f, toggle.y + 4.0f, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, toggle.x + 4.0f, toggle.y + 4.0f, UIPointerButton::Left)
MakePointer(UIInputEventType::PointerButtonDown, checkbox.x + 4.0f, checkbox.y + 4.0f, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, checkbox.x + 4.0f, checkbox.y + 4.0f, UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.valueChanged);
@@ -97,13 +97,13 @@ TEST(UIEditorBoolFieldInteractionTest, HoverTracksToggleHitTarget) {
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{});
const auto toggle = frame.layout.toggleRect;
const auto checkbox = frame.layout.checkboxRect;
frame = UpdateUIEditorBoolFieldInteraction(
state,
value,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{ MakePointer(UIInputEventType::PointerMove, toggle.x + 4.0f, toggle.y + 4.0f) });
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorBoolFieldHitTargetKind::Toggle);
{ MakePointer(UIInputEventType::PointerMove, checkbox.x + 4.0f, checkbox.y + 4.0f) });
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorBoolFieldHitTargetKind::Checkbox);
}

View File

@@ -1,35 +1,77 @@
#include <gtest/gtest.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEditor/Widgets/UIEditorEnumField.h>
namespace {
using XCEngine::UI::UIDrawCommandType;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::AppendUIEditorEnumFieldBackground;
using XCEngine::UI::Editor::Widgets::AppendUIEditorEnumFieldForeground;
using XCEngine::UI::Editor::Widgets::BuildUIEditorEnumFieldLayout;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorEnumField;
using XCEngine::UI::Editor::Widgets::ResolveUIEditorEnumFieldValueText;
using XCEngine::UI::Editor::Widgets::UIEditorEnumFieldHitTargetKind;
using XCEngine::UI::Editor::Widgets::UIEditorEnumFieldSpec;
using XCEngine::UI::Editor::Widgets::UIEditorEnumFieldState;
TEST(UIEditorEnumFieldTest, ValueTextUsesSelectedOption) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
EXPECT_EQ(ResolveUIEditorEnumFieldValueText(spec), "Cutout");
}
TEST(UIEditorEnumFieldTest, HitTestResolvesPreviousNextAndValueBox) {
TEST(UIEditorEnumFieldTest, LayoutKeepsInspectorControlColumnAndUnityArrowWidth) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
const auto layout = BuildUIEditorEnumFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
const auto layout = BuildUIEditorEnumFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
EXPECT_FLOAT_EQ(layout.controlRect.x, 236.0f);
EXPECT_FLOAT_EQ(layout.valueRect.y, 1.0f);
EXPECT_FLOAT_EQ(layout.valueRect.height, 20.0f);
EXPECT_FLOAT_EQ(layout.arrowRect.width, 20.0f);
}
TEST(UIEditorEnumFieldTest, HitTestResolvesArrowAndValueBox) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
const auto layout = BuildUIEditorEnumFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
EXPECT_EQ(
HitTestUIEditorEnumField(layout, UIPoint(layout.previousRect.x + 2.0f, layout.previousRect.y + 2.0f)).kind,
UIEditorEnumFieldHitTargetKind::PreviousButton);
EXPECT_EQ(
HitTestUIEditorEnumField(layout, UIPoint(layout.nextRect.x + 2.0f, layout.nextRect.y + 2.0f)).kind,
UIEditorEnumFieldHitTargetKind::NextButton);
HitTestUIEditorEnumField(layout, UIPoint(layout.arrowRect.x + 2.0f, layout.arrowRect.y + 2.0f)).kind,
UIEditorEnumFieldHitTargetKind::DropdownArrow);
EXPECT_EQ(
HitTestUIEditorEnumField(layout, UIPoint(layout.valueRect.x + 4.0f, layout.valueRect.y + 4.0f)).kind,
UIEditorEnumFieldHitTargetKind::ValueBox);
}
TEST(UIEditorEnumFieldTest, BackgroundAndForegroundEmitControlOnlyChromeAndCenteredText) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
UIEditorEnumFieldState state = {};
state.popupOpen = true;
state.hoveredTarget = UIEditorEnumFieldHitTargetKind::DropdownArrow;
XCEngine::UI::UIDrawData drawData = {};
auto& drawList = drawData.EmplaceDrawList("EnumField");
const auto layout = BuildUIEditorEnumFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 22.0f), spec);
AppendUIEditorEnumFieldBackground(drawList, layout, spec, state);
AppendUIEditorEnumFieldForeground(drawList, layout, spec);
const auto& commands = drawList.GetCommands();
ASSERT_EQ(commands.size(), 9u);
EXPECT_EQ(commands[0].type, UIDrawCommandType::FilledRect);
EXPECT_EQ(commands[0].rect.x, layout.valueRect.x);
EXPECT_EQ(commands[1].type, UIDrawCommandType::RectOutline);
EXPECT_EQ(commands[2].type, UIDrawCommandType::PushClipRect);
EXPECT_EQ(commands[3].type, UIDrawCommandType::Text);
EXPECT_FLOAT_EQ(commands[3].position.y, 2.0f);
EXPECT_EQ(commands[4].type, UIDrawCommandType::PopClipRect);
EXPECT_EQ(commands[5].type, UIDrawCommandType::PushClipRect);
EXPECT_EQ(commands[6].type, UIDrawCommandType::Text);
EXPECT_FLOAT_EQ(commands[6].position.y, 1.0f);
EXPECT_EQ(commands[7].type, UIDrawCommandType::PopClipRect);
EXPECT_EQ(commands[8].type, UIDrawCommandType::Text);
EXPECT_EQ(commands[8].text, "V");
EXPECT_FLOAT_EQ(commands[8].position.y, 2.0f);
}
} // namespace

View File

@@ -33,7 +33,7 @@ UIInputEvent MakeKey(KeyCode keyCode) {
} // namespace
TEST(UIEditorEnumFieldInteractionTest, ClickButtonsAdjustSelection) {
TEST(UIEditorEnumFieldInteractionTest, ClickValueBoxOpensPopupAndSelectsItem) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
UIEditorEnumFieldInteractionState state = {};
std::size_t selectedIndex = 1u;
@@ -51,14 +51,29 @@ TEST(UIEditorEnumFieldInteractionTest, ClickButtonsAdjustSelection) {
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{
MakePointer(UIInputEventType::PointerButtonDown, frame.layout.nextRect.x + 2.0f, frame.layout.nextRect.y + 2.0f, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, frame.layout.nextRect.x + 2.0f, frame.layout.nextRect.y + 2.0f, UIPointerButton::Left)
MakePointer(UIInputEventType::PointerButtonDown, frame.layout.valueRect.x + 2.0f, frame.layout.valueRect.y + 2.0f, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, frame.layout.valueRect.x + 2.0f, frame.layout.valueRect.y + 2.0f, UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.popupOpened);
EXPECT_TRUE(frame.popupOpen);
ASSERT_FALSE(frame.popupLayout.itemRects.empty());
const auto itemRect = frame.popupLayout.itemRects[2];
frame = UpdateUIEditorEnumFieldInteraction(
state,
selectedIndex,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{
MakePointer(UIInputEventType::PointerButtonDown, itemRect.x + 2.0f, itemRect.y + 2.0f, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, itemRect.x + 2.0f, itemRect.y + 2.0f, UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(selectedIndex, 2u);
EXPECT_FALSE(frame.popupOpen);
}
TEST(UIEditorEnumFieldInteractionTest, KeyboardControlsMoveToEnds) {
TEST(UIEditorEnumFieldInteractionTest, KeyboardCanOpenMoveAndCommitPopupSelection) {
UIEditorEnumFieldSpec spec = { "blend", "Blend", { "Opaque", "Cutout", "Fade" }, 1u, false };
UIEditorEnumFieldInteractionState state = {};
state.fieldState.focused = true;
@@ -69,16 +84,17 @@ TEST(UIEditorEnumFieldInteractionTest, KeyboardControlsMoveToEnds) {
selectedIndex,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{ MakeKey(KeyCode::Home) });
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(selectedIndex, 0u);
{ MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.popupOpened);
EXPECT_TRUE(frame.popupOpen);
frame = UpdateUIEditorEnumFieldInteraction(
state,
selectedIndex,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
spec,
{ MakeKey(KeyCode::End) });
{ MakeKey(KeyCode::Down), MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(selectedIndex, 2u);
EXPECT_FALSE(frame.popupOpen);
}

View File

@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
namespace {
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::BuildUIEditorFieldRowLayout;
using XCEngine::UI::Editor::Widgets::UIEditorFieldRowLayoutMetrics;
TEST(UIEditorFieldRowLayoutTest, WideRowsKeepStableControlColumnAnchor) {
UIEditorFieldRowLayoutMetrics metrics = {};
const auto layout = BuildUIEditorFieldRowLayout(
UIRect(10.0f, 20.0f, 392.0f, 22.0f),
96.0f,
metrics);
EXPECT_FLOAT_EQ(layout.bounds.height, 22.0f);
EXPECT_FLOAT_EQ(layout.labelRect.x, 22.0f);
EXPECT_FLOAT_EQ(layout.controlRect.x, 246.0f);
EXPECT_FLOAT_EQ(layout.controlRect.y, 21.0f);
EXPECT_FLOAT_EQ(layout.controlRect.height, 20.0f);
}
TEST(UIEditorFieldRowLayoutTest, NarrowRowsCompressFromRightWithoutMagicRatioFallback) {
UIEditorFieldRowLayoutMetrics metrics = {};
const auto layout = BuildUIEditorFieldRowLayout(
UIRect(10.0f, 20.0f, 280.0f, 22.0f),
96.0f,
metrics);
EXPECT_FLOAT_EQ(layout.controlRect.x, 186.0f);
EXPECT_FLOAT_EQ(layout.controlRect.width, 96.0f);
EXPECT_FLOAT_EQ(layout.labelRect.width, 144.0f);
}
TEST(UIEditorFieldRowLayoutTest, ZeroHeightFallsBackToMetricRowHeight) {
UIEditorFieldRowLayoutMetrics metrics = {};
metrics.rowHeight = 24.0f;
metrics.controlInsetY = 2.0f;
const auto layout = BuildUIEditorFieldRowLayout(
UIRect(0.0f, 0.0f, 360.0f, 0.0f),
120.0f,
metrics);
EXPECT_FLOAT_EQ(layout.bounds.height, 24.0f);
EXPECT_FLOAT_EQ(layout.controlRect.y, 2.0f);
EXPECT_FLOAT_EQ(layout.controlRect.height, 20.0f);
}
} // namespace

View File

@@ -20,30 +20,26 @@ TEST(UIEditorNumberFieldTest, FormatSupportsIntegerAndFloatMode) {
EXPECT_EQ(FormatUIEditorNumberFieldValue(floatSpec), "1.25");
}
TEST(UIEditorNumberFieldTest, LayoutBuildsValueAndStepperRects) {
TEST(UIEditorNumberFieldTest, LayoutBuildsValueRectWithoutStepperButtons) {
UIEditorNumberFieldSpec spec = { "queue", "Queue", 7.0, 1.0, 0.0, 10.0, true, false };
const auto layout = BuildUIEditorNumberFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
EXPECT_GT(layout.labelRect.width, 0.0f);
EXPECT_GT(layout.controlRect.width, 0.0f);
EXPECT_GT(layout.valueRect.width, 0.0f);
EXPECT_FLOAT_EQ(layout.decrementRect.width, 22.0f);
EXPECT_FLOAT_EQ(layout.incrementRect.width, 22.0f);
EXPECT_FLOAT_EQ(layout.controlRect.width, layout.valueRect.width);
}
TEST(UIEditorNumberFieldTest, HitTestResolvesButtonsAndValueBox) {
TEST(UIEditorNumberFieldTest, HitTestResolvesValueBoxAndRow) {
UIEditorNumberFieldSpec spec = { "queue", "Queue", 7.0, 1.0, 0.0, 10.0, true, false };
const auto layout = BuildUIEditorNumberFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
EXPECT_EQ(
HitTestUIEditorNumberField(layout, UIPoint(layout.decrementRect.x + 2.0f, layout.decrementRect.y + 2.0f)).kind,
UIEditorNumberFieldHitTargetKind::DecrementButton);
EXPECT_EQ(
HitTestUIEditorNumberField(layout, UIPoint(layout.incrementRect.x + 2.0f, layout.incrementRect.y + 2.0f)).kind,
UIEditorNumberFieldHitTargetKind::IncrementButton);
EXPECT_EQ(
HitTestUIEditorNumberField(layout, UIPoint(layout.valueRect.x + 4.0f, layout.valueRect.y + 4.0f)).kind,
UIEditorNumberFieldHitTargetKind::ValueBox);
EXPECT_EQ(
HitTestUIEditorNumberField(layout, UIPoint(layout.labelRect.x + 4.0f, layout.labelRect.y + 4.0f)).kind,
UIEditorNumberFieldHitTargetKind::Row);
}
} // namespace

View File

@@ -40,7 +40,7 @@ UIInputEvent MakeCharacter(char character) {
} // namespace
TEST(UIEditorNumberFieldInteractionTest, ClickStepperButtonsAdjustValue) {
TEST(UIEditorNumberFieldInteractionTest, ClickValueBoxStartsEditing) {
UIEditorNumberFieldSpec spec = { "queue", "Queue", 2.0, 1.0, 0.0, 5.0, true, false };
UIEditorNumberFieldInteractionState state = {};
@@ -57,20 +57,19 @@ TEST(UIEditorNumberFieldInteractionTest, ClickStepperButtonsAdjustValue) {
{
MakePointer(
UIInputEventType::PointerButtonDown,
frame.layout.incrementRect.x + 2.0f,
frame.layout.incrementRect.y + 2.0f,
frame.layout.valueRect.x + 2.0f,
frame.layout.valueRect.y + 2.0f,
UIPointerButton::Left),
MakePointer(
UIInputEventType::PointerButtonUp,
frame.layout.incrementRect.x + 2.0f,
frame.layout.incrementRect.y + 2.0f,
frame.layout.valueRect.x + 2.0f,
frame.layout.valueRect.y + 2.0f,
UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.stepApplied);
EXPECT_TRUE(frame.result.valueChanged);
EXPECT_DOUBLE_EQ(spec.value, 3.0);
EXPECT_DOUBLE_EQ(frame.result.valueAfter, 3.0);
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.numberFieldState.editing);
EXPECT_DOUBLE_EQ(spec.value, 2.0);
}
TEST(UIEditorNumberFieldInteractionTest, KeyboardStepAndBoundsWorkWhenFocused) {

View File

@@ -16,7 +16,9 @@ using XCEngine::UI::Editor::Widgets::FindUIEditorPropertyGridFieldLocation;
using XCEngine::UI::Editor::Widgets::FindUIEditorPropertyGridSectionIndex;
using XCEngine::UI::Editor::Widgets::FindUIEditorPropertyGridVisibleFieldIndex;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorPropertyGrid;
using XCEngine::UI::Editor::Widgets::ResolveUIEditorPropertyGridFieldValueText;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridField;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridFieldKind;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridHitTargetKind;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridSection;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridState;
@@ -36,23 +38,80 @@ bool ContainsTextCommand(
return false;
}
UIEditorPropertyGridField MakeTextField(
std::string id,
std::string label,
std::string value,
bool readOnly = false) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.valueText = std::move(value);
field.readOnly = readOnly;
return field;
}
UIEditorPropertyGridField MakeBoolField(
std::string id,
std::string label,
bool value) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Bool;
field.boolValue = value;
return field;
}
UIEditorPropertyGridField MakeNumberField(
std::string id,
std::string label,
double value,
bool integerMode = true) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Number;
field.numberValue.value = value;
field.numberValue.step = 1.0;
field.numberValue.minValue = 0.0;
field.numberValue.maxValue = 5000.0;
field.numberValue.integerMode = integerMode;
return field;
}
UIEditorPropertyGridField MakeEnumField(
std::string id,
std::string label,
std::vector<std::string> options,
std::size_t selectedIndex) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Enum;
field.enumValue.options = std::move(options);
field.enumValue.selectedIndex = selectedIndex;
return field;
}
std::vector<UIEditorPropertyGridSection> BuildSections() {
return {
{
"transform",
"Transform",
"inspector",
"Inspector",
{
{ "position", "Position", "0, 0, 0", false, 0.0f },
{ "rotation", "Rotation", "0, 45, 0", false, 0.0f }
MakeBoolField("enabled", "Enabled", true),
MakeNumberField("render_queue", "Render Queue", 2000.0),
MakeEnumField("render_mode", "Render Mode", { "Opaque", "Cutout", "Fade" }, 0u),
MakeTextField("tag", "Tag", "Player")
},
0.0f
},
{
"rendering",
"Rendering",
"metadata",
"Metadata",
{
{ "material", "Material", "Metal", false, 0.0f },
{ "guid", "GUID", "asset-guid-001", true, 0.0f }
MakeTextField("guid", "GUID", "asset-guid-001", true)
},
0.0f
}
@@ -68,55 +127,64 @@ UIPoint RectCenter(const XCEngine::UI::UIRect& rect) {
TEST(UIEditorPropertyGridTest, FindSectionAndFieldLocationReturnStableIndices) {
const auto sections = BuildSections();
EXPECT_EQ(FindUIEditorPropertyGridSectionIndex(sections, "transform"), 0u);
EXPECT_EQ(FindUIEditorPropertyGridSectionIndex(sections, "rendering"), 1u);
EXPECT_EQ(FindUIEditorPropertyGridSectionIndex(sections, "inspector"), 0u);
EXPECT_EQ(FindUIEditorPropertyGridSectionIndex(sections, "metadata"), 1u);
EXPECT_EQ(
FindUIEditorPropertyGridSectionIndex(sections, "missing"),
static_cast<std::size_t>(-1));
const auto materialLocation =
FindUIEditorPropertyGridFieldLocation(sections, "material");
EXPECT_TRUE(materialLocation.IsValid());
EXPECT_EQ(materialLocation.sectionIndex, 1u);
EXPECT_EQ(materialLocation.fieldIndex, 0u);
const auto renderModeLocation =
FindUIEditorPropertyGridFieldLocation(sections, "render_mode");
EXPECT_TRUE(renderModeLocation.IsValid());
EXPECT_EQ(renderModeLocation.sectionIndex, 0u);
EXPECT_EQ(renderModeLocation.fieldIndex, 2u);
const auto missingLocation =
FindUIEditorPropertyGridFieldLocation(sections, "unknown");
EXPECT_FALSE(missingLocation.IsValid());
}
TEST(UIEditorPropertyGridTest, LayoutBuildsSectionHeadersAndVisibleFieldRects) {
TEST(UIEditorPropertyGridTest, LayoutBuildsTypedFieldRectsWithStableColumns) {
const auto sections = BuildSections();
UIExpansionModel expansionModel = {};
expansionModel.Expand("transform");
expansionModel.Expand("inspector");
const auto layout = BuildUIEditorPropertyGridLayout(
UIRect(10.0f, 20.0f, 420.0f, 240.0f),
UIRect(10.0f, 20.0f, 420.0f, 260.0f),
sections,
expansionModel);
ASSERT_EQ(layout.sectionHeaderRects.size(), sections.size());
EXPECT_EQ(layout.visibleFieldIndices.size(), 2u);
ASSERT_EQ(layout.visibleFieldIndices.size(), 4u);
EXPECT_EQ(layout.visibleFieldSectionIndices[0], 0u);
EXPECT_EQ(layout.visibleFieldIndices[1], 1u);
EXPECT_FLOAT_EQ(layout.sectionHeaderRects[0].x, 18.0f);
EXPECT_FLOAT_EQ(layout.sectionHeaderRects[0].y, 28.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[0].x, 254.0f);
EXPECT_GT(layout.fieldValueRects[0].width, 0.0f);
EXPECT_EQ(
FindUIEditorPropertyGridVisibleFieldIndex(layout, "position", sections),
FindUIEditorPropertyGridVisibleFieldIndex(layout, "enabled", sections),
0u);
EXPECT_EQ(
FindUIEditorPropertyGridVisibleFieldIndex(layout, "material", sections),
FindUIEditorPropertyGridVisibleFieldIndex(layout, "guid", sections),
static_cast<std::size_t>(-1));
EXPECT_FLOAT_EQ(layout.fieldLabelRects[0].x, layout.fieldRowRects[0].x + 12.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[0].x, layout.fieldRowRects[0].x + 236.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[0].y, layout.fieldRowRects[0].y);
EXPECT_FLOAT_EQ(layout.fieldValueRects[1].x, layout.fieldRowRects[1].x + 236.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[1].y, layout.fieldRowRects[1].y + 4.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[2].x, layout.fieldRowRects[2].x + 236.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[2].y, layout.fieldRowRects[2].y + 4.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[3].x, layout.fieldRowRects[3].x + 236.0f);
EXPECT_FLOAT_EQ(layout.fieldValueRects[3].y, layout.fieldRowRects[3].y + 4.0f);
EXPECT_GT(layout.fieldValueRects[2].width, 0.0f);
EXPECT_GT(layout.fieldValueRects[3].width, 0.0f);
}
TEST(UIEditorPropertyGridTest, HitTestResolvesHeaderRowAndValueBox) {
TEST(UIEditorPropertyGridTest, HitTestResolvesHeaderRowAndTypedValueHosts) {
const auto sections = BuildSections();
UIExpansionModel expansionModel = {};
expansionModel.Expand("transform");
expansionModel.Expand("rendering");
expansionModel.Expand("inspector");
expansionModel.Expand("metadata");
const auto layout = BuildUIEditorPropertyGridLayout(
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
@@ -134,32 +202,46 @@ TEST(UIEditorPropertyGridTest, HitTestResolvesHeaderRowAndValueBox) {
layout.fieldRowRects[1].x + 16.0f,
layout.fieldRowRects[1].y + layout.fieldRowRects[1].height * 0.5f));
EXPECT_EQ(rowHit.kind, UIEditorPropertyGridHitTargetKind::FieldRow);
EXPECT_EQ(rowHit.sectionIndex, 0u);
EXPECT_EQ(rowHit.fieldIndex, 1u);
const auto valueHit =
const auto boolValueHit =
HitTestUIEditorPropertyGrid(layout, RectCenter(layout.fieldValueRects[0]));
EXPECT_EQ(boolValueHit.kind, UIEditorPropertyGridHitTargetKind::ValueBox);
EXPECT_EQ(boolValueHit.fieldIndex, 0u);
const auto boolTrailingHit = HitTestUIEditorPropertyGrid(
layout,
UIPoint(
layout.fieldValueRects[0].x + layout.fieldValueRects[0].width - 6.0f,
layout.fieldValueRects[0].y + layout.fieldValueRects[0].height * 0.5f));
EXPECT_EQ(boolTrailingHit.kind, UIEditorPropertyGridHitTargetKind::ValueBox);
EXPECT_EQ(boolTrailingHit.fieldIndex, 0u);
const auto enumValueHit =
HitTestUIEditorPropertyGrid(layout, RectCenter(layout.fieldValueRects[2]));
EXPECT_EQ(valueHit.kind, UIEditorPropertyGridHitTargetKind::ValueBox);
EXPECT_EQ(valueHit.sectionIndex, 1u);
EXPECT_EQ(valueHit.fieldIndex, 0u);
EXPECT_EQ(enumValueHit.kind, UIEditorPropertyGridHitTargetKind::ValueBox);
EXPECT_EQ(enumValueHit.fieldIndex, 2u);
}
TEST(UIEditorPropertyGridTest, BackgroundAndForegroundEmitStableCommands) {
TEST(UIEditorPropertyGridTest, BackgroundAndForegroundEmitTypedCommandsAndPopupOverlay) {
const auto sections = BuildSections();
UISelectionModel selectionModel = {};
selectionModel.SetSelection("rotation");
selectionModel.SetSelection("render_mode");
UIExpansionModel expansionModel = {};
expansionModel.Expand("transform");
expansionModel.Expand("rendering");
expansionModel.Expand("inspector");
expansionModel.Expand("metadata");
UIPropertyEditModel propertyEditModel = {};
propertyEditModel.BeginEdit("material", "Metal");
propertyEditModel.UpdateStagedValue("Mat_Inst");
propertyEditModel.BeginEdit("tag", "Player");
propertyEditModel.UpdateStagedValue("Hero");
UIEditorPropertyGridState state = {};
state.focused = true;
state.hoveredFieldId = "position";
state.hoveredFieldId = "enabled";
state.hoveredHitTarget = UIEditorPropertyGridHitTargetKind::ValueBox;
state.popupFieldId = "render_mode";
state.popupHighlightedIndex = 1u;
const auto layout = BuildUIEditorPropertyGridLayout(
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
expansionModel);
@@ -176,14 +258,19 @@ TEST(UIEditorPropertyGridTest, BackgroundAndForegroundEmitStableCommands) {
drawList,
layout,
sections,
state,
propertyEditModel);
const auto& commands = drawList.GetCommands();
ASSERT_GE(commands.size(), 12u);
ASSERT_GE(commands.size(), 16u);
EXPECT_EQ(commands[0].type, XCEngine::UI::UIDrawCommandType::FilledRect);
EXPECT_EQ(commands[1].type, XCEngine::UI::UIDrawCommandType::RectOutline);
EXPECT_TRUE(ContainsTextCommand(drawData, "Transform"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Position"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Mat_Inst"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Inspector"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Enabled"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Render Queue"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Opaque"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Hero"));
EXPECT_TRUE(ContainsTextCommand(drawData, "EDIT"));
EXPECT_TRUE(ContainsTextCommand(drawData, "Cutout"));
EXPECT_EQ(ResolveUIEditorPropertyGridFieldValueText(sections[0].fields[1]), "2000");
}

View File

@@ -17,16 +17,75 @@ using XCEngine::UI::Widgets::UIPropertyEditModel;
using XCEngine::UI::Widgets::UISelectionModel;
using XCEngine::UI::Editor::UIEditorPropertyGridInteractionState;
using XCEngine::UI::Editor::UpdateUIEditorPropertyGridInteraction;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridField;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridFieldKind;
using XCEngine::UI::Editor::Widgets::UIEditorPropertyGridSection;
UIEditorPropertyGridField MakeTextField(
std::string id,
std::string label,
std::string value,
bool readOnly = false) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.valueText = std::move(value);
field.readOnly = readOnly;
return field;
}
UIEditorPropertyGridField MakeBoolField(
std::string id,
std::string label,
bool value) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Bool;
field.boolValue = value;
return field;
}
UIEditorPropertyGridField MakeNumberField(
std::string id,
std::string label,
double value) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Number;
field.numberValue.value = value;
field.numberValue.step = 1.0;
field.numberValue.minValue = 0.0;
field.numberValue.maxValue = 5000.0;
field.numberValue.integerMode = true;
return field;
}
UIEditorPropertyGridField MakeEnumField(
std::string id,
std::string label,
std::vector<std::string> options,
std::size_t selectedIndex) {
UIEditorPropertyGridField field = {};
field.fieldId = std::move(id);
field.label = std::move(label);
field.kind = UIEditorPropertyGridFieldKind::Enum;
field.enumValue.options = std::move(options);
field.enumValue.selectedIndex = selectedIndex;
return field;
}
std::vector<UIEditorPropertyGridSection> BuildSections() {
return {
{
"transform",
"Transform",
"inspector",
"Inspector",
{
{ "position", "Position", "10", false, 0.0f },
{ "rotation", "Rotation", "0", false, 0.0f }
MakeBoolField("enabled", "Enabled", true),
MakeNumberField("render_queue", "Render Queue", 2000.0),
MakeEnumField("render_mode", "Render Mode", { "Opaque", "Cutout", "Fade" }, 0u),
MakeTextField("tag", "Tag", "Player")
},
0.0f
},
@@ -34,8 +93,7 @@ std::vector<UIEditorPropertyGridSection> BuildSections() {
"metadata",
"Metadata",
{
{ "tag", "Tag", "", false, 0.0f },
{ "guid", "GUID", "asset-guid-001", true, 0.0f }
MakeTextField("guid", "GUID", "asset-guid-001", true)
},
0.0f
}
@@ -84,14 +142,14 @@ UIPoint RectCenter(const XCEngine::UI::UIRect& rect) {
}
void ExpandAll(UIExpansionModel& expansionModel) {
expansionModel.Expand("transform");
expansionModel.Expand("inspector");
expansionModel.Expand("metadata");
}
} // namespace
TEST(UIEditorPropertyGridInteractionTest, PointerMoveUpdatesHoveredSectionAndField) {
const auto sections = BuildSections();
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
@@ -103,7 +161,7 @@ TEST(UIEditorPropertyGridInteractionTest, PointerMoveUpdatesHoveredSectionAndFie
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
@@ -112,12 +170,12 @@ TEST(UIEditorPropertyGridInteractionTest, PointerMoveUpdatesHoveredSectionAndFie
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{ MakePointerMove(
initialFrame.layout.sectionHeaderRects[0].x + 24.0f,
initialFrame.layout.sectionHeaderRects[0].y + 16.0f) });
EXPECT_EQ(state.propertyGridState.hoveredSectionId, "transform");
EXPECT_EQ(state.propertyGridState.hoveredSectionId, "inspector");
EXPECT_TRUE(state.propertyGridState.hoveredFieldId.empty());
frame = UpdateUIEditorPropertyGridInteraction(
@@ -125,20 +183,20 @@ TEST(UIEditorPropertyGridInteractionTest, PointerMoveUpdatesHoveredSectionAndFie
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{ MakePointerMove(
initialFrame.layout.fieldRowRects[1].x + 16.0f,
initialFrame.layout.fieldRowRects[1].y + 16.0f) });
EXPECT_EQ(frame.result.hitTarget.fieldIndex, 1u);
EXPECT_EQ(state.propertyGridState.hoveredFieldId, "rotation");
EXPECT_EQ(state.propertyGridState.hoveredFieldId, "render_queue");
}
TEST(UIEditorPropertyGridInteractionTest, LeftClickSectionHeaderTogglesExpansion) {
const auto sections = BuildSections();
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
expansionModel.Expand("transform");
expansionModel.Expand("inspector");
UIPropertyEditModel propertyEditModel = {};
UIEditorPropertyGridInteractionState state = {};
@@ -147,7 +205,7 @@ TEST(UIEditorPropertyGridInteractionTest, LeftClickSectionHeaderTogglesExpansion
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
const UIPoint metadataHeaderCenter = RectCenter(initialFrame.layout.sectionHeaderRects[1]);
@@ -157,7 +215,7 @@ TEST(UIEditorPropertyGridInteractionTest, LeftClickSectionHeaderTogglesExpansion
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(metadataHeaderCenter.x, metadataHeaderCenter.y),
@@ -170,8 +228,8 @@ TEST(UIEditorPropertyGridInteractionTest, LeftClickSectionHeaderTogglesExpansion
EXPECT_TRUE(state.propertyGridState.focused);
}
TEST(UIEditorPropertyGridInteractionTest, LeftClickFieldRowSelectsFieldAndFocusesGrid) {
const auto sections = BuildSections();
TEST(UIEditorPropertyGridInteractionTest, LeftClickBoolValueHostTogglesValueAndSelectsField) {
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
@@ -183,32 +241,34 @@ TEST(UIEditorPropertyGridInteractionTest, LeftClickFieldRowSelectsFieldAndFocuse
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
const UIPoint rowCenter = RectCenter(initialFrame.layout.fieldRowRects[1]);
const UIPoint boolValueCenter = RectCenter(initialFrame.layout.fieldValueRects[0]);
const auto frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(rowCenter.x, rowCenter.y),
MakePointerUp(rowCenter.x, rowCenter.y)
MakePointerDown(boolValueCenter.x, boolValueCenter.y),
MakePointerUp(boolValueCenter.x, boolValueCenter.y)
});
EXPECT_TRUE(frame.result.consumed);
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(frame.result.selectedFieldId, "rotation");
EXPECT_TRUE(selectionModel.IsSelected("rotation"));
EXPECT_TRUE(state.propertyGridState.focused);
EXPECT_TRUE(frame.result.fieldValueChanged);
EXPECT_EQ(frame.result.selectedFieldId, "enabled");
EXPECT_EQ(frame.result.changedFieldId, "enabled");
EXPECT_EQ(frame.result.changedValue, "false");
EXPECT_FALSE(sections[0].fields[0].boolValue);
EXPECT_TRUE(selectionModel.IsSelected("enabled"));
}
TEST(UIEditorPropertyGridInteractionTest, ValueBoxEditCanCommitWithEnter) {
const auto sections = BuildSections();
TEST(UIEditorPropertyGridInteractionTest, NumberValueBoxEditCanCommitWithEnter) {
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
@@ -220,25 +280,25 @@ TEST(UIEditorPropertyGridInteractionTest, ValueBoxEditCanCommitWithEnter) {
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
const UIPoint tagValueCenter = RectCenter(initialFrame.layout.fieldValueRects[2]);
const UIPoint numberValueCenter = RectCenter(initialFrame.layout.fieldValueRects[1]);
auto frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(tagValueCenter.x, tagValueCenter.y),
MakePointerUp(tagValueCenter.x, tagValueCenter.y)
MakePointerDown(numberValueCenter.x, numberValueCenter.y),
MakePointerUp(numberValueCenter.x, numberValueCenter.y)
});
EXPECT_TRUE(frame.result.consumed);
EXPECT_EQ(frame.result.selectedFieldId, "tag");
EXPECT_EQ(frame.result.activeFieldId, "tag");
EXPECT_EQ(frame.result.selectedFieldId, "render_queue");
EXPECT_EQ(frame.result.activeFieldId, "render_queue");
EXPECT_TRUE(propertyEditModel.HasActiveEdit());
frame = UpdateUIEditorPropertyGridInteraction(
@@ -246,22 +306,32 @@ TEST(UIEditorPropertyGridInteractionTest, ValueBoxEditCanCommitWithEnter) {
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakeCharacter('A'),
MakeCharacter('B'),
MakeKeyDown(KeyCode::Backspace),
MakeKeyDown(KeyCode::Backspace),
MakeKeyDown(KeyCode::Backspace),
MakeKeyDown(KeyCode::Backspace),
MakeCharacter('2'),
MakeCharacter('5'),
MakeCharacter('0'),
MakeCharacter('0'),
MakeKeyDown(KeyCode::Enter)
});
EXPECT_TRUE(frame.result.editCommitted);
EXPECT_EQ(frame.result.committedFieldId, "tag");
EXPECT_EQ(frame.result.committedValue, "AB");
EXPECT_TRUE(frame.result.fieldValueChanged);
EXPECT_EQ(frame.result.committedFieldId, "render_queue");
EXPECT_EQ(frame.result.committedValue, "2500");
EXPECT_EQ(frame.result.changedFieldId, "render_queue");
EXPECT_EQ(frame.result.changedValue, "2500");
EXPECT_FALSE(propertyEditModel.HasActiveEdit());
EXPECT_DOUBLE_EQ(sections[0].fields[1].numberValue.value, 2500.0);
}
TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClearsFocus) {
const auto sections = BuildSections();
TEST(UIEditorPropertyGridInteractionTest, EnumPopupCanOpenNavigateSelectAndClose) {
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
@@ -273,17 +343,70 @@ TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClears
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
const UIPoint tagValueCenter = RectCenter(initialFrame.layout.fieldValueRects[2]);
const UIPoint enumValueCenter = RectCenter(initialFrame.layout.fieldValueRects[2]);
auto frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(enumValueCenter.x, enumValueCenter.y),
MakePointerUp(enumValueCenter.x, enumValueCenter.y)
});
EXPECT_TRUE(frame.result.popupOpened);
EXPECT_EQ(state.propertyGridState.popupFieldId, "render_mode");
EXPECT_TRUE(selectionModel.IsSelected("render_mode"));
frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakeKeyDown(KeyCode::Down),
MakeKeyDown(KeyCode::Enter)
});
EXPECT_TRUE(frame.result.popupClosed);
EXPECT_TRUE(frame.result.fieldValueChanged);
EXPECT_EQ(frame.result.changedFieldId, "render_mode");
EXPECT_EQ(frame.result.changedValue, "Cutout");
EXPECT_TRUE(state.propertyGridState.popupFieldId.empty());
EXPECT_EQ(sections[0].fields[2].enumValue.selectedIndex, 1u);
}
TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClearsFocus) {
auto sections = BuildSections();
UISelectionModel selectionModel = {};
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
UIPropertyEditModel propertyEditModel = {};
UIEditorPropertyGridInteractionState state = {};
const auto initialFrame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{});
const UIPoint tagValueCenter = RectCenter(initialFrame.layout.fieldValueRects[3]);
UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(tagValueCenter.x, tagValueCenter.y),
@@ -295,7 +418,7 @@ TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClears
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakeCharacter('A'),
@@ -303,14 +426,14 @@ TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClears
});
EXPECT_TRUE(frame.result.editCanceled);
EXPECT_FALSE(propertyEditModel.HasActiveEdit());
EXPECT_TRUE(selectionModel.IsSelected("tag"));
EXPECT_EQ(sections[0].fields[3].valueText, "Player");
frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{
MakePointerDown(520.0f, 360.0f),
@@ -321,9 +444,9 @@ TEST(UIEditorPropertyGridInteractionTest, EscapeCancelsEditAndOutsideClickClears
}
TEST(UIEditorPropertyGridInteractionTest, ArrowAndHomeEndKeysNavigateVisibleFields) {
const auto sections = BuildSections();
auto sections = BuildSections();
UISelectionModel selectionModel = {};
selectionModel.SetSelection("rotation");
selectionModel.SetSelection("render_queue");
UIExpansionModel expansionModel = {};
ExpandAll(expansionModel);
UIPropertyEditModel propertyEditModel = {};
@@ -335,31 +458,31 @@ TEST(UIEditorPropertyGridInteractionTest, ArrowAndHomeEndKeysNavigateVisibleFiel
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{ MakeKeyDown(KeyCode::Down) });
EXPECT_TRUE(frame.result.keyboardNavigated);
EXPECT_EQ(frame.result.selectedFieldId, "tag");
EXPECT_TRUE(selectionModel.IsSelected("tag"));
EXPECT_EQ(frame.result.selectedFieldId, "render_mode");
EXPECT_TRUE(selectionModel.IsSelected("render_mode"));
frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{ MakeKeyDown(KeyCode::Home) });
EXPECT_TRUE(frame.result.keyboardNavigated);
EXPECT_EQ(frame.result.selectedFieldId, "position");
EXPECT_TRUE(selectionModel.IsSelected("position"));
EXPECT_EQ(frame.result.selectedFieldId, "enabled");
EXPECT_TRUE(selectionModel.IsSelected("enabled"));
frame = UpdateUIEditorPropertyGridInteraction(
state,
selectionModel,
expansionModel,
propertyEditModel,
UIRect(0.0f, 0.0f, 420.0f, 320.0f),
UIRect(0.0f, 0.0f, 420.0f, 340.0f),
sections,
{ MakeKeyDown(KeyCode::End) });
EXPECT_TRUE(frame.result.keyboardNavigated);

View File

@@ -0,0 +1,36 @@
#include <gtest/gtest.h>
#include <XCEditor/Widgets/UIEditorTextField.h>
namespace {
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::BuildUIEditorTextFieldLayout;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorTextField;
using XCEngine::UI::Editor::Widgets::UIEditorTextFieldHitTargetKind;
using XCEngine::UI::Editor::Widgets::UIEditorTextFieldSpec;
TEST(UIEditorTextFieldTest, LayoutBuildsValueRect) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
const auto layout = BuildUIEditorTextFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
EXPECT_GT(layout.labelRect.width, 0.0f);
EXPECT_GT(layout.controlRect.width, 0.0f);
EXPECT_GT(layout.valueRect.width, 0.0f);
EXPECT_FLOAT_EQ(layout.controlRect.width, layout.valueRect.width);
}
TEST(UIEditorTextFieldTest, HitTestResolvesValueBoxAndRow) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
const auto layout = BuildUIEditorTextFieldLayout(UIRect(0.0f, 0.0f, 360.0f, 32.0f), spec);
EXPECT_EQ(
HitTestUIEditorTextField(layout, UIPoint(layout.valueRect.x + 4.0f, layout.valueRect.y + 4.0f)).kind,
UIEditorTextFieldHitTargetKind::ValueBox);
EXPECT_EQ(
HitTestUIEditorTextField(layout, UIPoint(layout.labelRect.x + 4.0f, layout.labelRect.y + 4.0f)).kind,
UIEditorTextFieldHitTargetKind::Row);
}
} // namespace

View File

@@ -0,0 +1,152 @@
#include <gtest/gtest.h>
#include <XCEditor/Core/UIEditorTextFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIInputEvent;
using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIPointerButton;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::UIEditorTextFieldInteractionState;
using XCEngine::UI::Editor::UpdateUIEditorTextFieldInteraction;
using XCEngine::UI::Editor::Widgets::UIEditorTextFieldSpec;
UIInputEvent MakePointer(UIInputEventType type, float x, float y, UIPointerButton button = UIPointerButton::None) {
UIInputEvent event = {};
event.type = type;
event.position = UIPoint(x, y);
event.pointerButton = button;
return event;
}
UIInputEvent MakeKey(KeyCode keyCode) {
UIInputEvent event = {};
event.type = UIInputEventType::KeyDown;
event.keyCode = static_cast<std::int32_t>(keyCode);
return event;
}
UIInputEvent MakeCharacter(char character) {
UIInputEvent event = {};
event.type = UIInputEventType::Character;
event.character = static_cast<std::uint32_t>(character);
return event;
}
} // namespace
TEST(UIEditorTextFieldInteractionTest, ClickValueBoxStartsEditing) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
UIEditorTextFieldInteractionState state = {};
auto frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{});
frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{
MakePointer(
UIInputEventType::PointerButtonDown,
frame.layout.valueRect.x + 2.0f,
frame.layout.valueRect.y + 2.0f,
UIPointerButton::Left),
MakePointer(
UIInputEventType::PointerButtonUp,
frame.layout.valueRect.x + 2.0f,
frame.layout.valueRect.y + 2.0f,
UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.textFieldState.editing);
EXPECT_EQ(spec.value, "Player");
}
TEST(UIEditorTextFieldInteractionTest, EnterStartsEditingAndCommitUpdatesValue) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
UIEditorTextFieldInteractionState state = {};
state.textFieldState.focused = true;
auto frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{ MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.textFieldState.editing);
frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{ MakeCharacter('X'), MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editCommitted);
EXPECT_TRUE(frame.result.valueChanged);
EXPECT_FALSE(state.textFieldState.editing);
EXPECT_EQ(spec.value, "PlayerX");
EXPECT_EQ(frame.result.committedText, "PlayerX");
}
TEST(UIEditorTextFieldInteractionTest, CharacterInputCanStartEditingAndEscapeCancels) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
UIEditorTextFieldInteractionState state = {};
state.textFieldState.focused = true;
auto frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{ MakeCharacter('N') });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.textFieldState.editing);
EXPECT_EQ(state.textFieldState.displayText, "N");
frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{ MakeKey(KeyCode::Escape) });
EXPECT_TRUE(frame.result.editCanceled);
EXPECT_FALSE(state.textFieldState.editing);
EXPECT_EQ(spec.value, "Player");
EXPECT_EQ(state.textFieldState.displayText, "Player");
}
TEST(UIEditorTextFieldInteractionTest, FocusLostCommitsEdit) {
UIEditorTextFieldSpec spec = { "name", "Name", "Player", false };
UIEditorTextFieldInteractionState state = {};
state.textFieldState.focused = true;
auto frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{ MakeKey(KeyCode::Enter), MakeCharacter('1') });
EXPECT_TRUE(state.textFieldState.editing);
frame = UpdateUIEditorTextFieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 360.0f, 32.0f),
{
UIInputEvent {
.type = UIInputEventType::FocusLost
}
});
EXPECT_TRUE(frame.result.editCommitted);
EXPECT_EQ(spec.value, "Player1");
EXPECT_FALSE(state.textFieldState.editing);
EXPECT_FALSE(state.textFieldState.focused);
}

View File

@@ -0,0 +1,371 @@
#include <gtest/gtest.h>
#include <XCEditor/Core/UIEditorTheme.h>
#include <XCEngine/Core/Math/Color.h>
#include <XCEngine/UI/Style/StyleTypes.h>
#include <XCEngine/UI/Style/Theme.h>
namespace {
namespace Math = XCEngine::Math;
namespace Style = XCEngine::UI::Style;
namespace UI = XCEngine::UI;
namespace Editor = XCEngine::UI::Editor;
Style::UITheme BuildEditorFieldTheme() {
Style::UIThemeDefinition definition = {};
definition.SetToken("editor.size.field.row", Style::UIStyleValue(26.0f));
definition.SetToken("editor.space.field.padding_x", Style::UIStyleValue(8.0f));
definition.SetToken("editor.space.field.label_gap", Style::UIStyleValue(12.0f));
definition.SetToken("editor.layout.field.control_column", Style::UIStyleValue(220.0f));
definition.SetToken("editor.size.field.checkbox", Style::UIStyleValue(15.0f));
definition.SetToken("editor.space.field.control_inset_y", Style::UIStyleValue(3.0f));
definition.SetToken("editor.space.field.label_inset_y", Style::UIStyleValue(5.0f));
definition.SetToken("editor.space.field.value_inset_x", Style::UIStyleValue(6.0f));
definition.SetToken("editor.space.field.value_inset_y", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.field.checkbox_glyph_inset_x", Style::UIStyleValue(1.0f));
definition.SetToken("editor.space.field.checkbox_glyph_inset_y", Style::UIStyleValue(-2.0f));
definition.SetToken("editor.size.field.control_min_width", Style::UIStyleValue(88.0f));
definition.SetToken("editor.space.field.vector_component_gap", Style::UIStyleValue(6.0f));
definition.SetToken("editor.size.field.vector_component_min_width", Style::UIStyleValue(74.0f));
definition.SetToken("editor.size.field.vector_prefix_width", Style::UIStyleValue(18.0f));
definition.SetToken("editor.space.field.control_trailing_inset", Style::UIStyleValue(9.0f));
definition.SetToken("editor.space.field.vector_prefix_gap", Style::UIStyleValue(5.0f));
definition.SetToken("editor.space.field.vector_prefix_inset_x", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.field.vector_prefix_inset_y", Style::UIStyleValue(5.0f));
definition.SetToken("editor.size.field.dropdown_arrow_width", Style::UIStyleValue(14.0f));
definition.SetToken("editor.space.field.dropdown_arrow_inset_x", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.field.dropdown_arrow_inset_y", Style::UIStyleValue(4.0f));
definition.SetToken("editor.radius.field.row", Style::UIStyleValue(2.0f));
definition.SetToken("editor.radius.field.control", Style::UIStyleValue(2.0f));
definition.SetToken("editor.border.field", Style::UIStyleValue(1.0f));
definition.SetToken("editor.border.field.focus", Style::UIStyleValue(2.0f));
definition.SetToken("editor.font.field.label", Style::UIStyleValue(11.0f));
definition.SetToken("editor.font.field.value", Style::UIStyleValue(12.0f));
definition.SetToken("editor.font.field.glyph", Style::UIStyleValue(10.0f));
definition.SetToken("editor.space.menu_popup.padding_x", Style::UIStyleValue(6.0f));
definition.SetToken("editor.space.menu_popup.padding_y", Style::UIStyleValue(5.0f));
definition.SetToken("editor.size.menu_popup.item", Style::UIStyleValue(24.0f));
definition.SetToken("editor.size.menu_popup.separator", Style::UIStyleValue(8.0f));
definition.SetToken("editor.size.menu_popup.check_column", Style::UIStyleValue(16.0f));
definition.SetToken("editor.space.menu_popup.shortcut_gap", Style::UIStyleValue(18.0f));
definition.SetToken("editor.size.menu_popup.submenu_indicator", Style::UIStyleValue(12.0f));
definition.SetToken("editor.radius.menu_popup.row", Style::UIStyleValue(3.0f));
definition.SetToken("editor.radius.menu_popup.surface", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.menu_popup.label_inset_x", Style::UIStyleValue(10.0f));
definition.SetToken("editor.space.menu_popup.label_inset_y", Style::UIStyleValue(-1.0f));
definition.SetToken("editor.font.menu_popup.label", Style::UIStyleValue(11.0f));
definition.SetToken("editor.space.menu_popup.shortcut_inset_right", Style::UIStyleValue(18.0f));
definition.SetToken("editor.size.menu_popup.estimated_glyph_width", Style::UIStyleValue(6.0f));
definition.SetToken("editor.font.menu_popup.glyph", Style::UIStyleValue(10.0f));
definition.SetToken("editor.border.menu_popup.separator", Style::UIStyleValue(1.0f));
definition.SetToken("editor.border.menu_popup.surface", Style::UIStyleValue(1.0f));
definition.SetToken("editor.color.field.row", Style::UIStyleValue(Math::Color(0.16f, 0.16f, 0.16f, 1.0f)));
definition.SetToken("editor.color.field.row_hover", Style::UIStyleValue(Math::Color(0.20f, 0.20f, 0.20f, 1.0f)));
definition.SetToken("editor.color.field.row_active", Style::UIStyleValue(Math::Color(0.24f, 0.24f, 0.24f, 1.0f)));
definition.SetToken("editor.color.field.border", Style::UIStyleValue(Math::Color(0.12f, 0.12f, 0.12f, 1.0f)));
definition.SetToken("editor.color.field.border_focus", Style::UIStyleValue(Math::Color(0.78f, 0.78f, 0.78f, 1.0f)));
definition.SetToken("editor.color.field.label", Style::UIStyleValue(Math::Color(0.84f, 0.84f, 0.84f, 1.0f)));
definition.SetToken("editor.color.field.value", Style::UIStyleValue(Math::Color(0.92f, 0.92f, 0.92f, 1.0f)));
definition.SetToken("editor.color.field.value_readonly", Style::UIStyleValue(Math::Color(0.60f, 0.60f, 0.60f, 1.0f)));
definition.SetToken("editor.color.field.control", Style::UIStyleValue(Math::Color(0.18f, 0.18f, 0.18f, 1.0f)));
definition.SetToken("editor.color.field.control_hover", Style::UIStyleValue(Math::Color(0.21f, 0.21f, 0.21f, 1.0f)));
definition.SetToken("editor.color.field.control_editing", Style::UIStyleValue(Math::Color(0.24f, 0.24f, 0.24f, 1.0f)));
definition.SetToken("editor.color.field.control_readonly", Style::UIStyleValue(Math::Color(0.15f, 0.15f, 0.15f, 1.0f)));
definition.SetToken("editor.color.field.control_border", Style::UIStyleValue(Math::Color(0.30f, 0.30f, 0.30f, 1.0f)));
definition.SetToken("editor.color.field.control_border_focus", Style::UIStyleValue(Math::Color(0.64f, 0.64f, 0.64f, 1.0f)));
definition.SetToken("editor.color.field.vector_prefix", Style::UIStyleValue(Math::Color(0.20f, 0.20f, 0.20f, 1.0f)));
definition.SetToken("editor.color.field.vector_prefix_border", Style::UIStyleValue(Math::Color(0.31f, 0.31f, 0.31f, 1.0f)));
definition.SetToken("editor.color.field.vector_axis_x", Style::UIStyleValue(Math::Color(0.78f, 0.42f, 0.42f, 1.0f)));
definition.SetToken("editor.color.field.vector_axis_y", Style::UIStyleValue(Math::Color(0.56f, 0.72f, 0.46f, 1.0f)));
definition.SetToken("editor.color.field.vector_axis_z", Style::UIStyleValue(Math::Color(0.45f, 0.62f, 0.82f, 1.0f)));
definition.SetToken("editor.color.field.checkbox", Style::UIStyleValue(Math::Color(0.19f, 0.19f, 0.19f, 1.0f)));
definition.SetToken("editor.color.field.checkbox_hover", Style::UIStyleValue(Math::Color(0.22f, 0.22f, 0.22f, 1.0f)));
definition.SetToken("editor.color.field.checkbox_border", Style::UIStyleValue(Math::Color(0.33f, 0.33f, 0.33f, 1.0f)));
definition.SetToken("editor.color.field.checkbox_mark", Style::UIStyleValue(Math::Color(0.90f, 0.90f, 0.90f, 1.0f)));
definition.SetToken("editor.color.field.dropdown_arrow", Style::UIStyleValue(Math::Color(0.88f, 0.88f, 0.88f, 1.0f)));
definition.SetToken("editor.color.menu_popup.surface", Style::UIStyleValue(Math::Color(0.15f, 0.15f, 0.15f, 1.0f)));
definition.SetToken("editor.color.menu_popup.border", Style::UIStyleValue(Math::Color(0.32f, 0.32f, 0.32f, 1.0f)));
definition.SetToken("editor.color.menu_popup.item_hover", Style::UIStyleValue(Math::Color(0.24f, 0.24f, 0.24f, 1.0f)));
definition.SetToken("editor.color.menu_popup.item_open", Style::UIStyleValue(Math::Color(0.27f, 0.27f, 0.27f, 1.0f)));
definition.SetToken("editor.color.menu_popup.separator", Style::UIStyleValue(Math::Color(0.35f, 0.35f, 0.35f, 1.0f)));
definition.SetToken("editor.color.menu_popup.label", Style::UIStyleValue(Math::Color(0.91f, 0.91f, 0.91f, 1.0f)));
definition.SetToken("editor.color.menu_popup.shortcut", Style::UIStyleValue(Math::Color(0.76f, 0.76f, 0.76f, 1.0f)));
definition.SetToken("editor.color.menu_popup.text_disabled", Style::UIStyleValue(Math::Color(0.48f, 0.48f, 0.48f, 1.0f)));
definition.SetToken("editor.color.menu_popup.glyph", Style::UIStyleValue(Math::Color(0.86f, 0.86f, 0.86f, 1.0f)));
return Style::BuildTheme(definition);
}
Style::UITheme BuildPropertyGridTheme() {
Style::UIThemeDefinition definition = {};
definition.SetToken("editor.space.property.content_inset", Style::UIStyleValue(6.0f));
definition.SetToken("editor.space.property.section_gap", Style::UIStyleValue(4.0f));
definition.SetToken("editor.size.property.section_header", Style::UIStyleValue(24.0f));
definition.SetToken("editor.size.property.field_row", Style::UIStyleValue(26.0f));
definition.SetToken("editor.space.property.row_gap", Style::UIStyleValue(1.0f));
definition.SetToken("editor.size.property.disclosure", Style::UIStyleValue(10.0f));
definition.SetToken("editor.space.property.disclosure_label_gap", Style::UIStyleValue(6.0f));
definition.SetToken("editor.space.property.section_inset_y", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.property.disclosure_glyph_inset_x", Style::UIStyleValue(1.0f));
definition.SetToken("editor.space.property.disclosure_glyph_inset_y", Style::UIStyleValue(-1.0f));
definition.SetToken("editor.space.property.label_inset_y", Style::UIStyleValue(5.0f));
definition.SetToken("editor.space.property.value_inset_y", Style::UIStyleValue(4.0f));
definition.SetToken("editor.space.property.value_box_inset_y", Style::UIStyleValue(3.0f));
definition.SetToken("editor.space.property.value_box_inset_x", Style::UIStyleValue(6.0f));
definition.SetToken("editor.radius.property.panel", Style::UIStyleValue(0.0f));
definition.SetToken("editor.radius.property.value", Style::UIStyleValue(2.0f));
definition.SetToken("editor.border.property", Style::UIStyleValue(1.0f));
definition.SetToken("editor.border.property.focus", Style::UIStyleValue(1.0f));
definition.SetToken("editor.border.property.edit", Style::UIStyleValue(1.0f));
definition.SetToken("editor.font.property.section", Style::UIStyleValue(11.0f));
definition.SetToken("editor.font.property.disclosure", Style::UIStyleValue(10.0f));
definition.SetToken("editor.font.property.label", Style::UIStyleValue(11.0f));
definition.SetToken("editor.font.property.value", Style::UIStyleValue(12.0f));
definition.SetToken("editor.font.property.tag", Style::UIStyleValue(10.0f));
definition.SetToken("editor.color.property.surface", Style::UIStyleValue(Math::Color(0.17f, 0.17f, 0.17f, 1.0f)));
definition.SetToken("editor.color.property.border", Style::UIStyleValue(Math::Color(0.10f, 0.10f, 0.10f, 1.0f)));
definition.SetToken("editor.color.property.border_focus", Style::UIStyleValue(Math::Color(0.75f, 0.75f, 0.75f, 1.0f)));
definition.SetToken("editor.color.property.section", Style::UIStyleValue(Math::Color(0.21f, 0.21f, 0.21f, 1.0f)));
definition.SetToken("editor.color.property.section_hover", Style::UIStyleValue(Math::Color(0.24f, 0.24f, 0.24f, 1.0f)));
definition.SetToken("editor.color.property.field_hover", Style::UIStyleValue(Math::Color(0.22f, 0.22f, 0.22f, 1.0f)));
definition.SetToken("editor.color.property.field_selected", Style::UIStyleValue(Math::Color(0.26f, 0.26f, 0.26f, 1.0f)));
definition.SetToken("editor.color.property.field_selected_focused", Style::UIStyleValue(Math::Color(0.30f, 0.30f, 0.30f, 1.0f)));
definition.SetToken("editor.color.property.value", Style::UIStyleValue(Math::Color(0.18f, 0.18f, 0.18f, 1.0f)));
definition.SetToken("editor.color.property.value_hover", Style::UIStyleValue(Math::Color(0.21f, 0.21f, 0.21f, 1.0f)));
definition.SetToken("editor.color.property.value_editing", Style::UIStyleValue(Math::Color(0.23f, 0.23f, 0.23f, 1.0f)));
definition.SetToken("editor.color.property.value_readonly", Style::UIStyleValue(Math::Color(0.15f, 0.15f, 0.15f, 1.0f)));
definition.SetToken("editor.color.property.value_border", Style::UIStyleValue(Math::Color(0.28f, 0.28f, 0.28f, 1.0f)));
definition.SetToken("editor.color.property.value_border_editing", Style::UIStyleValue(Math::Color(0.72f, 0.72f, 0.72f, 1.0f)));
definition.SetToken("editor.color.property.disclosure", Style::UIStyleValue(Math::Color(0.84f, 0.84f, 0.84f, 1.0f)));
definition.SetToken("editor.color.property.section_text", Style::UIStyleValue(Math::Color(0.92f, 0.92f, 0.92f, 1.0f)));
definition.SetToken("editor.color.property.label", Style::UIStyleValue(Math::Color(0.80f, 0.80f, 0.80f, 1.0f)));
definition.SetToken("editor.color.property.value_text", Style::UIStyleValue(Math::Color(0.92f, 0.92f, 0.92f, 1.0f)));
definition.SetToken("editor.color.property.value_text_readonly", Style::UIStyleValue(Math::Color(0.60f, 0.60f, 0.60f, 1.0f)));
definition.SetToken("editor.color.property.edit_tag", Style::UIStyleValue(Math::Color(0.55f, 0.70f, 0.96f, 1.0f)));
definition.SetToken("editor.space.menu.padding_x", Style::UIStyleValue(7.0f));
definition.SetToken("editor.space.menu.padding_y", Style::UIStyleValue(5.0f));
definition.SetToken("editor.size.menu.item", Style::UIStyleValue(24.0f));
definition.SetToken("editor.size.menu.separator", Style::UIStyleValue(8.0f));
definition.SetToken("editor.size.menu.check_column", Style::UIStyleValue(16.0f));
definition.SetToken("editor.space.menu.shortcut_gap", Style::UIStyleValue(18.0f));
definition.SetToken("editor.size.menu.submenu_indicator", Style::UIStyleValue(12.0f));
definition.SetToken("editor.radius.menu.row", Style::UIStyleValue(3.0f));
definition.SetToken("editor.radius.menu.surface", Style::UIStyleValue(6.0f));
definition.SetToken("editor.space.menu.label_inset_x", Style::UIStyleValue(11.0f));
definition.SetToken("editor.space.menu.label_inset_y", Style::UIStyleValue(1.0f));
definition.SetToken("editor.font.menu.label", Style::UIStyleValue(11.0f));
definition.SetToken("editor.space.menu.shortcut_inset_right", Style::UIStyleValue(18.0f));
definition.SetToken("editor.size.menu.glyph_width", Style::UIStyleValue(6.0f));
definition.SetToken("editor.font.menu.glyph", Style::UIStyleValue(10.0f));
definition.SetToken("editor.border.menu.separator", Style::UIStyleValue(1.0f));
definition.SetToken("editor.border.menu.surface", Style::UIStyleValue(1.0f));
definition.SetToken("editor.color.menu.surface", Style::UIStyleValue(Math::Color(0.16f, 0.16f, 0.16f, 1.0f)));
definition.SetToken("editor.color.menu.border", Style::UIStyleValue(Math::Color(0.24f, 0.24f, 0.24f, 1.0f)));
definition.SetToken("editor.color.menu.item_hover", Style::UIStyleValue(Math::Color(0.23f, 0.23f, 0.23f, 1.0f)));
definition.SetToken("editor.color.menu.item_open", Style::UIStyleValue(Math::Color(0.28f, 0.28f, 0.28f, 1.0f)));
definition.SetToken("editor.color.menu.separator", Style::UIStyleValue(Math::Color(0.30f, 0.30f, 0.30f, 1.0f)));
definition.SetToken("editor.color.menu.text", Style::UIStyleValue(Math::Color(0.92f, 0.92f, 0.92f, 1.0f)));
definition.SetToken("editor.color.menu.text_muted", Style::UIStyleValue(Math::Color(0.74f, 0.74f, 0.74f, 1.0f)));
definition.SetToken("editor.color.menu.text_disabled", Style::UIStyleValue(Math::Color(0.47f, 0.47f, 0.47f, 1.0f)));
definition.SetToken("editor.color.menu.glyph", Style::UIStyleValue(Math::Color(0.86f, 0.86f, 0.86f, 1.0f)));
return Style::BuildTheme(definition);
}
TEST(UIEditorThemeTest, FieldResolversReadEditorThemeTokens) {
const Style::UITheme theme = BuildEditorFieldTheme();
const auto boolMetrics = Editor::ResolveUIEditorBoolFieldMetrics(theme);
const auto boolPalette = Editor::ResolveUIEditorBoolFieldPalette(theme);
EXPECT_FLOAT_EQ(boolMetrics.rowHeight, 26.0f);
EXPECT_FLOAT_EQ(boolMetrics.horizontalPadding, 8.0f);
EXPECT_FLOAT_EQ(boolMetrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(boolMetrics.checkboxGlyphFontSize, 10.0f);
EXPECT_FLOAT_EQ(boolPalette.rowHoverColor.r, 0.20f);
EXPECT_FLOAT_EQ(boolPalette.checkboxBorderColor.r, 0.33f);
const auto numberMetrics = Editor::ResolveUIEditorNumberFieldMetrics(theme);
const auto numberPalette = Editor::ResolveUIEditorNumberFieldPalette(theme);
EXPECT_FLOAT_EQ(numberMetrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(numberMetrics.controlInsetY, 3.0f);
EXPECT_FLOAT_EQ(numberMetrics.valueTextInsetX, 6.0f);
EXPECT_FLOAT_EQ(numberMetrics.valueFontSize, 12.0f);
EXPECT_FLOAT_EQ(numberPalette.valueBoxEditingColor.r, 0.24f);
EXPECT_FLOAT_EQ(numberPalette.controlBorderColor.r, 0.30f);
EXPECT_FLOAT_EQ(numberPalette.controlFocusedBorderColor.r, 0.64f);
const auto textMetrics = Editor::ResolveUIEditorTextFieldMetrics(theme);
const auto textPalette = Editor::ResolveUIEditorTextFieldPalette(theme);
EXPECT_FLOAT_EQ(textMetrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(textMetrics.valueBoxMinWidth, 88.0f);
EXPECT_FLOAT_EQ(textMetrics.valueTextInsetY, 4.0f);
EXPECT_FLOAT_EQ(textMetrics.valueFontSize, 12.0f);
EXPECT_FLOAT_EQ(textPalette.valueBoxEditingColor.r, 0.24f);
EXPECT_FLOAT_EQ(textPalette.readOnlyValueColor.r, 0.60f);
EXPECT_FLOAT_EQ(textPalette.controlFocusedBorderColor.r, 0.64f);
const auto vector2Metrics = Editor::ResolveUIEditorVector2FieldMetrics(theme);
const auto vector2Palette = Editor::ResolveUIEditorVector2FieldPalette(theme);
EXPECT_FLOAT_EQ(vector2Metrics.componentGap, 6.0f);
EXPECT_FLOAT_EQ(vector2Metrics.componentPrefixWidth, 18.0f);
EXPECT_FLOAT_EQ(vector2Metrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(vector2Metrics.componentLabelGap, 5.0f);
EXPECT_FLOAT_EQ(vector2Metrics.prefixFontSize, 10.0f);
EXPECT_FLOAT_EQ(vector2Palette.componentFocusedBorderColor.r, 0.64f);
EXPECT_FLOAT_EQ(vector2Palette.axisXColor.r, 0.78f);
EXPECT_FLOAT_EQ(vector2Palette.axisYColor.g, 0.72f);
const auto vector3Metrics = Editor::ResolveUIEditorVector3FieldMetrics(theme);
const auto vector3Palette = Editor::ResolveUIEditorVector3FieldPalette(theme);
EXPECT_FLOAT_EQ(vector3Metrics.componentGap, 6.0f);
EXPECT_FLOAT_EQ(vector3Metrics.componentPrefixWidth, 18.0f);
EXPECT_FLOAT_EQ(vector3Metrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(vector3Metrics.componentLabelGap, 5.0f);
EXPECT_FLOAT_EQ(vector3Metrics.prefixFontSize, 10.0f);
EXPECT_FLOAT_EQ(vector3Palette.componentFocusedBorderColor.r, 0.64f);
EXPECT_FLOAT_EQ(vector3Palette.axisZColor.b, 0.82f);
const auto enumMetrics = Editor::ResolveUIEditorEnumFieldMetrics(theme);
const auto enumPalette = Editor::ResolveUIEditorEnumFieldPalette(theme);
EXPECT_FLOAT_EQ(enumMetrics.controlTrailingInset, 9.0f);
EXPECT_FLOAT_EQ(enumMetrics.valueBoxMinWidth, 88.0f);
EXPECT_FLOAT_EQ(enumMetrics.dropdownArrowWidth, 14.0f);
EXPECT_FLOAT_EQ(enumMetrics.dropdownArrowFontSize, 10.0f);
EXPECT_FLOAT_EQ(enumPalette.arrowColor.r, 0.88f);
const auto popupMetrics = Editor::ResolveUIEditorMenuPopupMetrics(theme);
const auto popupPalette = Editor::ResolveUIEditorMenuPopupPalette(theme);
EXPECT_FLOAT_EQ(popupMetrics.contentPaddingX, 6.0f);
EXPECT_FLOAT_EQ(popupMetrics.itemHeight, 24.0f);
EXPECT_FLOAT_EQ(popupMetrics.glyphFontSize, 10.0f);
EXPECT_FLOAT_EQ(popupPalette.popupColor.r, 0.15f);
EXPECT_FLOAT_EQ(popupPalette.textPrimary.r, 0.91f);
EXPECT_FLOAT_EQ(popupPalette.textMuted.r, 0.76f);
}
TEST(UIEditorThemeTest, PropertyGridResolversSupportOverridesAndFallbacks) {
const Style::UITheme theme = BuildPropertyGridTheme();
const Style::UITheme fallbackTheme = Style::UITheme();
UI::Editor::Widgets::UIEditorPropertyGridMetrics fallbackMetrics = {};
fallbackMetrics.sectionHeaderHeight = 40.0f;
fallbackMetrics.disclosureGlyphFontSize = 15.0f;
fallbackMetrics.tagFontSize = 13.0f;
const auto fallbackResolvedMetrics =
Editor::ResolveUIEditorPropertyGridMetrics(fallbackTheme, fallbackMetrics);
EXPECT_FLOAT_EQ(fallbackResolvedMetrics.sectionHeaderHeight, 40.0f);
EXPECT_FLOAT_EQ(fallbackResolvedMetrics.disclosureGlyphFontSize, 15.0f);
EXPECT_FLOAT_EQ(fallbackResolvedMetrics.tagFontSize, 13.0f);
UI::Editor::Widgets::UIEditorPropertyGridPalette fallbackPalette = {};
fallbackPalette.surfaceColor = UI::UIColor(0.5f, 0.4f, 0.3f, 1.0f);
const auto fallbackResolvedPalette =
Editor::ResolveUIEditorPropertyGridPalette(fallbackTheme, fallbackPalette);
EXPECT_FLOAT_EQ(fallbackResolvedPalette.surfaceColor.r, 0.5f);
const auto themedMetrics = Editor::ResolveUIEditorPropertyGridMetrics(theme);
const auto themedPalette = Editor::ResolveUIEditorPropertyGridPalette(theme);
const auto popupMetrics = Editor::ResolveUIEditorMenuPopupMetrics(theme);
const auto popupPalette = Editor::ResolveUIEditorMenuPopupPalette(theme);
EXPECT_FLOAT_EQ(themedMetrics.contentInset, 6.0f);
EXPECT_FLOAT_EQ(themedMetrics.sectionHeaderHeight, 24.0f);
EXPECT_FLOAT_EQ(themedMetrics.disclosureGlyphFontSize, 10.0f);
EXPECT_FLOAT_EQ(themedMetrics.tagFontSize, 10.0f);
EXPECT_FLOAT_EQ(themedPalette.sectionHeaderColor.r, 0.21f);
EXPECT_FLOAT_EQ(themedPalette.valueBoxEditingBorderColor.r, 0.72f);
EXPECT_FLOAT_EQ(themedPalette.valueTextColor.r, 0.92f);
EXPECT_FLOAT_EQ(themedPalette.readOnlyValueTextColor.r, 0.60f);
EXPECT_FLOAT_EQ(popupMetrics.contentPaddingX, 7.0f);
EXPECT_FLOAT_EQ(popupMetrics.itemHeight, 24.0f);
EXPECT_FLOAT_EQ(popupMetrics.popupCornerRounding, 6.0f);
EXPECT_FLOAT_EQ(popupMetrics.glyphFontSize, 10.0f);
EXPECT_FLOAT_EQ(popupPalette.popupColor.r, 0.16f);
EXPECT_FLOAT_EQ(popupPalette.itemHoverColor.r, 0.23f);
EXPECT_FLOAT_EQ(popupPalette.glyphColor.r, 0.86f);
}
TEST(UIEditorThemeTest, HostedFieldBuildersInheritPropertyGridMetricsAndPalette) {
UI::Editor::Widgets::UIEditorPropertyGridMetrics propertyMetrics = {};
propertyMetrics.fieldRowHeight = 25.0f;
propertyMetrics.horizontalPadding = 7.0f;
propertyMetrics.labelControlGap = 11.0f;
propertyMetrics.controlColumnStart = 210.0f;
propertyMetrics.labelTextInsetY = 4.0f;
propertyMetrics.labelFontSize = 10.0f;
propertyMetrics.valueTextInsetY = 3.0f;
propertyMetrics.valueFontSize = 12.0f;
propertyMetrics.valueBoxInsetY = 2.0f;
propertyMetrics.valueBoxInsetX = 5.0f;
propertyMetrics.cornerRounding = 1.0f;
propertyMetrics.valueBoxRounding = 2.0f;
propertyMetrics.borderThickness = 1.0f;
propertyMetrics.focusedBorderThickness = 2.0f;
UI::Editor::Widgets::UIEditorPropertyGridPalette propertyPalette = {};
propertyPalette.valueBoxColor = UI::UIColor(0.2f, 0.2f, 0.2f, 1.0f);
propertyPalette.valueBoxHoverColor = UI::UIColor(0.3f, 0.3f, 0.3f, 1.0f);
propertyPalette.valueBoxEditingColor = UI::UIColor(0.4f, 0.4f, 0.4f, 1.0f);
propertyPalette.valueBoxReadOnlyColor = UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
propertyPalette.valueBoxBorderColor = UI::UIColor(0.5f, 0.5f, 0.5f, 1.0f);
propertyPalette.valueBoxEditingBorderColor = UI::UIColor(0.55f, 0.55f, 0.55f, 1.0f);
propertyPalette.labelTextColor = UI::UIColor(0.8f, 0.8f, 0.8f, 1.0f);
propertyPalette.valueTextColor = UI::UIColor(0.9f, 0.9f, 0.9f, 1.0f);
propertyPalette.readOnlyValueTextColor = UI::UIColor(0.6f, 0.6f, 0.6f, 1.0f);
const auto boolMetrics = Editor::BuildUIEditorHostedBoolFieldMetrics(propertyMetrics);
const auto boolPalette = Editor::BuildUIEditorHostedBoolFieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(boolMetrics.rowHeight, 25.0f);
EXPECT_FLOAT_EQ(boolMetrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(boolMetrics.labelFontSize, 10.0f);
EXPECT_FLOAT_EQ(boolMetrics.checkboxGlyphFontSize, 12.0f);
EXPECT_FLOAT_EQ(boolPalette.checkboxColor.r, 0.2f);
EXPECT_FLOAT_EQ(boolPalette.labelColor.r, 0.8f);
const auto numberMetrics = Editor::BuildUIEditorHostedNumberFieldMetrics(propertyMetrics);
const auto numberPalette = Editor::BuildUIEditorHostedNumberFieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(numberMetrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(numberMetrics.controlInsetY, 2.0f);
EXPECT_FLOAT_EQ(numberMetrics.valueTextInsetX, 5.0f);
EXPECT_FLOAT_EQ(numberMetrics.valueFontSize, 12.0f);
EXPECT_FLOAT_EQ(numberPalette.valueBoxEditingColor.r, 0.4f);
EXPECT_FLOAT_EQ(numberPalette.readOnlyValueColor.r, 0.6f);
EXPECT_FLOAT_EQ(numberPalette.controlFocusedBorderColor.r, 0.55f);
const auto textMetrics = Editor::BuildUIEditorHostedTextFieldMetrics(propertyMetrics);
const auto textPalette = Editor::BuildUIEditorHostedTextFieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(textMetrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(textMetrics.controlInsetY, 2.0f);
EXPECT_FLOAT_EQ(textMetrics.valueTextInsetX, 5.0f);
EXPECT_FLOAT_EQ(textMetrics.valueFontSize, 12.0f);
EXPECT_FLOAT_EQ(textPalette.valueBoxEditingColor.r, 0.4f);
EXPECT_FLOAT_EQ(textPalette.readOnlyValueColor.r, 0.6f);
EXPECT_FLOAT_EQ(textPalette.controlFocusedBorderColor.r, 0.55f);
const auto vector2Metrics = Editor::BuildUIEditorHostedVector2FieldMetrics(propertyMetrics);
const auto vector2Palette = Editor::BuildUIEditorHostedVector2FieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(vector2Metrics.controlInsetY, 2.0f);
EXPECT_FLOAT_EQ(vector2Metrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(vector2Metrics.valueTextInsetX, 5.0f);
EXPECT_FLOAT_EQ(vector2Metrics.componentRounding, 2.0f);
EXPECT_FLOAT_EQ(vector2Palette.componentEditingColor.r, 0.4f);
EXPECT_FLOAT_EQ(vector2Palette.componentFocusedBorderColor.r, 0.55f);
const auto vector3Metrics = Editor::BuildUIEditorHostedVector3FieldMetrics(propertyMetrics);
const auto vector3Palette = Editor::BuildUIEditorHostedVector3FieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(vector3Metrics.controlInsetY, 2.0f);
EXPECT_FLOAT_EQ(vector3Metrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(vector3Metrics.valueTextInsetX, 5.0f);
EXPECT_FLOAT_EQ(vector3Metrics.componentRounding, 2.0f);
EXPECT_FLOAT_EQ(vector3Palette.componentEditingColor.r, 0.4f);
EXPECT_FLOAT_EQ(vector3Palette.componentFocusedBorderColor.r, 0.55f);
const auto enumMetrics = Editor::BuildUIEditorHostedEnumFieldMetrics(propertyMetrics);
const auto enumPalette = Editor::BuildUIEditorHostedEnumFieldPalette(propertyPalette);
EXPECT_FLOAT_EQ(enumMetrics.controlTrailingInset, 5.0f);
EXPECT_FLOAT_EQ(enumMetrics.controlInsetY, 2.0f);
EXPECT_FLOAT_EQ(enumMetrics.valueTextInsetX, 5.0f);
EXPECT_FLOAT_EQ(enumMetrics.dropdownArrowFontSize, 12.0f);
EXPECT_FLOAT_EQ(enumPalette.arrowColor.r, 0.9f);
}
} // namespace

View File

@@ -0,0 +1,89 @@
#include <gtest/gtest.h>
#include <XCEditor/Widgets/UIEditorVector2Field.h>
namespace {
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::BuildUIEditorVector2FieldLayout;
using XCEngine::UI::Editor::Widgets::FormatUIEditorVector2FieldComponentValue;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorVector2Field;
using XCEngine::UI::Editor::Widgets::UIEditorVector2FieldHitTargetKind;
using XCEngine::UI::Editor::Widgets::UIEditorVector2FieldSpec;
UIRect MakeInspectorBounds() {
return UIRect(0.0f, 0.0f, 392.0f, 22.0f);
}
TEST(UIEditorVector2FieldTest, FormatSupportsPerComponentDisplay) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.25, -3.5 };
EXPECT_EQ(FormatUIEditorVector2FieldComponentValue(spec, 0u), "1.25");
EXPECT_EQ(FormatUIEditorVector2FieldComponentValue(spec, 1u), "-3.5");
}
TEST(UIEditorVector2FieldTest, LayoutBuildsTwoComponentRects) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
const auto layout = BuildUIEditorVector2FieldLayout(MakeInspectorBounds(), spec);
EXPECT_GT(layout.labelRect.width, 0.0f);
EXPECT_GT(layout.controlRect.width, 0.0f);
EXPECT_GT(layout.componentRects[0].width, 0.0f);
EXPECT_GT(layout.componentRects[1].width, 0.0f);
EXPECT_LT(layout.componentRects[0].x + layout.componentRects[0].width, layout.componentRects[1].x);
EXPECT_GT(layout.componentPrefixRects[0].width, 0.0f);
EXPECT_GT(layout.componentValueRects[0].width, 0.0f);
EXPECT_GT(layout.componentValueRects[0].x, layout.componentPrefixRects[0].x + layout.componentPrefixRects[0].width);
EXPECT_GT(layout.componentValueRects[1].x, layout.componentPrefixRects[1].x + layout.componentPrefixRects[1].width);
EXPECT_EQ(layout.componentValueRects[0].height, layout.componentRects[0].height);
EXPECT_EQ(layout.componentValueRects[1].height, layout.componentRects[1].height);
}
TEST(UIEditorVector2FieldTest, HitTestTreatsAxisLabelAreaAsComponentHost) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
const auto layout = BuildUIEditorVector2FieldLayout(MakeInspectorBounds(), spec);
const auto prefixHit = HitTestUIEditorVector2Field(
layout,
UIPoint(
layout.componentPrefixRects[0].x + layout.componentPrefixRects[0].width * 0.5f,
layout.componentPrefixRects[0].y + layout.componentPrefixRects[0].height * 0.5f));
EXPECT_EQ(prefixHit.kind, UIEditorVector2FieldHitTargetKind::Component);
EXPECT_EQ(prefixHit.componentIndex, 0u);
const auto valueHit = HitTestUIEditorVector2Field(
layout,
UIPoint(
layout.componentValueRects[1].x + 4.0f,
layout.componentValueRects[1].y + 4.0f));
EXPECT_EQ(valueHit.kind, UIEditorVector2FieldHitTargetKind::Component);
EXPECT_EQ(valueHit.componentIndex, 1u);
}
TEST(UIEditorVector2FieldTest, HitTestResolvesComponentAndRow) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
const auto layout = BuildUIEditorVector2FieldLayout(MakeInspectorBounds(), spec);
const auto firstHit = HitTestUIEditorVector2Field(
layout,
UIPoint(layout.componentRects[0].x + 4.0f, layout.componentRects[0].y + 4.0f));
EXPECT_EQ(firstHit.kind, UIEditorVector2FieldHitTargetKind::Component);
EXPECT_EQ(firstHit.componentIndex, 0u);
const auto rowHit = HitTestUIEditorVector2Field(
layout,
UIPoint(layout.labelRect.x + 4.0f, layout.labelRect.y + 4.0f));
EXPECT_EQ(rowHit.kind, UIEditorVector2FieldHitTargetKind::Row);
}
} // namespace

View File

@@ -0,0 +1,199 @@
#include <gtest/gtest.h>
#include <XCEditor/Core/UIEditorVector2FieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIInputEvent;
using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIPointerButton;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::UIEditorVector2FieldInteractionState;
using XCEngine::UI::Editor::UpdateUIEditorVector2FieldInteraction;
using XCEngine::UI::Editor::Widgets::UIEditorVector2FieldSpec;
UIRect MakeInspectorBounds() {
return UIRect(0.0f, 0.0f, 392.0f, 22.0f);
}
UIInputEvent MakePointer(UIInputEventType type, float x, float y, UIPointerButton button = UIPointerButton::None) {
UIInputEvent event = {};
event.type = type;
event.position = UIPoint(x, y);
event.pointerButton = button;
return event;
}
UIInputEvent MakeKey(KeyCode keyCode) {
UIInputEvent event = {};
event.type = UIInputEventType::KeyDown;
event.keyCode = static_cast<std::int32_t>(keyCode);
return event;
}
UIInputEvent MakeCharacter(char character) {
UIInputEvent event = {};
event.type = UIInputEventType::Character;
event.character = static_cast<std::uint32_t>(character);
return event;
}
} // namespace
TEST(UIEditorVector2FieldInteractionTest, ClickSecondComponentStartsEditing) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0 };
UIEditorVector2FieldInteractionState state = {};
auto frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{});
frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{
MakePointer(
UIInputEventType::PointerButtonDown,
frame.layout.componentRects[1].x + 4.0f,
frame.layout.componentRects[1].y + 4.0f,
UIPointerButton::Left),
MakePointer(
UIInputEventType::PointerButtonUp,
frame.layout.componentRects[1].x + 4.0f,
frame.layout.componentRects[1].y + 4.0f,
UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector2FieldState.editing);
EXPECT_EQ(state.vector2FieldState.selectedComponentIndex, 1u);
}
TEST(UIEditorVector2FieldInteractionTest, ClickAxisLabelAreaAlsoStartsEditing) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0 };
UIEditorVector2FieldInteractionState state = {};
auto frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{});
const float clickX =
frame.layout.componentPrefixRects[0].x + frame.layout.componentPrefixRects[0].width * 0.5f;
const float clickY =
frame.layout.componentPrefixRects[0].y + frame.layout.componentPrefixRects[0].height * 0.5f;
frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{
MakePointer(UIInputEventType::PointerButtonDown, clickX, clickY, UIPointerButton::Left),
MakePointer(UIInputEventType::PointerButtonUp, clickX, clickY, UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector2FieldState.editing);
EXPECT_EQ(state.vector2FieldState.selectedComponentIndex, 0u);
}
TEST(UIEditorVector2FieldInteractionTest, TabSelectsNextComponentAndArrowAppliesStep) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0 };
spec.step = 0.5;
UIEditorVector2FieldInteractionState state = {};
state.vector2FieldState.focused = true;
state.vector2FieldState.selectedComponentIndex = 0u;
auto frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeKey(KeyCode::Tab) });
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(state.vector2FieldState.selectedComponentIndex, 1u);
frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeKey(KeyCode::Up) });
EXPECT_TRUE(frame.result.stepApplied);
EXPECT_EQ(frame.result.changedComponentIndex, 1u);
EXPECT_DOUBLE_EQ(spec.values[1], 2.5);
}
TEST(UIEditorVector2FieldInteractionTest, EnterStartsEditingAndCommitUpdatesSelectedComponent) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0 };
spec.integerMode = true;
UIEditorVector2FieldInteractionState state = {};
state.vector2FieldState.focused = true;
state.vector2FieldState.selectedComponentIndex = 0u;
auto frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector2FieldState.editing);
frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeCharacter('4'), MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editCommitted);
EXPECT_TRUE(frame.result.valueChanged);
EXPECT_EQ(frame.result.changedComponentIndex, 0u);
EXPECT_DOUBLE_EQ(spec.values[0], 14.0);
EXPECT_DOUBLE_EQ(spec.values[1], 2.0);
}
TEST(UIEditorVector2FieldInteractionTest, CharacterInputCanStartEditingAndEscapeCancels) {
UIEditorVector2FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0 };
UIEditorVector2FieldInteractionState state = {};
state.vector2FieldState.focused = true;
state.vector2FieldState.selectedComponentIndex = 1u;
auto frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeCharacter('9') });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector2FieldState.editing);
EXPECT_EQ(state.vector2FieldState.displayTexts[1], "9");
frame = UpdateUIEditorVector2FieldInteraction(
state,
spec,
MakeInspectorBounds(),
{ MakeKey(KeyCode::Escape) });
EXPECT_TRUE(frame.result.editCanceled);
EXPECT_FALSE(state.vector2FieldState.editing);
EXPECT_DOUBLE_EQ(spec.values[1], 2.0);
}

View File

@@ -0,0 +1,58 @@
#include <gtest/gtest.h>
#include <XCEditor/Widgets/UIEditorVector3Field.h>
namespace {
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::Widgets::BuildUIEditorVector3FieldLayout;
using XCEngine::UI::Editor::Widgets::FormatUIEditorVector3FieldComponentValue;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorVector3Field;
using XCEngine::UI::Editor::Widgets::UIEditorVector3FieldHitTargetKind;
using XCEngine::UI::Editor::Widgets::UIEditorVector3FieldSpec;
TEST(UIEditorVector3FieldTest, FormatSupportsPerComponentDisplay) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.25, -3.5, 8.0 };
EXPECT_EQ(FormatUIEditorVector3FieldComponentValue(spec, 0u), "1.25");
EXPECT_EQ(FormatUIEditorVector3FieldComponentValue(spec, 1u), "-3.5");
EXPECT_EQ(FormatUIEditorVector3FieldComponentValue(spec, 2u), "8");
}
TEST(UIEditorVector3FieldTest, LayoutBuildsThreeComponentRects) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
const auto layout = BuildUIEditorVector3FieldLayout(UIRect(0.0f, 0.0f, 520.0f, 32.0f), spec);
EXPECT_GT(layout.labelRect.width, 0.0f);
EXPECT_GT(layout.componentRects[0].width, 0.0f);
EXPECT_GT(layout.componentRects[1].width, 0.0f);
EXPECT_GT(layout.componentRects[2].width, 0.0f);
EXPECT_LT(layout.componentRects[0].x + layout.componentRects[0].width, layout.componentRects[1].x);
EXPECT_LT(layout.componentRects[1].x + layout.componentRects[1].width, layout.componentRects[2].x);
}
TEST(UIEditorVector3FieldTest, HitTestResolvesComponentAndRow) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
const auto layout = BuildUIEditorVector3FieldLayout(UIRect(0.0f, 0.0f, 520.0f, 32.0f), spec);
const auto thirdHit = HitTestUIEditorVector3Field(
layout,
UIPoint(layout.componentRects[2].x + 4.0f, layout.componentRects[2].y + 4.0f));
EXPECT_EQ(thirdHit.kind, UIEditorVector3FieldHitTargetKind::Component);
EXPECT_EQ(thirdHit.componentIndex, 2u);
const auto rowHit = HitTestUIEditorVector3Field(
layout,
UIPoint(layout.labelRect.x + 4.0f, layout.labelRect.y + 4.0f));
EXPECT_EQ(rowHit.kind, UIEditorVector3FieldHitTargetKind::Row);
}
} // namespace

View File

@@ -0,0 +1,164 @@
#include <gtest/gtest.h>
#include <XCEditor/Core/UIEditorVector3FieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIInputEvent;
using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIPointerButton;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::UIEditorVector3FieldInteractionState;
using XCEngine::UI::Editor::UpdateUIEditorVector3FieldInteraction;
using XCEngine::UI::Editor::Widgets::UIEditorVector3FieldSpec;
UIInputEvent MakePointer(UIInputEventType type, float x, float y, UIPointerButton button = UIPointerButton::None) {
UIInputEvent event = {};
event.type = type;
event.position = UIPoint(x, y);
event.pointerButton = button;
return event;
}
UIInputEvent MakeKey(KeyCode keyCode) {
UIInputEvent event = {};
event.type = UIInputEventType::KeyDown;
event.keyCode = static_cast<std::int32_t>(keyCode);
return event;
}
UIInputEvent MakeCharacter(char character) {
UIInputEvent event = {};
event.type = UIInputEventType::Character;
event.character = static_cast<std::uint32_t>(character);
return event;
}
} // namespace
TEST(UIEditorVector3FieldInteractionTest, ClickThirdComponentStartsEditing) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0, 3.0 };
UIEditorVector3FieldInteractionState state = {};
auto frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{});
frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{
MakePointer(
UIInputEventType::PointerButtonDown,
frame.layout.componentRects[2].x + 4.0f,
frame.layout.componentRects[2].y + 4.0f,
UIPointerButton::Left),
MakePointer(
UIInputEventType::PointerButtonUp,
frame.layout.componentRects[2].x + 4.0f,
frame.layout.componentRects[2].y + 4.0f,
UIPointerButton::Left)
});
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector3FieldState.editing);
EXPECT_EQ(state.vector3FieldState.selectedComponentIndex, 2u);
}
TEST(UIEditorVector3FieldInteractionTest, TabSelectsNextComponentAndArrowAppliesStep) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0, 3.0 };
spec.step = 0.5;
UIEditorVector3FieldInteractionState state = {};
state.vector3FieldState.focused = true;
state.vector3FieldState.selectedComponentIndex = 1u;
auto frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeKey(KeyCode::Tab) });
EXPECT_TRUE(frame.result.selectionChanged);
EXPECT_EQ(state.vector3FieldState.selectedComponentIndex, 2u);
frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeKey(KeyCode::Up) });
EXPECT_TRUE(frame.result.stepApplied);
EXPECT_EQ(frame.result.changedComponentIndex, 2u);
EXPECT_DOUBLE_EQ(spec.values[2], 3.5);
}
TEST(UIEditorVector3FieldInteractionTest, EnterStartsEditingAndCommitUpdatesSelectedComponent) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0, 3.0 };
spec.integerMode = true;
UIEditorVector3FieldInteractionState state = {};
state.vector3FieldState.focused = true;
state.vector3FieldState.selectedComponentIndex = 1u;
auto frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector3FieldState.editing);
frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeCharacter('4'), MakeKey(KeyCode::Enter) });
EXPECT_TRUE(frame.result.editCommitted);
EXPECT_TRUE(frame.result.valueChanged);
EXPECT_EQ(frame.result.changedComponentIndex, 1u);
EXPECT_DOUBLE_EQ(spec.values[0], 1.0);
EXPECT_DOUBLE_EQ(spec.values[1], 24.0);
EXPECT_DOUBLE_EQ(spec.values[2], 3.0);
}
TEST(UIEditorVector3FieldInteractionTest, CharacterInputCanStartEditingAndEscapeCancels) {
UIEditorVector3FieldSpec spec = {};
spec.fieldId = "position";
spec.label = "Position";
spec.values = { 1.0, 2.0, 3.0 };
UIEditorVector3FieldInteractionState state = {};
state.vector3FieldState.focused = true;
state.vector3FieldState.selectedComponentIndex = 0u;
auto frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeCharacter('9') });
EXPECT_TRUE(frame.result.editStarted);
EXPECT_TRUE(state.vector3FieldState.editing);
EXPECT_EQ(state.vector3FieldState.displayTexts[0], "9");
frame = UpdateUIEditorVector3FieldInteraction(
state,
spec,
UIRect(0.0f, 0.0f, 520.0f, 32.0f),
{ MakeKey(KeyCode::Escape) });
EXPECT_TRUE(frame.result.editCanceled);
EXPECT_FALSE(state.vector3FieldState.editing);
EXPECT_DOUBLE_EQ(spec.values[0], 1.0);
}