Merged in RangeSlider-upgrade (pull request #131)

Upgraded RangeSlider to work in both Horizontal and Verticle setups, just like regular slider.

Approved-by: Simon Jackson
pull/413/head
Ben MacKinnon 2022-04-23 10:43:01 +00:00 committed by Simon Jackson
commit ca090d1797
2 changed files with 182 additions and 61 deletions

View File

@ -12,6 +12,7 @@ namespace UnityEngine.UI.Extensions
[CanEditMultipleObjects]
public class RangeSliderEditor : SelectableEditor
{
SerializedProperty m_Direction;
SerializedProperty m_LowHandleRect;
SerializedProperty m_HighHandleRect;
SerializedProperty m_FillRect;
@ -36,6 +37,7 @@ namespace UnityEngine.UI.Extensions
m_LowHandleRect = serializedObject.FindProperty("m_LowHandleRect");
m_HighHandleRect = serializedObject.FindProperty("m_HighHandleRect");
m_FillRect = serializedObject.FindProperty("m_FillRect");
m_Direction = serializedObject.FindProperty("m_Direction");
m_MinValue = serializedObject.FindProperty("m_MinValue");
m_MaxValue = serializedObject.FindProperty("m_MaxValue");
@ -66,6 +68,16 @@ namespace UnityEngine.UI.Extensions
if (m_LowHandleRect.objectReferenceValue != null && m_HighHandleRect.objectReferenceValue != null)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Direction);
if (EditorGUI.EndChangeCheck())
{
RangeSlider.Direction direction = (RangeSlider.Direction)m_Direction.enumValueIndex;
foreach (var obj in serializedObject.targetObjects)
{
RangeSlider rangeSlider = obj as RangeSlider;
rangeSlider.SetDirection(direction, true);
}
}
EditorGUILayout.PropertyField(m_MinValue);
EditorGUILayout.PropertyField(m_MaxValue);
@ -120,4 +132,3 @@ namespace UnityEngine.UI.Extensions
}
}

View File

@ -14,45 +14,124 @@ namespace UnityEngine.UI.Extensions
[RequireComponent(typeof(RectTransform))]
public class RangeSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
{
public enum Direction
{
Horizontal,
Vertical
}
[Serializable]
public class RangeSliderEvent : UnityEvent<float, float> { }
public class RangeSliderEvent : UnityEvent<float, float>
{
}
[SerializeField]
private RectTransform m_FillRect;
[SerializeField] private RectTransform m_FillRect;
public RectTransform FillRect { get { return m_FillRect; } set { if (SetClass(ref m_FillRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
public RectTransform FillRect
{
get { return m_FillRect; }
set
{
if (SetClass(ref m_FillRect, value))
{
UpdateCachedReferences();
UpdateVisuals();
}
}
}
[SerializeField]
private RectTransform m_LowHandleRect;
[SerializeField] private RectTransform m_LowHandleRect;
public RectTransform LowHandleRect { get { return m_LowHandleRect; } set { if (SetClass(ref m_LowHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
public RectTransform LowHandleRect
{
get { return m_LowHandleRect; }
set
{
if (SetClass(ref m_LowHandleRect, value))
{
UpdateCachedReferences();
UpdateVisuals();
}
}
}
[SerializeField]
private RectTransform m_HighHandleRect;
[SerializeField] private RectTransform m_HighHandleRect;
public RectTransform HighHandleRect { get { return m_HighHandleRect; } set { if (SetClass(ref m_HighHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
public RectTransform HighHandleRect
{
get { return m_HighHandleRect; }
set
{
if (SetClass(ref m_HighHandleRect, value))
{
UpdateCachedReferences();
UpdateVisuals();
}
}
}
[Space]
[Space] [SerializeField] private Direction m_Direction = Direction.Horizontal;
[SerializeField]
private float m_MinValue = 0;
public Direction direction
{
get { return m_Direction; }
set
{
if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals();
}
}
public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
[SerializeField] private float m_MinValue = 0;
public float MinValue
{
get { return m_MinValue; }
set
{
if (SetStruct(ref m_MinValue, value))
{
SetLow(m_LowValue);
SetHigh(m_HighValue);
UpdateVisuals();
}
}
}
[SerializeField]
private float m_MaxValue = 1;
[SerializeField] private float m_MaxValue = 1;
public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
public float MaxValue
{
get { return m_MaxValue; }
set
{
if (SetStruct(ref m_MaxValue, value))
{
SetLow(m_LowValue);
SetHigh(m_HighValue);
UpdateVisuals();
}
}
}
[SerializeField]
private bool m_WholeNumbers = false;
[SerializeField] private bool m_WholeNumbers = false;
public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
public bool WholeNumbers
{
get { return m_WholeNumbers; }
set
{
if (SetStruct(ref m_WholeNumbers, value))
{
SetLow(m_LowValue);
SetHigh(m_HighValue);
UpdateVisuals();
}
}
}
[SerializeField] private float m_LowValue;
[SerializeField]
private float m_LowValue;
public virtual float LowValue
{
get
@ -78,6 +157,7 @@ namespace UnityEngine.UI.Extensions
{
return 0;
}
return Mathf.InverseLerp(MinValue, MaxValue, LowValue);
}
set
@ -87,8 +167,8 @@ namespace UnityEngine.UI.Extensions
}
[SerializeField]
private float m_HighValue;
[SerializeField] private float m_HighValue;
public virtual float HighValue
{
get
@ -114,6 +194,7 @@ namespace UnityEngine.UI.Extensions
{
return 0;
}
return Mathf.InverseLerp(MinValue, MaxValue, HighValue);
}
set
@ -132,10 +213,7 @@ namespace UnityEngine.UI.Extensions
SetHigh(high, false);
}
[Space]
[SerializeField]
private RangeSliderEvent m_OnValueChanged = new RangeSliderEvent();
[Space] [SerializeField] private RangeSliderEvent m_OnValueChanged = new RangeSliderEvent();
public RangeSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
@ -162,10 +240,8 @@ namespace UnityEngine.UI.Extensions
private Transform m_LowHandleTransform;
private RectTransform m_LowHandleContainerRect;
// The offset from handle position to mouse down position
private Vector2 m_LowOffset = Vector2.zero;
// The offset from handle position to mouse down position
private Vector2 m_HighOffset = Vector2.zero;
// The offset from interacted component position to mouse down position
private Vector2 m_Offset = Vector2.zero;
private DrivenRectTransformTracker m_Tracker;
@ -176,7 +252,8 @@ namespace UnityEngine.UI.Extensions
float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } }
protected RangeSlider()
{ }
{
}
#if UNITY_EDITOR
protected override void OnValidate()
@ -219,13 +296,15 @@ namespace UnityEngine.UI.Extensions
/// See ICanvasElement.LayoutComplete
/// </summary>
public virtual void LayoutComplete()
{ }
{
}
/// <summary>
/// See ICanvasElement.GraphicUpdateComplete
/// </summary>
public virtual void GraphicUpdateComplete()
{ }
{
}
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
{
@ -321,7 +400,7 @@ namespace UnityEngine.UI.Extensions
m_LowHandleContainerRect = null;
}
}
void SetLow(float input)
{
SetLow(input, true);
@ -388,6 +467,13 @@ namespace UnityEngine.UI.Extensions
UpdateVisuals();
}
enum Axis
{
Horizontal = 0,
Vertical = 1
}
Axis axis { get { return m_Direction == Direction.Horizontal ? Axis.Horizontal : Axis.Vertical; } }
// Force-update the slider. Useful if you've changed the properties and want it to update visually.
private void UpdateVisuals()
@ -407,8 +493,8 @@ namespace UnityEngine.UI.Extensions
//this is where some new magic must happen. Slider just uses a filled image
//and changes the % of fill. We must move the image anchors to be between the two handles.
anchorMin[0] = NormalizedLowValue;
anchorMax[0] = NormalizedHighValue;
anchorMin[(int)axis] = NormalizedLowValue;
anchorMax[(int)axis] = NormalizedHighValue;
m_FillRect.anchorMin = anchorMin;
m_FillRect.anchorMax = anchorMax;
@ -419,7 +505,7 @@ namespace UnityEngine.UI.Extensions
m_Tracker.Add(this, m_LowHandleRect, DrivenTransformProperties.Anchors);
Vector2 anchorMin = Vector2.zero;
Vector2 anchorMax = Vector2.one;
anchorMin[0] = anchorMax[0] = NormalizedLowValue;
anchorMin[(int)axis] = anchorMax[(int)axis] = NormalizedLowValue;
m_LowHandleRect.anchorMin = anchorMin;
m_LowHandleRect.anchorMax = anchorMax;
}
@ -429,7 +515,7 @@ namespace UnityEngine.UI.Extensions
m_Tracker.Add(this, m_HighHandleRect, DrivenTransformProperties.Anchors);
Vector2 anchorMin = Vector2.zero;
Vector2 anchorMax = Vector2.one;
anchorMin[0] = anchorMax[0] = NormalizedHighValue;
anchorMin[(int)axis] = anchorMax[(int)axis] = NormalizedHighValue;
m_HighHandleRect.anchorMin = anchorMin;
m_HighHandleRect.anchorMax = anchorMax;
}
@ -446,10 +532,10 @@ namespace UnityEngine.UI.Extensions
switch (interactionState)
{
case InteractionState.Low:
NormalizedLowValue = CalculateDrag(eventData, cam, m_LowHandleContainerRect, m_LowOffset);
NormalizedLowValue = CalculateDrag(eventData, cam, m_LowHandleContainerRect);
break;
case InteractionState.High:
NormalizedHighValue = CalculateDrag(eventData, cam, m_HighHandleContainerRect, m_HighOffset);
NormalizedHighValue = CalculateDrag(eventData, cam, m_HighHandleContainerRect);
break;
case InteractionState.Bar:
//special case
@ -460,35 +546,40 @@ namespace UnityEngine.UI.Extensions
}
}
private float CalculateDrag(PointerEventData eventData, Camera cam, RectTransform containerRect, Vector2 offset)
{
private float CalculateDrag(PointerEventData eventData, Camera cam, RectTransform containerRect)
{
RectTransform clickRect = containerRect ?? m_FillContainerRect;
if (clickRect != null && clickRect.rect.size[0] > 0)
if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
{
Vector2 localCursor;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam,
out localCursor))
{
return 0f;
}
localCursor -= clickRect.rect.position;
float val = Mathf.Clamp01((localCursor - offset)[0] / clickRect.rect.size[0]);
float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
return val;
}
return 0;
}
private void CalculateBarDrag(PointerEventData eventData, Camera cam)
{
RectTransform clickRect = m_FillContainerRect;
if (clickRect != null && clickRect.rect.size[0] > 0)
if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
{
Vector2 localCursor;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam,
out localCursor))
{
return;
}
localCursor -= clickRect.rect.position;
//now we need to get the delta drag on the bar
@ -497,9 +588,9 @@ namespace UnityEngine.UI.Extensions
if (NormalizedLowValue >= 0 && NormalizedHighValue <= 1)
{
//find the mid point on the current bar
float mid = (NormalizedHighValue + NormalizedLowValue)/2;
float mid = (NormalizedHighValue + NormalizedLowValue) / 2;
//find where the new mid point should be
float val = Mathf.Clamp01((localCursor)[0] / clickRect.rect.size[0]);
float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
//calculate the delta
float delta = val - mid;
//check the clamp range
@ -529,9 +620,8 @@ namespace UnityEngine.UI.Extensions
if (!MayDrag(eventData))
return;
//HANDLE DRAG EVENTS
m_LowOffset = m_HighOffset = Vector2.zero;
m_Offset = Vector2.zero;
if(m_LowHandleRect != null && LowValue == MaxValue && RectTransformUtility.RectangleContainsScreenPoint(m_LowHandleRect, eventData.position, eventData.enterEventCamera))
{
SetToMoveLowValueHandle(m_LowHandleRect, eventData);
@ -544,19 +634,27 @@ namespace UnityEngine.UI.Extensions
{
SetToMoveLowValueHandle(m_LowHandleRect, eventData);
}
else
else if (m_FillRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_FillRect, eventData.position, eventData.enterEventCamera))
{
//outside the handles, move the entire slider along
UpdateDrag(eventData, eventData.pressEventCamera);
if (eventData.pointerCurrentRaycast.gameObject == m_FillRect.gameObject)
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_FillRect, eventData.position, eventData.pressEventCamera, out var localMousePos))
{
interactionState = InteractionState.Bar;
m_Offset = localMousePos;
}
interactionState = InteractionState.Bar;
if (transition == Transition.ColorTint)
{
targetGraphic = m_FillImage;
}
}
else
{
//outside the handles, move the entire slider along
UpdateDrag(eventData, eventData.pressEventCamera);
}
base.OnPointerDown(eventData);
}
@ -565,7 +663,7 @@ namespace UnityEngine.UI.Extensions
//dragging the low value handle
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(transform, eventData.position, eventData.pressEventCamera, out var localMousePos))
{
m_LowOffset = localMousePos;
m_Offset = localMousePos;
}
interactionState = InteractionState.Low;
if (transition == Transition.ColorTint)
@ -579,7 +677,7 @@ namespace UnityEngine.UI.Extensions
//dragging the low value handle
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(transform, eventData.position, eventData.pressEventCamera, out var localMousePos))
{
m_HighOffset = localMousePos;
m_Offset = localMousePos;
}
interactionState = InteractionState.High;
if (transition == Transition.ColorTint)
@ -613,5 +711,17 @@ namespace UnityEngine.UI.Extensions
{
eventData.useDragThreshold = false;
}
public void SetDirection(Direction direction, bool includeRectLayouts)
{
Axis oldAxis = axis;
this.direction = direction;
if (!includeRectLayouts)
return;
if (axis != oldAxis)
RectTransformUtility.FlipLayoutAxes(transform as RectTransform, true, true);
}
}
}