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