You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
542 lines
12 KiB
542 lines
12 KiB
using UnityEngine; |
|
using UnityEngine.EventSystems; |
|
using UnityEngine.Events; |
|
using System.Collections.Generic; |
|
|
|
namespace UIWidgets { |
|
/// <summary> |
|
/// Centered slider base class (zero at center, positive and negative parts have different scales). |
|
/// </summary> |
|
public abstract class CenteredSliderBase<T> : UIBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler |
|
where T : struct |
|
{ |
|
/// <summary> |
|
/// OnChangeEvent |
|
/// </summary> |
|
[System.Serializable] |
|
public class OnChangeEvent: UnityEvent<T> { |
|
|
|
} |
|
|
|
/// <summary> |
|
/// Value. |
|
/// </summary> |
|
[SerializeField] |
|
protected T _value; |
|
|
|
/// <summary> |
|
/// Gets or sets the value. |
|
/// </summary> |
|
/// <value>Value.</value> |
|
public T Value { |
|
get { |
|
return _value; |
|
} |
|
set { |
|
SetValue(value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The minimum limit. |
|
/// </summary> |
|
[SerializeField] |
|
protected T limitMin; |
|
|
|
/// <summary> |
|
/// Gets or sets the minimum limit. |
|
/// </summary> |
|
/// <value>The minimum limit.</value> |
|
public T LimitMin { |
|
get { |
|
return limitMin; |
|
} |
|
set { |
|
limitMin = value; |
|
SetValue(_value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The maximum limit. |
|
/// </summary> |
|
[SerializeField] |
|
protected T limitMax; |
|
|
|
/// <summary> |
|
/// Gets or sets the maximum limit. |
|
/// </summary> |
|
/// <value>The maximum limit.</value> |
|
public T LimitMax { |
|
get { |
|
return limitMax; |
|
} |
|
set { |
|
limitMax = value; |
|
SetValue(_value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The use value limits. |
|
/// </summary> |
|
[SerializeField] |
|
protected bool useValueLimits; |
|
|
|
/// <summary> |
|
/// Gets or sets use value limits. |
|
/// </summary> |
|
/// <value><c>true</c> if use value limits; otherwise, <c>false</c>.</value> |
|
public bool UseValueLimits { |
|
get { |
|
return useValueLimits; |
|
} |
|
set { |
|
useValueLimits = value; |
|
SetValue(_value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The value minimum limit. |
|
/// </summary> |
|
[SerializeField] |
|
protected T valueMin; |
|
|
|
/// <summary> |
|
/// Gets or sets the value minimum limit. |
|
/// </summary> |
|
/// <value>The value minimum limit.</value> |
|
public T ValueMin { |
|
get { |
|
return valueMin; |
|
} |
|
set { |
|
valueMin = value; |
|
SetValue(_value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The value maximum limit. |
|
/// </summary> |
|
[SerializeField] |
|
protected T valueMax; |
|
|
|
/// <summary> |
|
/// Gets or sets the value maximum limit. |
|
/// </summary> |
|
/// <value>The maximum limit.</value> |
|
public T ValueMax { |
|
get { |
|
return valueMax; |
|
} |
|
set { |
|
valueMax = value; |
|
SetValue(_value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The step. |
|
/// </summary> |
|
[SerializeField] |
|
protected T step; |
|
|
|
/// <summary> |
|
/// Gets or sets the step. |
|
/// </summary> |
|
/// <value>The step.</value> |
|
public T Step { |
|
get { |
|
return step; |
|
} |
|
set { |
|
step = value; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Whole number of steps. |
|
/// </summary> |
|
public bool WholeNumberOfSteps = false; |
|
|
|
/// <summary> |
|
/// The handle. |
|
/// </summary> |
|
[SerializeField] |
|
protected RangeSliderHandle handle; |
|
|
|
/// <summary> |
|
/// The handle rect. |
|
/// </summary> |
|
protected RectTransform handleRect; |
|
|
|
/// <summary> |
|
/// Gets the handle rect. |
|
/// </summary> |
|
/// <value>The handle rect.</value> |
|
public RectTransform HandleRect { |
|
get { |
|
if (handle!=null && handleRect==null) |
|
{ |
|
handleRect = handle.transform as RectTransform; |
|
} |
|
return handleRect; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets or sets the handle. |
|
/// </summary> |
|
/// <value>The handle.</value> |
|
public RangeSliderHandle Handle { |
|
get { |
|
return handle; |
|
} |
|
set { |
|
SetHandle(value); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The usable range rect. |
|
/// </summary> |
|
[SerializeField] |
|
protected RectTransform UsableRangeRect; |
|
|
|
/// <summary> |
|
/// The fill rect. |
|
/// </summary> |
|
[SerializeField] |
|
protected RectTransform FillRect; |
|
|
|
/// <summary> |
|
/// The range slider rect. |
|
/// </summary> |
|
protected RectTransform rangeSliderRect; |
|
|
|
/// <summary> |
|
/// Gets the handle maximum rect. |
|
/// </summary> |
|
/// <value>The handle maximum rect.</value> |
|
public RectTransform RangeSliderRect { |
|
get { |
|
if (rangeSliderRect==null) |
|
{ |
|
rangeSliderRect = transform as RectTransform; |
|
} |
|
return rangeSliderRect; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// OnValuesChange event. |
|
/// </summary> |
|
public OnChangeEvent OnValuesChange = new OnChangeEvent(); |
|
|
|
/// <summary> |
|
/// OnChange event. |
|
/// </summary> |
|
public UnityEvent OnChange = new UnityEvent(); |
|
|
|
/// <summary> |
|
/// Is init called? |
|
/// </summary> |
|
bool isInitCalled; |
|
|
|
/// <summary> |
|
/// Init this instance. |
|
/// </summary> |
|
protected virtual void Init() |
|
{ |
|
if (isInitCalled) |
|
{ |
|
return ; |
|
} |
|
isInitCalled = true; |
|
|
|
SetHandle(handle); |
|
UpdateHandle(); |
|
UpdateFill(); |
|
} |
|
|
|
/// <summary> |
|
/// Implementation of a callback that is sent if an associated RectTransform has it's dimensions changed. |
|
/// </summary> |
|
protected override void OnRectTransformDimensionsChange() |
|
{ |
|
UpdateHandle(); |
|
UpdateFill(); |
|
} |
|
|
|
/// <summary> |
|
/// Called by a BaseInputModule when an OnPointerDown event occurs. |
|
/// </summary> |
|
/// <param name="eventData">Event data.</param> |
|
public void OnPointerDown(PointerEventData eventData) |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Called by a BaseInputModule when an OnPointerUp event occurs. |
|
/// </summary> |
|
/// <param name="eventData">Event data.</param> |
|
public void OnPointerUp(PointerEventData eventData) |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Sets the value. |
|
/// </summary> |
|
/// <param name="value">Value.</param> |
|
protected virtual void SetValue(T value) |
|
{ |
|
if (!EqualityComparer<T>.Default.Equals(_value, InBounds(value))) |
|
{ |
|
_value = InBounds(value); |
|
UpdateHandle(); |
|
OnValuesChange.Invoke(_value); |
|
OnChange.Invoke(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Sets the handle. |
|
/// </summary> |
|
/// <param name="value">Value.</param> |
|
protected virtual void SetHandle(RangeSliderHandle value) |
|
{ |
|
handle = value; |
|
handle.IsHorizontal = IsHorizontal; |
|
handle.PositionLimits = PositionLimits; |
|
handle.PositionChanged = UpdateValue; |
|
handle.Increase = Increase; |
|
handle.Decrease = Decrease; |
|
} |
|
|
|
/// <summary> |
|
/// Start this instance. |
|
/// </summary> |
|
protected override void Start() |
|
{ |
|
Init(); |
|
} |
|
|
|
/// <summary> |
|
/// Sets the limits. |
|
/// </summary> |
|
/// <param name="min">Minimum.</param> |
|
/// <param name="max">Max.</param> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// Sets the value limits. |
|
/// </summary> |
|
/// <param name="min">Minimum.</param> |
|
/// <param name="max">Max.</param> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// Determines whether this instance is horizontal. |
|
/// </summary> |
|
/// <returns><c>true</c> if this instance is horizontal; otherwise, <c>false</c>.</returns> |
|
protected virtual bool IsHorizontal() |
|
{ |
|
return true; |
|
} |
|
|
|
/// <summary> |
|
/// Returns size of usable rect. |
|
/// </summary> |
|
/// <returns>The size.</returns> |
|
protected float RangeSize() |
|
{ |
|
return (IsHorizontal()) ? UsableRangeRect.rect.width : UsableRangeRect.rect.height; |
|
} |
|
|
|
/// <summary> |
|
/// Size of the handle. |
|
/// </summary> |
|
/// <returns>The handle size.</returns> |
|
protected float HandleSize() |
|
{ |
|
return (IsHorizontal()) ? HandleRect.rect.width : HandleRect.rect.height; |
|
} |
|
|
|
/// <summary> |
|
/// Updates the minimum value. |
|
/// </summary> |
|
/// <param name="position">Position.</param> |
|
protected void UpdateValue(float position) |
|
{ |
|
_value = PositionToValue(position - GetStartPoint()); |
|
UpdateHandle(); |
|
OnValuesChange.Invoke(_value); |
|
OnChange.Invoke(); |
|
} |
|
|
|
/// <summary> |
|
/// Value to position. |
|
/// </summary> |
|
/// <returns>Position.</returns> |
|
/// <param name="value">Value.</param> |
|
protected abstract float ValueToPosition(T value); |
|
|
|
/// <summary> |
|
/// Position to value. |
|
/// </summary> |
|
/// <returns>Value.</returns> |
|
/// <param name="position">Position.</param> |
|
protected abstract T PositionToValue(float position); |
|
|
|
/// <summary> |
|
/// Gets the start point. |
|
/// </summary> |
|
/// <returns>The start point.</returns> |
|
protected float GetStartPoint() |
|
{ |
|
return IsHorizontal() ? -UsableRangeRect.sizeDelta.x / 2f : -UsableRangeRect.sizeDelta.y / 2f; |
|
} |
|
|
|
/// <summary> |
|
/// Position range for minimum handle. |
|
/// </summary> |
|
/// <returns>The position limits.</returns> |
|
protected abstract Vector2 PositionLimits(); |
|
|
|
/// <summary> |
|
/// Fit value to bounds. |
|
/// </summary> |
|
/// <returns>Value.</returns> |
|
/// <param name="value">Value.</param> |
|
protected abstract T InBounds(T value); |
|
|
|
/// <summary> |
|
/// Increases the minimum value. |
|
/// </summary> |
|
protected abstract void Increase(); |
|
|
|
/// <summary> |
|
/// Decreases the minimum value. |
|
/// </summary> |
|
protected abstract void Decrease(); |
|
|
|
/// <summary> |
|
/// Updates the handle. |
|
/// </summary> |
|
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(); |
|
} |
|
|
|
/// <summary> |
|
/// Determines whether this instance is positive value. |
|
/// </summary> |
|
/// <returns><c>true</c> if this instance is positive value; otherwise, <c>false</c>.</returns> |
|
protected abstract bool IsPositiveValue(); |
|
|
|
/// <summary> |
|
/// Updates the fill size. |
|
/// </summary> |
|
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); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Raises the pointer click event. |
|
/// </summary> |
|
/// <param name="eventData">Event data.</param> |
|
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 |
|
/// <summary> |
|
/// Handle values change from editor. |
|
/// </summary> |
|
public void EditorUpdate() |
|
{ |
|
if (handle!=null && UsableRangeRect!=null && FillRect!=null) |
|
{ |
|
UpdateHandle(); |
|
} |
|
} |
|
#endif |
|
} |
|
} |