-
Notifications
You must be signed in to change notification settings - Fork 290
/
Toggle.cs
326 lines (287 loc) · 10.2 KB
/
Toggle.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
/// <summary>
/// A standard toggle that has an on / off state.
/// </summary>
/// <remarks>
/// The toggle component is a Selectable that controls a child graphic which displays the on / off state.
/// When a toggle event occurs a callback is sent to any registered listeners of UI.Toggle._onValueChanged.
/// </remarks>
[AddComponentMenu("UI/Toggle", 31)]
[RequireComponent(typeof(RectTransform))]
public class Toggle : Selectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
{
/// <summary>
/// Display settings for when a toggle is activated or deactivated.
/// </summary>
public enum ToggleTransition
{
/// <summary>
/// Show / hide the toggle instantly
/// </summary>
None,
/// <summary>
/// Fade the toggle in / out smoothly.
/// </summary>
Fade
}
[Serializable]
/// <summary>
/// UnityEvent callback for when a toggle is toggled.
/// </summary>
public class ToggleEvent : UnityEvent<bool>
{}
/// <summary>
/// Transition mode for the toggle.
/// </summary>
public ToggleTransition toggleTransition = ToggleTransition.Fade;
/// <summary>
/// Graphic the toggle should be working with.
/// </summary>
public Graphic graphic;
[SerializeField]
private ToggleGroup m_Group;
/// <summary>
/// Group the toggle belongs to.
/// </summary>
public ToggleGroup group
{
get { return m_Group; }
set
{
m_Group = value;
SetToggleGroup(m_Group, true);
PlayEffect(true);
}
}
/// <summary>
/// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
/// </summary>
/// <example>
/// <code>
/// //Attach this script to a Toggle GameObject. To do this, go to Create>UI>Toggle.
/// //Set your own Text in the Inspector window
///
/// using UnityEngine;
/// using UnityEngine.UI;
///
/// public class Example : MonoBehaviour
/// {
/// Toggle m_Toggle;
/// public Text m_Text;
///
/// void Start()
/// {
/// //Fetch the Toggle GameObject
/// m_Toggle = GetComponent<Toggle>();
/// //Add listener for when the state of the Toggle changes, to take action
/// m_Toggle.onValueChanged.AddListener(delegate {
/// ToggleValueChanged(m_Toggle);
/// });
///
/// //Initialise the Text to say the first state of the Toggle
/// m_Text.text = "First Value : " + m_Toggle.isOn;
/// }
///
/// //Output the new state of the Toggle into Text
/// void ToggleValueChanged(Toggle change)
/// {
/// m_Text.text = "New Value : " + m_Toggle.isOn;
/// }
/// }
/// </code>
/// </example>
public ToggleEvent onValueChanged = new ToggleEvent();
// Whether the toggle is on
[Tooltip("Is the toggle currently on or off?")]
[SerializeField]
private bool m_IsOn;
protected Toggle()
{}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
}
#endif // if UNITY_EDITOR
public virtual void Rebuild(CanvasUpdate executing)
{
#if UNITY_EDITOR
if (executing == CanvasUpdate.Prelayout)
onValueChanged.Invoke(m_IsOn);
#endif
}
public virtual void LayoutComplete()
{}
public virtual void GraphicUpdateComplete()
{}
protected override void OnEnable()
{
base.OnEnable();
SetToggleGroup(m_Group, false);
PlayEffect(true);
}
protected override void OnDisable()
{
SetToggleGroup(null, false);
base.OnDisable();
}
protected override void OnDidApplyAnimationProperties()
{
// Check if isOn has been changed by the animation.
// Unfortunately there is no way to check if we don�t have a graphic.
if (graphic != null)
{
bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
if (m_IsOn != oldValue)
{
m_IsOn = oldValue;
Set(!oldValue);
}
}
base.OnDidApplyAnimationProperties();
}
private void SetToggleGroup(ToggleGroup newGroup, bool setMemberValue)
{
// Sometimes IsActive returns false in OnDisable so don't check for it.
// Rather remove the toggle too often than too little.
if (m_Group != null)
m_Group.UnregisterToggle(this);
// At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable.
// That's why we use the setMemberValue parameter.
if (setMemberValue)
m_Group = newGroup;
// Only register to the new group if this Toggle is active.
if (newGroup != null && IsActive())
newGroup.RegisterToggle(this);
// If we are in a new group, and this toggle is on, notify group.
// Note: Don't refer to m_Group here as it's not guaranteed to have been set.
if (newGroup != null && isOn && IsActive())
newGroup.NotifyToggleOn(this);
}
/// <summary>
/// Whether the toggle is currently active.
/// </summary>
/// <example>
/// <code>
/// /Attach this script to a Toggle GameObject. To do this, go to Create>UI>Toggle.
/// //Set your own Text in the Inspector window
///
/// using UnityEngine;
/// using UnityEngine.UI;
///
/// public class Example : MonoBehaviour
/// {
/// Toggle m_Toggle;
/// public Text m_Text;
///
/// void Start()
/// {
/// //Fetch the Toggle GameObject
/// m_Toggle = GetComponent<Toggle>();
/// //Add listener for when the state of the Toggle changes, and output the state
/// m_Toggle.onValueChanged.AddListener(delegate {
/// ToggleValueChanged(m_Toggle);
/// });
///
/// //Initialize the Text to say whether the Toggle is in a positive or negative state
/// m_Text.text = "Toggle is : " + m_Toggle.isOn;
/// }
///
/// //Output the new state of the Toggle into Text when the user uses the Toggle
/// void ToggleValueChanged(Toggle change)
/// {
/// m_Text.text = "Toggle is : " + m_Toggle.isOn;
/// }
/// }
/// </code>
/// </example>
public bool isOn
{
get { return m_IsOn; }
set
{
Set(value);
}
}
/// <summary>
/// Set isOn without invoking onValueChanged callback.
/// </summary>
/// <param name="value">New Value for isOn.</param>
public void SetIsOnWithoutNotify(bool value)
{
Set(value, false);
}
void Set(bool value, bool sendCallback = true)
{
if (m_IsOn == value)
return;
// if we are in a group and set to true, do group logic
m_IsOn = value;
if (m_Group != null && IsActive())
{
if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.allowSwitchOff))
{
m_IsOn = true;
m_Group.NotifyToggleOn(this, sendCallback);
}
}
// Always send event when toggle is clicked, even if value didn't change
// due to already active toggle in a toggle group being clicked.
// Controls like Dropdown rely on this.
// It's up to the user to ignore a selection being set to the same value it already was, if desired.
PlayEffect(toggleTransition == ToggleTransition.None);
if (sendCallback)
{
UISystemProfilerApi.AddMarker("Toggle.value", this);
onValueChanged.Invoke(m_IsOn);
}
}
/// <summary>
/// Play the appropriate effect.
/// </summary>
private void PlayEffect(bool instant)
{
if (graphic == null)
return;
#if UNITY_EDITOR
if (!Application.isPlaying)
graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
else
#endif
graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
}
/// <summary>
/// Assume the correct visual state.
/// </summary>
protected override void Start()
{
PlayEffect(true);
}
private void InternalToggle()
{
if (!IsActive() || !IsInteractable())
return;
isOn = !isOn;
}
/// <summary>
/// React to clicks.
/// </summary>
public virtual void OnPointerClick(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
return;
InternalToggle();
}
public virtual void OnSubmit(BaseEventData eventData)
{
InternalToggle();
}
}
}