///Credit judah4 ///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/ using System; using UnityEngine.Events; using UnityEngine.EventSystems; namespace UnityEngine.UI.Extensions { [AddComponentMenu("UI/BoxSlider", 35)] [RequireComponent(typeof(RectTransform))] public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement { public enum Direction { LeftToRight, RightToLeft, BottomToTop, TopToBottom, } [Serializable] public class BoxSliderEvent : UnityEvent { } [SerializeField] private RectTransform m_HandleRect; public RectTransform HandleRect { get { return m_HandleRect; } set { if (SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } } [Space(6)] [SerializeField] private float m_MinValue = 0; public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { Set(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } [SerializeField] private float m_MaxValue = 1; public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { Set(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } [SerializeField] private bool m_WholeNumbers = false; public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { Set(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } [SerializeField] private float m_ValueX = 1f; public float ValueX { get { if (WholeNumbers) return Mathf.Round(m_ValueX); return m_ValueX; } set { Set(value); } } public float NormalizedValueX { get { if (Mathf.Approximately(MinValue, MaxValue)) return 0; return Mathf.InverseLerp(MinValue, MaxValue, ValueX); } set { this.ValueX = Mathf.Lerp(MinValue, MaxValue, value); } } [SerializeField] private float m_ValueY = 1f; public float ValueY { get { if (WholeNumbers) return Mathf.Round(m_ValueY); return m_ValueY; } set { SetY(value); } } public float NormalizedValueY { get { if (Mathf.Approximately(MinValue, MaxValue)) return 0; return Mathf.InverseLerp(MinValue, MaxValue, ValueY); } set { this.ValueY = Mathf.Lerp(MinValue, MaxValue, value); } } [Space(6)] // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers. [SerializeField] private BoxSliderEvent m_OnValueChanged = new BoxSliderEvent(); public BoxSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } } // Private fields //private Image m_FillImage; //private Transform m_FillTransform; //private RectTransform m_FillContainerRect; private Transform m_HandleTransform; private RectTransform m_HandleContainerRect; // The offset from handle position to mouse down position private Vector2 m_Offset = Vector2.zero; private DrivenRectTransformTracker m_Tracker; // Size of each step. float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } } protected BoxSlider() { } #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); if (WholeNumbers) { m_MinValue = Mathf.Round(m_MinValue); m_MaxValue = Mathf.Round(m_MaxValue); } UpdateCachedReferences(); Set(m_ValueX, false); SetY(m_ValueY, false); // Update rects since other things might affect them even if value didn't change. UpdateVisuals(); var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this); if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying) CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); } #endif // if UNITY_EDITOR public virtual void Rebuild(CanvasUpdate executing) { #if UNITY_EDITOR if (executing == CanvasUpdate.Prelayout) OnValueChanged.Invoke(ValueX, ValueY); #endif } public void LayoutComplete() { } public 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(); Set(m_ValueX, false); SetY(m_ValueY, false); // Update rects since they need to be initialized correctly. UpdateVisuals(); } protected override void OnDisable() { m_Tracker.Clear(); base.OnDisable(); } void UpdateCachedReferences() { if (m_HandleRect) { m_HandleTransform = m_HandleRect.transform; if (m_HandleTransform.parent != null) m_HandleContainerRect = m_HandleTransform.parent.GetComponent(); } else { m_HandleContainerRect = null; } } // Set the valueUpdate the visible Image. void Set(float input) { Set(input, true); } void Set(float input, bool sendCallback) { // Clamp the input float newValue = Mathf.Clamp(input, MinValue, MaxValue); if (WholeNumbers) newValue = Mathf.Round(newValue); // If the stepped value doesn't match the last one, it's time to update if (m_ValueX == newValue) return; m_ValueX = newValue; UpdateVisuals(); if (sendCallback) m_OnValueChanged.Invoke(newValue, ValueY); } void SetY(float input) { SetY(input, true); } void SetY(float input, bool sendCallback) { // Clamp the input float newValue = Mathf.Clamp(input, MinValue, MaxValue); if (WholeNumbers) newValue = Mathf.Round(newValue); // If the stepped value doesn't match the last one, it's time to update if (m_ValueY == newValue) return; m_ValueY = newValue; UpdateVisuals(); if (sendCallback) m_OnValueChanged.Invoke(ValueX, newValue); } protected override void OnRectTransformDimensionsChange() { base.OnRectTransformDimensionsChange(); UpdateVisuals(); } enum Axis { Horizontal = 0, Vertical = 1 } // 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(); //to business! if (m_HandleContainerRect != null) { m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors); Vector2 anchorMin = Vector2.zero; Vector2 anchorMax = Vector2.one; anchorMin[0] = anchorMax[0] = (NormalizedValueX); anchorMin[1] = anchorMax[1] = (NormalizedValueY); m_HandleRect.anchorMin = anchorMin; m_HandleRect.anchorMax = anchorMax; } } // Update the slider's position based on the mouse. void UpdateDrag(PointerEventData eventData, Camera cam) { RectTransform clickRect = m_HandleContainerRect; if (clickRect != null && clickRect.rect.size[0] > 0) { Vector2 localCursor; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor)) return; localCursor -= clickRect.rect.position; float val = Mathf.Clamp01((localCursor - m_Offset)[0] / clickRect.rect.size[0]); NormalizedValueX = (val); float valY = Mathf.Clamp01((localCursor - m_Offset)[1] / clickRect.rect.size[1]); NormalizedValueY = (valY); } } private bool MayDrag(PointerEventData eventData) { return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left; } public override void OnPointerDown(PointerEventData eventData) { if (!MayDrag(eventData)) return; base.OnPointerDown(eventData); m_Offset = Vector2.zero; if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera)) { Vector2 localMousePos; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos)) m_Offset = localMousePos; m_Offset.y = -m_Offset.y; } else { // Outside the slider handle - jump to this point instead UpdateDrag(eventData, eventData.pressEventCamera); } } public virtual void OnDrag(PointerEventData eventData) { if (!MayDrag(eventData)) return; UpdateDrag(eventData, eventData.pressEventCamera); } //public override void OnMove(AxisEventData eventData) //{ // if (!IsActive() || !IsInteractable()) // { // base.OnMove(eventData); // return; // } // switch (eventData.moveDir) // { // case MoveDirection.Left: // if (axis == Axis.Horizontal && FindSelectableOnLeft() == null) { // Set(reverseValue ? value + stepSize : value - stepSize); // SetY (reverseValue ? valueY + stepSize : valueY - stepSize); // } // else // base.OnMove(eventData); // break; // case MoveDirection.Right: // if (axis == Axis.Horizontal && FindSelectableOnRight() == null) { // Set(reverseValue ? value - stepSize : value + stepSize); // SetY(reverseValue ? valueY - stepSize : valueY + stepSize); // } // else // base.OnMove(eventData); // break; // case MoveDirection.Up: // if (axis == Axis.Vertical && FindSelectableOnUp() == null) { // Set(reverseValue ? value - stepSize : value + stepSize); // SetY(reverseValue ? valueY - stepSize : valueY + stepSize); // } // else // base.OnMove(eventData); // break; // case MoveDirection.Down: // if (axis == Axis.Vertical && FindSelectableOnDown() == null) { // Set(reverseValue ? value + stepSize : value - stepSize); // SetY(reverseValue ? valueY + stepSize : valueY - stepSize); // } // else // base.OnMove(eventData); // break; // } //} //public override Selectable FindSelectableOnLeft() //{ // if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal) // return null; // return base.FindSelectableOnLeft(); //} //public override Selectable FindSelectableOnRight() //{ // if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal) // return null; // return base.FindSelectableOnRight(); //} //public override Selectable FindSelectableOnUp() //{ // if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical) // return null; // return base.FindSelectableOnUp(); //} //public override Selectable FindSelectableOnDown() //{ // if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical) // return null; // return base.FindSelectableOnDown(); //} public virtual void OnInitializePotentialDrag(PointerEventData eventData) { eventData.useDragThreshold = false; } } }