From dfef0711755c6fc3a96cd8644aa73da133ca7cd7 Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Mon, 20 Jul 2020 00:46:44 -0700 Subject: [PATCH] Box slider script --- Assets/ConformalDecals/BoxSlider.cs | 36 +++++ Source/ConformalDecals/UI/BoxSlider.cs | 176 +++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 Assets/ConformalDecals/BoxSlider.cs create mode 100644 Source/ConformalDecals/UI/BoxSlider.cs diff --git a/Assets/ConformalDecals/BoxSlider.cs b/Assets/ConformalDecals/BoxSlider.cs new file mode 100644 index 0000000..9521bc3 --- /dev/null +++ b/Assets/ConformalDecals/BoxSlider.cs @@ -0,0 +1,36 @@ +using System; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace ConformalDecals.UI { + [AddComponentMenu("UI/BoxSlider", 35)] + [RequireComponent(typeof(RectTransform))] + public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement { + [Serializable] + public class BoxSliderEvent : UnityEvent { } + + [SerializeField] private RectTransform _handleRect; + [SerializeField] private Vector2 _value = Vector2.zero; + + // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers. + [SerializeField] private BoxSliderEvent _onValueChanged = new BoxSliderEvent(); + + public BoxSliderEvent OnValueChanged { + get => _onValueChanged; + set => _onValueChanged = value; + } + + // Private fields + public void OnDrag(PointerEventData eventData) { } + + public void OnInitializePotentialDrag(PointerEventData eventData) { } + + public void Rebuild(CanvasUpdate executing) { } + + public void LayoutComplete() { } + + public void GraphicUpdateComplete() { } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/UI/BoxSlider.cs b/Source/ConformalDecals/UI/BoxSlider.cs new file mode 100644 index 0000000..cf637ef --- /dev/null +++ b/Source/ConformalDecals/UI/BoxSlider.cs @@ -0,0 +1,176 @@ +using System; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace ConformalDecals.UI { + [AddComponentMenu("UI/BoxSlider", 35)] + [RequireComponent(typeof(RectTransform))] + public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement { + [Serializable] + public class BoxSliderEvent : UnityEvent { } + + [SerializeField] private RectTransform _handleRect; + [SerializeField] private Vector2 _value = Vector2.zero; + + public RectTransform HandleRect { + get => _handleRect; + set { + if (value == null) throw new ArgumentNullException(nameof(value)); + if (value != _handleRect) { + _handleRect = value; + UpdateCachedReferences(); + UpdateVisuals(); + } + } + } + + + public Vector2 Value { + get => _value; + set { + _value = value; + UpdateVisuals(); + } + } + + [Space(6)] + + // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers. + [SerializeField] + private BoxSliderEvent _onValueChanged = new BoxSliderEvent(); + + public BoxSliderEvent OnValueChanged { + get => _onValueChanged; + set => _onValueChanged = value; + } + + // Private fields + + private Transform _handleTransform; + private RectTransform _handleContainerRect; + + // The offset from handle position to mouse down position + private Vector2 _offset = Vector2.zero; + +#if UNITY_EDITOR + protected override void OnValidate() { + base.OnValidate(); + + //Onvalidate is called before OnEnabled. We need to make sure not to touch any other objects before OnEnable is run. + if (IsActive()) { + UpdateCachedReferences(); + // Update rects since other things might affect them even if value didn't change. + UpdateVisuals(); + } + +#if UNITY_2018_3_OR_NEWER + + if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying) + CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); + +#else + var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this); + if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying) + CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); +#endif + } +#endif // if UNITY_EDITOR + + public virtual void Rebuild(CanvasUpdate executing) { +#if UNITY_EDITOR + if (executing == CanvasUpdate.Prelayout) + OnValueChanged.Invoke(Value); +#endif + } + + public void LayoutComplete() { } + + public void GraphicUpdateComplete() { } + + protected override void OnEnable() { + base.OnEnable(); + UpdateCachedReferences(); + // Update rects since they need to be initialized correctly. + UpdateVisuals(); + } + + private void UpdateCachedReferences() { + if (_handleRect) { + _handleTransform = _handleRect.transform; + if (_handleTransform.parent != null) + _handleContainerRect = _handleTransform.parent.GetComponent(); + } + else { + _handleContainerRect = null; + } + } + + protected override void OnRectTransformDimensionsChange() { + base.OnRectTransformDimensionsChange(); + UpdateVisuals(); + } + + // Force-update the slider. Useful if you've changed the properties and want it to update visually. + private void UpdateVisuals() { + if (_handleContainerRect != null) { + _handleRect.anchorMin = _value; + _handleRect.anchorMax = _value; + } + } + + // Update the slider's position based on the mouse. + private void UpdateDrag(PointerEventData eventData, Camera cam) { + var clickRect = _handleContainerRect; + if (clickRect != null && clickRect.rect.size[0] > 0) { + if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out var localCursor)) + return; + + var rect = clickRect.rect; + localCursor -= rect.position; + + Vector2 newVal; + newVal.x = Mathf.Clamp01((localCursor - _offset).x / rect.size.x); + newVal.y = Mathf.Clamp01((localCursor - _offset).y / rect.size.y); + + Value = newVal; + } + } + + 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); + + _offset = Vector2.zero; + if (_handleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(_handleRect, eventData.position, eventData.enterEventCamera)) { + Vector2 localMousePos; + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_handleRect, eventData.position, eventData.pressEventCamera, out localMousePos)) + _offset = localMousePos; + + _offset.y = -_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 virtual void OnInitializePotentialDrag(PointerEventData eventData) { + eventData.useDragThreshold = false; + } + } +} \ No newline at end of file