From 8ef328fad35bf0f246a004cdc5b5dd35eb81e173 Mon Sep 17 00:00:00 2001 From: Ben MacKinnon Date: Wed, 13 Nov 2019 11:06:33 +0000 Subject: [PATCH] Squashed commit of the following: commit 6c5cf2dc036cf385b81fdfc8072300704b06b435 Merge: fb991af bf30261 Author: Ben MacKinnon Date: Tue Nov 12 17:54:34 2019 +0000 Merge branch 'refs/heads/master' into range-slider commit bf302616b8ae377a859d2012b856dc370efe40c2 Merge: 10919c6 76d2299 Author: Ben MacKinnon Date: Tue Nov 12 17:44:57 2019 +0000 Merge pull request #1 from Dover8/range-slider Range slider commit 76d22998d38801ab35ba19743d944263041924a5 Author: Ben MacKinnon Date: Tue Nov 12 17:36:23 2019 +0000 Updated PointerDown to update the target graphic Re-enabling colour tint interaction. commit 2d2ca219d5c0865ba4eaa428e8a167118aa06360 Author: Ben MacKinnon Date: Tue Nov 12 16:59:29 2019 +0000 Updated headers to match the standard format of the project commit 9a0d3f4aaddbcee2565693b0733c0001a2de2a90 Author: Ben MacKinnon Date: Tue Nov 12 16:43:22 2019 +0000 Some further default value set when creating a RangeSlider commit 748d1484568e16aa0bd2e3949330e48ce7245570 Author: Ben MacKinnon Date: Tue Nov 12 16:38:34 2019 +0000 Added the menu creation options for the Range Slider commit fa9e9beceede8c708529cd6a3ea98673b2d6d2c2 Author: Ben MacKinnon Date: Tue Nov 12 12:55:38 2019 +0000 Editor layout for the Range Slider Using the EditorGUILayout.MinMaxSlider to control the RangeSlider. It was this control that we are replicating in the UI. commit 32b731548e8e7f286964a4a7a35fd945e5d5a31a Author: Ben MacKinnon Date: Mon Nov 11 16:19:16 2019 +0000 Updated namespace and header commit 4d0fa2e7dc535b343cf10def6e01886383cc88ac Author: Ben MacKinnon Date: Mon Sep 16 12:41:45 2019 +0100 Added interaction Range slider is now interactable with both handles and the bar commit f4be93752719a08b95594e545eb16b27d3b3bcea Author: Ben MacKinnon Date: Fri Sep 13 15:27:15 2019 +0100 copied over the WIP RangeSlider from current project --- Editor/RangeSliderEditor.cs | 123 ++++++ Editor/RangeSliderEditor.cs.meta | 11 + Editor/UIExtensionsMenuOptions.cs | 68 +++ Scripts/Controls/RangeSlider.cs | 600 +++++++++++++++++++++++++++ Scripts/Controls/RangeSlider.cs.meta | 11 + 5 files changed, 813 insertions(+) create mode 100644 Editor/RangeSliderEditor.cs create mode 100644 Editor/RangeSliderEditor.cs.meta create mode 100644 Scripts/Controls/RangeSlider.cs create mode 100644 Scripts/Controls/RangeSlider.cs.meta diff --git a/Editor/RangeSliderEditor.cs b/Editor/RangeSliderEditor.cs new file mode 100644 index 0000000..516d5ab --- /dev/null +++ b/Editor/RangeSliderEditor.cs @@ -0,0 +1,123 @@ +/// Credit Ben MacKinnon @Dover8 +/// Sourced from - https://github.com/Dover8/Unity-UI-Extensions/tree/range-slider +/// Usage: Extension of the standard slider. Two handles determine a low and high value between a Min and Max. +/// Raises a UnityEvent passing the low and high values + +using UnityEditor; +using UnityEditor.UI; + +namespace UnityEngine.UI.Extensions +{ + [CustomEditor(typeof(RangeSlider), true)] + [CanEditMultipleObjects] + public class RangeSliderEditor : SelectableEditor + { + SerializedProperty m_LowHandleRect; + SerializedProperty m_HighHandleRect; + SerializedProperty m_FillRect; + + SerializedProperty m_MinValue; + SerializedProperty m_MaxValue; + SerializedProperty m_WholeNumbers; + + SerializedProperty m_LowValue; + SerializedProperty m_HighValue; + + //need ref values for the editor MinMaxSlider + float low = 0; + float high = 1; + + SerializedProperty m_OnValueChanged; + + + protected override void OnEnable() + { + base.OnEnable(); + m_LowHandleRect = serializedObject.FindProperty("m_LowHandleRect"); + m_HighHandleRect = serializedObject.FindProperty("m_HighHandleRect"); + m_FillRect = serializedObject.FindProperty("m_FillRect"); + + m_MinValue = serializedObject.FindProperty("m_MinValue"); + m_MaxValue = serializedObject.FindProperty("m_MaxValue"); + m_WholeNumbers = serializedObject.FindProperty("m_WholeNumbers"); + + m_LowValue = serializedObject.FindProperty("m_LowValue"); + low = m_LowValue.floatValue; + m_HighValue = serializedObject.FindProperty("m_HighValue"); + high = m_HighValue.floatValue; + + m_OnValueChanged = serializedObject.FindProperty("m_OnValueChanged"); + } + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + EditorGUILayout.Space(); + + serializedObject.Update(); + //grab the updated value affected by m_WholeNumbers + low = m_LowValue.floatValue; + high = m_HighValue.floatValue; + + EditorGUILayout.PropertyField(m_LowHandleRect); + EditorGUILayout.PropertyField(m_HighHandleRect); + EditorGUILayout.PropertyField(m_FillRect); + + if (m_LowHandleRect.objectReferenceValue != null && m_HighHandleRect.objectReferenceValue != null) + { + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.PropertyField(m_MinValue); + EditorGUILayout.PropertyField(m_MaxValue); + EditorGUILayout.PropertyField(m_WholeNumbers); + + //We're going to do a fair bit of layout here + EditorGUILayout.BeginHorizontal(); + //Low Label and value + EditorGUILayout.BeginVertical(); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("Low"); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + low = EditorGUILayout.DelayedFloatField(low, GUILayout.MaxWidth(100)); + EditorGUILayout.EndVertical(); + + GUILayout.FlexibleSpace(); + + //Slider + EditorGUILayout.BeginVertical(); + GUILayout.FlexibleSpace(); + EditorGUILayout.MinMaxSlider(ref low, ref high, m_MinValue.floatValue, m_MaxValue.floatValue, GUILayout.ExpandWidth(true)); + EditorGUILayout.EndVertical(); + + GUILayout.FlexibleSpace(); + + //High label and value + EditorGUILayout.BeginVertical(); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("High"); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + high = EditorGUILayout.DelayedFloatField(high, GUILayout.MaxWidth(100)); + EditorGUILayout.EndVertical(); + EditorGUILayout.EndHorizontal(); + + m_LowValue.floatValue = low; + m_HighValue.floatValue = high; + + EditorGUILayout.Space(); + EditorGUILayout.PropertyField(m_OnValueChanged); + } + else + { + EditorGUILayout.HelpBox("Specify a RectTransform for the RangeSlider fill or the RangeSlider handles or both. Each must have a parent RectTransform that it can slide within.", MessageType.Info); + } + + serializedObject.ApplyModifiedProperties(); + } + } + +} + diff --git a/Editor/RangeSliderEditor.cs.meta b/Editor/RangeSliderEditor.cs.meta new file mode 100644 index 0000000..b4dd551 --- /dev/null +++ b/Editor/RangeSliderEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac33445ed95315743983e4b657921146 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UIExtensionsMenuOptions.cs b/Editor/UIExtensionsMenuOptions.cs index b2b895f..ad7959e 100644 --- a/Editor/UIExtensionsMenuOptions.cs +++ b/Editor/UIExtensionsMenuOptions.cs @@ -1927,6 +1927,74 @@ namespace UnityEditor.UI } #endregion + #region RangeSlider + [MenuItem("GameObject/UI/Extensions/RangeSlider", false)] + static public void AddRangeSlider(MenuCommand menuCommand) + { + GameObject rangeSliderRoot = CreateUIElementRoot("Range Slider", menuCommand, new Vector2(160, 20)); + + GameObject background = CreateUIObject("Background", rangeSliderRoot); + + GameObject fillArea = CreateUIObject("Fill Area", rangeSliderRoot); + GameObject fill = CreateUIObject("Fill", fillArea); + + GameObject handleSlideArea = CreateUIObject("Handle Slide Area", rangeSliderRoot); + GameObject lowHandle = CreateUIObject("Low Handle", handleSlideArea); + GameObject highHandle = CreateUIObject("High Handle", handleSlideArea); + + SetAnchorsAndStretch(rangeSliderRoot); + Image backgroundImage = background.AddComponent(); + backgroundImage.sprite = AssetDatabase.GetBuiltinExtraResource(kBackgroundSpriteResourcePath); + backgroundImage.type = Image.Type.Sliced; + backgroundImage.fillCenter = false; + + RectTransform backgroundRect = backgroundImage.rectTransform; + backgroundRect.anchorMin = new Vector2(0, 0.25f); + backgroundRect.anchorMax = new Vector2(1, 0.75f); + backgroundRect.sizeDelta = Vector2.zero; + + RectTransform fillAreaRect = SetAnchorsAndStretch(fillArea); + fillAreaRect.anchorMin = new Vector2(0, 0.25f); + fillAreaRect.anchorMax = new Vector2(1, 0.75f); + fillAreaRect.offsetMin = new Vector2(5, 0); + fillAreaRect.offsetMax = new Vector2(-5, 0); + + RectTransform fillRect = SetAnchorsAndStretch(fill); + Image fillImage = fill.AddComponent(); + fillImage.sprite = AssetDatabase.GetBuiltinExtraResource(kStandardSpritePath); + fillImage.type = Image.Type.Sliced; + fillImage.fillCenter = true; + fillRect.offsetMin = new Vector2(-5, 0); + fillRect.offsetMax = new Vector2(5, 0); + + RectTransform handleSlideRect = SetAnchorsAndStretch(handleSlideArea); + handleSlideRect.anchorMin = new Vector2(0, 0.5f); + handleSlideRect.anchorMax = new Vector2(1, 0.5f); + handleSlideRect.offsetMin = new Vector2(10, -10); + handleSlideRect.offsetMax = new Vector2(-10, 10); + + RectTransform lowHandleRect = SetAnchorsAndStretch(lowHandle); + Image lowHandleImage = lowHandle.AddComponent(); + lowHandleImage.sprite = AssetDatabase.GetBuiltinExtraResource(kKnobPath); + lowHandleRect.sizeDelta = new Vector2(20, 0); + + RectTransform highHandleRect = SetAnchorsAndStretch(highHandle); + Image highHandleImage = highHandle.AddComponent(); + highHandleImage.sprite = AssetDatabase.GetBuiltinExtraResource(kKnobPath); + highHandleRect.sizeDelta = new Vector2(20, 0); + + RangeSlider rangeSlider = rangeSliderRoot.AddComponent(); + rangeSlider.FillRect = fillRect; + rangeSlider.LowHandleRect = lowHandleRect; + rangeSlider.HighHandleRect = highHandleRect; + rangeSlider.LowValue = rangeSlider.MinValue; + rangeSlider.HighValue = rangeSlider.MaxValue; + rangeSlider.targetGraphic = fillImage; + + Selection.activeGameObject = rangeSliderRoot; + } + #endregion + #region Menu Manager GO [MenuItem("GameObject/UI/Extensions/Menu Manager", false)] static public void AddMenuManager(MenuCommand menuCommand) diff --git a/Scripts/Controls/RangeSlider.cs b/Scripts/Controls/RangeSlider.cs new file mode 100644 index 0000000..2b64925 --- /dev/null +++ b/Scripts/Controls/RangeSlider.cs @@ -0,0 +1,600 @@ +/// Credit Ben MacKinnon @Dover8 +/// Sourced from - https://github.com/Dover8/Unity-UI-Extensions/tree/range-slider +/// Usage: Extension of the standard slider. Two handles determine a low and high value between a Min and Max. +/// Raises a UnityEvent passing the low and high values + +using System; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI.Extensions +{ + [AddComponentMenu("UI/Range Slider", 34)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + public class RangeSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement + { + + [Serializable] + public class RangeSliderEvent : UnityEvent { } + + [SerializeField] + private RectTransform m_FillRect; + + public RectTransform FillRect { get { return m_FillRect; } set { if (SetClass(ref m_FillRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } } + + [SerializeField] + private RectTransform m_LowHandleRect; + + public RectTransform LowHandleRect { get { return m_LowHandleRect; } set { if (SetClass(ref m_LowHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } } + + [SerializeField] + private RectTransform m_HighHandleRect; + + public RectTransform HighHandleRect { get { return m_HighHandleRect; } set { if (SetClass(ref m_HighHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } } + + [Space] + + [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; + + 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; + + 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; + public virtual float LowValue + { + get + { + if (WholeNumbers) + { + return Mathf.Round(m_LowValue); + } + + return m_LowValue; + } + set + { + SetLow(value); + } + } + + public float NormalizedLowValue + { + get + { + if (Mathf.Approximately(MinValue, MaxValue)) + { + return 0; + } + return Mathf.InverseLerp(MinValue, MaxValue, LowValue); + } + set + { + this.LowValue = Mathf.Lerp(MinValue, MaxValue, value); + } + } + + + [SerializeField] + private float m_HighValue; + public virtual float HighValue + { + get + { + if (WholeNumbers) + { + return Mathf.Round(m_HighValue); + } + + return m_HighValue; + } + set + { + SetHigh(value); + } + } + + public float NormalizedHighValue + { + get + { + if (Mathf.Approximately(MinValue, MaxValue)) + { + return 0; + } + return Mathf.InverseLerp(MinValue, MaxValue, HighValue); + } + set + { + this.HighValue = Mathf.Lerp(MinValue, MaxValue, value); + } + } + + /// + /// Set the value of the slider without invoking onValueChanged callback. + /// + /// The new value for the slider. + public virtual void SetValueWithoutNotify(float low, float high) + { + SetLow(low, false); + SetHigh(high, false); + } + + [Space] + + [SerializeField] + private RangeSliderEvent m_OnValueChanged = new RangeSliderEvent(); + + public RangeSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } } + + // Private fields + + /// + /// An Enum that says in what state we and interacting with the slider + /// + private enum InteractionState + { + Low, + High, + Bar, + None + } + + private InteractionState interactionState = InteractionState.None; + + private Image m_FillImage; + private Transform m_FillTransform; + private RectTransform m_FillContainerRect; + private Transform m_HighHandleTransform; + private RectTransform m_HighHandleContainerRect; + 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; + + private DrivenRectTransformTracker m_Tracker; + + // This "delayed" mechanism is required for case 1037681. + private bool m_DelayedUpdateVisuals = false; + + // Size of each step. + float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } } + + protected RangeSlider() + { } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + + if (WholeNumbers) + { + m_MinValue = Mathf.Round(m_MinValue); + m_MaxValue = Mathf.Round(m_MaxValue); + } + + if (IsActive()) + { + UpdateCachedReferences(); + SetLow(m_LowValue, false); + SetHigh(m_HighValue, false); + //Update rects since other things might affect them even if value didn't change + m_DelayedUpdateVisuals = true; + } + + if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying) + { + CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); + } + } +#endif + + public virtual void Rebuild(CanvasUpdate executing) + { +#if UNITY_EDITOR + if (executing == CanvasUpdate.Prelayout) + { + OnValueChanged.Invoke(LowValue, HighValue); + } +#endif + } + + /// + /// See ICanvasElement.LayoutComplete + /// + public virtual void LayoutComplete() + { } + + /// + /// See ICanvasElement.GraphicUpdateComplete + /// + public virtual void GraphicUpdateComplete() + { } + + public static bool SetClass(ref T currentValue, T newValue) where T : class + { + if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue))) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetStruct(ref T currentValue, T newValue) where T : struct + { + if (currentValue.Equals(newValue)) + return false; + + currentValue = newValue; + return true; + } + + protected override void OnEnable() + { + base.OnEnable(); + UpdateCachedReferences(); + SetLow(LowValue, false); + SetHigh(HighValue, false); + // Update rects since they need to be initialized correctly. + UpdateVisuals(); + } + + protected override void OnDisable() + { + m_Tracker.Clear(); + base.OnDisable(); + } + + /// + /// Update the rect based on the delayed update visuals. + /// Got around issue of calling sendMessage from onValidate. + /// + protected virtual void Update() + { + if (m_DelayedUpdateVisuals) + { + m_DelayedUpdateVisuals = false; + UpdateVisuals(); + } + } + + protected override void OnDidApplyAnimationProperties() + { + base.OnDidApplyAnimationProperties(); + } + + void UpdateCachedReferences() + { + if (m_FillRect && m_FillRect != (RectTransform)transform) + { + m_FillTransform = m_FillRect.transform; + m_FillImage = m_FillRect.GetComponent(); + if (m_FillTransform.parent != null) + m_FillContainerRect = m_FillTransform.parent.GetComponent(); + } + else + { + m_FillRect = null; + m_FillContainerRect = null; + m_FillImage = null; + } + + if (m_HighHandleRect && m_HighHandleRect != (RectTransform)transform) + { + m_HighHandleTransform = m_HighHandleRect.transform; + if (m_HighHandleTransform.parent != null) + m_HighHandleContainerRect = m_HighHandleTransform.parent.GetComponent(); + } + else + { + m_HighHandleRect = null; + m_HighHandleContainerRect = null; + } + + if (m_LowHandleRect && m_LowHandleRect != (RectTransform)transform) + { + m_LowHandleTransform = m_LowHandleRect.transform; + if (m_LowHandleTransform.parent != null) + { + m_LowHandleContainerRect = m_LowHandleTransform.parent.GetComponent(); + } + } + else + { + m_LowHandleRect = null; + m_LowHandleContainerRect = null; + } + } + + void SetLow(float input) + { + SetLow(input, true); + } + + protected virtual void SetLow(float input, bool sendCallback) + { + // Clamp the input + float newValue = Mathf.Clamp(input, MinValue, HighValue); //clamp between min and High + if (WholeNumbers) + { + newValue = Mathf.Round(newValue); + } + + // If the stepped value doesn't match the last one, it's time to update + if (m_LowValue == newValue) + return; + + m_LowValue = newValue; + UpdateVisuals(); + if (sendCallback) + { + UISystemProfilerApi.AddMarker("RangeSlider.lowValue", this); + m_OnValueChanged.Invoke(newValue, HighValue); + } + } + + void SetHigh(float input) + { + SetHigh(input, true); + } + + protected virtual void SetHigh(float input, bool sendCallback) + { + // Clamp the input + float newValue = Mathf.Clamp(input, LowValue, MaxValue); //clamp between min and High + if (WholeNumbers) + { + newValue = Mathf.Round(newValue); + } + + // If the stepped value doesn't match the last one, it's time to update + if (m_HighValue == newValue) + return; + + m_HighValue = newValue; + UpdateVisuals(); + if (sendCallback) + { + UISystemProfilerApi.AddMarker("RangeSlider.highValue", this); + m_OnValueChanged.Invoke(LowValue, newValue); + } + } + + + protected override void OnRectTransformDimensionsChange() + { + base.OnRectTransformDimensionsChange(); + + //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called. + if (!IsActive()) + return; + + UpdateVisuals(); + } + + + // Force-update the slider. Useful if you've changed the properties and want it to update visually. + private void UpdateVisuals() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + UpdateCachedReferences(); +#endif + + m_Tracker.Clear(); + + if (m_FillContainerRect != null) + { + m_Tracker.Add(this, m_FillRect, DrivenTransformProperties.Anchors); + Vector2 anchorMin = Vector2.zero; + Vector2 anchorMax = Vector2.one; + + //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; + + m_FillRect.anchorMin = anchorMin; + m_FillRect.anchorMax = anchorMax; + } + + if (m_LowHandleContainerRect != null) + { + m_Tracker.Add(this, m_LowHandleRect, DrivenTransformProperties.Anchors); + Vector2 anchorMin = Vector2.zero; + Vector2 anchorMax = Vector2.one; + anchorMin[0] = anchorMax[0] = NormalizedLowValue; + m_LowHandleRect.anchorMin = anchorMin; + m_LowHandleRect.anchorMax = anchorMax; + } + + if (m_HighHandleContainerRect != null) + { + m_Tracker.Add(this, m_HighHandleRect, DrivenTransformProperties.Anchors); + Vector2 anchorMin = Vector2.zero; + Vector2 anchorMax = Vector2.one; + anchorMin[0] = anchorMax[0] = NormalizedHighValue; + m_HighHandleRect.anchorMin = anchorMin; + m_HighHandleRect.anchorMax = anchorMax; + } + } + + // Update the slider's position based on the mouse. + void UpdateDrag(PointerEventData eventData, Camera cam) + { + //this needs to differ from slider in that we have two handles, and need to move the right one. + //and if it was neither handle, we will have a seperate case where both handles move uniformly + //moving the entire range + + //this is where we use our interationState + switch (interactionState) + { + case InteractionState.Low: + NormalizedLowValue = CalculateDrag(eventData, cam, m_LowHandleContainerRect, m_LowOffset); + break; + case InteractionState.High: + NormalizedHighValue = CalculateDrag(eventData, cam, m_HighHandleContainerRect, m_HighOffset); + break; + case InteractionState.Bar: + //special case + CalculateBarDrag(eventData, cam); + break; + case InteractionState.None: + break; + } + } + + private float CalculateDrag(PointerEventData eventData, Camera cam, RectTransform containerRect, Vector2 offset) + { + RectTransform clickRect = containerRect ?? m_FillContainerRect; + if (clickRect != null && clickRect.rect.size[0] > 0) + { + Vector2 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]); + + return val; + } + return 0; + } + + private void CalculateBarDrag(PointerEventData eventData, Camera cam) + { + RectTransform clickRect = m_FillContainerRect; + if (clickRect != null && clickRect.rect.size[0] > 0) + { + Vector2 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 + //and move both the normalized low and high values by this amount + //but also check that neither is going beyond the bounds + if (NormalizedLowValue >= 0 && NormalizedHighValue <= 1) + { + //find the mid point on the current bar + float mid = (NormalizedHighValue + NormalizedLowValue)/2; + //find where the new mid point should be + float val = Mathf.Clamp01((localCursor)[0] / clickRect.rect.size[0]); + //calculate the delta + float delta = val - mid; + //check the clamp range + if (NormalizedLowValue + delta < 0) + { + delta = -NormalizedLowValue; + } + else if (NormalizedHighValue + delta > 1) + { + delta = 1 - NormalizedHighValue; + } + + //adjust both ends + NormalizedLowValue += delta; + NormalizedHighValue += delta; + } + } + } + + private bool MayDrag(PointerEventData eventData) + { + return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left; + } + + public override void OnPointerDown(PointerEventData eventData) + { + if (!MayDrag(eventData)) + return; + + + //HANDLE DRAG EVENTS + m_LowOffset = m_HighOffset = Vector2.zero; + Vector2 localMousePos; + if (m_HighHandleRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HighHandleRect, eventData.position, eventData.enterEventCamera)) + { + //dragging the high value handle + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HighHandleRect, eventData.position, eventData.pressEventCamera, out localMousePos)) + { + m_HighOffset = localMousePos; + } + interactionState = InteractionState.High; + if (transition == Transition.ColorTint) + { + targetGraphic = m_HighHandleRect.GetComponent(); + } + } + else if (m_LowHandleRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_LowHandleRect, eventData.position, eventData.enterEventCamera)) + { + //dragging the low value handle + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_LowHandleRect, eventData.position, eventData.pressEventCamera, out localMousePos)) + { + m_LowOffset = localMousePos; + } + interactionState = InteractionState.Low; + if (transition == Transition.ColorTint) + { + targetGraphic = m_LowHandleRect.GetComponent(); + } + } + else + { + //outside the handles, move the entire slider along + UpdateDrag(eventData, eventData.pressEventCamera); + interactionState = InteractionState.Bar; + if (transition == Transition.ColorTint) + { + targetGraphic = m_FillImage; + } + } + base.OnPointerDown(eventData); + } + + public virtual void OnDrag(PointerEventData eventData) + { + if (!MayDrag(eventData)) + { + return; + } + UpdateDrag(eventData, eventData.pressEventCamera); + } + + public override void OnPointerUp(PointerEventData eventData) + { + base.OnPointerUp(eventData); + interactionState = InteractionState.None; + } + + public override void OnMove(AxisEventData eventData) + { + //this requires further investigation + } + + public virtual void OnInitializePotentialDrag(PointerEventData eventData) + { + eventData.useDragThreshold = false; + } + } +} diff --git a/Scripts/Controls/RangeSlider.cs.meta b/Scripts/Controls/RangeSlider.cs.meta new file mode 100644 index 0000000..7b3d7aa --- /dev/null +++ b/Scripts/Controls/RangeSlider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4735d2b59d5c699488c30a34a6a76e72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: