ExtensionsToggle.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using UnityEngine.Events;
  3. using UnityEngine.EventSystems;
  4. using UnityEngine.Serialization;
  5. namespace UnityEngine.UI
  6. {
  7. /// <summary>
  8. /// Simple toggle -- something that has an 'on' and 'off' states: checkbox, toggle button, radio button, etc.
  9. /// </summary>
  10. [AddComponentMenu("UI/Extensions/Extensions Toggle", 31)]
  11. [RequireComponent(typeof(RectTransform))]
  12. public class ExtensionsToggle : Selectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
  13. {
  14. /// <summary>
  15. /// Variable to identify this script, change the datatype if needed to fit your use case
  16. /// </summary>
  17. public string UniqueID;
  18. public enum ToggleTransition
  19. {
  20. None,
  21. Fade
  22. }
  23. [Serializable]
  24. public class ToggleEvent : UnityEvent<bool>
  25. { }
  26. [Serializable]
  27. public class ToggleEventObject : UnityEvent<ExtensionsToggle>
  28. { }
  29. /// <summary>
  30. /// Transition type.
  31. /// </summary>
  32. public ToggleTransition toggleTransition = ToggleTransition.Fade;
  33. /// <summary>
  34. /// Graphic the toggle should be working with.
  35. /// </summary>
  36. public Graphic graphic;
  37. // group that this toggle can belong to
  38. [SerializeField]
  39. private ExtensionsToggleGroup m_Group;
  40. public ExtensionsToggleGroup Group
  41. {
  42. get { return m_Group; }
  43. set
  44. {
  45. m_Group = value;
  46. #if UNITY_EDITOR
  47. if (Application.isPlaying)
  48. #endif
  49. {
  50. SetToggleGroup(m_Group, true);
  51. PlayEffect(true);
  52. }
  53. }
  54. }
  55. /// <summary>
  56. /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
  57. /// </summary>
  58. [Tooltip("Use this event if you only need the bool state of the toggle that was changed")]
  59. public ToggleEvent onValueChanged = new ToggleEvent();
  60. /// <summary>
  61. /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
  62. /// </summary>
  63. [Tooltip("Use this event if you need access to the toggle that was changed")]
  64. public ToggleEventObject onToggleChanged = new ToggleEventObject();
  65. // Whether the toggle is on
  66. [FormerlySerializedAs("m_IsActive")]
  67. [Tooltip("Is the toggle currently on or off?")]
  68. [SerializeField]
  69. private bool m_IsOn;
  70. protected ExtensionsToggle()
  71. { }
  72. #if UNITY_EDITOR
  73. protected override void OnValidate()
  74. {
  75. base.OnValidate();
  76. Set(m_IsOn, false);
  77. PlayEffect(toggleTransition == ToggleTransition.None);
  78. #if UNITY_2018_3_OR_NEWER
  79. if (!Application.isPlaying)
  80. #else
  81. var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
  82. if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
  83. #endif
  84. {
  85. CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
  86. }
  87. }
  88. #endif // if UNITY_EDITOR
  89. public virtual void Rebuild(CanvasUpdate executing)
  90. {
  91. #if UNITY_EDITOR
  92. if (executing == CanvasUpdate.Prelayout)
  93. {
  94. onValueChanged.Invoke(m_IsOn);
  95. onToggleChanged.Invoke(this);
  96. }
  97. #endif
  98. }
  99. public virtual void LayoutComplete()
  100. { }
  101. public virtual void GraphicUpdateComplete()
  102. { }
  103. protected override void OnEnable()
  104. {
  105. base.OnEnable();
  106. SetToggleGroup(m_Group, false);
  107. PlayEffect(true);
  108. }
  109. protected override void OnDisable()
  110. {
  111. SetToggleGroup(null, false);
  112. base.OnDisable();
  113. }
  114. protected override void OnDidApplyAnimationProperties()
  115. {
  116. // Check if isOn has been changed by the animation.
  117. // Unfortunately there is no way to check if we don't have a graphic.
  118. if (graphic != null)
  119. {
  120. bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
  121. if (m_IsOn != oldValue)
  122. {
  123. m_IsOn = oldValue;
  124. Set(!oldValue);
  125. }
  126. }
  127. base.OnDidApplyAnimationProperties();
  128. }
  129. private void SetToggleGroup(ExtensionsToggleGroup newGroup, bool setMemberValue)
  130. {
  131. ExtensionsToggleGroup oldGroup = m_Group;
  132. // Sometimes IsActive returns false in OnDisable so don't check for it.
  133. // Rather remove the toggle too often than too little.
  134. if (m_Group != null)
  135. m_Group.UnregisterToggle(this);
  136. // At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable.
  137. // That's why we use the setMemberValue parameter.
  138. if (setMemberValue)
  139. m_Group = newGroup;
  140. // Only register to the new group if this Toggle is active.
  141. if (m_Group != null && IsActive())
  142. m_Group.RegisterToggle(this);
  143. // If we are in a new group, and this toggle is on, notify group.
  144. // Note: Don't refer to m_Group here as it's not guaranteed to have been set.
  145. if (newGroup != null && newGroup != oldGroup && IsOn && IsActive())
  146. m_Group.NotifyToggleOn(this);
  147. }
  148. /// <summary>
  149. /// Whether the toggle is currently active.
  150. /// </summary>
  151. public bool IsOn
  152. {
  153. get { return m_IsOn; }
  154. set
  155. {
  156. Set(value);
  157. }
  158. }
  159. void Set(bool value)
  160. {
  161. Set(value, true);
  162. }
  163. void Set(bool value, bool sendCallback)
  164. {
  165. if (m_IsOn == value)
  166. return;
  167. // if we are in a group and set to true, do group logic
  168. m_IsOn = value;
  169. if (m_Group != null && IsActive())
  170. {
  171. if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.AllowSwitchOff))
  172. {
  173. m_IsOn = true;
  174. m_Group.NotifyToggleOn(this);
  175. }
  176. }
  177. // Always send event when toggle is clicked, even if value didn't change
  178. // due to already active toggle in a toggle group being clicked.
  179. // Controls like Dropdown rely on this.
  180. // It's up to the user to ignore a selection being set to the same value it already was, if desired.
  181. PlayEffect(toggleTransition == ToggleTransition.None);
  182. if (sendCallback)
  183. {
  184. onValueChanged.Invoke(m_IsOn);
  185. onToggleChanged.Invoke(this);
  186. }
  187. }
  188. /// <summary>
  189. /// Play the appropriate effect.
  190. /// </summary>
  191. private void PlayEffect(bool instant)
  192. {
  193. if (graphic == null)
  194. return;
  195. #if UNITY_EDITOR
  196. if (!Application.isPlaying)
  197. graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
  198. else
  199. #endif
  200. graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
  201. }
  202. /// <summary>
  203. /// Assume the correct visual state.
  204. /// </summary>
  205. protected override void Start()
  206. {
  207. PlayEffect(true);
  208. }
  209. private void InternalToggle()
  210. {
  211. if (!IsActive() || !IsInteractable())
  212. return;
  213. IsOn = !IsOn;
  214. }
  215. /// <summary>
  216. /// React to clicks.
  217. /// </summary>
  218. public virtual void OnPointerClick(PointerEventData eventData)
  219. {
  220. if (eventData.button != PointerEventData.InputButton.Left)
  221. return;
  222. InternalToggle();
  223. }
  224. public virtual void OnSubmit(BaseEventData eventData)
  225. {
  226. InternalToggle();
  227. }
  228. }
  229. }