diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs
new file mode 100644
index 0000000000..a318732dfd
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs
@@ -0,0 +1,143 @@
+#nullable enable
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+using io.github.hatayama.UnityCliLoop.Runtime;
+using io.github.hatayama.UnityCliLoop.ToolContracts;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// Executes pointer event mechanics shared by one-shot and incremental UI drags.
+ ///
+ internal static class MouseUiDragEventExecutor
+ {
+ internal static PointerEventData InitiateDrag(
+ EventSystem eventSystem,
+ Vector2 screenPos,
+ RaycastResult raycastResult,
+ GameObject dragTarget,
+ PointerEventData.InputButton inputButton)
+ {
+ PointerEventData pointerData = new(eventSystem)
+ {
+ position = screenPos,
+ pressPosition = screenPos,
+ button = inputButton,
+ pointerCurrentRaycast = raycastResult,
+ pointerPressRaycast = raycastResult,
+ pointerDrag = dragTarget,
+ rawPointerPress = raycastResult.gameObject
+ };
+
+ // Slider.OnPointerDown initializes m_Offset for handle positioning
+ GameObject? pressTarget = ExecuteEvents.ExecuteHierarchy(
+ raycastResult.gameObject, pointerData, ExecuteEvents.pointerDownHandler);
+ pointerData.pointerPress = pressTarget;
+
+ // ScrollRect.OnInitializePotentialDrag clears inertia, Slider sets useDragThreshold=false
+ ExecuteEvents.Execute(dragTarget, pointerData, ExecuteEvents.initializePotentialDrag);
+
+ return pointerData;
+ }
+
+ // Lifecycle must match StandaloneInputModule: raycast → pointerUp → drop → endDrag
+ internal static void FinalizeDrag(
+ PointerEventData pointerData,
+ GameObject target,
+ GameObject? explicitDropTarget)
+ {
+ if (explicitDropTarget != null)
+ {
+ pointerData.pointerCurrentRaycast = MouseUiPointerTargetResolver.CreateDirectRaycastResult(explicitDropTarget);
+ }
+ else
+ {
+ UpdatePointerRaycast(pointerData);
+ }
+
+ if (pointerData.pointerPress != null)
+ {
+ ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerUpHandler);
+ }
+
+ // Standard IDropHandler dispatch so Unity drop targets respond without manual workarounds
+ GameObject? dropTarget = pointerData.pointerCurrentRaycast.gameObject;
+ if (dropTarget != null)
+ {
+ ExecuteEvents.ExecuteHierarchy(dropTarget, pointerData, ExecuteEvents.dropHandler);
+ }
+
+ pointerData.dragging = false;
+ ExecuteEvents.Execute(target, pointerData, ExecuteEvents.endDragHandler);
+ }
+
+ private static void UpdatePointerRaycast(PointerEventData pointerData)
+ {
+ RaycastResult? hit = UiRaycastHelper.RaycastUI(pointerData.position, EventSystem.current);
+ pointerData.pointerCurrentRaycast = hit ?? new RaycastResult();
+ }
+
+ internal static async Task InterpolateDragPosition(
+ PointerEventData pointerData,
+ GameObject target,
+ Vector2 endPos,
+ float dragSpeed,
+ CancellationToken ct)
+ {
+ Debug.Assert(dragSpeed >= 0f, "dragSpeed must be non-negative");
+
+ Vector2 startPos = pointerData.position;
+ float distance = Vector2.Distance(startPos, endPos);
+ float duration = dragSpeed > 0f ? distance / dragSpeed : 0f;
+
+ if (duration <= 0f)
+ {
+ bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
+ if (!frameReady)
+ {
+ return false;
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ Vector2 previousPosition = pointerData.position;
+ pointerData.position = endPos;
+ pointerData.delta = endPos - previousPosition;
+ ExecuteEvents.Execute(target, pointerData, ExecuteEvents.dragHandler);
+
+ SimulateMouseUiOverlayState.UpdatePosition(MouseUiCoordinateConverter.ScreenToInput(endPos));
+ return true;
+ }
+
+ float startTime = Time.realtimeSinceStartup;
+ float t;
+
+ do
+ {
+ bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
+ if (!frameReady)
+ {
+ return false;
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ float elapsed = Time.realtimeSinceStartup - startTime;
+ t = Mathf.Clamp01(elapsed / duration);
+ Vector2 previousPosition = pointerData.position;
+ Vector2 currentPosition = Vector2.Lerp(startPos, endPos, t);
+
+ pointerData.position = currentPosition;
+ pointerData.delta = currentPosition - previousPosition;
+
+ ExecuteEvents.Execute(target, pointerData, ExecuteEvents.dragHandler);
+
+ SimulateMouseUiOverlayState.UpdatePosition(MouseUiCoordinateConverter.ScreenToInput(currentPosition));
+ }
+ while (t < 1.0f);
+
+ return true;
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs.meta b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs.meta
new file mode 100644
index 0000000000..abb02a6a7a
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiDragEventExecutor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6528ed09150a4ced91b1dffea8ada186
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs
new file mode 100644
index 0000000000..4b5b25e43a
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs
@@ -0,0 +1,158 @@
+#nullable enable
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+using io.github.hatayama.UnityCliLoop.Runtime;
+using io.github.hatayama.UnityCliLoop.ToolContracts;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// Executes a complete mouse UI drag in one request.
+ ///
+ internal static class MouseUiOneShotDragExecutor
+ {
+ internal static async Task ExecuteDragOneShot(
+ MouseUiSimulationCommand parameters,
+ EventSystem eventSystem,
+ MouseUiMainThreadCleanupScheduler cleanupScheduler,
+ CancellationToken ct)
+ {
+ Vector2 inputStart = new(parameters.FromX, parameters.FromY);
+ Vector2 inputEnd = new(parameters.X, parameters.Y);
+ Vector2 screenStart = MouseUiCoordinateConverter.InputToScreen(inputStart);
+ Vector2 screenEnd = MouseUiCoordinateConverter.InputToScreen(inputEnd);
+ RaycastResult? hit = parameters.BypassRaycast ? null : UiRaycastHelper.RaycastUI(screenStart, eventSystem);
+ RaycastResult startRaycast = new();
+ GameObject? rawTarget = null;
+
+ if (parameters.BypassRaycast)
+ {
+ if (!MouseUiPointerTargetResolver.TryResolveGameObjectPath(
+ parameters.TargetPath,
+ "TargetPath",
+ MouseAction.Drag,
+ inputStart,
+ out rawTarget,
+ out SimulateMouseUiResponse? failureResponse))
+ {
+ return failureResponse!;
+ }
+
+ startRaycast = MouseUiPointerTargetResolver.CreateDirectRaycastResult(rawTarget!);
+ }
+ else if (hit != null)
+ {
+ rawTarget = hit.Value.gameObject;
+ startRaycast = hit.Value;
+ }
+
+ GameObject? explicitDropTarget = null;
+ if (!MouseUiPointerTargetResolver.TryResolveDropTargetPath(
+ parameters,
+ MouseAction.Drag,
+ inputEnd,
+ out explicitDropTarget,
+ out SimulateMouseUiResponse? dropFailureResponse))
+ {
+ return dropFailureResponse!;
+ }
+
+ // Execute dispatches only to the exact target; resolve the actual drag handler up the hierarchy
+ GameObject? target = rawTarget != null
+ ? ExecuteEvents.GetEventHandler(rawTarget)
+ : null;
+
+ if (target == null)
+ {
+ SimulateMouseUiOverlayState.Update(
+ MouseAction.Drag, inputStart, null, null, Handles.GetMainGameViewSize());
+ bool expandCompleted = await MouseUiOverlayAnimator.PlayExpandAnimation(ct).ConfigureAwait(false);
+ if (!expandCompleted)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, null);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ bool dissipateCompleted = await MouseUiOverlayAnimator.PlayDissipateAnimation(ct).ConfigureAwait(false);
+ if (!dissipateCompleted)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, null);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ return new SimulateMouseUiResponse
+ {
+ Success = false,
+ Message = parameters.BypassRaycast
+ ? $"TargetPath '{parameters.TargetPath}' has no drag handler."
+ : $"No draggable UI element at ({inputStart.x:F1}, {inputStart.y:F1}). Use find-game-objects or screenshot to verify positions.",
+ Action = MouseAction.Drag.ToString(),
+ PositionX = inputStart.x,
+ PositionY = inputStart.y,
+ EndPositionX = inputEnd.x,
+ EndPositionY = inputEnd.y
+ };
+ }
+
+ // uGUI drag controls (ScrollRect, Slider) only respond to left-button drags
+ PointerEventData pointerData = MouseUiDragEventExecutor.InitiateDrag(eventSystem, screenStart, startRaycast, target, PointerEventData.InputButton.Left);
+ ExecuteEvents.Execute(target, pointerData, ExecuteEvents.beginDragHandler);
+ pointerData.dragging = true;
+
+ string targetName = target.name;
+ SimulateMouseUiOverlayState.Update(
+ MouseAction.Drag, inputStart, inputStart, targetName, Handles.GetMainGameViewSize());
+
+ try
+ {
+ bool expandCompleted = await MouseUiOverlayAnimator.PlayExpandAnimation(ct).ConfigureAwait(false);
+ if (!expandCompleted)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ bool dragCompleted = await MouseUiDragEventExecutor.InterpolateDragPosition(pointerData, target, screenEnd, parameters.DragSpeed, ct)
+ .ConfigureAwait(false);
+ if (!dragCompleted)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
+ if (!frameReady)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+ }
+ finally
+ {
+ cleanupScheduler.ExecuteCleanupOnMainThread(() => MouseUiDragEventExecutor.FinalizeDrag(pointerData, target, explicitDropTarget));
+ }
+
+ SimulateMouseUiOverlayState.Update(
+ MouseAction.Drag, inputEnd, inputStart, targetName, Handles.GetMainGameViewSize());
+
+ bool completedDissipate = await MouseUiOverlayAnimator.PlayDissipateAnimation(ct).ConfigureAwait(false);
+ if (!completedDissipate)
+ {
+ cleanupScheduler.QueueOverlayClear();
+ return MouseUiSimulationResponseFactory.CreateDragResult(parameters, inputStart, inputEnd, targetName);
+ }
+ await MainThreadSwitcher.SwitchToMainThread(ct);
+
+ return MouseUiSimulationResponseFactory.CreateDragResult(parameters, inputStart, inputEnd, targetName);
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs.meta b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs.meta
new file mode 100644
index 0000000000..fbc4f5ef41
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/MouseUiOneShotDragExecutor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0eff98014e504554a60bbee519a91190
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/SimulateMouseUiUseCase.cs b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/SimulateMouseUiUseCase.cs
index 4f30a03383..8bf22ca3aa 100644
--- a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/SimulateMouseUiUseCase.cs
+++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/SimulateMouseUiUseCase.cs
@@ -92,7 +92,11 @@ private async Task ExecuteMouseAction(
ct).ConfigureAwait(false);
case MouseAction.Drag:
- return await ExecuteDragOneShot(parameters, eventSystem, ct).ConfigureAwait(false);
+ return await MouseUiOneShotDragExecutor.ExecuteDragOneShot(
+ parameters,
+ eventSystem,
+ _mainThreadCleanupScheduler,
+ ct).ConfigureAwait(false);
case MouseAction.DragStart:
return await ExecuteDragStart(parameters, eventSystem, ct).ConfigureAwait(false);
@@ -119,266 +123,6 @@ private async Task ExecuteMouseAction(
}
}
- private PointerEventData InitiateDrag(
- EventSystem eventSystem,
- Vector2 screenPos,
- RaycastResult raycastResult,
- GameObject dragTarget,
- PointerEventData.InputButton inputButton)
- {
- PointerEventData pointerData = new(eventSystem)
- {
- position = screenPos,
- pressPosition = screenPos,
- button = inputButton,
- pointerCurrentRaycast = raycastResult,
- pointerPressRaycast = raycastResult,
- pointerDrag = dragTarget,
- rawPointerPress = raycastResult.gameObject
- };
-
- // Slider.OnPointerDown initializes m_Offset for handle positioning
- GameObject? pressTarget = ExecuteEvents.ExecuteHierarchy(
- raycastResult.gameObject, pointerData, ExecuteEvents.pointerDownHandler);
- pointerData.pointerPress = pressTarget;
-
- // ScrollRect.OnInitializePotentialDrag clears inertia, Slider sets useDragThreshold=false
- ExecuteEvents.Execute(dragTarget, pointerData, ExecuteEvents.initializePotentialDrag);
-
- return pointerData;
- }
-
- private async Task ExecuteDragOneShot(
- MouseUiSimulationCommand parameters, EventSystem eventSystem, CancellationToken ct)
- {
- Vector2 inputStart = new(parameters.FromX, parameters.FromY);
- Vector2 inputEnd = new(parameters.X, parameters.Y);
- Vector2 screenStart = MouseUiCoordinateConverter.InputToScreen(inputStart);
- Vector2 screenEnd = MouseUiCoordinateConverter.InputToScreen(inputEnd);
- RaycastResult? hit = parameters.BypassRaycast ? null : UiRaycastHelper.RaycastUI(screenStart, eventSystem);
- RaycastResult startRaycast = new();
- GameObject? rawTarget = null;
-
- if (parameters.BypassRaycast)
- {
- if (!MouseUiPointerTargetResolver.TryResolveGameObjectPath(
- parameters.TargetPath,
- "TargetPath",
- MouseAction.Drag,
- inputStart,
- out rawTarget,
- out SimulateMouseUiResponse? failureResponse))
- {
- return failureResponse!;
- }
-
- startRaycast = MouseUiPointerTargetResolver.CreateDirectRaycastResult(rawTarget!);
- }
- else if (hit != null)
- {
- rawTarget = hit.Value.gameObject;
- startRaycast = hit.Value;
- }
-
- GameObject? explicitDropTarget = null;
- if (!MouseUiPointerTargetResolver.TryResolveDropTargetPath(
- parameters,
- MouseAction.Drag,
- inputEnd,
- out explicitDropTarget,
- out SimulateMouseUiResponse? dropFailureResponse))
- {
- return dropFailureResponse!;
- }
-
- // Execute dispatches only to the exact target; resolve the actual drag handler up the hierarchy
- GameObject? target = rawTarget != null
- ? ExecuteEvents.GetEventHandler(rawTarget)
- : null;
-
- if (target == null)
- {
- SimulateMouseUiOverlayState.Update(
- MouseAction.Drag, inputStart, null, null, Handles.GetMainGameViewSize());
- bool expandCompleted = await MouseUiOverlayAnimator.PlayExpandAnimation(ct).ConfigureAwait(false);
- if (!expandCompleted)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, null);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- bool dissipateCompleted = await MouseUiOverlayAnimator.PlayDissipateAnimation(ct).ConfigureAwait(false);
- if (!dissipateCompleted)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, null);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- return new SimulateMouseUiResponse
- {
- Success = false,
- Message = parameters.BypassRaycast
- ? $"TargetPath '{parameters.TargetPath}' has no drag handler."
- : $"No draggable UI element at ({inputStart.x:F1}, {inputStart.y:F1}). Use find-game-objects or screenshot to verify positions.",
- Action = MouseAction.Drag.ToString(),
- PositionX = inputStart.x,
- PositionY = inputStart.y,
- EndPositionX = inputEnd.x,
- EndPositionY = inputEnd.y
- };
- }
-
- // uGUI drag controls (ScrollRect, Slider) only respond to left-button drags
- PointerEventData pointerData = InitiateDrag(eventSystem, screenStart, startRaycast, target, PointerEventData.InputButton.Left);
- ExecuteEvents.Execute(target, pointerData, ExecuteEvents.beginDragHandler);
- pointerData.dragging = true;
-
- string targetName = target.name;
- SimulateMouseUiOverlayState.Update(
- MouseAction.Drag, inputStart, inputStart, targetName, Handles.GetMainGameViewSize());
-
- try
- {
- bool expandCompleted = await MouseUiOverlayAnimator.PlayExpandAnimation(ct).ConfigureAwait(false);
- if (!expandCompleted)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- bool dragCompleted = await InterpolateDragPosition(pointerData, target, screenEnd, parameters.DragSpeed, ct)
- .ConfigureAwait(false);
- if (!dragCompleted)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
- if (!frameReady)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateFrameTimeoutResult(MouseAction.Drag, inputStart, inputEnd, targetName);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
- }
- finally
- {
- _mainThreadCleanupScheduler.ExecuteCleanupOnMainThread(() => FinalizeDrag(pointerData, target, explicitDropTarget));
- }
-
- SimulateMouseUiOverlayState.Update(
- MouseAction.Drag, inputEnd, inputStart, targetName, Handles.GetMainGameViewSize());
-
- bool completedDissipate = await MouseUiOverlayAnimator.PlayDissipateAnimation(ct).ConfigureAwait(false);
- if (!completedDissipate)
- {
- _mainThreadCleanupScheduler.QueueOverlayClear();
- return MouseUiSimulationResponseFactory.CreateDragResult(parameters, inputStart, inputEnd, targetName);
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- return MouseUiSimulationResponseFactory.CreateDragResult(parameters, inputStart, inputEnd, targetName);
- }
-
- // Lifecycle must match StandaloneInputModule: raycast → pointerUp → drop → endDrag
- private void FinalizeDrag(PointerEventData pointerData, GameObject target, GameObject? explicitDropTarget)
- {
- if (explicitDropTarget != null)
- {
- pointerData.pointerCurrentRaycast = MouseUiPointerTargetResolver.CreateDirectRaycastResult(explicitDropTarget);
- }
- else
- {
- UpdatePointerRaycast(pointerData);
- }
-
- if (pointerData.pointerPress != null)
- {
- ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerUpHandler);
- }
-
- // Standard IDropHandler dispatch so Unity drop targets respond without manual workarounds
- GameObject? dropTarget = pointerData.pointerCurrentRaycast.gameObject;
- if (dropTarget != null)
- {
- ExecuteEvents.ExecuteHierarchy(dropTarget, pointerData, ExecuteEvents.dropHandler);
- }
-
- pointerData.dragging = false;
- ExecuteEvents.Execute(target, pointerData, ExecuteEvents.endDragHandler);
- }
-
- private void UpdatePointerRaycast(PointerEventData pointerData)
- {
- RaycastResult? hit = UiRaycastHelper.RaycastUI(pointerData.position, EventSystem.current);
- pointerData.pointerCurrentRaycast = hit ?? new RaycastResult();
- }
-
- private async Task InterpolateDragPosition(
- PointerEventData pointerData,
- GameObject target,
- Vector2 endPos,
- float dragSpeed,
- CancellationToken ct)
- {
- Debug.Assert(dragSpeed >= 0f, "dragSpeed must be non-negative");
-
- Vector2 startPos = pointerData.position;
- float distance = Vector2.Distance(startPos, endPos);
- float duration = dragSpeed > 0f ? distance / dragSpeed : 0f;
-
- if (duration <= 0f)
- {
- bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
- if (!frameReady)
- {
- return false;
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- Vector2 previousPosition = pointerData.position;
- pointerData.position = endPos;
- pointerData.delta = endPos - previousPosition;
- ExecuteEvents.Execute(target, pointerData, ExecuteEvents.dragHandler);
-
- SimulateMouseUiOverlayState.UpdatePosition(MouseUiCoordinateConverter.ScreenToInput(endPos));
- return true;
- }
-
- float startTime = Time.realtimeSinceStartup;
- float t;
-
- do
- {
- bool frameReady = await MouseUiEditorFrameWaiter.WaitForEditorFrameAndSwitchToMainThreadAsync(ct).ConfigureAwait(false);
- if (!frameReady)
- {
- return false;
- }
- await MainThreadSwitcher.SwitchToMainThread(ct);
-
- float elapsed = Time.realtimeSinceStartup - startTime;
- t = Mathf.Clamp01(elapsed / duration);
- Vector2 previousPosition = pointerData.position;
- Vector2 currentPosition = Vector2.Lerp(startPos, endPos, t);
-
- pointerData.position = currentPosition;
- pointerData.delta = currentPosition - previousPosition;
-
- ExecuteEvents.Execute(target, pointerData, ExecuteEvents.dragHandler);
-
- SimulateMouseUiOverlayState.UpdatePosition(MouseUiCoordinateConverter.ScreenToInput(currentPosition));
- }
- while (t < 1.0f);
-
- return true;
- }
-
private async Task ExecuteDragStart(
MouseUiSimulationCommand parameters, EventSystem eventSystem, CancellationToken ct)
{
@@ -457,7 +201,7 @@ private async Task ExecuteDragStart(
};
}
- PointerEventData pointerData = InitiateDrag(eventSystem, screenPos, startRaycast, target, PointerEventData.InputButton.Left);
+ PointerEventData pointerData = MouseUiDragEventExecutor.InitiateDrag(eventSystem, screenPos, startRaycast, target, PointerEventData.InputButton.Left);
ExecuteEvents.Execute(target, pointerData, ExecuteEvents.beginDragHandler);
pointerData.dragging = true;
@@ -487,7 +231,7 @@ private async Task ExecuteDragStart(
{
_mainThreadCleanupScheduler.ExecuteCleanupOnMainThread(() =>
{
- FinalizeDrag(pointerData, target, null);
+ MouseUiDragEventExecutor.FinalizeDrag(pointerData, target, null);
MouseDragState.Clear();
});
}
@@ -541,7 +285,7 @@ private async Task ExecuteDragMove(
targetName, Handles.GetMainGameViewSize());
// Cancellation leaves drag state intact so the user can continue with DragMove/DragEnd
- bool dragCompleted = await InterpolateDragPosition(
+ bool dragCompleted = await MouseUiDragEventExecutor.InterpolateDragPosition(
pointerData, target, screenEnd,
parameters.DragSpeed, ct).ConfigureAwait(false);
if (!dragCompleted)
@@ -613,7 +357,7 @@ private async Task ExecuteDragEnd(
try
{
- bool dragCompleted = await InterpolateDragPosition(
+ bool dragCompleted = await MouseUiDragEventExecutor.InterpolateDragPosition(
pointerData, target, screenEnd,
parameters.DragSpeed, ct).ConfigureAwait(false);
if (!dragCompleted)
@@ -635,7 +379,7 @@ private async Task ExecuteDragEnd(
{
_mainThreadCleanupScheduler.ExecuteCleanupOnMainThread(() =>
{
- FinalizeDrag(pointerData, target, explicitDropTarget);
+ MouseUiDragEventExecutor.FinalizeDrag(pointerData, target, explicitDropTarget);
MouseDragState.Clear();
});
}