438 lines
14 KiB
C#
438 lines
14 KiB
C#
///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<float, float> { }
|
|
|
|
[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<T>(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<T>(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<RectTransform>();
|
|
}
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|