2026-04-04 01:28:53 +08:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
|
|
#include "Viewport/SceneViewportNavigation.h"
|
|
|
|
|
|
|
|
|
|
using XCEngine::Editor::BuildSceneViewportCaptureFlags;
|
|
|
|
|
using XCEngine::Editor::BuildSceneViewportInput;
|
|
|
|
|
using XCEngine::Editor::BuildSceneViewportToolShortcutAction;
|
|
|
|
|
using XCEngine::Editor::CanResolveSceneViewportInteraction;
|
|
|
|
|
using XCEngine::Editor::SceneViewportCaptureRequest;
|
|
|
|
|
using XCEngine::Editor::SceneViewportInputBuildRequest;
|
|
|
|
|
using XCEngine::Editor::SceneViewportNavigationRequest;
|
|
|
|
|
using XCEngine::Editor::SceneViewportNavigationState;
|
|
|
|
|
using XCEngine::Editor::SceneViewportToolMode;
|
|
|
|
|
using XCEngine::Editor::SceneViewportToolShortcutRequest;
|
|
|
|
|
using XCEngine::Editor::UpdateSceneViewportNavigationState;
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, ToolShortcutActionIgnoresShortcutsDuringTextInputOrDrag) {
|
|
|
|
|
SceneViewportToolShortcutRequest request = {};
|
|
|
|
|
request.pressedMove = true;
|
|
|
|
|
request.wantTextInput = true;
|
|
|
|
|
EXPECT_FALSE(BuildSceneViewportToolShortcutAction(request).HasAction());
|
|
|
|
|
|
|
|
|
|
request.wantTextInput = false;
|
|
|
|
|
request.lookDragging = true;
|
|
|
|
|
EXPECT_FALSE(BuildSceneViewportToolShortcutAction(request).HasAction());
|
|
|
|
|
|
|
|
|
|
request.lookDragging = false;
|
|
|
|
|
request.panDragging = true;
|
|
|
|
|
EXPECT_FALSE(BuildSceneViewportToolShortcutAction(request).HasAction());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, ToolShortcutActionMapsShortcutTargetsAndCancelFlags) {
|
|
|
|
|
SceneViewportToolShortcutRequest request = {};
|
|
|
|
|
request.pressedViewMove = true;
|
|
|
|
|
const auto viewMoveAction = BuildSceneViewportToolShortcutAction(request);
|
|
|
|
|
EXPECT_TRUE(viewMoveAction.HasAction());
|
|
|
|
|
EXPECT_EQ(viewMoveAction.targetTool, SceneViewportToolMode::ViewMove);
|
|
|
|
|
EXPECT_TRUE(viewMoveAction.cancelMoveGizmo);
|
|
|
|
|
EXPECT_TRUE(viewMoveAction.cancelRotateGizmo);
|
|
|
|
|
EXPECT_TRUE(viewMoveAction.cancelScaleGizmo);
|
|
|
|
|
|
|
|
|
|
request = {};
|
|
|
|
|
request.pressedRotate = true;
|
|
|
|
|
const auto rotateAction = BuildSceneViewportToolShortcutAction(request);
|
|
|
|
|
EXPECT_TRUE(rotateAction.HasAction());
|
|
|
|
|
EXPECT_EQ(rotateAction.targetTool, SceneViewportToolMode::Rotate);
|
|
|
|
|
EXPECT_TRUE(rotateAction.cancelMoveGizmo);
|
|
|
|
|
EXPECT_FALSE(rotateAction.cancelRotateGizmo);
|
|
|
|
|
EXPECT_TRUE(rotateAction.cancelScaleGizmo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, NavigationUpdateBeginsLookAndPanDrags) {
|
|
|
|
|
SceneViewportNavigationRequest lookRequest = {};
|
|
|
|
|
lookRequest.hasInteractiveViewport = true;
|
|
|
|
|
lookRequest.viewportHovered = true;
|
|
|
|
|
lookRequest.clickedRight = true;
|
|
|
|
|
lookRequest.rightMouseDown = true;
|
|
|
|
|
const auto lookUpdate = UpdateSceneViewportNavigationState(lookRequest);
|
|
|
|
|
EXPECT_TRUE(lookUpdate.beginLookDrag);
|
|
|
|
|
EXPECT_FALSE(lookUpdate.beginPanDrag);
|
|
|
|
|
EXPECT_TRUE(lookUpdate.state.lookDragging);
|
|
|
|
|
EXPECT_FALSE(lookUpdate.state.panDragging);
|
|
|
|
|
|
|
|
|
|
SceneViewportNavigationRequest panRequest = {};
|
|
|
|
|
panRequest.hasInteractiveViewport = true;
|
|
|
|
|
panRequest.viewportHovered = true;
|
|
|
|
|
panRequest.usingViewMoveTool = true;
|
|
|
|
|
panRequest.clickedLeft = true;
|
|
|
|
|
panRequest.leftMouseDown = true;
|
|
|
|
|
const auto panUpdate = UpdateSceneViewportNavigationState(panRequest);
|
|
|
|
|
EXPECT_FALSE(panUpdate.beginLookDrag);
|
|
|
|
|
EXPECT_TRUE(panUpdate.beginPanDrag);
|
|
|
|
|
EXPECT_TRUE(panUpdate.beginLeftPanDrag);
|
|
|
|
|
EXPECT_TRUE(panUpdate.state.panDragging);
|
|
|
|
|
EXPECT_EQ(panUpdate.state.panDragButton, ImGuiMouseButton_Left);
|
2026-04-06 04:11:00 +08:00
|
|
|
|
|
|
|
|
SceneViewportNavigationRequest middlePanRequest = {};
|
|
|
|
|
middlePanRequest.hasInteractiveViewport = true;
|
|
|
|
|
middlePanRequest.viewportHovered = true;
|
|
|
|
|
middlePanRequest.clickedMiddle = true;
|
|
|
|
|
middlePanRequest.middleMouseDown = true;
|
|
|
|
|
const auto middlePanUpdate = UpdateSceneViewportNavigationState(middlePanRequest);
|
|
|
|
|
EXPECT_FALSE(middlePanUpdate.beginLookDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.beginPanDrag);
|
|
|
|
|
EXPECT_FALSE(middlePanUpdate.beginLeftPanDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.beginMiddlePanDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.state.panDragging);
|
|
|
|
|
EXPECT_EQ(middlePanUpdate.state.panDragButton, ImGuiMouseButton_Middle);
|
2026-04-04 01:28:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-08 16:09:15 +08:00
|
|
|
TEST(SceneViewportNavigationTest, NavigationUpdateBeginsMiddlePanWhenHeldButtonMissedClickEdge) {
|
|
|
|
|
SceneViewportNavigationRequest middlePanRequest = {};
|
|
|
|
|
middlePanRequest.hasInteractiveViewport = true;
|
|
|
|
|
middlePanRequest.viewportHovered = true;
|
|
|
|
|
middlePanRequest.middleMouseDown = true;
|
|
|
|
|
|
|
|
|
|
const auto middlePanUpdate = UpdateSceneViewportNavigationState(middlePanRequest);
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(middlePanUpdate.beginLookDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.beginPanDrag);
|
|
|
|
|
EXPECT_FALSE(middlePanUpdate.beginLeftPanDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.beginMiddlePanDrag);
|
|
|
|
|
EXPECT_TRUE(middlePanUpdate.state.panDragging);
|
|
|
|
|
EXPECT_EQ(middlePanUpdate.state.panDragButton, ImGuiMouseButton_Middle);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 01:28:53 +08:00
|
|
|
TEST(SceneViewportNavigationTest, NavigationUpdateEndsDragsWhenButtonsRelease) {
|
|
|
|
|
SceneViewportNavigationRequest lookRequest = {};
|
|
|
|
|
lookRequest.state.lookDragging = true;
|
|
|
|
|
const auto lookUpdate = UpdateSceneViewportNavigationState(lookRequest);
|
|
|
|
|
EXPECT_FALSE(lookUpdate.state.lookDragging);
|
|
|
|
|
|
|
|
|
|
SceneViewportNavigationRequest panRequest = {};
|
|
|
|
|
panRequest.state.panDragging = true;
|
|
|
|
|
panRequest.state.panDragButton = ImGuiMouseButton_Left;
|
|
|
|
|
const auto panUpdate = UpdateSceneViewportNavigationState(panRequest);
|
|
|
|
|
EXPECT_FALSE(panUpdate.state.panDragging);
|
|
|
|
|
EXPECT_EQ(panUpdate.state.panDragButton, ImGuiMouseButton_Middle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, CaptureFlagsTrackNavigationAndActiveGizmos) {
|
|
|
|
|
SceneViewportCaptureRequest request = {};
|
|
|
|
|
request.state.lookDragging = true;
|
|
|
|
|
const auto lookFlags = BuildSceneViewportCaptureFlags(request);
|
|
|
|
|
EXPECT_TRUE(lookFlags.captureMouse);
|
|
|
|
|
EXPECT_TRUE(lookFlags.captureKeyboard);
|
|
|
|
|
|
|
|
|
|
request = {};
|
|
|
|
|
request.rotateGizmoActive = true;
|
|
|
|
|
const auto gizmoFlags = BuildSceneViewportCaptureFlags(request);
|
|
|
|
|
EXPECT_TRUE(gizmoFlags.captureMouse);
|
|
|
|
|
EXPECT_FALSE(gizmoFlags.captureKeyboard);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, BuildInputRoutesWheelFocusMovementAndMouseDelta) {
|
|
|
|
|
SceneViewportInputBuildRequest request = {};
|
2026-04-08 16:09:15 +08:00
|
|
|
request.viewportSize = XCEngine::UI::UISize(640.0f, 360.0f);
|
2026-04-04 01:28:53 +08:00
|
|
|
request.viewportHovered = true;
|
|
|
|
|
request.viewportFocused = true;
|
|
|
|
|
request.mouseWheel = 2.0f;
|
|
|
|
|
const auto zoomInput = BuildSceneViewportInput(request);
|
|
|
|
|
EXPECT_FLOAT_EQ(zoomInput.mouseWheel, 2.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(zoomInput.flySpeedDelta, 0.0f);
|
|
|
|
|
EXPECT_FALSE(zoomInput.looking);
|
|
|
|
|
EXPECT_TRUE(zoomInput.focused);
|
|
|
|
|
|
|
|
|
|
request = {};
|
|
|
|
|
request.state.lookDragging = true;
|
2026-04-08 16:09:15 +08:00
|
|
|
request.viewportSize = XCEngine::UI::UISize(640.0f, 360.0f);
|
2026-04-04 01:28:53 +08:00
|
|
|
request.viewportHovered = true;
|
|
|
|
|
request.mouseWheel = 1.5f;
|
2026-04-08 16:09:15 +08:00
|
|
|
request.mouseDelta = XCEngine::UI::UIPoint(5.0f, -3.0f);
|
2026-04-04 01:28:53 +08:00
|
|
|
request.fastMove = true;
|
|
|
|
|
request.focusSelectionKeyPressed = true;
|
|
|
|
|
request.moveForwardKeyDown = true;
|
|
|
|
|
request.moveRightKeyDown = true;
|
|
|
|
|
request.moveUpKeyDown = true;
|
|
|
|
|
const auto flyInput = BuildSceneViewportInput(request);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.mouseWheel, 0.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.flySpeedDelta, 1.5f);
|
|
|
|
|
EXPECT_TRUE(flyInput.looking);
|
|
|
|
|
EXPECT_TRUE(flyInput.fastMove);
|
|
|
|
|
EXPECT_TRUE(flyInput.focused);
|
|
|
|
|
EXPECT_TRUE(flyInput.focusSelectionRequested);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.moveForward, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.moveRight, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.moveUp, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.mouseDelta.x, 5.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(flyInput.mouseDelta.y, -3.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SceneViewportNavigationTest, CanResolveInteractionRequiresHoverWithoutNavigationOrActiveGizmo) {
|
|
|
|
|
SceneViewportNavigationState state = {};
|
|
|
|
|
EXPECT_TRUE(CanResolveSceneViewportInteraction(true, true, false, state, false));
|
|
|
|
|
|
|
|
|
|
state.lookDragging = true;
|
|
|
|
|
EXPECT_FALSE(CanResolveSceneViewportInteraction(true, true, false, state, false));
|
|
|
|
|
|
|
|
|
|
state = {};
|
|
|
|
|
EXPECT_FALSE(CanResolveSceneViewportInteraction(true, true, true, state, false));
|
|
|
|
|
EXPECT_FALSE(CanResolveSceneViewportInteraction(true, true, false, state, true));
|
|
|
|
|
EXPECT_FALSE(CanResolveSceneViewportInteraction(false, true, false, state, false));
|
|
|
|
|
}
|