editor: apply pending updates
This commit is contained in:
@@ -6,6 +6,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_ui_editor_menu_session.cpp
|
||||
test_ui_editor_menu_bar.cpp
|
||||
test_ui_editor_menu_popup.cpp
|
||||
test_ui_editor_menu_popup_interaction.cpp
|
||||
test_ui_editor_panel_content_host.cpp
|
||||
test_ui_editor_panel_host_lifecycle.cpp
|
||||
test_ui_editor_panel_input_filter.cpp
|
||||
|
||||
182
tests/UI/Editor/unit/test_ui_editor_menu_popup_interaction.cpp
Normal file
182
tests/UI/Editor/unit/test_ui_editor_menu_popup_interaction.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEditor/Menu/UIEditorMenuPopupInteraction.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::ResetUIEditorMenuPopupInteractionState;
|
||||
using XCEngine::UI::Editor::UIEditorMenuItemKind;
|
||||
using XCEngine::UI::Editor::UIEditorMenuPopupInteractionRequest;
|
||||
using XCEngine::UI::Editor::UIEditorMenuPopupInteractionState;
|
||||
using XCEngine::UI::Editor::UpdateUIEditorMenuPopupInteraction;
|
||||
using XCEngine::UI::Editor::Widgets::BuildUIEditorMenuPopupLayout;
|
||||
using XCEngine::UI::Editor::Widgets::MeasureUIEditorMenuPopupHeight;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorMenuPopupItem;
|
||||
|
||||
UIEditorMenuPopupItem MakeCommandItem(
|
||||
std::string itemId,
|
||||
std::string label,
|
||||
bool enabled = true) {
|
||||
UIEditorMenuPopupItem item = {};
|
||||
item.itemId = std::move(itemId);
|
||||
item.kind = UIEditorMenuItemKind::Command;
|
||||
item.label = std::move(label);
|
||||
item.enabled = enabled;
|
||||
return item;
|
||||
}
|
||||
|
||||
UIEditorMenuPopupItem MakeSeparatorItem(std::string itemId) {
|
||||
UIEditorMenuPopupItem item = {};
|
||||
item.itemId = std::move(itemId);
|
||||
item.kind = UIEditorMenuItemKind::Separator;
|
||||
item.enabled = false;
|
||||
return item;
|
||||
}
|
||||
|
||||
std::vector<UIEditorMenuPopupItem> BuildPopupItems() {
|
||||
return {
|
||||
MakeCommandItem("open", "Open"),
|
||||
MakeSeparatorItem("separator"),
|
||||
MakeCommandItem("duplicate", "Duplicate", false),
|
||||
MakeCommandItem("delete", "Delete")
|
||||
};
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerEvent(
|
||||
UIInputEventType type,
|
||||
const UIPoint& position,
|
||||
UIPointerButton button = UIPointerButton::Left) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
event.position = position;
|
||||
event.pointerButton = button;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakeKeyDown(KeyCode keyCode) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::KeyDown;
|
||||
event.keyCode = static_cast<std::int32_t>(keyCode);
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakeFocusEvent(UIInputEventType type) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIPoint RectCenter(const UIRect& rect) {
|
||||
return UIPoint(
|
||||
rect.x + rect.width * 0.5f,
|
||||
rect.y + rect.height * 0.5f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorMenuPopupInteractionTest, PointerHoverAndClickActivatesEnabledItem) {
|
||||
const std::vector<UIEditorMenuPopupItem> items = BuildPopupItems();
|
||||
const UIRect popupRect(
|
||||
20.0f,
|
||||
10.0f,
|
||||
180.0f,
|
||||
MeasureUIEditorMenuPopupHeight(items));
|
||||
const auto layout = BuildUIEditorMenuPopupLayout(popupRect, items);
|
||||
UIEditorMenuPopupInteractionState state = {};
|
||||
|
||||
const auto frame = UpdateUIEditorMenuPopupInteraction(
|
||||
state,
|
||||
UIEditorMenuPopupInteractionRequest{
|
||||
.layout = layout,
|
||||
.items = items,
|
||||
.preferredHighlightedItemId = {},
|
||||
.closeOnFocusLost = true,
|
||||
},
|
||||
{
|
||||
MakePointerEvent(
|
||||
UIInputEventType::PointerMove,
|
||||
RectCenter(layout.itemRects[0])),
|
||||
MakePointerEvent(
|
||||
UIInputEventType::PointerButtonDown,
|
||||
RectCenter(layout.itemRects[0])),
|
||||
MakePointerEvent(
|
||||
UIInputEventType::PointerButtonUp,
|
||||
RectCenter(layout.itemRects[0]))
|
||||
});
|
||||
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.itemActivated);
|
||||
EXPECT_TRUE(frame.result.closeRequested);
|
||||
EXPECT_EQ(frame.result.activatedIndex, 0u);
|
||||
EXPECT_EQ(frame.result.activatedItemId, "open");
|
||||
EXPECT_EQ(frame.popupState.hoveredIndex, 0u);
|
||||
}
|
||||
|
||||
TEST(UIEditorMenuPopupInteractionTest, KeyboardNavigationSkipsNonInteractiveItems) {
|
||||
const std::vector<UIEditorMenuPopupItem> items = BuildPopupItems();
|
||||
const UIRect popupRect(
|
||||
20.0f,
|
||||
10.0f,
|
||||
180.0f,
|
||||
MeasureUIEditorMenuPopupHeight(items));
|
||||
const auto layout = BuildUIEditorMenuPopupLayout(popupRect, items);
|
||||
UIEditorMenuPopupInteractionState state = {};
|
||||
state.focused = true;
|
||||
|
||||
const auto frame = UpdateUIEditorMenuPopupInteraction(
|
||||
state,
|
||||
UIEditorMenuPopupInteractionRequest{
|
||||
.layout = layout,
|
||||
.items = items,
|
||||
.preferredHighlightedItemId = {},
|
||||
.closeOnFocusLost = true,
|
||||
},
|
||||
{
|
||||
MakeKeyDown(KeyCode::Down),
|
||||
MakeKeyDown(KeyCode::Enter)
|
||||
});
|
||||
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.itemActivated);
|
||||
EXPECT_EQ(frame.result.activatedItemId, "delete");
|
||||
EXPECT_EQ(frame.popupState.hoveredIndex, 3u);
|
||||
}
|
||||
|
||||
TEST(UIEditorMenuPopupInteractionTest, FocusLostRequestsCloseAndClearsFocus) {
|
||||
const std::vector<UIEditorMenuPopupItem> items = BuildPopupItems();
|
||||
const UIRect popupRect(
|
||||
20.0f,
|
||||
10.0f,
|
||||
180.0f,
|
||||
MeasureUIEditorMenuPopupHeight(items));
|
||||
const auto layout = BuildUIEditorMenuPopupLayout(popupRect, items);
|
||||
UIEditorMenuPopupInteractionState state = {};
|
||||
state.focused = true;
|
||||
|
||||
const auto frame = UpdateUIEditorMenuPopupInteraction(
|
||||
state,
|
||||
UIEditorMenuPopupInteractionRequest{
|
||||
.layout = layout,
|
||||
.items = items,
|
||||
.preferredHighlightedItemId = {},
|
||||
.closeOnFocusLost = true,
|
||||
},
|
||||
{
|
||||
MakeFocusEvent(UIInputEventType::FocusLost)
|
||||
});
|
||||
|
||||
EXPECT_TRUE(frame.result.closeRequested);
|
||||
EXPECT_FALSE(frame.popupState.focused);
|
||||
|
||||
ResetUIEditorMenuPopupInteractionState(state);
|
||||
EXPECT_FALSE(state.focused);
|
||||
EXPECT_TRUE(state.highlightedItemId.empty());
|
||||
}
|
||||
@@ -46,6 +46,20 @@ UIInputEvent MakePointerLeave() {
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerLeave(float x, float y) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerLeave;
|
||||
event.position = UIPoint(x, y);
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerEnter(float x, float y) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerEnter;
|
||||
event.position = UIPoint(x, y);
|
||||
return event;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorPanelInputFilterTests, FiltersPointerByBoundsAndPreservesKeyboardAndFocusChannels) {
|
||||
@@ -57,7 +71,7 @@ TEST(UIEditorPanelInputFilterTests, FiltersPointerByBoundsAndPreservesKeyboardAn
|
||||
MakePointerButtonDown(140.0f, 20.0f),
|
||||
MakeKeyDown(),
|
||||
MakeFocusLost(),
|
||||
MakePointerLeave()
|
||||
MakePointerLeave(140.0f, 20.0f)
|
||||
},
|
||||
UIEditorPanelInputFilterOptions{
|
||||
.allowPointerInBounds = true,
|
||||
@@ -121,6 +135,32 @@ TEST(UIEditorPanelInputFilterTests, ExtraOverlayBoundsAllowPointerEventsOutsideP
|
||||
EXPECT_EQ(filtered[1].type, UIInputEventType::PointerButtonDown);
|
||||
}
|
||||
|
||||
TEST(UIEditorPanelInputFilterTests, OverlayBoundsKeepPointerEnterAndSuppressSyntheticLeave) {
|
||||
const std::vector<UIRect> overlayBounds = {
|
||||
UIRect(120.0f, 10.0f, 80.0f, 80.0f)
|
||||
};
|
||||
const std::vector<UIInputEvent> filtered =
|
||||
FilterUIEditorPanelInputEvents(
|
||||
UIRect(0.0f, 0.0f, 100.0f, 100.0f),
|
||||
{
|
||||
MakePointerLeave(130.0f, 20.0f),
|
||||
MakePointerEnter(130.0f, 20.0f),
|
||||
MakePointerMove(130.0f, 24.0f)
|
||||
},
|
||||
UIEditorPanelInputFilterOptions{
|
||||
.allowPointerInBounds = true,
|
||||
.allowPointerWhileCaptured = false,
|
||||
.allowKeyboardInput = false,
|
||||
.allowFocusEvents = false,
|
||||
.includePointerLeave = true
|
||||
},
|
||||
&overlayBounds);
|
||||
|
||||
ASSERT_EQ(filtered.size(), 2u);
|
||||
EXPECT_EQ(filtered[0].type, UIInputEventType::PointerEnter);
|
||||
EXPECT_EQ(filtered[1].type, UIInputEventType::PointerMove);
|
||||
}
|
||||
|
||||
TEST(UIEditorPanelInputFilterTests, SyntheticFocusTransitionsArePrependedToFilteredEvents) {
|
||||
const std::vector<UIInputEvent> filtered =
|
||||
BuildUIEditorPanelInputEvents(
|
||||
|
||||
Reference in New Issue
Block a user