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
2 years ago
|
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
|
||
|
}
|
||
|
}
|