[NUI] Fix Switch selection bug. (#1798)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / Style / Selector.cs
1 /*
2  * Copyright(c) 2019 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 Tizen.NUI.Binding;
20 using Tizen.NUI.Components;
21
22 namespace Tizen.NUI.BaseComponents
23 {
24     /// <summary>
25     /// Selector class, which is related by Control State, it is base class for other Selector.
26     /// </summary>
27     /// <since_tizen> 6 </since_tizen>
28     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class Selector<T> : StateValueCollection<T>
31     {
32         /// <since_tizen> 6 </since_tizen>
33         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
34         public static implicit operator Selector<T>(T value)
35         {
36             Selector<T> selector = new Selector<T>();
37             selector.All = value;
38             return selector;
39         }
40
41         /// Default Contructor
42         [EditorBrowsable(EditorBrowsableState.Never)]
43         public Selector()
44         {
45         }
46
47         /// Contructor with T
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public Selector(T value) : this()
50         {
51             All = value;
52         }
53
54         /// <summary>
55         /// All State.
56         /// </summary>
57         /// <since_tizen> 6 </since_tizen>
58         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
59         [EditorBrowsable(EditorBrowsableState.Never)]
60         public T All
61         {
62             get;
63             set;
64         }
65         /// <summary>
66         /// Normal State.
67         /// </summary>
68         /// <since_tizen> 6 </since_tizen>
69         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
70         [EditorBrowsable(EditorBrowsableState.Never)]
71         public T Normal
72         {
73             get => Find(x => x.State == ControlState.Normal).Value;
74             set => Add(ControlState.Normal, value);
75         }
76         /// <summary>
77         /// Pressed State.
78         /// </summary>
79         /// <since_tizen> 6 </since_tizen>
80         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
81         [EditorBrowsable(EditorBrowsableState.Never)]
82         public T Pressed
83         {
84             get => Find(x => x.State == ControlState.Pressed).Value;
85             set => Add(ControlState.Pressed, value);
86         }
87         /// <summary>
88         /// Focused State.
89         /// </summary>
90         /// <since_tizen> 6 </since_tizen>
91         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
92         [EditorBrowsable(EditorBrowsableState.Never)]
93         public T Focused
94         {
95             get => Find(x => x.State == ControlState.Focused).Value;
96             set => Add(ControlState.Focused, value);
97         }
98         /// <summary>
99         /// Selected State.
100         /// </summary>
101         /// <since_tizen> 6 </since_tizen>
102         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
103         [EditorBrowsable(EditorBrowsableState.Never)]
104         public T Selected
105         {
106             get => Find(x => x.State == ControlState.Selected).Value;
107             set => Add(ControlState.Selected, value);
108         }
109         /// <summary>
110         /// Disabled State.
111         /// </summary>
112         /// <since_tizen> 6 </since_tizen>
113         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
114         [EditorBrowsable(EditorBrowsableState.Never)]
115         public T Disabled
116         {
117             get => Find(x => x.State == ControlState.Disabled).Value;
118             set => Add(ControlState.Disabled, value);
119         }
120         /// <summary>
121         /// DisabledFocused State.
122         /// </summary>
123         /// <since_tizen> 6 </since_tizen>
124         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
125         [EditorBrowsable(EditorBrowsableState.Never)]
126         public T DisabledFocused
127         {
128             get => Find(x => x.State == ControlState.DisabledFocused).Value;
129             set => Add(ControlState.DisabledFocused, value);
130         }
131         /// <summary>
132         /// SelectedFocused State.
133         /// </summary>
134         /// <since_tizen> 6 </since_tizen>
135         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
136         public T SelectedFocused
137         {
138             get => Find(x => x.State == ControlState.SelectedFocused).Value;
139             set => Add(ControlState.SelectedFocused, value);
140         }
141         /// <summary>
142         /// DisabledSelected State.
143         /// </summary>
144         /// <since_tizen> 6 </since_tizen>
145         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
146         [EditorBrowsable(EditorBrowsableState.Never)]
147         public T DisabledSelected
148         {
149             get => Find(x => x.State == ControlState.DisabledSelected).Value;
150             set => Add(ControlState.DisabledSelected, value);
151         }
152
153         /// <summary>
154         /// Other State.
155         /// </summary>
156         /// <since_tizen> 6 </since_tizen>
157         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
158         [EditorBrowsable(EditorBrowsableState.Never)]
159         public T Other
160         {
161             get => Find(x => x.State == ControlState.Other).Value;
162             set => Add(ControlState.Other, value);
163         }
164         /// <summary>
165         /// Get value by State.
166         /// </summary>
167         /// <since_tizen> 6 </since_tizen>
168         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
169         [EditorBrowsable(EditorBrowsableState.Never)]
170         public T GetValue(ControlState state)
171         {
172             if (All != null)
173             {
174                 return All;
175             }
176
177             StateValuePair<T> value = Find(x => x.State == state);
178             if (value.Value != null)
179             {
180                 return value.Value;
181             }
182
183             if (state.IsCombined)
184             {
185                 value = Find(x => state.Contains(x.State));
186                 if (value.Value != null)
187                 {
188                     return value.Value;
189                 }
190             }
191
192             return Other;
193         }
194
195         /// <summary>
196         /// Clone function.
197         /// </summary>
198         /// <since_tizen> 6 </since_tizen>
199         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
200         [EditorBrowsable(EditorBrowsableState.Never)]
201         public void Clone(Selector<T> selector)
202         {
203             All = selector.All;
204             Normal = selector.Normal;
205             Focused = selector.Focused;
206             Pressed = selector.Pressed;
207             Disabled = selector.Disabled;
208             Selected = selector.Selected;
209             DisabledSelected = selector.DisabledSelected;
210             DisabledFocused = selector.DisabledFocused;
211             SelectedFocused = selector.SelectedFocused;
212             Other = selector.Other;
213         }
214
215         internal void Clone<U>(Selector<U> other) where U : T, Tizen.NUI.Internal.ICloneable
216         {
217             // TODO Apply constraint to the Selector (not to Clone method)
218
219             All = (T)(other.All)?.Clone();
220             Normal = (T)(other.Normal)?.Clone();
221             Focused = (T)(other.Focused)?.Clone();
222             Pressed = (T)(other.Pressed)?.Clone();
223             Disabled = (T)(other.Disabled)?.Clone();
224             Selected = (T)(other.Selected)?.Clone();
225             DisabledSelected = (T)(other.DisabledSelected)?.Clone();
226             DisabledFocused = (T)(other.DisabledFocused)?.Clone();
227             SelectedFocused = (T)(other.SelectedFocused)?.Clone();
228             Other = (T)(other.Other)?.Clone();
229         }
230
231         internal bool HasMultiValue()
232         {
233             return All == null;
234         }
235     }
236
237     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
238     [EditorBrowsable(EditorBrowsableState.Never)]
239     public class TriggerableSelector<T> : Selector<T>
240     {
241         public TriggerableSelector(View view, BindableProperty bindableProperty)
242         {
243             targetView = view;
244             targetBindableProperty = bindableProperty;
245             view.ControlStateChangeEventInternal += OnViewControlState;
246         }
247
248         /// <summary>
249         /// Clone function.
250         /// </summary>
251         /// <since_tizen> 6 </since_tizen>
252         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
253         [EditorBrowsable(EditorBrowsableState.Never)]
254         public new void Clone(Selector<T> selector)
255         {
256             base.Clone(selector);
257
258             if (null != targetView && null != GetValue(targetView.ControlState))
259             {
260                 targetView.SetValue(targetBindableProperty, GetValue(targetView.ControlState));
261             }
262         }
263
264         private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
265         {
266             View view = obj as View;
267             if (null != view && null != GetValue(controlStateChangedInfo.CurrentState))
268             {
269                 view.SetValue(targetBindableProperty, GetValue(controlStateChangedInfo.CurrentState));
270             }
271         }
272
273         private View targetView;
274         private BindableProperty targetBindableProperty;
275     }
276
277     /// <summary>
278     /// A class that helps binding a non-selector property in View to selector property in ViewStyle.
279     /// </summary>
280     internal class ViewSelector<T>
281     {
282         protected Selector<T> selector;
283         protected View view;
284         protected EventHandler<View.ControlStateChangedEventArgs> controlStateChanged;
285
286         internal ViewSelector(View view, EventHandler<View.ControlStateChangedEventArgs> controlStateChanged)
287         {
288             if (view == null || controlStateChanged == null)
289             {
290                 throw new global::System.ArgumentNullException();
291             }
292             this.view = view;
293             this.controlStateChanged = controlStateChanged;
294             this.selector = null;
295         }
296
297         internal T GetValue()
298         {
299             return selector == null ? default(T) : selector.GetValue(view.ControlState);
300         }
301
302         internal void Set(object value)
303         {
304             bool hadMultiValue = HasMultiValue();
305             var type = value?.GetType();
306
307             if (type == typeof(T))
308             {
309                 CopyValueToSelector((T)value);
310             }
311             else if (type == typeof(Selector<T>))
312             {
313                 CopySelectorToSelector((Selector<T>)value);
314             }
315             else if (type == Nullable.GetUnderlyingType(typeof(T)))
316             {
317                 CopyValueToSelector((T)value);
318             }
319             else
320             {
321                 selector = null;
322             }
323
324             if (hadMultiValue != HasMultiValue())
325             {
326                 if (hadMultiValue) view.ControlStateChangeEventInternal -= controlStateChanged;
327                 else view.ControlStateChangeEventInternal += controlStateChanged;
328             }
329         }
330
331         protected virtual void CopyValueToSelector(T value)
332         {
333             selector = new Selector<T>();
334             selector.All = value;
335         }
336
337         protected virtual void CopySelectorToSelector(Selector<T> value)
338         {
339             selector = new Selector<T>();
340             selector.Clone(value);
341         }
342
343         internal void Clear()
344         {
345             if (HasMultiValue())
346             {
347                 view.ControlStateChangeEventInternal -= controlStateChanged;
348             }
349             selector = null;
350         }
351
352         internal bool IsEmpty()
353         {
354             return selector == null;
355         }
356
357         protected bool HasMultiValue()
358         {
359             return (selector != null && selector.All == null);
360         }
361     }
362
363     /// <summary>
364     /// ViewSelector class for ICloneable type
365     /// </summary>
366     internal class CloneableViewSelector<T> : ViewSelector<T> where T : Tizen.NUI.Internal.ICloneable
367     {
368         internal CloneableViewSelector(View view, EventHandler<View.ControlStateChangedEventArgs> controlStateChanged) : base(view, controlStateChanged)
369         {
370         }
371
372         protected override void CopyValueToSelector(T value)
373         {
374             selector = new Selector<T>();
375             selector.All = (T)((T)value).Clone();
376         }
377
378         protected override void CopySelectorToSelector(Selector<T> value)
379         {
380             selector = new Selector<T>();
381             selector.Clone<T>((Selector<T>)value);
382         }
383     }
384
385     internal static class SelectorHelper
386     {
387         /// <summary>
388         /// For the object type of T or Selector T, convert it to Selector T and return the cloned one.
389         /// Otherwise, return null. <br/>
390         /// </summary>
391         static internal Selector<T> CopyCloneable<T>(object value) where T : class, Tizen.NUI.Internal.ICloneable
392         {
393             if (null != value)
394             {
395                 var type = value.GetType();
396
397                 if (type == typeof(Selector<T>))
398                 {
399                     var result = new Selector<T>();
400                     result.Clone<T>((Selector<T>)value);
401                     return result;
402                 }
403
404                 if (type == typeof(T))
405                 {
406                     return new Selector<T>((T)((T)value).Clone());
407                 }
408             }
409
410             return null;
411         }
412
413         /// <summary>
414         /// For the value type of T or Selector T, convert it to Selector T and return the cloned one.
415         /// Otherwise, return null. <br/>
416         /// </summary>
417         static internal Selector<T> CopyValue<T>(object value)
418         {
419             if (null != value)
420             {
421                 var type = value.GetType();
422
423                 if (type == typeof(Selector<T>))
424                 {
425                     var result = new Selector<T>();
426                     result.Clone((Selector<T>)value);
427                     return result;
428                 }
429
430                 if (type == typeof(T))
431                 {
432                     return new Selector<T>((T)value);
433                 }
434             }
435
436             return null;
437         }
438     }
439 }