using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using System.Collections.Generic; namespace UIWidgets { /// /// Centered slider base class (zero at center, positive and negative parts have different scales). /// public abstract class CenteredSliderBase : UIBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler where T : struct { /// /// OnChangeEvent /// [System.Serializable] public class OnChangeEvent: UnityEvent { } /// /// Value. /// [SerializeField] protected T _value; /// /// Gets or sets the value. /// /// Value. public T Value { get { return _value; } set { SetValue(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; SetValue(_value); } } /// /// 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; SetValue(_value); } } /// /// The use value limits. /// [SerializeField] protected bool useValueLimits; /// /// Gets or sets use value limits. /// /// true if use value limits; otherwise, false. public bool UseValueLimits { get { return useValueLimits; } set { useValueLimits = value; SetValue(_value); } } /// /// The value minimum limit. /// [SerializeField] protected T valueMin; /// /// Gets or sets the value minimum limit. /// /// The value minimum limit. public T ValueMin { get { return valueMin; } set { valueMin = value; SetValue(_value); } } /// /// The value maximum limit. /// [SerializeField] protected T valueMax; /// /// Gets or sets the value maximum limit. /// /// The maximum limit. public T ValueMax { get { return valueMax; } set { valueMax = value; SetValue(_value); } } /// /// The step. /// [SerializeField] protected T step; /// /// Gets or sets the step. /// /// The step. public T Step { get { return step; } set { step = value; } } /// /// Whole number of steps. /// public bool WholeNumberOfSteps = false; /// /// The handle. /// [SerializeField] protected RangeSliderHandle handle; /// /// The handle rect. /// protected RectTransform handleRect; /// /// Gets the handle rect. /// /// The handle rect. public RectTransform HandleRect { get { if (handle!=null && handleRect==null) { handleRect = handle.transform as RectTransform; } return handleRect; } } /// /// Gets or sets the handle. /// /// The handle. public RangeSliderHandle Handle { get { return handle; } set { SetHandle(value); } } /// /// 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(); /// /// Is init called? /// bool isInitCalled; /// /// Init this instance. /// protected virtual void Init() { if (isInitCalled) { return ; } isInitCalled = true; SetHandle(handle); UpdateHandle(); UpdateFill(); } /// /// Implementation of a callback that is sent if an associated RectTransform has it's dimensions changed. /// protected override void OnRectTransformDimensionsChange() { UpdateHandle(); UpdateFill(); } /// /// Called by a BaseInputModule when an OnPointerDown event occurs. /// /// Event data. public void OnPointerDown(PointerEventData eventData) { } /// /// Called by a BaseInputModule when an OnPointerUp event occurs. /// /// Event data. public void OnPointerUp(PointerEventData eventData) { } /// /// Sets the value. /// /// Value. protected virtual void SetValue(T value) { if (!EqualityComparer.Default.Equals(_value, InBounds(value))) { _value = InBounds(value); UpdateHandle(); OnValuesChange.Invoke(_value); OnChange.Invoke(); } } /// /// Sets the handle. /// /// Value. protected virtual void SetHandle(RangeSliderHandle value) { handle = value; handle.IsHorizontal = IsHorizontal; handle.PositionLimits = PositionLimits; handle.PositionChanged = UpdateValue; handle.Increase = Increase; handle.Decrease = Decrease; } /// /// Start this instance. /// protected override void Start() { Init(); } /// /// Sets the limits. /// /// Minimum. /// Max. public void SetLimit(T min, T max) { // set limits to skip InBounds check limitMin = min; limitMax = max; // set limits with InBounds check and update handle's positions LimitMin = limitMin; LimitMax = limitMax; } /// /// Sets the value limits. /// /// Minimum. /// Max. public void SetValueLimit(T min, T max) { // set limits to skip InBounds check valueMin = min; valueMax = max; // set limits with InBounds check and update handle's positions ValueMin = valueMin; ValueMax = valueMax; } /// /// 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 RangeSize() { return (IsHorizontal()) ? UsableRangeRect.rect.width : UsableRangeRect.rect.height; } /// /// Size of the handle. /// /// The handle size. protected float HandleSize() { return (IsHorizontal()) ? HandleRect.rect.width : HandleRect.rect.height; } /// /// Updates the minimum value. /// /// Position. protected void UpdateValue(float position) { _value = PositionToValue(position - GetStartPoint()); UpdateHandle(); OnValuesChange.Invoke(_value); 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 PositionLimits(); /// /// Fit value to bounds. /// /// Value. /// Value. protected abstract T InBounds(T value); /// /// Increases the minimum value. /// protected abstract void Increase(); /// /// Decreases the minimum value. /// protected abstract void Decrease(); /// /// Updates the handle. /// protected void UpdateHandle() { var new_position = HandleRect.anchoredPosition; if (IsHorizontal()) { new_position.x = ValueToPosition(_value) + HandleRect.rect.width * (HandleRect.pivot.x - 0.5f); } else { new_position.y = ValueToPosition(_value) + HandleRect.rect.width * (HandleRect.pivot.x - 0.5f); } HandleRect.anchoredPosition = new_position; UpdateFill(); } /// /// Determines whether this instance is positive value. /// /// true if this instance is positive value; otherwise, false. protected abstract bool IsPositiveValue(); /// /// Updates the fill size. /// protected virtual void UpdateFill() { FillRect.anchorMin = new Vector2(0.5f, 0.5f); FillRect.anchorMax = new Vector2(0.5f, 0.5f);//1.0 if (IsHorizontal()) { if (IsPositiveValue()) { FillRect.pivot = new Vector2(0.0f, 0.5f); FillRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, HandleRect.localPosition.x - UsableRangeRect.localPosition.x); } else { FillRect.pivot = new Vector2(1.0f, 0.5f); FillRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, UsableRangeRect.localPosition.x - HandleRect.localPosition.x); } } else { if (IsPositiveValue()) { FillRect.pivot = new Vector2(0.5f, 0.0f); FillRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, HandleRect.localPosition.y - UsableRangeRect.localPosition.y); } else { FillRect.pivot = new Vector2(0.5f, 1.0f); FillRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, UsableRangeRect.localPosition.y - HandleRect.localPosition.y); } } } /// /// Raises the pointer click event. /// /// Event data. public virtual 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_position = (IsHorizontal() ? curCursor.x : curCursor.y) + GetStartPoint(); UpdateValue(new_position); } #if UNITY_EDITOR /// /// Handle values change from editor. /// public void EditorUpdate() { if (handle!=null && UsableRangeRect!=null && FillRect!=null) { UpdateHandle(); } } #endif } }