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

@@ -12,7 +12,6 @@
#include <XCEngine/Input/InputTypes.h>
#include <algorithm>
#include <string_view>
namespace XCEngine::UI::Editor::App {
@@ -22,10 +21,6 @@ using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIPointerButton;
using ::XCEngine::UI::UIRect;
using ::XCEngine::UI::Editor::Widgets::HitTestUIEditorViewportSlot;
using ::XCEngine::UI::Editor::Widgets::UIEditorViewportSlotHitTarget;
using ::XCEngine::UI::Editor::Widgets::UIEditorViewportSlotHitTargetKind;
constexpr float kWheelDeltaPerStep = 120.0f;
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
@@ -69,6 +64,17 @@ float NormalizeWheelDelta(float wheelDelta) {
return wheelDelta / kWheelDeltaPerStep;
}
bool IsViewMoveTool(SceneToolMode mode) {
return mode == SceneToolMode::View;
}
bool IsPanDragButtonDown(
const UIEditorViewportInputBridgeState& state,
UIPointerButton button) {
return button != UIPointerButton::None &&
IsUIEditorViewportInputBridgePointerButtonDown(state, button);
}
bool TryResolveToolModeShortcut(
const UIEditorViewportInputBridgeFrame& inputFrame,
SceneToolMode& outMode) {
@@ -98,29 +104,6 @@ bool TryResolveToolModeShortcut(
return false;
}
bool ApplySceneToolbarCommand(
std::string_view itemId,
EditorSceneRuntime& sceneRuntime) {
if (itemId == "scene.pivot.pivot") {
sceneRuntime.SetToolPivotMode(SceneToolPivotMode::Pivot);
return true;
}
if (itemId == "scene.pivot.center") {
sceneRuntime.SetToolPivotMode(SceneToolPivotMode::Center);
return true;
}
if (itemId == "scene.space.world") {
sceneRuntime.SetToolSpaceMode(SceneToolSpaceMode::World);
return true;
}
if (itemId == "scene.space.local") {
sceneRuntime.SetToolSpaceMode(SceneToolSpaceMode::Local);
return true;
}
return false;
}
void ApplySceneToolMode(
SceneToolMode mode,
EditorSceneRuntime& sceneRuntime,
@@ -166,7 +149,6 @@ void SceneViewportController::Update(
if (m_legacyGizmo.IsActive()) {
m_legacyGizmo.CancelDrag(sceneRuntime);
}
sceneRuntime.ClearToolbarInteraction();
sceneRuntime.SetHoveredToolHandle(SceneToolHandle::None);
ResetInteractionState();
return;
@@ -175,7 +157,6 @@ void SceneViewportController::Update(
const auto& inputFrame = viewportFrame->viewportShellFrame.inputFrame;
const auto& inputState = panelState->viewportShellState.inputBridgeState;
const auto& slotLayout = viewportFrame->viewportShellFrame.slotLayout;
const auto& toolItems = viewportFrame->viewportShellModel.spec.toolItems;
const bool leftMouseDown = IsUIEditorViewportInputBridgePointerButtonDown(
inputState,
UIPointerButton::Left);
@@ -196,55 +177,8 @@ void SceneViewportController::Update(
if (m_legacyGizmo.IsActive()) {
m_legacyGizmo.CancelDrag(sceneRuntime);
}
sceneRuntime.ClearToolbarInteraction();
}
std::size_t hoveredToolbarIndex = kSceneToolInvalidToolbarIndex;
if (!toolItems.empty() && inputState.hasPointerPosition) {
const UIEditorViewportSlotHitTarget toolbarHit =
HitTestUIEditorViewportSlot(slotLayout, pointerScreen);
if (toolbarHit.kind == UIEditorViewportSlotHitTargetKind::ToolItem &&
toolbarHit.index < toolItems.size() &&
toolItems[toolbarHit.index].enabled) {
hoveredToolbarIndex = toolbarHit.index;
}
}
if (m_legacyGizmo.IsActive()) {
sceneRuntime.ClearToolbarInteraction();
} else {
sceneRuntime.SetToolbarHoveredIndex(hoveredToolbarIndex);
if (inputFrame.changedPointerButton == UIPointerButton::Left &&
leftMouseDown &&
hoveredToolbarIndex != kSceneToolInvalidToolbarIndex) {
sceneRuntime.SetToolbarActiveIndex(hoveredToolbarIndex);
sceneRuntime.SetToolInteractionLock(SceneToolInteractionLock::Toolbar);
} else if (inputFrame.changedPointerButton == UIPointerButton::Left &&
!leftMouseDown) {
const std::size_t activeToolbarIndex =
sceneRuntime.GetToolState().toolbarActiveIndex;
const bool applyCommand =
activeToolbarIndex != kSceneToolInvalidToolbarIndex &&
activeToolbarIndex == hoveredToolbarIndex &&
activeToolbarIndex < toolItems.size() &&
toolItems[activeToolbarIndex].enabled;
sceneRuntime.ClearToolbarInteraction();
if (applyCommand) {
ApplySceneToolbarCommand(
toolItems[activeToolbarIndex].itemId,
sceneRuntime);
}
} else if (!leftMouseDown &&
sceneRuntime.GetToolState().interactionLock ==
SceneToolInteractionLock::Toolbar) {
sceneRuntime.ClearToolbarInteraction();
}
}
const bool toolbarInteractionActive =
sceneRuntime.GetToolState().interactionLock ==
SceneToolInteractionLock::Toolbar;
m_toolOverlay.BuildFrame(
slotLayout.inputRect,
sceneRuntime.GetToolMode(),
@@ -258,24 +192,30 @@ void SceneViewportController::Update(
? m_toolOverlay.HitTest(pointerScreen)
: kSceneViewportToolOverlayInvalidIndex;
if (inputFrame.changedPointerButton == UIPointerButton::Left &&
leftMouseDown &&
hoveredToolOverlayIndex != kSceneViewportToolOverlayInvalidIndex &&
!toolbarInteractionActive &&
!m_legacyGizmo.IsActive()) {
m_activeToolOverlayIndex = hoveredToolOverlayIndex;
} else if (inputFrame.changedPointerButton == UIPointerButton::Left &&
!leftMouseDown) {
if (m_activeToolOverlayIndex != kSceneViewportToolOverlayInvalidIndex &&
m_activeToolOverlayIndex == hoveredToolOverlayIndex &&
m_activeToolOverlayIndex < m_toolOverlay.GetFrame().buttons.size()) {
ApplySceneToolMode(
m_toolOverlay.GetFrame().buttons[m_activeToolOverlayIndex].mode,
sceneRuntime,
m_legacyGizmo);
for (const auto& transition : inputFrame.pointerButtonTransitions) {
if (transition.button != UIPointerButton::Left) {
continue;
}
const std::size_t transitionHoveredToolOverlayIndex =
m_toolOverlay.HitTest(transition.screenPosition);
if (transition.pressed &&
transitionHoveredToolOverlayIndex !=
kSceneViewportToolOverlayInvalidIndex &&
!m_legacyGizmo.IsActive()) {
m_activeToolOverlayIndex = transitionHoveredToolOverlayIndex;
if (m_activeToolOverlayIndex < m_toolOverlay.GetFrame().buttons.size()) {
ApplySceneToolMode(
m_toolOverlay.GetFrame().buttons[m_activeToolOverlayIndex].mode,
sceneRuntime,
m_legacyGizmo);
}
continue;
}
m_activeToolOverlayIndex = kSceneViewportToolOverlayInvalidIndex;
} else if (!leftMouseDown) {
}
if (!leftMouseDown) {
m_activeToolOverlayIndex = kSceneViewportToolOverlayInvalidIndex;
}
m_hoveredToolOverlayIndex = hoveredToolOverlayIndex;
@@ -289,43 +229,55 @@ void SceneViewportController::Update(
const bool toolOverlayInteractionActive =
m_activeToolOverlayIndex != kSceneViewportToolOverlayInvalidIndex;
if (!toolbarInteractionActive &&
!toolOverlayInteractionActive &&
!m_legacyGizmo.IsActive()) {
if (inputFrame.pointerPressedInside &&
inputFrame.changedPointerButton == UIPointerButton::Right &&
!pointerOverToolOverlay) {
m_navigationState.lookDragging = true;
m_navigationState.panDragging = false;
}
if (inputFrame.pointerPressedInside &&
inputFrame.changedPointerButton == UIPointerButton::Middle &&
!pointerOverToolOverlay) {
m_navigationState.panDragging = true;
m_navigationState.lookDragging = false;
const bool usingViewMoveTool = IsViewMoveTool(sceneRuntime.GetToolMode());
if (!m_legacyGizmo.IsActive()) {
for (const auto& transition : inputFrame.pointerButtonTransitions) {
if (!transition.pressed ||
!ContainsPoint(slotLayout.inputRect, transition.screenPosition) ||
m_toolOverlay.Contains(transition.screenPosition)) {
continue;
}
if (usingViewMoveTool &&
transition.button == UIPointerButton::Left) {
m_navigationState.panDragging = true;
m_navigationState.lookDragging = false;
m_navigationState.panDragButton = UIPointerButton::Left;
} else if (transition.button == UIPointerButton::Right) {
m_navigationState.lookDragging = true;
m_navigationState.panDragging = false;
m_navigationState.panDragButton = UIPointerButton::None;
} else if (transition.button == UIPointerButton::Middle) {
m_navigationState.panDragging = true;
m_navigationState.lookDragging = false;
m_navigationState.panDragButton = UIPointerButton::Middle;
}
}
}
if (m_navigationState.lookDragging && !rightMouseDown) {
m_navigationState.lookDragging = false;
}
if (m_navigationState.panDragging && !middleMouseDown) {
if (m_navigationState.panDragging &&
!IsPanDragButtonDown(inputState, m_navigationState.panDragButton)) {
m_navigationState.panDragging = false;
m_navigationState.panDragButton = UIPointerButton::None;
}
const bool viewportHoverEligible =
inputState.hasPointerPosition &&
ContainsPoint(slotLayout.inputRect, pointerScreen) &&
!pointerOverToolOverlay &&
!toolbarInteractionActive &&
!toolOverlayInteractionActive &&
!m_navigationState.lookDragging &&
!m_navigationState.panDragging;
if (!m_legacyGizmo.IsActive() &&
!toolbarInteractionActive &&
!toolOverlayInteractionActive &&
inputFrame.focused) {
inputFrame.focused &&
!m_navigationState.lookDragging &&
!m_navigationState.panDragging) {
SceneToolMode shortcutMode = SceneToolMode::View;
if (TryResolveToolModeShortcut(inputFrame, shortcutMode)) {
ApplySceneToolMode(shortcutMode, sceneRuntime, m_legacyGizmo);
@@ -356,8 +308,7 @@ void SceneViewportController::Update(
return;
}
if (inputFrame.changedPointerButton == UIPointerButton::Left &&
!leftMouseDown) {
if (!leftMouseDown) {
m_legacyGizmo.EndDrag(sceneRuntime);
m_legacyGizmo.Refresh(
sceneRuntime,
@@ -376,48 +327,52 @@ void SceneViewportController::Update(
return;
}
const bool shouldStartTransformDrag =
inputFrame.pointerPressedInside &&
inputFrame.changedPointerButton == UIPointerButton::Left &&
viewportHoverEligible &&
m_legacyGizmo.IsHoveringHandle();
if (shouldStartTransformDrag &&
m_legacyGizmo.TryBeginDrag(sceneRuntime)) {
m_navigationState = {};
m_legacyGizmo.Refresh(
sceneRuntime,
slotLayout.inputRect,
pointerScreen,
viewportHoverEligible);
return;
}
const bool shouldPickSelection =
inputFrame.pointerPressedInside &&
inputFrame.changedPointerButton == UIPointerButton::Left &&
viewportHoverEligible &&
!m_legacyGizmo.IsHoveringHandle();
if (shouldPickSelection) {
const ViewportObjectIdPickResult pickResult =
viewportHostService.PickSceneViewportObject(
viewportFrame->viewportShellFrame.requestedViewportSize,
inputFrame.localPointerPosition);
if (pickResult.status == ViewportObjectIdPickStatus::Success) {
if (pickResult.entityId != 0u) {
sceneRuntime.SetSelection(pickResult.entityId);
} else {
sceneRuntime.ClearSelection();
if (!usingViewMoveTool) {
for (const auto& transition : inputFrame.pointerButtonTransitions) {
if (!transition.pressed ||
transition.button != UIPointerButton::Left ||
!ContainsPoint(slotLayout.inputRect, transition.screenPosition) ||
m_toolOverlay.Contains(transition.screenPosition)) {
continue;
}
}
m_legacyGizmo.Refresh(
sceneRuntime,
slotLayout.inputRect,
pointerScreen,
viewportHoverEligible);
m_legacyGizmo.Refresh(
sceneRuntime,
slotLayout.inputRect,
transition.screenPosition,
true);
if (m_legacyGizmo.IsHoveringHandle() &&
m_legacyGizmo.TryBeginDrag(sceneRuntime)) {
m_navigationState = {};
m_legacyGizmo.Refresh(
sceneRuntime,
slotLayout.inputRect,
pointerScreen,
viewportHoverEligible);
return;
}
const ViewportObjectIdPickResult pickResult =
viewportHostService.PickSceneViewportObject(
viewportFrame->viewportShellFrame.requestedViewportSize,
transition.localPointerPosition);
if (pickResult.status == ViewportObjectIdPickStatus::Success) {
if (pickResult.entityId != 0u) {
sceneRuntime.SetSelection(pickResult.entityId);
} else {
sceneRuntime.ClearSelection();
}
}
m_legacyGizmo.Refresh(
sceneRuntime,
slotLayout.inputRect,
pointerScreen,
viewportHoverEligible);
}
}
if (toolbarInteractionActive || toolOverlayInteractionActive) {
if (toolOverlayInteractionActive) {
return;
}
@@ -443,7 +398,7 @@ void SceneViewportController::Update(
} else if (m_navigationState.panDragging) {
input.panDeltaX = inputFrame.pointerDelta.x;
input.panDeltaY = inputFrame.pointerDelta.y;
} else if (inputFrame.hovered && !pointerOverToolOverlay) {
} else if (ContainsPoint(slotLayout.inputRect, pointerScreen) && !pointerOverToolOverlay) {
input.zoomDelta = NormalizeWheelDelta(inputFrame.wheelDelta);
}