Refine new_editor scene viewport flow

This commit is contained in:
2026-04-18 23:56:17 +08:00
parent f544b2792b
commit 7dfdda2b11
15 changed files with 1425 additions and 296 deletions

View File

@@ -6,6 +6,7 @@
#include <XCEditor/App/EditorPanelIds.h>
#include <XCEditor/Viewport/UIEditorViewportInputBridge.h>
#include <XCEditor/Viewport/UIEditorViewportSlot.h>
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
@@ -30,6 +31,7 @@ using ::XCEngine::Components::SceneManager;
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIInputModifiers;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIPointerButton;
using ::XCEngine::UI::UIRect;
@@ -113,6 +115,45 @@ UIInputEvent MakePointerEvent(
return event;
}
UIInputEvent MakePointerEventWithModifiers(
UIInputEventType type,
float x,
float y,
const UIInputModifiers& modifiers,
UIPointerButton button = UIPointerButton::None) {
UIInputEvent event = {};
event.type = type;
event.position = UIPoint(x, y);
event.pointerButton = button;
event.modifiers = modifiers;
return event;
}
UIInputModifiers MakePointerModifiers(UIPointerButton button) {
UIInputModifiers modifiers = {};
switch (button) {
case UIPointerButton::Left:
modifiers.leftMouse = true;
break;
case UIPointerButton::Right:
modifiers.rightMouse = true;
break;
case UIPointerButton::Middle:
modifiers.middleMouse = true;
break;
case UIPointerButton::X1:
modifiers.x1Mouse = true;
break;
case UIPointerButton::X2:
modifiers.x2Mouse = true;
break;
case UIPointerButton::None:
default:
break;
}
return modifiers;
}
UIInputEvent MakeWheelEvent(float x, float y, float wheelDelta) {
UIInputEvent event = {};
event.type = UIInputEventType::PointerWheel;
@@ -436,10 +477,11 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 220.0f, 180.0f),
MakePointerEvent(
MakePointerEventWithModifiers(
UIInputEventType::PointerButtonDown,
220.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Right),
UIPointerButton::Right)
});
controller.Update(
@@ -452,7 +494,11 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC
inputBridgeState,
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 280.0f, 220.0f)
MakePointerEventWithModifiers(
UIInputEventType::PointerMove,
280.0f,
220.0f,
MakePointerModifiers(UIPointerButton::Right))
});
controller.Update(
runtime,
@@ -515,10 +561,11 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics)
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 220.0f, 180.0f),
MakePointerEvent(
MakePointerEventWithModifiers(
UIInputEventType::PointerButtonDown,
220.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Middle),
UIPointerButton::Middle)
});
controller.Update(
@@ -531,7 +578,70 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics)
inputBridgeState,
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 280.0f, 180.0f)
MakePointerEventWithModifiers(
UIInputEventType::PointerMove,
280.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Middle))
});
controller.Update(
runtime,
viewportHostService,
BuildSceneComposeState(inputBridgeState),
BuildSceneComposeFrame(dragFrame, inputRect, viewportSize));
const Math::Vector3 after = transform->GetPosition();
EXPECT_LT(after.x, before.x);
}
TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSemantics) {
ScopedSceneManagerReset reset = {};
TemporaryProjectRoot projectRoot = {};
SaveMainScene(projectRoot, Math::Vector3(0.0f, 0.0f, 0.0f));
EditorSceneRuntime runtime = {};
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
runtime.SetToolMode(SceneToolMode::View);
auto* camera = runtime.GetSceneViewCamera();
ASSERT_NE(camera, nullptr);
auto* transform = camera->GetGameObject()->GetTransform();
ASSERT_NE(transform, nullptr);
const Math::Vector3 before = transform->GetPosition();
SceneViewportController controller = {};
ViewportHostService viewportHostService = {};
UIEditorViewportInputBridgeState inputBridgeState = {};
const UIRect inputRect(100.0f, 80.0f, 640.0f, 360.0f);
const UISize viewportSize(640.0f, 360.0f);
const auto pressFrame = UpdateUIEditorViewportInputBridge(
inputBridgeState,
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 220.0f, 180.0f),
MakePointerEventWithModifiers(
UIInputEventType::PointerButtonDown,
220.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Left),
UIPointerButton::Left)
});
controller.Update(
runtime,
viewportHostService,
BuildSceneComposeState(inputBridgeState),
BuildSceneComposeFrame(pressFrame, inputRect, viewportSize));
const auto dragFrame = UpdateUIEditorViewportInputBridge(
inputBridgeState,
inputRect,
{
MakePointerEventWithModifiers(
UIInputEventType::PointerMove,
280.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Left))
});
controller.Update(
runtime,
@@ -610,10 +720,11 @@ TEST(SceneViewportRuntimeTests, ToolShortcutSwitchesFocusedSceneViewportIntoTran
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, 220.0f, 180.0f),
MakePointerEvent(
MakePointerEventWithModifiers(
UIInputEventType::PointerButtonDown,
220.0f,
180.0f,
MakePointerModifiers(UIPointerButton::Left),
UIPointerButton::Left),
MakePointerEvent(
UIInputEventType::PointerButtonUp,
@@ -646,5 +757,130 @@ TEST(SceneViewportRuntimeTests, ToolShortcutSwitchesFocusedSceneViewportIntoTran
EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Translate);
}
TEST(SceneViewportRuntimeTests, SceneToolOverlayClickSwitchesModeOnPointerDown) {
ScopedSceneManagerReset reset = {};
TemporaryProjectRoot projectRoot = {};
SaveMainScene(projectRoot, Math::Vector3(0.0f, 0.0f, 0.0f));
EditorSceneRuntime runtime = {};
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
runtime.SetToolMode(SceneToolMode::Translate);
SceneViewportController controller = {};
ViewportHostService viewportHostService = {};
UIEditorViewportInputBridgeState inputBridgeState = {};
const UIRect inputRect(100.0f, 104.0f, 640.0f, 360.0f);
const UISize viewportSize(640.0f, 360.0f);
const UIPoint rotateButtonCenter(
inputRect.x + 28.0f,
inputRect.y + 82.0f);
const auto frame = UpdateUIEditorViewportInputBridge(
inputBridgeState,
inputRect,
{
MakePointerEvent(UIInputEventType::PointerMove, rotateButtonCenter.x, rotateButtonCenter.y),
MakePointerEvent(
UIInputEventType::PointerButtonDown,
rotateButtonCenter.x,
rotateButtonCenter.y,
UIPointerButton::Left)
});
controller.Update(
runtime,
viewportHostService,
BuildSceneComposeState(inputBridgeState),
BuildSceneComposeFrame(frame, inputRect, viewportSize));
EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Rotate);
}
TEST(SceneViewportRuntimeTests, SceneToolOverlayIncludesTransformButtonAndSwitchesModeOnPointerDown) {
ScopedSceneManagerReset reset = {};
TemporaryProjectRoot projectRoot = {};
SaveMainScene(projectRoot, Math::Vector3(0.0f, 0.0f, 0.0f));
EditorSceneRuntime runtime = {};
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
runtime.SetToolMode(SceneToolMode::Translate);
SceneViewportController controller = {};
ViewportHostService viewportHostService = {};
UIEditorViewportInputBridgeState inputBridgeState = {};
const UIRect inputRect(100.0f, 104.0f, 640.0f, 360.0f);
const UISize viewportSize(640.0f, 360.0f);
const UIPoint transformButtonCenter(
inputRect.x + 28.0f,
inputRect.y + 148.0f);
const auto frame = UpdateUIEditorViewportInputBridge(
inputBridgeState,
inputRect,
{
MakePointerEvent(
UIInputEventType::PointerMove,
transformButtonCenter.x,
transformButtonCenter.y),
MakePointerEvent(
UIInputEventType::PointerButtonDown,
transformButtonCenter.x,
transformButtonCenter.y,
UIPointerButton::Left)
});
controller.Update(
runtime,
viewportHostService,
BuildSceneComposeState(inputBridgeState),
BuildSceneComposeFrame(frame, inputRect, viewportSize));
EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Transform);
}
TEST(SceneViewportRuntimeTests, SceneToolOverlayHandlesCoalescedClickInSingleFrame) {
ScopedSceneManagerReset reset = {};
TemporaryProjectRoot projectRoot = {};
SaveMainScene(projectRoot, Math::Vector3(0.0f, 0.0f, 0.0f));
EditorSceneRuntime runtime = {};
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
runtime.SetToolMode(SceneToolMode::Translate);
SceneViewportController controller = {};
ViewportHostService viewportHostService = {};
UIEditorViewportInputBridgeState inputBridgeState = {};
const UIRect inputRect(100.0f, 104.0f, 640.0f, 360.0f);
const UISize viewportSize(640.0f, 360.0f);
const UIPoint rotateButtonCenter(
inputRect.x + 28.0f,
inputRect.y + 82.0f);
const auto frame = UpdateUIEditorViewportInputBridge(
inputBridgeState,
inputRect,
{
MakePointerEvent(
UIInputEventType::PointerMove,
rotateButtonCenter.x,
rotateButtonCenter.y),
MakePointerEvent(
UIInputEventType::PointerButtonDown,
rotateButtonCenter.x,
rotateButtonCenter.y,
UIPointerButton::Left),
MakePointerEvent(
UIInputEventType::PointerButtonUp,
rotateButtonCenter.x,
rotateButtonCenter.y,
UIPointerButton::Left)
});
controller.Update(
runtime,
viewportHostService,
BuildSceneComposeState(inputBridgeState),
BuildSceneComposeFrame(frame, inputRect, viewportSize));
EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Rotate);
}
} // namespace
} // namespace XCEngine::UI::Editor::App