[NUI] Fix button bugs and improve switch.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Switch.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19 using System.Diagnostics;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Components.Extension;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// Switch is a kind of <see cref="Button"/> component that uses icon part as a toggle shape.
27     /// The icon part consists of track and thumb.
28     /// </summary>
29     /// <since_tizen> 6 </since_tizen>
30     public partial class Switch : Button
31     {
32         private ImageView thumb = null;
33
34         static Switch() { }
35
36         /// <summary>
37         /// Creates a new instance of a Switch.
38         /// </summary>
39         /// <since_tizen> 6 </since_tizen>
40         public Switch() : base()
41         {
42         }
43
44         /// <summary>
45         /// Creates a new instance of a Switch with style.
46         /// </summary>
47         /// <param name="style">Create Switch by special style defined in UX.</param>
48         /// <since_tizen> 8 </since_tizen>
49         public Switch(string style) : base(style)
50         {
51         }
52
53         /// <summary>
54         /// Creates a new instance of a Switch with style.
55         /// </summary>
56         /// <param name="switchStyle">Create Switch by style customized by user.</param>
57         /// <since_tizen> 8 </since_tizen>
58         public Switch(SwitchStyle switchStyle) : base(switchStyle)
59         {
60         }
61
62         /// <summary>
63         /// Initialize AT-SPI object.
64         /// </summary>
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         public override void OnInitialize()
67         {
68             base.OnInitialize();
69             AccessibilityRole = Role.ToggleButton;
70
71             IsSelectable = true;
72
73             Feedback = true;
74         }
75
76         /// <summary>
77         /// Informs AT-SPI bridge about the set of AT-SPI states associated with this object.
78         /// </summary>
79         [EditorBrowsable(EditorBrowsableState.Never)]
80         protected override AccessibilityStates AccessibilityCalculateStates()
81         {
82             var states = base.AccessibilityCalculateStates();
83
84             states[AccessibilityState.Checked] = this.IsSelected;
85
86             return states;
87         }
88
89         /// <summary>
90         /// An event for the item selected signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
91         /// </summary>
92         /// <since_tizen> 6 </since_tizen>
93         [Obsolete("Deprecated in API8; Will be removed in API10. Please use SelectedChanged event instead.")]
94         public event EventHandler<SelectEventArgs> SelectedEvent;
95
96         /// <summary>
97         /// An event for the item selected signal which can be used to subscribe or unsubscribe the event handler provided by the user.
98         /// </summary>
99         /// <since_tizen> 8 </since_tizen>
100         public event EventHandler<SelectedChangedEventArgs> SelectedChanged;
101
102         /// <summary>
103         /// Return currently applied style.
104         /// </summary>
105         /// <remarks>
106         /// Modifying contents in style may cause unexpected behaviour.
107         /// </remarks>
108         /// <since_tizen> 8 </since_tizen>
109         public new SwitchStyle Style => (SwitchStyle)(ViewStyle as SwitchStyle)?.Clone();
110
111         /// <summary>
112         /// Apply style to switch.
113         /// </summary>
114         /// <param name="viewStyle">The style to apply.</param>
115         [EditorBrowsable(EditorBrowsableState.Never)]
116         public override void ApplyStyle(ViewStyle viewStyle)
117         {
118             styleApplying = true;
119
120             base.ApplyStyle(viewStyle);
121
122             if (viewStyle is SwitchStyle switchStyle)
123             {
124                 if (Extension != null) Extension.OnDispose(this);
125
126                 if ((Extension = switchStyle.CreateExtension()) != null && Extension is SwitchExtension extension)
127                 {
128                     Icon.Unparent();
129                     thumb.Unparent();
130                     TextLabel.Unparent();
131                     Icon = extension.OnCreateTrack(this, Icon);
132                     thumb = extension.OnCreateThumb(this, thumb);
133                     Icon.Add(thumb);
134                     LayoutItems();
135
136                     Icon.Relayout -= OnTrackOrThumbRelayout;
137                     Icon.Relayout += OnTrackOrThumbRelayout;
138
139                     thumb.Relayout -= OnTrackOrThumbRelayout;
140                     thumb.Relayout += OnTrackOrThumbRelayout;
141                 }
142
143                 if (switchStyle.Track != null)
144                 {
145                     Track.ApplyStyle(switchStyle.Track);
146                 }
147
148                 if (switchStyle.Thumb != null)
149                 {
150                     Thumb.ApplyStyle(switchStyle.Thumb);
151                 }
152
153                 if (switchStyle.Text != null)
154                 {
155                     TextLabel.ThemeChangeSensitive = false;
156                     TextLabel.ApplyStyle(switchStyle.Text);
157                 }
158             }
159             styleApplying = false;
160
161             UpdateState();
162         }
163
164         private void OnTrackOrThumbRelayout(object sender, EventArgs args)
165         {
166             if (Extension is SwitchExtension switchExtension)
167             {
168                 switchExtension.OnTrackOrThumbResized(this, Icon, thumb);
169             }
170         }
171
172         /// <summary>
173         /// Switch's track part.
174         /// </summary>
175         /// <since_tizen> 8 </since_tizen>
176         public ImageView Track
177         {
178             get => Icon;
179             internal set
180             {
181                 Icon = value;
182             }
183         }
184
185         /// <summary>
186         /// Switch's thumb part.
187         /// </summary>
188         /// <since_tizen> 8 </since_tizen>
189         public ImageView Thumb
190         {
191             get => thumb;
192             internal set
193             {
194                 thumb = value;
195             }
196         }
197
198         /// <summary>
199         /// Switch's track part image url selector.
200         /// </summary>
201         /// <since_tizen> 6 </since_tizen>
202         public StringSelector SwitchBackgroundImageURLSelector
203         {
204             get
205             {
206                 return GetValue(SwitchBackgroundImageURLSelectorProperty) as StringSelector;
207             }
208             set
209             {
210                 SetValue(SwitchBackgroundImageURLSelectorProperty, value);
211                 NotifyPropertyChanged();
212             }
213         }
214         private StringSelector InternalSwitchBackgroundImageURLSelector
215         {
216             get => Icon?.ResourceUrlSelector == null ? null : new StringSelector(Icon.ResourceUrlSelector);
217             set
218             {
219                 Debug.Assert(Icon != null);
220                 Icon.ResourceUrlSelector = value;
221             }
222         }
223
224         /// <summary>
225         /// Handler image's resource url in Switch.
226         /// </summary>
227         /// <since_tizen> 6 </since_tizen>
228         public string SwitchHandlerImageURL
229         {
230             get
231             {
232                 return GetValue(SwitchHandlerImageURLProperty) as string;
233             }
234             set
235             {
236                 SetValue(SwitchHandlerImageURLProperty, value);
237                 NotifyPropertyChanged();
238             }
239         }
240         private string InternalSwitchHandlerImageURL
241         {
242             get
243             {
244                 return Thumb.ResourceUrl;
245             }
246             set
247             {
248                 Thumb.ResourceUrl = value;
249             }
250         }
251
252         /// <summary>
253         /// Handler image's resource url selector in Switch.
254         /// Getter returns copied selector value if exist, null otherwise.
255         /// </summary>
256         /// <since_tizen> 6 </since_tizen>
257         public StringSelector SwitchHandlerImageURLSelector
258         {
259             get
260             {
261                 return GetValue(SwitchHandlerImageURLSelectorProperty) as StringSelector;
262             }
263             set
264             {
265                 SetValue(SwitchHandlerImageURLSelectorProperty, value);
266                 NotifyPropertyChanged();
267             }
268         }
269         private StringSelector InternalSwitchHandlerImageURLSelector
270         {
271             get => new StringSelector(thumb.ResourceUrlSelector);
272             set
273             {
274                 Debug.Assert(thumb != null);
275                 thumb.ResourceUrlSelector = value;
276             }
277         }
278
279         /// <summary>
280         /// Handler image's size in Switch.
281         /// </summary>
282         /// <since_tizen> 6 </since_tizen>
283         public Size SwitchHandlerImageSize
284         {
285             get
286             {
287                 return GetValue(SwitchHandlerImageSizeProperty) as Size;
288             }
289             set
290             {
291                 SetValue(SwitchHandlerImageSizeProperty, value);
292                 NotifyPropertyChanged();
293             }
294         }
295         private Size InternalSwitchHandlerImageSize
296         {
297             get
298             {
299                 return Thumb.Size;
300             }
301             set
302             {
303                 Thumb.Size = value;
304             }
305         }
306
307         /// <summary>
308         /// Dispose Switch and all children on it.
309         /// </summary>
310         /// <param name="type">Dispose type.</param>
311         /// <since_tizen> 6 </since_tizen>
312         protected override void Dispose(DisposeTypes type)
313         {
314             if (disposed) return;
315
316             if (type == DisposeTypes.Explicit)
317             {
318                 if (Icon != null)
319                 {
320                     Icon.Relayout -= OnTrackOrThumbRelayout;
321                 }
322                 if (thumb != null)
323                 {
324                     thumb.Relayout -= OnTrackOrThumbRelayout;
325                     Utility.Dispose(thumb);
326                 }
327             }
328
329             base.Dispose(type);
330         }
331
332         /// <summary>
333         /// Called after a key event is received by the view that has had its focus set.
334         /// </summary>
335         /// <param name="key">The key event.</param>
336         /// <returns>True if the key event should be consumed.</returns>
337         /// <since_tizen> 8 </since_tizen>
338         public override bool OnKey(Key key)
339         {
340             return base.OnKey(key);
341         }
342
343         /// <summary>
344         /// Called after a touch event is received by the owning view.<br />
345         /// CustomViewBehaviour.REQUIRES_TOUCH_EVENTS must be enabled during construction. See CustomView(ViewWrapperImpl.CustomViewBehaviour behaviour).<br />
346         /// </summary>
347         /// <param name="touch">The touch event.</param>
348         /// <returns>True if the event should be consumed.</returns>
349         /// <since_tizen> 8 </since_tizen>
350         [Obsolete("Deprecated in API8; Will be removed in API10. Please use OnClicked instead.")]
351 #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member, It will be removed in API10
352         public override bool OnTouch(Touch touch)
353 #pragma warning restore CS0809 // Obsolete member overrides non-obsolete member, It will be removed in API10
354         {
355             return base.OnTouch(touch);
356         }
357
358         /// <summary>
359         /// Get Switch style.
360         /// </summary>
361         /// <returns>The default switch style.</returns>
362         /// <since_tizen> 8 </since_tizen>
363         protected override ViewStyle CreateViewStyle()
364         {
365             return new SwitchStyle();
366         }
367
368         /// <inheritdoc/>
369         [EditorBrowsable(EditorBrowsableState.Never)]
370         protected override ImageView CreateIcon()
371         {
372             var icon = new ImageView()
373             {
374                 AccessibilityHidden = true,
375                 EnableControlStatePropagation = true
376             };
377
378             thumb = new ImageView();
379             icon.Add(thumb);
380
381             return icon;
382         }
383
384         /// <inheritdoc/>
385         [EditorBrowsable(EditorBrowsableState.Never)]
386         protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
387         {
388             if (controlStateChangedInfo == null) throw new ArgumentNullException(nameof(controlStateChangedInfo));
389             base.OnControlStateChanged(controlStateChangedInfo);
390
391             if (IsSelectable)
392             {
393                 if (controlStateChangedInfo.PreviousState.Contains(ControlState.Selected) != controlStateChangedInfo.CurrentState.Contains(ControlState.Selected))
394                 {
395                    OnSelect();
396                 }
397             }
398         }
399
400         private void OnSelect()
401         {
402             if (Accessibility.Accessibility.IsEnabled && IsHighlighted)
403             {
404                 EmitAccessibilityStateChangedEvent(AccessibilityState.Checked, IsSelected);
405             }
406
407             ((SwitchExtension)Extension)?.OnSelectedChanged(this);
408
409             if (SelectedEvent != null)
410             {
411                 SelectEventArgs eventArgs = new SelectEventArgs();
412                 eventArgs.IsSelected = IsSelected;
413                 SelectedEvent(this, eventArgs);
414             }
415
416             if (SelectedChanged != null)
417             {
418                 SelectedChangedEventArgs eventArgs = new SelectedChangedEventArgs();
419                 eventArgs.IsSelected = IsSelected;
420                 SelectedChanged(this, eventArgs);
421             }
422         }
423
424         /// <summary>
425         /// SelectEventArgs is a class to record item selected arguments which will sent to user.
426         /// </summary>
427         /// <since_tizen> 6 </since_tizen>
428         /// It will be removed in API10
429         [Obsolete("Deprecated in API8; Will be removed in API10. Please use SelectedChangedEventArgs instead.")]
430         [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:Do not declare visible instance fields")]
431         [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
432         public class SelectEventArgs : EventArgs
433         {
434             /// <summary> Select state of Switch </summary>
435             /// <since_tizen> 6 </since_tizen>
436             public bool IsSelected;
437         }
438     }
439 }