fix(editor_ui): resolve splitter and tab drag gesture conflict

This commit is contained in:
2026-04-11 20:33:53 +08:00
parent 0a015b52ca
commit 3e55f8c204
4 changed files with 134 additions and 5 deletions

View File

@@ -143,6 +143,18 @@ const UIEditorDockHostTabStackLayout* FindTabStackByNodeId(
return nullptr;
}
const XCEngine::UI::Editor::Widgets::UIEditorDockHostSplitterLayout* FindSplitterByNodeId(
const UIEditorDockHostLayout& layout,
std::string_view nodeId) {
for (const auto& splitter : layout.splitters) {
if (splitter.nodeId == nodeId) {
return &splitter;
}
}
return nullptr;
}
} // namespace
TEST(UIEditorDockHostInteractionTest, SplitterDragUpdatesWorkspaceSplitRatio) {
@@ -216,6 +228,87 @@ TEST(UIEditorDockHostInteractionTest, FocusLostWhileDraggingSplitterRequestsPoin
EXPECT_TRUE(state.dockHostState.activeSplitterNodeId.empty());
}
TEST(UIEditorDockHostInteractionTest, SplitterGestureDoesNotLeaveGhostTabDragWhenHitZoneOverlapsTabHeader) {
auto controller =
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
UIEditorDockHostInteractionState state = {};
auto frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{});
const auto* consoleStack = FindTabStackByNodeId(frame.layout, "console-node");
const auto* rightSplitter = FindSplitterByNodeId(frame.layout, "right-split");
ASSERT_NE(consoleStack, nullptr);
ASSERT_NE(rightSplitter, nullptr);
const UIRect overlapRect(
(std::max)(consoleStack->tabStripLayout.headerRect.x, rightSplitter->handleHitRect.x),
(std::max)(consoleStack->tabStripLayout.headerRect.y, rightSplitter->handleHitRect.y),
(std::min)(
consoleStack->tabStripLayout.headerRect.x + consoleStack->tabStripLayout.headerRect.width,
rightSplitter->handleHitRect.x + rightSplitter->handleHitRect.width) -
(std::max)(consoleStack->tabStripLayout.headerRect.x, rightSplitter->handleHitRect.x),
(std::min)(
consoleStack->tabStripLayout.headerRect.y + consoleStack->tabStripLayout.headerRect.height,
rightSplitter->handleHitRect.y + rightSplitter->handleHitRect.height) -
(std::max)(consoleStack->tabStripLayout.headerRect.y, rightSplitter->handleHitRect.y));
ASSERT_GT(overlapRect.width, 0.0f);
ASSERT_GT(overlapRect.height, 0.0f);
const UIPoint overlapPoint = RectCenter(overlapRect);
const UIPoint splitterDragPoint(overlapPoint.x, overlapPoint.y + 24.0f);
const UIPoint probeMovePoint(
consoleStack->tabStripLayout.headerRect.x + 8.0f,
consoleStack->tabStripLayout.headerRect.y + consoleStack->tabStripLayout.headerRect.height * 0.5f);
frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{ MakePointerMove(overlapPoint.x, overlapPoint.y) });
frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{ MakePointerDown(overlapPoint.x, overlapPoint.y) });
EXPECT_TRUE(frame.result.consumed);
EXPECT_TRUE(frame.result.requestPointerCapture);
EXPECT_EQ(frame.result.activeSplitterNodeId, "right-split");
frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{ MakePointerMove(splitterDragPoint.x, splitterDragPoint.y) });
EXPECT_TRUE(frame.result.consumed);
EXPECT_TRUE(frame.result.layoutChanged);
frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{ MakePointerUp(splitterDragPoint.x, splitterDragPoint.y) });
EXPECT_TRUE(frame.result.releasePointerCapture);
EXPECT_TRUE(state.dockHostState.activeSplitterNodeId.empty());
EXPECT_TRUE(state.activeTabDragNodeId.empty());
EXPECT_TRUE(state.activeTabDragPanelId.empty());
EXPECT_FALSE(state.dockHostState.dropPreview.visible);
frame = UpdateUIEditorDockHostInteraction(
state,
controller,
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
{ MakePointerMove(probeMovePoint.x, probeMovePoint.y) });
EXPECT_FALSE(frame.result.requestPointerCapture);
EXPECT_FALSE(frame.result.releasePointerCapture);
EXPECT_FALSE(frame.result.layoutChanged);
EXPECT_TRUE(state.activeTabDragNodeId.empty());
EXPECT_TRUE(state.activeTabDragPanelId.empty());
EXPECT_FALSE(state.dockHostState.dropPreview.visible);
}
TEST(UIEditorDockHostInteractionTest, ClickingTabActivatesTargetPanel) {
auto controller =
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());