Files

219 lines
8.2 KiB
C#

using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using PointerType = UnityEngine.UIElements.PointerType;
namespace Unity.Cinemachine.Editor
{
interface IDelayedFriendlyDragger
{
/// <summary>If true, temporarily disable isDelayed when dragging</summary>
public bool CancelDelayedWhenDragging { get; set; }
/// <summary>Called when dragging starts.
public Action<IDelayedFriendlyDragger> OnStartDrag { get; set; }
/// <summary>Called when dragging stops.
public Action<IDelayedFriendlyDragger> OnStopDrag { get; set; }
/// <summary>Called when the value changes during dragging.</summary>
public Action<int> OnDragValueChangedInt { get; set; }
/// <summary>Called when the value changes during dragging.</summary>
public Action<float> OnDragValueChangedFloat { get; set; }
/// <summary>Get the VisualElement being dragged</summary>
public VisualElement DragElement { get; }
}
/// <summary>
/// Provides dragging on a visual element to change a value field with
/// isDelayed set, but for int and float driven fields can turn off isDelayed while dragging.
/// </summary>
class DelayedFriendlyFieldDragger<T> : BaseFieldMouseDragger, IDelayedFriendlyDragger
{
/// <summary>DelayedFriendlyFieldDragger's constructor./// </summary>
/// <param name="drivenField">The field.</param>
public DelayedFriendlyFieldDragger(IValueField<T> drivenField)
{
m_DrivenField = drivenField;
m_DragElement = null;
m_DragHotZone = new Rect(0, 0, -1, -1);
dragging = false;
}
private readonly IValueField<T> m_DrivenField;
private VisualElement m_DragElement;
private Rect m_DragHotZone;
private bool m_WasDelayed;
/// <summary>Is dragging.</summary>
public bool dragging { get; set; }
/// <inheritdoc/>
public T startValue { get; set; }
/// <inheritdoc/>
public bool CancelDelayedWhenDragging { get; set; }
/// <inheritdoc/>
public Action<IDelayedFriendlyDragger> OnStartDrag { get; set; }
/// <inheritdoc/>
public Action<IDelayedFriendlyDragger> OnStopDrag { get; set; }
/// <inheritdoc/>
public Action<int> OnDragValueChangedInt { get; set; }
/// <inheritdoc/>
public Action<float> OnDragValueChangedFloat { get; set; }
/// <inheritdoc/>
public VisualElement DragElement => m_DragElement;
/// <inheritdoc />
public sealed override void SetDragZone(VisualElement dragElement, Rect hotZone)
{
if (m_DragElement != null)
{
m_DragElement.UnregisterCallback<PointerDownEvent>(UpdateValueOnPointerDown, TrickleDown.TrickleDown);
m_DragElement.UnregisterCallback<PointerUpEvent>(UpdateValueOnPointerUp);
m_DragElement.UnregisterCallback<KeyDownEvent>(UpdateValueOnKeyDown);
}
m_DragElement = dragElement;
m_DragHotZone = hotZone;
if (m_DragElement != null)
{
dragging = false;
m_DragElement.RegisterCallback<PointerDownEvent>(UpdateValueOnPointerDown, TrickleDown.TrickleDown);
m_DragElement.RegisterCallback<PointerUpEvent>(UpdateValueOnPointerUp);
m_DragElement.RegisterCallback<KeyDownEvent>(UpdateValueOnKeyDown);
}
}
private bool CanStartDrag(int button, Vector2 localPosition)
{
return button == 0 && (m_DragHotZone.width < 0 || m_DragHotZone.height < 0 ||
m_DragHotZone.Contains(m_DragElement.WorldToLocal(localPosition)));
}
private void UpdateValueOnPointerDown(PointerDownEvent evt)
{
if (CanStartDrag(evt.button, evt.localPosition))
{
// We want to allow dragging when using a mouse in any context and when in an Editor context with any pointer type.
if (evt.pointerType == PointerType.mouse)
{
m_DragElement.CaptureMouse();
ProcessDownEvent(evt);
}
else if (m_DragElement.panel.contextType == ContextType.Editor)
{
m_DragElement.CapturePointer(evt.pointerId);
ProcessDownEvent(evt);
}
}
}
private void ProcessDownEvent(EventBase evt)
{
// Make sure no other elements can capture the mouse!
evt.StopPropagation();
dragging = true;
m_DragElement.RegisterCallback<PointerMoveEvent>(UpdateValueOnPointerMove);
startValue = m_DrivenField.value;
if (m_DrivenField is TextInputBaseField<float> floatField)
{
m_WasDelayed = floatField.isDelayed;
if (CancelDelayedWhenDragging)
floatField.isDelayed = false;
}
else if (m_DrivenField is TextInputBaseField<int> intField)
{
m_WasDelayed = intField.isDelayed;
if (CancelDelayedWhenDragging)
intField.isDelayed = false;
}
m_DrivenField.StartDragging();
EditorGUIUtility.SetWantsMouseJumping(1);
OnStartDrag?.Invoke(this);
}
private void UpdateValueOnPointerMove(PointerMoveEvent evt)
{
ProcessMoveEvent(evt.shiftKey, evt.altKey, evt.deltaPosition);
}
private void ProcessMoveEvent(bool shiftKey, bool altKey, Vector2 deltaPosition)
{
if (dragging)
{
DeltaSpeed s = shiftKey ? DeltaSpeed.Fast : (altKey ? DeltaSpeed.Slow : DeltaSpeed.Normal);
m_DrivenField.ApplyInputDeviceDelta(deltaPosition, s, startValue);
if (OnDragValueChangedFloat != null && m_DrivenField is TextInputBaseField<float> floatField)
{
var textElement = floatField.Q<TextElement>();
if (textElement != null)
OnDragValueChangedFloat.Invoke((float)(object)float.Parse(textElement.text));
}
else if (OnDragValueChangedInt != null && m_DrivenField is TextInputBaseField<int> intField)
{
var textElement = intField.Q<TextElement>();
if (textElement != null)
OnDragValueChangedInt.Invoke((int)(object)int.Parse(textElement.text));
}
}
}
private void UpdateValueOnPointerUp(PointerUpEvent evt)
{
ProcessUpEvent(evt, evt.pointerId);
}
private void ProcessUpEvent(EventBase evt, int pointerId)
{
if (dragging)
{
OnStopDrag?.Invoke(this);
dragging = false;
m_DragElement.UnregisterCallback<PointerMoveEvent>(UpdateValueOnPointerMove);
m_DragElement.ReleasePointer(pointerId);
//if (evt is IMouseEvent)
// m_DragElement.panel.ProcessPointerCapture(PointerId.mousePointerId);
EditorGUIUtility.SetWantsMouseJumping(0);
m_DrivenField.StopDragging();
if (m_WasDelayed)
{
if (m_DrivenField is TextInputBaseField<float> floatField)
floatField.isDelayed = true;
else if (m_DrivenField is TextInputBaseField<int> intField)
intField.isDelayed = true;
}
}
}
private void UpdateValueOnKeyDown(KeyDownEvent evt)
{
if (dragging && evt.keyCode == KeyCode.Escape)
{
dragging = false;
m_DrivenField.value = startValue;
m_DrivenField.StopDragging();
var target = evt.target as VisualElement;
IPanel panel = target?.panel;
panel?.ReleasePointer(PointerId.mousePointerId);
EditorGUIUtility.SetWantsMouseJumping(0);
}
}
}
}