using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using System.Collections.Generic; namespace UIWidgets { /// /// Range slider base. /// public abstract class RangeSliderBase : MonoBehaviour, IPointerClickHandler where T : struct { /// /// OnChangeEvent. /// [System.Serializable] public class OnChangeEvent : UnityEvent { } /// /// The minimum value. /// [SerializeField] protected T valueMin; /// /// Gets or sets the minimum value. /// /// The minimum value. public T ValueMin { get { return valueMin; } set { if (!EqualityComparer.Default.Equals(valueMin, InBoundsMin(value))) { valueMin = InBoundsMin(value); UpdateMinHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } } } /// /// The maximum value. /// [SerializeField] protected T valueMax; /// /// Gets or sets the maximum value. /// /// The maximum value. public T ValueMax { get { return valueMax; } set { if (!EqualityComparer.Default.Equals(valueMax, InBoundsMax(value))) { valueMax = InBoundsMax(value); UpdateMaxHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } } } /// /// The step. /// [SerializeField] protected T step; /// /// Gets or sets the step. /// /// The step. public T Step { get { return step; } set { step = value; } } /// /// The minimum limit. /// [SerializeField] protected T limitMin; /// /// Gets or sets the minimum limit. /// /// The minimum limit. public T LimitMin { get { return limitMin; } set { limitMin = value; ValueMin = valueMin; ValueMax = valueMax; } } /// /// The maximum limit. /// [SerializeField] protected T limitMax; /// /// Gets or sets the maximum limit. /// /// The maximum limit. public T LimitMax { get { return limitMax; } set { limitMax = value; ValueMin = valueMin; ValueMax = valueMax; } } /// /// The handle minimum. /// [SerializeField] protected RangeSliderHandle handleMin; /// /// The handle minimum rect. /// protected RectTransform handleMinRect; /// /// Gets the handle minimum rect. /// /// The handle minimum rect. public RectTransform HandleMinRect { get { if (handleMin!=null && handleMinRect==null) { handleMinRect = handleMin.transform as RectTransform; } return handleMinRect; } } /// /// Gets or sets the handle minimum. /// /// The handle minimum. public RangeSliderHandle HandleMin { get { return handleMin; } set { handleMin = value; handleMinRect = null; handleMin.IsHorizontal = IsHorizontal; handleMin.PositionLimits = MinPositionLimits; handleMin.PositionChanged = UpdateMinValue; handleMin.OnSubmit = SelectMaxHandle; handleMin.Increase = IncreaseMin; handleMin.Decrease = DecreaseMin; HandleMinRect.anchorMin = new Vector2(0, 0); HandleMinRect.anchorMax = (IsHorizontal()) ? new Vector2(0, 1) : new Vector2(1, 0); } } /// /// The handle max. /// [SerializeField] protected RangeSliderHandle handleMax; /// /// The handle max rect. /// protected RectTransform handleMaxRect; /// /// Gets the handle maximum rect. /// /// The handle maximum rect. public RectTransform HandleMaxRect { get { if (handleMax!=null && handleMaxRect==null) { handleMaxRect = handleMax.transform as RectTransform; } return handleMaxRect; } } /// /// Gets or sets the handle max. /// /// The handle max. public RangeSliderHandle HandleMax { get { return handleMax; } set { handleMax = value; handleMaxRect = null; handleMax.IsHorizontal = IsHorizontal; handleMax.PositionLimits = MaxPositionLimits; handleMax.PositionChanged = UpdateMaxValue; handleMax.OnSubmit = SelectMinHandle; handleMax.Increase = IncreaseMax; handleMax.Decrease = DecreaseMax; HandleMaxRect.anchorMin = new Vector2(0, 0); HandleMaxRect.anchorMax = (IsHorizontal()) ? new Vector2(0, 1) : new Vector2(1, 0); } } /// /// The usable range rect. /// [SerializeField] protected RectTransform UsableRangeRect; /// /// The fill rect. /// [SerializeField] protected RectTransform FillRect; /// /// The range slider rect. /// protected RectTransform rangeSliderRect; /// /// Gets the handle maximum rect. /// /// The handle maximum rect. public RectTransform RangeSliderRect { get { if (rangeSliderRect==null) { rangeSliderRect = transform as RectTransform; } return rangeSliderRect; } } /// /// OnValuesChange event. /// public OnChangeEvent OnValuesChange = new OnChangeEvent(); /// /// OnChange event. /// public UnityEvent OnChange = new UnityEvent(); /// /// Whole number of steps. /// public bool WholeNumberOfSteps = false; void Awake() { } bool initCalled; void Init() { if (initCalled) { return ; } initCalled = true; HandleMin = handleMin; HandleMax = handleMax; UpdateMinHandle(); UpdateMaxHandle(); UpdateFill(); } void Start() { Init(); } /// /// Sets the values. /// /// Minimum. /// Max. public void SetValue(T min, T max) { var oldValueMin = valueMin; var oldValueMax = valueMax; valueMin = min; valueMax = max; valueMin = InBoundsMin(valueMin); valueMax = InBoundsMax(valueMax); var min_equals = EqualityComparer.Default.Equals(oldValueMin, valueMin); var max_equals = EqualityComparer.Default.Equals(oldValueMax, valueMax); if (!min_equals || !max_equals) { UpdateMinHandle(); UpdateMaxHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } } /// /// Sets the limits. /// /// Minimum. /// Max. public void SetLimit(T min, T max) { var oldValueMin = valueMin; var oldValueMax = valueMax; limitMin = min; limitMax = max; valueMin = InBoundsMin(valueMin); valueMax = InBoundsMax(valueMax); var min_equals = EqualityComparer.Default.Equals(oldValueMin, valueMin); var max_equals = EqualityComparer.Default.Equals(oldValueMax, valueMax); if (!min_equals || !max_equals) { UpdateMinHandle(); UpdateMaxHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } } /// /// Determines whether this instance is horizontal. /// /// true if this instance is horizontal; otherwise, false. protected virtual bool IsHorizontal() { return true; } /// /// Returns size of usable rect. /// /// The size. protected float FillSize() { return (IsHorizontal()) ? UsableRangeRect.rect.width : UsableRangeRect.rect.height; } /// /// Minimum size of the handle. /// /// The handle size. protected float MinHandleSize() { if (IsHorizontal()) { return HandleMinRect.rect.width; } else { return HandleMinRect.rect.height; } } /// /// Maximum size of the handle. /// /// The handle size. protected float MaxHandleSize() { if (IsHorizontal()) { return HandleMaxRect.rect.width; } else { return HandleMaxRect.rect.height; } } /// /// Updates the minimum value. /// /// Position. protected void UpdateMinValue(float position) { valueMin = PositionToValue(position - GetStartPoint()); UpdateMinHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } /// /// Updates the maximum value. /// /// Position. protected void UpdateMaxValue(float position) { valueMax = PositionToValue(position - GetStartPoint()); UpdateMaxHandle(); UpdateFill(); OnValuesChange.Invoke(valueMin, valueMax); OnChange.Invoke(); } /// /// Value to position. /// /// Position. /// Value. protected abstract float ValueToPosition(T value); /// /// Position to value. /// /// Value. /// Position. protected abstract T PositionToValue(float position); /// /// Gets the start point. /// /// The start point. protected float GetStartPoint() { return IsHorizontal() ? -UsableRangeRect.sizeDelta.x / 2f : -UsableRangeRect.sizeDelta.y / 2f; } /// /// Position range for minimum handle. /// /// The position limits. protected abstract Vector2 MinPositionLimits(); /// /// Position range for maximum handle. /// /// The position limits. protected abstract Vector2 MaxPositionLimits(); /// /// Fit value to bounds. /// /// Value. /// Value. protected abstract T InBounds(T value); /// /// Fit minumum value to bounds. /// /// Value. /// Value. protected abstract T InBoundsMin(T value); /// /// Fit maximum value to bounds. /// /// Value. /// Value. protected abstract T InBoundsMax(T value); /// /// Increases the minimum value. /// protected abstract void IncreaseMin(); /// /// Decreases the minimum value. /// protected abstract void DecreaseMin(); /// /// Increases the maximum value. /// protected abstract void IncreaseMax(); /// /// Decreases the maximum value. /// protected abstract void DecreaseMax(); /// /// Updates the minimum handle. /// protected void UpdateMinHandle() { UpdateHandle(HandleMinRect, valueMin); } /// /// Updates the maximum handle. /// protected void UpdateMaxHandle() { UpdateHandle(HandleMaxRect, valueMax); } /// /// Updates the fill. /// void UpdateFill() { if (IsHorizontal()) { FillRect.anchoredPosition = new Vector2(HandleMinRect.anchoredPosition.x, FillRect.anchoredPosition.y); var sizeDelta = new Vector2((HandleMaxRect.anchoredPosition.x - HandleMinRect.anchoredPosition.x), FillRect.sizeDelta.y); FillRect.sizeDelta = sizeDelta; } else { FillRect.anchoredPosition = new Vector2(FillRect.anchoredPosition.x, HandleMinRect.anchoredPosition.y); var sizeDelta = new Vector2(FillRect.sizeDelta.x, (+ HandleMaxRect.anchoredPosition.y - HandleMinRect.anchoredPosition.y)); FillRect.sizeDelta = sizeDelta; } } /// /// Updates the handle. /// /// Handle transform. /// Value. protected void UpdateHandle(RectTransform handleTransform, T value) { var new_position = handleTransform.anchoredPosition; if (IsHorizontal()) { new_position.x = ValueToPosition(value) + handleTransform.rect.width * (handleTransform.pivot.x - 0.5f); } else { new_position.y = ValueToPosition(value) + handleTransform.rect.height * (handleTransform.pivot.y - 0.5f); } handleTransform.anchoredPosition = new_position; } /// /// Selects the minimum handle. /// void SelectMinHandle() { if ((EventSystem.current!=null) && (!EventSystem.current.alreadySelecting)) { EventSystem.current.SetSelectedGameObject(handleMin.gameObject); } } /// /// Selects the max handle. /// void SelectMaxHandle() { if ((EventSystem.current!=null) && (!EventSystem.current.alreadySelecting)) { EventSystem.current.SetSelectedGameObject(handleMax.gameObject); } } /// /// Raises the pointer click event. /// /// Event data. public void OnPointerClick(PointerEventData eventData) { if (eventData.button!=PointerEventData.InputButton.Left) { return; } Vector2 curCursor; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(UsableRangeRect, eventData.position, eventData.pressEventCamera, out curCursor)) { return ; } curCursor -= UsableRangeRect.rect.position; var new_min_position = (IsHorizontal() ? curCursor.x : curCursor.y) + GetStartPoint(); var new_max_position = new_min_position - MaxHandleSize(); var min_position = IsHorizontal() ? HandleMinRect.anchoredPosition.x : HandleMinRect.anchoredPosition.y; var max_position = IsHorizontal() ? HandleMaxRect.anchoredPosition.x : HandleMaxRect.anchoredPosition.y; var delta_min = new_min_position - min_position; var delta_max = max_position - new_max_position; if (delta_min < delta_max) { UpdateMinValue(new_min_position); } else { UpdateMaxValue(new_max_position); } } #if UNITY_EDITOR /// /// Handle values change from editor. /// public void EditorUpdate() { if (handleMin!=null && handleMax!=null && UsableRangeRect!=null && FillRect!=null) { UpdateMinHandle(); UpdateMaxHandle(); UpdateFill(); } } #endif } }