Add dock host interaction contract validation
This commit is contained in:
@@ -4,6 +4,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_structured_editor_shell.cpp
|
||||
test_ui_editor_command_dispatcher.cpp
|
||||
test_ui_editor_command_registry.cpp
|
||||
test_ui_editor_dock_host_interaction.cpp
|
||||
test_ui_editor_menu_model.cpp
|
||||
test_ui_editor_menu_session.cpp
|
||||
test_ui_editor_menu_bar.cpp
|
||||
|
||||
302
tests/UI/Editor/unit/test_ui_editor_dock_host_interaction.cpp
Normal file
302
tests/UI/Editor/unit/test_ui_editor_dock_host_interaction.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEditor/Core/UIEditorDockHostInteraction.h>
|
||||
#include <XCEditor/Core/UIEditorWorkspaceController.h>
|
||||
#include <XCEditor/Core/UIEditorWorkspaceModel.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::UIInputEvent;
|
||||
using XCEngine::UI::UIInputEventType;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UIPointerButton;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::FindUIEditorPanelSessionState;
|
||||
using XCEngine::UI::Editor::UIEditorDockHostInteractionState;
|
||||
using XCEngine::UI::Editor::UIEditorPanelRegistry;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
using XCEngine::UI::Editor::UpdateUIEditorDockHostInteraction;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostHitTargetKind;
|
||||
|
||||
UIEditorPanelRegistry BuildPanelRegistry() {
|
||||
UIEditorPanelRegistry registry = {};
|
||||
registry.panels = {
|
||||
{ "doc-a", "Document A", {}, true, true, true },
|
||||
{ "doc-b", "Document B", {}, true, true, true },
|
||||
{ "details", "Details", {}, true, true, true },
|
||||
{ "console", "Console", {}, true, true, true }
|
||||
};
|
||||
return registry;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceModel BuildWorkspace() {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.5f,
|
||||
BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
1u),
|
||||
BuildUIEditorWorkspaceSplit(
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
0.6f,
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspacePanel("console-node", "console", "Console", true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
return workspace;
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerMove(float x, float y) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerMove;
|
||||
event.position = UIPoint(x, y);
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerDown(float x, float y) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerButtonDown;
|
||||
event.position = UIPoint(x, y);
|
||||
event.pointerButton = UIPointerButton::Left;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerUp(float x, float y) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerButtonUp;
|
||||
event.position = UIPoint(x, y);
|
||||
event.pointerButton = UIPointerButton::Left;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIInputEvent MakeFocusLost() {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::FocusLost;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIPoint RectCenter(const UIRect& rect) {
|
||||
return UIPoint(rect.x + rect.width * 0.5f, rect.y + rect.height * 0.5f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, SplitterDragUpdatesWorkspaceSplitRatio) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(396.0f, 120.0f) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::SplitterHandle);
|
||||
EXPECT_EQ(frame.result.hitTarget.nodeId, "root-split");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerDown(396.0f, 120.0f) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.requestPointerCapture);
|
||||
EXPECT_EQ(frame.result.activeSplitterNodeId, "root-split");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(520.0f, 120.0f) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.layoutChanged);
|
||||
EXPECT_GT(controller.GetWorkspace().root.splitRatio, 0.5f);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(520.0f, 120.0f) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.releasePointerCapture);
|
||||
EXPECT_TRUE(state.dockHostState.activeSplitterNodeId.empty());
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, FocusLostWhileDraggingSplitterRequestsPointerRelease) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(396.0f, 120.0f) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::SplitterHandle);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerDown(396.0f, 120.0f) });
|
||||
EXPECT_TRUE(frame.result.requestPointerCapture);
|
||||
EXPECT_FALSE(state.dockHostState.activeSplitterNodeId.empty());
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakeFocusLost() });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.releasePointerCapture);
|
||||
EXPECT_TRUE(state.dockHostState.activeSplitterNodeId.empty());
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingTabActivatesTargetPanel) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const UIRect docARect = frame.layout.tabStacks.front().tabStripLayout.tabHeaderRects[0];
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(RectCenter(docARect).x, RectCenter(docARect).y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::Tab);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "doc-a");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(RectCenter(docARect).x, RectCenter(docARect).y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "doc-a");
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
EXPECT_EQ(frame.layout.tabStacks.front().selectedPanelId, "doc-a");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingTabCloseClosesPanelThroughController) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const UIRect closeRect = frame.layout.tabStacks.front().tabStripLayout.closeButtonRects[1];
|
||||
const UIPoint closeCenter = RectCenter(closeRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::TabCloseButton);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "doc-b");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
|
||||
const auto* panelState = FindUIEditorPanelSessionState(controller.GetSession(), "doc-b");
|
||||
ASSERT_NE(panelState, nullptr);
|
||||
EXPECT_FALSE(panelState->open);
|
||||
EXPECT_FALSE(panelState->visible);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "doc-a");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelBodyActivatesTargetPanel) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.panels.size(), 2u);
|
||||
const UIRect detailsBodyRect = frame.layout.panels[0].frameLayout.bodyRect;
|
||||
const UIPoint detailsBodyCenter = RectCenter(detailsBodyRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(detailsBodyCenter.x, detailsBodyCenter.y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::PanelBody);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "details");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(detailsBodyCenter.x, detailsBodyCenter.y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "details");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelCloseClosesPanelThroughController) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.panels.size(), 2u);
|
||||
const UIRect closeRect = frame.layout.panels[1].frameLayout.closeButtonRect;
|
||||
const UIPoint closeCenter = RectCenter(closeRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::PanelCloseButton);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "console");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
|
||||
const auto* panelState = FindUIEditorPanelSessionState(controller.GetSession(), "console");
|
||||
ASSERT_NE(panelState, nullptr);
|
||||
EXPECT_FALSE(panelState->open);
|
||||
EXPECT_FALSE(panelState->visible);
|
||||
}
|
||||
Reference in New Issue
Block a user