BoxSlider.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. ///Credit judah4
  2. ///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
  3. using System;
  4. using UnityEngine.Events;
  5. using UnityEngine.EventSystems;
  6. namespace UnityEngine.UI.Extensions
  7. {
  8. [RequireComponent(typeof(RectTransform))]
  9. [AddComponentMenu("UI/Extensions/BoxSlider")]
  10. public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
  11. {
  12. public enum Direction
  13. {
  14. LeftToRight,
  15. RightToLeft,
  16. BottomToTop,
  17. TopToBottom,
  18. }
  19. [Serializable]
  20. public class BoxSliderEvent : UnityEvent<float, float> { }
  21. [SerializeField]
  22. private RectTransform m_HandleRect;
  23. public RectTransform HandleRect { get { return m_HandleRect; } set { if (SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
  24. [Space(6)]
  25. [SerializeField]
  26. private float m_MinValue = 0;
  27. public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
  28. [SerializeField]
  29. private float m_MaxValue = 1;
  30. public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
  31. [SerializeField]
  32. private bool m_WholeNumbers = false;
  33. public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
  34. [SerializeField]
  35. private float m_ValueX = 1f;
  36. public float ValueX
  37. {
  38. get
  39. {
  40. if (WholeNumbers)
  41. return Mathf.Round(m_ValueX);
  42. return m_ValueX;
  43. }
  44. set
  45. {
  46. SetX(value);
  47. }
  48. }
  49. public float NormalizedValueX
  50. {
  51. get
  52. {
  53. if (Mathf.Approximately(MinValue, MaxValue))
  54. return 0;
  55. return Mathf.InverseLerp(MinValue, MaxValue, ValueX);
  56. }
  57. set
  58. {
  59. this.ValueX = Mathf.Lerp(MinValue, MaxValue, value);
  60. }
  61. }
  62. [SerializeField]
  63. private float m_ValueY = 1f;
  64. public float ValueY
  65. {
  66. get
  67. {
  68. if (WholeNumbers)
  69. return Mathf.Round(m_ValueY);
  70. return m_ValueY;
  71. }
  72. set
  73. {
  74. SetY(value);
  75. }
  76. }
  77. public float NormalizedValueY
  78. {
  79. get
  80. {
  81. if (Mathf.Approximately(MinValue, MaxValue))
  82. return 0;
  83. return Mathf.InverseLerp(MinValue, MaxValue, ValueY);
  84. }
  85. set
  86. {
  87. this.ValueY = Mathf.Lerp(MinValue, MaxValue, value);
  88. }
  89. }
  90. [Space(6)]
  91. // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
  92. [SerializeField]
  93. private BoxSliderEvent m_OnValueChanged = new BoxSliderEvent();
  94. public BoxSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
  95. // Private fields
  96. private Transform m_HandleTransform;
  97. private RectTransform m_HandleContainerRect;
  98. // The offset from handle position to mouse down position
  99. private Vector2 m_Offset = Vector2.zero;
  100. private DrivenRectTransformTracker m_Tracker;
  101. // Size of each step.
  102. float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } }
  103. protected BoxSlider()
  104. { }
  105. #if UNITY_EDITOR
  106. protected override void OnValidate()
  107. {
  108. base.OnValidate();
  109. if (WholeNumbers)
  110. {
  111. m_MinValue = Mathf.Round(m_MinValue);
  112. m_MaxValue = Mathf.Round(m_MaxValue);
  113. }
  114. UpdateCachedReferences();
  115. SetX(m_ValueX, false);
  116. SetY(m_ValueY, false);
  117. // Update rects since other things might affect them even if value didn't change.
  118. if(!Application.isPlaying) UpdateVisuals();
  119. #if UNITY_2018_3_OR_NEWER
  120. if (!Application.isPlaying)
  121. #else
  122. var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
  123. if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
  124. #endif
  125. {
  126. CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
  127. }
  128. }
  129. #endif // if UNITY_EDITOR
  130. public virtual void Rebuild(CanvasUpdate executing)
  131. {
  132. #if UNITY_EDITOR
  133. if (executing == CanvasUpdate.Prelayout)
  134. OnValueChanged.Invoke(ValueX, ValueY);
  135. #endif
  136. }
  137. public void LayoutComplete()
  138. {
  139. }
  140. public void GraphicUpdateComplete()
  141. {
  142. }
  143. public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
  144. {
  145. if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
  146. return false;
  147. currentValue = newValue;
  148. return true;
  149. }
  150. public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
  151. {
  152. if (currentValue.Equals(newValue))
  153. return false;
  154. currentValue = newValue;
  155. return true;
  156. }
  157. protected override void OnEnable()
  158. {
  159. base.OnEnable();
  160. UpdateCachedReferences();
  161. SetX(m_ValueX, false);
  162. SetY(m_ValueY, false);
  163. // Update rects since they need to be initialized correctly.
  164. UpdateVisuals();
  165. }
  166. protected override void OnDisable()
  167. {
  168. m_Tracker.Clear();
  169. base.OnDisable();
  170. }
  171. void UpdateCachedReferences()
  172. {
  173. if (m_HandleRect)
  174. {
  175. m_HandleTransform = m_HandleRect.transform;
  176. if (m_HandleTransform.parent != null)
  177. m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
  178. }
  179. else
  180. {
  181. m_HandleContainerRect = null;
  182. }
  183. }
  184. // Set the valueUpdate the visible Image.
  185. void SetX(float input)
  186. {
  187. SetX(input, true);
  188. }
  189. void SetX(float input, bool sendCallback)
  190. {
  191. // Clamp the input
  192. float newValue = Mathf.Clamp(input, MinValue, MaxValue);
  193. if (WholeNumbers)
  194. newValue = Mathf.Round(newValue);
  195. // If the stepped value doesn't match the last one, it's time to update
  196. if (m_ValueX == newValue)
  197. return;
  198. m_ValueX = newValue;
  199. UpdateVisuals();
  200. if (sendCallback)
  201. m_OnValueChanged.Invoke(newValue, ValueY);
  202. }
  203. void SetY(float input)
  204. {
  205. SetY(input, true);
  206. }
  207. void SetY(float input, bool sendCallback)
  208. {
  209. // Clamp the input
  210. float newValue = Mathf.Clamp(input, MinValue, MaxValue);
  211. if (WholeNumbers)
  212. newValue = Mathf.Round(newValue);
  213. // If the stepped value doesn't match the last one, it's time to update
  214. if (m_ValueY == newValue)
  215. return;
  216. m_ValueY = newValue;
  217. UpdateVisuals();
  218. if (sendCallback)
  219. m_OnValueChanged.Invoke(ValueX, newValue);
  220. }
  221. protected override void OnRectTransformDimensionsChange()
  222. {
  223. base.OnRectTransformDimensionsChange();
  224. UpdateVisuals();
  225. }
  226. enum Axis
  227. {
  228. Horizontal = 0,
  229. Vertical = 1
  230. }
  231. // Force-update the slider. Useful if you've changed the properties and want it to update visually.
  232. private void UpdateVisuals()
  233. {
  234. #if UNITY_EDITOR
  235. if (!Application.isPlaying)
  236. UpdateCachedReferences();
  237. #endif
  238. m_Tracker.Clear();
  239. //to business!
  240. if (m_HandleContainerRect != null)
  241. {
  242. m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
  243. Vector2 anchorMin = Vector2.zero;
  244. Vector2 anchorMax = Vector2.one;
  245. anchorMin[0] = anchorMax[0] = (NormalizedValueX);
  246. anchorMin[1] = anchorMax[1] = (NormalizedValueY);
  247. if (Application.isPlaying)
  248. {
  249. m_HandleRect.anchorMin = anchorMin;
  250. m_HandleRect.anchorMax = anchorMax;
  251. }
  252. }
  253. }
  254. // Update the slider's position based on the mouse.
  255. void UpdateDrag(PointerEventData eventData, Camera cam)
  256. {
  257. RectTransform clickRect = m_HandleContainerRect;
  258. if (clickRect != null && clickRect.rect.size[0] > 0)
  259. {
  260. Vector2 localCursor;
  261. if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
  262. return;
  263. localCursor -= clickRect.rect.position;
  264. float val = Mathf.Clamp01((localCursor - m_Offset)[0] / clickRect.rect.size[0]);
  265. NormalizedValueX = (val);
  266. float valY = Mathf.Clamp01((localCursor - m_Offset)[1] / clickRect.rect.size[1]);
  267. NormalizedValueY = (valY);
  268. }
  269. }
  270. private bool CanDrag(PointerEventData eventData)
  271. {
  272. return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
  273. }
  274. public override void OnPointerDown(PointerEventData eventData)
  275. {
  276. if (!CanDrag(eventData))
  277. return;
  278. base.OnPointerDown(eventData);
  279. m_Offset = Vector2.zero;
  280. if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
  281. {
  282. Vector2 localMousePos;
  283. if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
  284. m_Offset = localMousePos;
  285. m_Offset.y = -m_Offset.y;
  286. }
  287. else
  288. {
  289. // Outside the slider handle - jump to this point instead
  290. UpdateDrag(eventData, eventData.pressEventCamera);
  291. }
  292. }
  293. public virtual void OnDrag(PointerEventData eventData)
  294. {
  295. if (!CanDrag(eventData))
  296. return;
  297. UpdateDrag(eventData, eventData.pressEventCamera);
  298. }
  299. public virtual void OnInitializePotentialDrag(PointerEventData eventData)
  300. {
  301. eventData.useDragThreshold = false;
  302. }
  303. }
  304. }