ui: add typed editor field foundations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
52
tests/UI/Editor/unit/test_ui_editor_field_row_layout.cpp
Normal file
52
tests/UI/Editor/unit/test_ui_editor_field_row_layout.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
36
tests/UI/Editor/unit/test_ui_editor_text_field.cpp
Normal file
36
tests/UI/Editor/unit/test_ui_editor_text_field.cpp
Normal 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
|
||||
152
tests/UI/Editor/unit/test_ui_editor_text_field_interaction.cpp
Normal file
152
tests/UI/Editor/unit/test_ui_editor_text_field_interaction.cpp
Normal 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);
|
||||
}
|
||||
371
tests/UI/Editor/unit/test_ui_editor_theme.cpp
Normal file
371
tests/UI/Editor/unit/test_ui_editor_theme.cpp
Normal 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
|
||||
89
tests/UI/Editor/unit/test_ui_editor_vector2_field.cpp
Normal file
89
tests/UI/Editor/unit/test_ui_editor_vector2_field.cpp
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
58
tests/UI/Editor/unit/test_ui_editor_vector3_field.cpp
Normal file
58
tests/UI/Editor/unit/test_ui_editor_vector3_field.cpp
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user