using System; using System.Collections.Generic; using System.Linq; using RosettaUI.Builder; using UnityEngine; using UnityEngine.UIElements; namespace RosettaUI.UIToolkit.AnimationCurveEditor { /// /// ControlPointとSelectedControlPointsRectにAddしてドラッグ操作を受け付けるマニュピレーター生成クラス /// public class ControlPointsDragManipulatorSource { private enum MoveAxis { Both, Horizontal, Vertical } private readonly CurveController _curveController; private readonly PreviewTransform _previewTransform; private readonly ControlPointDisplayPositionPopup _positionPopup; private readonly CurveSnapshot _curveSnapshot; private readonly Dictionary _pointerToKeyframePositionOffsetsOnDragStart = new(); private Manipulator _currentManipulator; private Vector2 _pointerPositionOnDragStart; private MoveAxis _moveAxis = MoveAxis.Both; private CurveController.RecordUndoSnapshotScope _undoScopeOnDragStart; private IEnumerable SelectedControlPoints => _curveController.SelectedControlPoints; public ControlPointsDragManipulatorSource( CurveController curveController, PreviewTransform previewTransform, ControlPointDisplayPositionPopup positionPopup ) { _curveController = curveController; _previewTransform = previewTransform; _positionPopup = positionPopup; _curveSnapshot = new CurveSnapshot(curveController); } public Manipulator CreateManipulator() { var manipulator = new DragManipulator(); manipulator.onDragStarting += OnDragStarting; manipulator.onDrag += OnDrag; manipulator.onDragEnd += OnDragEnd; return manipulator; } private bool OnDragStarting(DragManipulator manipulator, PointerDownEvent evt) { if (evt.button != 0) return false; // left button only if(_currentManipulator != null) return false; // すでにドラッグ中 _currentManipulator = manipulator; _undoScopeOnDragStart = _curveController.RecordUndoSnapshot(); // start drag _pointerPositionOnDragStart = _previewTransform.GetCurvePosFromUIWorldPos(evt.position); _pointerToKeyframePositionOffsetsOnDragStart.Clear(); foreach (var cp in SelectedControlPoints) { _pointerToKeyframePositionOffsetsOnDragStart[cp] = cp.KeyframePosition - _pointerPositionOnDragStart; } // ControlPointとKeyframeのスナップショットを保存 // ドラッグ中に選択中のKeyframeが同一timeに来ると上書きされて消えるが、 // さらにドラッグしてtimeがずれたら復活したい _curveSnapshot.TakeSnapshot(); _moveAxis = MoveAxis.Both; _positionPopup.Show(); UpdatePositionPopup(evt.position); evt.StopPropagation(); return true; } private void OnDrag(DragManipulator _, PointerMoveEvent evt) { // スナップショットに保存されたKeyframeのうち選択中ではないものを復元 // ドラッグ中に選択中のKeyframeが同一timeに来ると上書きされて消えるがさらにドラッグしてtimeがずれたら復活させる // すでに同一timeにKeyframeがある場合はAddKeyframeで無視されるので問題ない _curveSnapshot.ApplySnapshotWithoutSelection(); if (_moveAxis == MoveAxis.Both && evt.shiftKey) { var delta = evt.deltaPosition; _moveAxis = Mathf.Abs(delta.x) > Mathf.Abs(delta.y) ? MoveAxis.Horizontal : MoveAxis.Vertical; } var pointerPositionOnCurve = _previewTransform.GetCurvePosFromUIWorldPos(evt.position); _curveController.UpdateSelectedControlPointKeyframes(cp => { if (!_pointerToKeyframePositionOffsetsOnDragStart.TryGetValue(cp, out var pointerToKeyframeOffsetOnDragStart)) { return cp.Keyframe; } switch (_moveAxis) { case MoveAxis.Both: break; case MoveAxis.Horizontal: pointerPositionOnCurve.y = _pointerPositionOnDragStart.y; break; case MoveAxis.Vertical: pointerPositionOnCurve.x = _pointerPositionOnDragStart.x; break; default: throw new ArgumentOutOfRangeException(); } var keyframePosition = pointerToKeyframeOffsetOnDragStart + pointerPositionOnCurve; keyframePosition = _previewTransform.SnapCurvePositionIfEnabled(keyframePosition); var keyframe = cp.Keyframe; keyframe.SetPosition(keyframePosition); return keyframe; }); UpdatePositionPopup(evt.position); } private void OnDragEnd(DragManipulator _, EventBase evt) { _undoScopeOnDragStart.Dispose(); _undoScopeOnDragStart = default; _currentManipulator = null; _positionPopup.Hide(); } // cursorPosition: 画面上の座標。PreviewTransformのScreenPosではない(ので注意 private void UpdatePositionPopup(Vector2 pointerPosition) { Vector2 elementPosition; Vector2 keyframePosition; var isMultiSelection = _curveController.IsMultiSelection; if (isMultiSelection) { elementPosition = _previewTransform.GetScreenPosFromUIWorldPos(pointerPosition); keyframePosition = _previewTransform.GetCurvePosFromScreenPos(elementPosition); } else { var cp = SelectedControlPoints.FirstOrDefault(); if (cp == null) { return; } elementPosition = cp.GetLocalPosition(); keyframePosition = cp.KeyframePosition; } _positionPopup.Update(elementPosition, keyframePosition); } } }