Lay groundwork for detached editor windows
This commit is contained in:
@@ -61,6 +61,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_ui_editor_workspace_layout_persistence.cpp
|
||||
test_ui_editor_workspace_model.cpp
|
||||
test_ui_editor_workspace_session.cpp
|
||||
test_ui_editor_window_workspace_controller.cpp
|
||||
)
|
||||
|
||||
add_executable(editor_ui_tests ${EDITOR_UI_UNIT_TEST_SOURCES})
|
||||
@@ -88,7 +89,6 @@ if(MSVC)
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Release"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/MinSizeRel"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/RelWithDebInfo"
|
||||
VS_GLOBAL_UseMultiToolTask "false"
|
||||
)
|
||||
set_property(TARGET editor_ui_tests PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
|
||||
@@ -352,6 +352,57 @@ TEST(UIEditorDockHostInteractionTest, ClickingTabActivatesTargetPanel) {
|
||||
EXPECT_EQ(documentStack->selectedPanelId, "doc-a");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ReleasingActiveTabDragOutsideDockHostRequestsDetach) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildThreeDocumentWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
|
||||
auto frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
const auto* documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
const UIRect draggedTabRect = documentStack->tabStripLayout.tabHeaderRects[2];
|
||||
const UIPoint draggedTabCenter = RectCenter(draggedTabRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(draggedTabCenter.x, draggedTabCenter.y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::Tab);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "doc-c");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerDown(draggedTabCenter.x, draggedTabCenter.y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(860.0f, draggedTabCenter.y) });
|
||||
EXPECT_EQ(state.activeTabDragPanelId, "doc-c");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerUp(860.0f, draggedTabCenter.y) });
|
||||
EXPECT_TRUE(frame.result.detachRequested);
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_EQ(frame.result.detachedNodeId, "document-tabs");
|
||||
EXPECT_EQ(frame.result.detachedPanelId, "doc-c");
|
||||
EXPECT_TRUE(state.activeTabDragNodeId.empty());
|
||||
EXPECT_TRUE(state.activeTabDragPanelId.empty());
|
||||
EXPECT_FALSE(state.dockHostState.dropPreview.visible);
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, FocusedTabStripHandlesKeyboardNavigationThroughTabStripInteraction) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEditor/Shell/UIEditorWindowWorkspaceController.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWindowWorkspaceController;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::CollectUIEditorWorkspaceVisiblePanels;
|
||||
using XCEngine::UI::Editor::ContainsUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::FindUIEditorPanelSessionState;
|
||||
using XCEngine::UI::Editor::FindUIEditorWindowWorkspaceState;
|
||||
using XCEngine::UI::Editor::UIEditorPanelRegistry;
|
||||
using XCEngine::UI::Editor::UIEditorWindowWorkspaceController;
|
||||
using XCEngine::UI::Editor::UIEditorWindowWorkspaceOperationStatus;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceDockPlacement;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
|
||||
UIEditorPanelRegistry BuildPanelRegistry() {
|
||||
UIEditorPanelRegistry registry = {};
|
||||
registry.panels = {
|
||||
{ "doc-a", "Document A", {}, true, true, true },
|
||||
{ "doc-b", "Document B", {}, true, true, true },
|
||||
{ "inspector", "Inspector", {}, true, true, true }
|
||||
};
|
||||
return registry;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceModel BuildWorkspace() {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.7f,
|
||||
BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
0u),
|
||||
BuildUIEditorWorkspaceSingleTabStack(
|
||||
"inspector-panel",
|
||||
"inspector",
|
||||
"Inspector",
|
||||
true));
|
||||
workspace.activePanelId = "doc-a";
|
||||
return workspace;
|
||||
}
|
||||
|
||||
std::vector<std::string> CollectVisiblePanelIds(
|
||||
const UIEditorWorkspaceModel& workspace,
|
||||
const XCEngine::UI::Editor::UIEditorWorkspaceSession& session) {
|
||||
const auto panels = CollectUIEditorWorkspaceVisiblePanels(workspace, session);
|
||||
std::vector<std::string> ids = {};
|
||||
ids.reserve(panels.size());
|
||||
for (const auto& panel : panels) {
|
||||
ids.push_back(panel.panelId);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorWindowWorkspaceControllerTest, DetachPanelCreatesNewDetachedWindowAndMovesSessionState) {
|
||||
UIEditorWindowWorkspaceController controller =
|
||||
BuildDefaultUIEditorWindowWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
|
||||
const auto result = controller.DetachPanelToNewWindow(
|
||||
"main-window",
|
||||
"document-tabs",
|
||||
"doc-b",
|
||||
"doc-b-window");
|
||||
EXPECT_EQ(result.status, UIEditorWindowWorkspaceOperationStatus::Changed);
|
||||
EXPECT_EQ(result.targetWindowId, "doc-b-window");
|
||||
EXPECT_EQ(result.activeWindowId, "doc-b-window");
|
||||
ASSERT_EQ(result.windowIds.size(), 2u);
|
||||
|
||||
const auto* mainWindow =
|
||||
FindUIEditorWindowWorkspaceState(controller.GetWindowSet(), "main-window");
|
||||
ASSERT_NE(mainWindow, nullptr);
|
||||
EXPECT_FALSE(ContainsUIEditorWorkspacePanel(mainWindow->workspace, "doc-b"));
|
||||
EXPECT_EQ(FindUIEditorPanelSessionState(mainWindow->session, "doc-b"), nullptr);
|
||||
|
||||
const auto* detachedWindow =
|
||||
FindUIEditorWindowWorkspaceState(controller.GetWindowSet(), "doc-b-window");
|
||||
ASSERT_NE(detachedWindow, nullptr);
|
||||
ASSERT_TRUE(ContainsUIEditorWorkspacePanel(detachedWindow->workspace, "doc-b"));
|
||||
ASSERT_NE(FindUIEditorPanelSessionState(detachedWindow->session, "doc-b"), nullptr);
|
||||
EXPECT_EQ(detachedWindow->workspace.activePanelId, "doc-b");
|
||||
|
||||
const auto mainVisibleIds =
|
||||
CollectVisiblePanelIds(mainWindow->workspace, mainWindow->session);
|
||||
ASSERT_EQ(mainVisibleIds.size(), 2u);
|
||||
EXPECT_EQ(mainVisibleIds[0], "doc-a");
|
||||
EXPECT_EQ(mainVisibleIds[1], "inspector");
|
||||
}
|
||||
|
||||
TEST(UIEditorWindowWorkspaceControllerTest, MovingSinglePanelDetachedWindowBackToMainClosesSourceWindow) {
|
||||
UIEditorWindowWorkspaceController controller =
|
||||
BuildDefaultUIEditorWindowWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
ASSERT_EQ(
|
||||
controller.DetachPanelToNewWindow(
|
||||
"main-window",
|
||||
"document-tabs",
|
||||
"doc-b",
|
||||
"doc-b-window").status,
|
||||
UIEditorWindowWorkspaceOperationStatus::Changed);
|
||||
|
||||
const auto result = controller.MovePanelToStack(
|
||||
"doc-b-window",
|
||||
"doc-b-window-root",
|
||||
"doc-b",
|
||||
"main-window",
|
||||
"document-tabs",
|
||||
1u);
|
||||
EXPECT_EQ(result.status, UIEditorWindowWorkspaceOperationStatus::Changed);
|
||||
EXPECT_EQ(result.activeWindowId, "main-window");
|
||||
ASSERT_EQ(result.windowIds.size(), 1u);
|
||||
EXPECT_EQ(result.windowIds[0], "main-window");
|
||||
|
||||
EXPECT_EQ(
|
||||
FindUIEditorWindowWorkspaceState(controller.GetWindowSet(), "doc-b-window"),
|
||||
nullptr);
|
||||
|
||||
const auto* mainWindow =
|
||||
FindUIEditorWindowWorkspaceState(controller.GetWindowSet(), "main-window");
|
||||
ASSERT_NE(mainWindow, nullptr);
|
||||
ASSERT_TRUE(ContainsUIEditorWorkspacePanel(mainWindow->workspace, "doc-b"));
|
||||
ASSERT_NE(FindUIEditorPanelSessionState(mainWindow->session, "doc-b"), nullptr);
|
||||
EXPECT_EQ(mainWindow->workspace.activePanelId, "doc-b");
|
||||
}
|
||||
|
||||
TEST(UIEditorWindowWorkspaceControllerTest, DockingDetachedPanelIntoMainWindowAlsoClosesSourceWindow) {
|
||||
UIEditorWindowWorkspaceController controller =
|
||||
BuildDefaultUIEditorWindowWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
ASSERT_EQ(
|
||||
controller.DetachPanelToNewWindow(
|
||||
"main-window",
|
||||
"document-tabs",
|
||||
"doc-b",
|
||||
"doc-b-window").status,
|
||||
UIEditorWindowWorkspaceOperationStatus::Changed);
|
||||
|
||||
const auto result = controller.DockPanelRelative(
|
||||
"doc-b-window",
|
||||
"doc-b-window-root",
|
||||
"doc-b",
|
||||
"main-window",
|
||||
"inspector-panel",
|
||||
UIEditorWorkspaceDockPlacement::Left,
|
||||
0.4f);
|
||||
EXPECT_EQ(result.status, UIEditorWindowWorkspaceOperationStatus::Changed);
|
||||
EXPECT_EQ(result.activeWindowId, "main-window");
|
||||
ASSERT_EQ(result.windowIds.size(), 1u);
|
||||
|
||||
const auto* mainWindow =
|
||||
FindUIEditorWindowWorkspaceState(controller.GetWindowSet(), "main-window");
|
||||
ASSERT_NE(mainWindow, nullptr);
|
||||
ASSERT_TRUE(ContainsUIEditorWorkspacePanel(mainWindow->workspace, "doc-b"));
|
||||
ASSERT_NE(FindUIEditorPanelSessionState(mainWindow->session, "doc-b"), nullptr);
|
||||
|
||||
const auto visibleIds =
|
||||
CollectVisiblePanelIds(mainWindow->workspace, mainWindow->session);
|
||||
ASSERT_EQ(visibleIds.size(), 3u);
|
||||
EXPECT_EQ(visibleIds[0], "doc-a");
|
||||
EXPECT_EQ(visibleIds[1], "doc-b");
|
||||
EXPECT_EQ(visibleIds[2], "inspector");
|
||||
}
|
||||
Reference in New Issue
Block a user