Tanoda
Stepper.cs
Go to the documentation of this file.
1
3
4using System;
5using System.Collections;
8
10{
11 // Stepper control
12 [AddComponentMenu("UI/Extensions/Stepper")]
13 [RequireComponent(typeof(RectTransform))]
14 public class Stepper : UIBehaviour
15 {
16 private Selectable[] _sides;
17 [SerializeField]
18 [Tooltip("The current step value of the control")]
19 private int _value = 0;
20 [SerializeField]
21 [Tooltip("The minimum step value allowed by the control. When reached it will disable the '-' button")]
22 private int _minimum = 0;
23 [SerializeField]
24 [Tooltip("The maximum step value allowed by the control. When reached it will disable the '+' button")]
25 private int _maximum = 100;
26 [SerializeField]
27 [Tooltip("The step increment used to increment / decrement the step value")]
28 private int _step = 1;
29 [SerializeField]
30 [Tooltip("Does the step value loop around from end to end")]
31 private bool _wrap = false;
32 [SerializeField]
33 [Tooltip("A GameObject with an Image to use as a separator between segments. Size of the RectTransform will determine the size of the separator used.\nNote, make sure to disable the separator GO so that it does not affect the scene")]
34 private Graphic _separator;
35 private float _separatorWidth = 0;
36
37 private float separatorWidth
38 {
39 get
40 {
41 if (_separatorWidth == 0 && separator)
42 {
43 _separatorWidth = separator.rectTransform.rect.width;
44 var image = separator.GetComponent<Image>();
45 if (image)
46 _separatorWidth /= image.pixelsPerUnit;
47 }
48 return _separatorWidth;
49 }
50 }
51
52 // Event delegates triggered on click.
53 [SerializeField]
54 private StepperValueChangedEvent _onValueChanged = new StepperValueChangedEvent();
55
56 [Serializable]
57 public class StepperValueChangedEvent : UnityEvent<int> { }
58
59 public Selectable[] sides
60 {
61 get
62 {
63 if (_sides == null || _sides.Length == 0)
64 {
65 _sides = GetSides();
66 }
67 return _sides;
68 }
69 }
70
71 public int value { get { return _value; } set { _value = value; } }
72
73 public int minimum { get { return _minimum; } set { _minimum = value; } }
74
75 public int maximum { get { return _maximum; } set { _maximum = value; } }
76
77 public int step { get { return _step; } set { _step = value; } }
78
79 public bool wrap { get { return _wrap; } set { _wrap = value; } }
80
81 public Graphic separator { get { return _separator; } set { _separator = value; _separatorWidth = 0; LayoutSides(sides); } }
82
84 {
85 get { return _onValueChanged; }
86 set { _onValueChanged = value; }
87 }
88
89 protected Stepper()
90 { }
91
92#if UNITY_EDITOR
93 protected override void OnValidate()
94 {
95 base.OnValidate();
96
97 RecreateSprites(sides);
98 if (separator)
100
101 if (!wrap)
102 {
103 DisableAtExtremes(sides);
104 }
105 }
106#endif
107
108 protected override void Start()
109 {
110 if (isActiveAndEnabled)
111 StartCoroutine(DelayedInit());
112 }
113
114 protected override void OnEnable()
115 {
116 StartCoroutine(DelayedInit());
117 }
118
119 IEnumerator DelayedInit()
120 {
121 yield return null;
122
123 RecreateSprites(sides);
124 }
125
126 private Selectable[] GetSides()
127 {
128 var buttons = GetComponentsInChildren<Selectable>();
129 if (buttons.Length != 2)
130 {
131 throw new InvalidOperationException("A stepper must have two Button children");
132 }
133
134 if (!wrap)
135 {
136 DisableAtExtremes(buttons);
137 }
138 LayoutSides(buttons);
139
140 return buttons;
141 }
142
143 public void StepUp()
144 {
145 Step(step);
146 }
147
148 public void StepDown()
149 {
150 Step(-step);
151 }
152
153 private void Step(int amount)
154 {
155 value += amount;
156
157 if (wrap)
158 {
159 if (value > maximum) value = minimum;
160 if (value < minimum) value = maximum;
161 }
162 else
163 {
164 value = Math.Max(minimum, value);
165 value = Math.Min(maximum, value);
166
167 DisableAtExtremes(sides);
168 }
169
170 _onValueChanged.Invoke(value);
171 }
172
173 private void DisableAtExtremes(Selectable[] sides)
174 {
175 sides[0].interactable = wrap || value > minimum;
176 sides[1].interactable = wrap || value < maximum;
177 }
178
179 private void RecreateSprites(Selectable[] sides)
180 {
181 for (int i = 0; i < 2; i++)
182 {
183 if (sides[i].image == null)
184 continue;
185
186 var sprite = CutSprite(sides[i].image.sprite, i == 0);
187 var side = sides[i].GetComponent<StepperSide>();
188 if (side)
189 {
190 side.cutSprite = sprite;
191 }
192 sides[i].image.overrideSprite = sprite;
193 }
194 }
195
196 static internal Sprite CutSprite(Sprite sprite, bool leftmost)
197 {
198 if (sprite.border.x == 0 || sprite.border.z == 0)
199 return sprite;
200
201 var rect = sprite.rect;
202 var border = sprite.border;
203
204 if (leftmost)
205 {
206 rect.xMax = border.z;
207 border.z = 0;
208 }
209 else
210 {
211 rect.xMin = border.x;
212 border.x = 0;
213 }
214
215 return Sprite.Create(sprite.texture, rect, sprite.pivot, sprite.pixelsPerUnit, 0, SpriteMeshType.FullRect, border);
216 }
217
218 public void LayoutSides(Selectable[] sides = null)
219 {
220 sides = sides ?? this.sides;
221
222 RecreateSprites(sides);
223
224 RectTransform transform = this.transform as RectTransform;
225 float width = (transform.rect.width / 2) - separatorWidth;
226
227 for (int i = 0; i < 2; i++)
228 {
229 float insetX = i == 0 ? 0 : width + separatorWidth;
230
231 var rectTransform = sides[i].GetComponent<RectTransform>();
232 rectTransform.anchorMin = Vector2.zero;
233 rectTransform.anchorMax = Vector2.zero;
234 rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, insetX, width);
235 rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
236
237// TODO: maybe adjust text position
238 }
239
240 if (separator)
241 {
242 var sepTransform = gameObject.transform.Find("Separator");
243 Graphic sep = (sepTransform != null) ? sepTransform.GetComponent<Graphic>() : (GameObject.Instantiate(separator.gameObject) as GameObject).GetComponent<Graphic>();
244 sep.gameObject.name = "Separator";
245 sep.gameObject.SetActive(true);
246 sep.rectTransform.SetParent(this.transform, false);
247 sep.rectTransform.anchorMin = Vector2.zero;
248 sep.rectTransform.anchorMax = Vector2.zero;
249 sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, width, separatorWidth);
250 sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
251 }
252 }
253 }
254}
Es.InkPainter.Math Math
Definition: PaintTest.cs:7
System.Drawing.Image Image
Definition: TestScript.cs:37
void LayoutSides(Selectable[] sides=null)
Definition: Stepper.cs:218
StepperValueChangedEvent onValueChanged
Definition: Stepper.cs:84
Credit Erdener Gonenc - @PixelEnvision.