[NUI] Refactoring Theme and StyleManager (#1981)
[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     /// <typeparam name="T">The property type of the selector. if it's reference type, it should be of type <see cref="ICloneable"/> that implement deep copy in <see cref="ICloneable.Clone"/>.</typeparam>
28     /// <since_tizen> 6 </since_tizen>
29     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
30     [EditorBrowsable(EditorBrowsableState.Never)]
31     public class Selector<T> : StateValueCollection<T>
32     {
33         private readonly bool isCloneable = typeof(ICloneable).IsAssignableFrom(typeof(T));
34         /// <since_tizen> 6 </since_tizen>
35         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
36         public static implicit operator Selector<T>(T value)
37         {
38             return new Selector<T>(value);
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 = isCloneable ? (T)((ICloneable)value)?.Clone() : value;
52         }
53
54         /// Copy constructor
55         [EditorBrowsable(EditorBrowsableState.Never)]
56         public Selector(Selector<T> value) : this()
57         {
58             Clone(value);
59         }
60
61         /// <summary>
62         /// All State.
63         /// </summary>
64         /// <since_tizen> 6 </since_tizen>
65         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
66         [EditorBrowsable(EditorBrowsableState.Never)]
67         public T All
68         {
69             get;
70             set;
71         }
72         /// <summary>
73         /// Normal State.
74         /// </summary>
75         /// <since_tizen> 6 </since_tizen>
76         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
77         [EditorBrowsable(EditorBrowsableState.Never)]
78         public T Normal
79         {
80             get => Find(x => x.State == ControlState.Normal).Value;
81             set => Add(ControlState.Normal, value);
82         }
83         /// <summary>
84         /// Pressed State.
85         /// </summary>
86         /// <since_tizen> 6 </since_tizen>
87         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
88         [EditorBrowsable(EditorBrowsableState.Never)]
89         public T Pressed
90         {
91             get => Find(x => x.State == ControlState.Pressed).Value;
92             set => Add(ControlState.Pressed, value);
93         }
94         /// <summary>
95         /// Focused State.
96         /// </summary>
97         /// <since_tizen> 6 </since_tizen>
98         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
99         [EditorBrowsable(EditorBrowsableState.Never)]
100         public T Focused
101         {
102             get => Find(x => x.State == ControlState.Focused).Value;
103             set => Add(ControlState.Focused, value);
104         }
105         /// <summary>
106         /// Selected State.
107         /// </summary>
108         /// <since_tizen> 6 </since_tizen>
109         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
110         [EditorBrowsable(EditorBrowsableState.Never)]
111         public T Selected
112         {
113             get => Find(x => x.State == ControlState.Selected).Value;
114             set => Add(ControlState.Selected, value);
115         }
116         /// <summary>
117         /// Disabled State.
118         /// </summary>
119         /// <since_tizen> 6 </since_tizen>
120         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
121         [EditorBrowsable(EditorBrowsableState.Never)]
122         public T Disabled
123         {
124             get => Find(x => x.State == ControlState.Disabled).Value;
125             set => Add(ControlState.Disabled, value);
126         }
127         /// <summary>
128         /// DisabledFocused State.
129         /// </summary>
130         /// <since_tizen> 6 </since_tizen>
131         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
132         [EditorBrowsable(EditorBrowsableState.Never)]
133         public T DisabledFocused
134         {
135             get => Find(x => x.State == ControlState.DisabledFocused).Value;
136             set => Add(ControlState.DisabledFocused, value);
137         }
138         /// <summary>
139         /// SelectedFocused State.
140         /// </summary>
141         /// <since_tizen> 6 </since_tizen>
142         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
143         public T SelectedFocused
144         {
145             get => Find(x => x.State == ControlState.SelectedFocused).Value;
146             set => Add(ControlState.SelectedFocused, value);
147         }
148         /// <summary>
149         /// DisabledSelected State.
150         /// </summary>
151         /// <since_tizen> 6 </since_tizen>
152         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
153         [EditorBrowsable(EditorBrowsableState.Never)]
154         public T DisabledSelected
155         {
156             get => Find(x => x.State == ControlState.DisabledSelected).Value;
157             set => Add(ControlState.DisabledSelected, value);
158         }
159
160         /// <summary>
161         /// Other State.
162         /// </summary>
163         /// <since_tizen> 6 </since_tizen>
164         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
165         [EditorBrowsable(EditorBrowsableState.Never)]
166         public T Other
167         {
168             get => Find(x => x.State == ControlState.Other).Value;
169             set => Add(ControlState.Other, value);
170         }
171         /// <summary>
172         /// Get value by State.
173         /// </summary>
174         /// <since_tizen> 6 </since_tizen>
175         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
176         /// <returns>True if the selector has a given state value, false otherwise.</returns>
177         [EditorBrowsable(EditorBrowsableState.Never)]
178         public bool GetValue(ControlState state, out T result)
179         {
180             if (All != null)
181             {
182                 result = All;
183
184                 return true;
185             }
186
187             result = default;
188
189             int index = StateValueList.FindIndex(x => x.State == state);
190             if (index >= 0)
191             {
192                 result = StateValueList[index].Value;
193                 return true;
194             }
195
196             if (state.IsCombined)
197             {
198                 index = StateValueList.FindIndex(x => state.Contains(x.State));
199                 if (index >= 0)
200                 {
201                     result = StateValueList[index].Value;
202                     return true;
203                 }
204             }
205
206             index = StateValueList.FindIndex(x => x.State == ControlState.Other);
207             if (index >= 0)
208             {
209                 result = StateValueList[index].Value;
210                 return true;
211             }
212
213             return false;
214         }
215
216         /// <inheritdoc/>
217         [EditorBrowsable(EditorBrowsableState.Never)]
218         public override void Clear()
219         {
220             All = default;
221             base.Clear();
222         }
223
224         /// <inheritdoc/>
225         [EditorBrowsable(EditorBrowsableState.Never)]
226         public override string ToString()
227         {
228             string result = $"[All, {All}]";
229
230             foreach (var item in StateValueList)
231             {
232                 result += $", {item}";
233             }
234
235             return result;
236         }
237
238         /// <summary>
239         /// Clone itself.
240         /// If type T implements ISelectorItem, it calls Clone() method to clone values, otherwise use operator=.
241         /// </summary>
242         /// <since_tizen> 6 </since_tizen>
243         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
244         [EditorBrowsable(EditorBrowsableState.Never)]
245         public Selector<T> Clone()
246         {
247             var cloned = new Selector<T>();
248             cloned.Clone(this);
249             return cloned;
250         }
251
252         /// <summary>
253         /// Copy values from other selector.
254         /// </summary>
255         [EditorBrowsable(EditorBrowsableState.Never)]
256         public void Clone(Selector<T> other)
257         {
258             Clear();
259
260             if (isCloneable)
261             {
262                 All = (T)((ICloneable)other.All)?.Clone();
263                 foreach (var item in other.StateValueList)
264                 {
265                     AddWithoutCheck(new StateValuePair<T>(item.State, (T)((ICloneable)item.Value)?.Clone()));
266                 }
267             }
268             else
269             {
270                 All = other.All;
271                 foreach (var item in other.StateValueList)
272                 {
273                     AddWithoutCheck(item);
274                 }
275             }
276         }
277
278         internal bool HasMultiValue()
279         {
280             return StateValueList.Count > 1;
281         }
282     }
283
284     /// <summary>
285     /// This will be attached to a View to detect ControlState change.
286     /// </summary>
287     [EditorBrowsable(EditorBrowsableState.Never)]
288     public class TriggerableSelector<T>
289     {
290         /// <summary>
291         /// Create an TriggerableSelector.
292         /// </summary>
293         [EditorBrowsable(EditorBrowsableState.Never)]
294         public delegate T ValueGetter(View view);
295
296         private readonly BindableProperty targetBindableProperty;
297         private readonly ValueGetter propertyGetter;
298         private bool dirty = true;
299         private Selector<T> selector;
300
301         /// <summary>
302         /// Create an TriggerableSelector.
303         /// </summary>
304         /// <param name="targetBindableProperty">The TriggerableSelector will change this bindable property value when the view's ControlState has changed.</param>
305         /// <param name="propertyGetter">It is optional value in case the target bindable property getter is not proper to use.</param>
306         [EditorBrowsable(EditorBrowsableState.Never)]
307         public TriggerableSelector(BindableProperty targetBindableProperty, ValueGetter propertyGetter = null)
308         {
309             this.targetBindableProperty = targetBindableProperty;
310             this.propertyGetter = propertyGetter;
311         }
312
313         /// <summary>
314         /// Return the containing selector. It can be null.
315         /// </summary>
316         [EditorBrowsable(EditorBrowsableState.Never)]
317         public Selector<T> Get(View view)
318         {
319             if (!dirty) return selector;
320
321             T value = default;
322
323             if (propertyGetter != null)
324             {
325                 value = propertyGetter(view);
326             }
327             else
328             {
329                 value = (T)view.GetValue(targetBindableProperty);
330             }
331
332             Selector<T> converted = value == null ? null : new Selector<T>(value);
333             Update(view, converted);
334
335             return selector;
336         }
337
338         /// <summary>
339         /// Update containing selector from the other selector.
340         /// </summary>
341         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
342         /// <param name="otherSelector">The copy target.</param>
343         /// <param name="updateView">Whether it updates the target view after update the selector or not.</param>
344         [EditorBrowsable(EditorBrowsableState.Never)]
345         public void Update(View view, Selector<T> otherSelector, bool updateView = false)
346         {
347             Reset(view);
348
349             if (otherSelector == null)
350             {
351                 return;   
352             }
353
354             selector = otherSelector.Clone();
355
356             if (otherSelector.HasMultiValue())
357             {
358                 view.ControlStateChangeEventInternal += OnViewControlState;
359             }
360
361             if (updateView && otherSelector.GetValue(view.ControlState, out var value))
362             {
363                 view.SetValue(targetBindableProperty, value);
364             }
365         }
366
367         /// <summary>
368         /// Update containing selector value from a single value.
369         /// Note that, it updates lazily if possible.
370         /// If you need to udpate directly, please use <seealso cref="Update" />.
371         /// </summary>
372         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
373         /// <param name="value">The copy target.</param>
374         [EditorBrowsable(EditorBrowsableState.Never)]
375         public void UpdateIfNeeds(View view, T value)
376         {
377             if (selector != null && selector.HasMultiValue())
378             {
379                 Selector<T> converted = value == null ? null : new Selector<T>(value);
380                 Update(view, converted);
381                 return;
382             }
383
384             dirty = true;
385         }
386
387         /// <summary>
388         /// Reset selector and listeners.
389         /// </summary>
390         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
391         [EditorBrowsable(EditorBrowsableState.Never)]
392         public void Reset(View view)
393         {
394             view.ControlStateChangeEventInternal -= OnViewControlState;
395             selector?.Clear();
396             selector = null;
397             dirty = false;
398         }
399
400         private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
401         {
402             View view = obj as View;
403             if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value))
404             {
405                 view.SetValue(targetBindableProperty, value);
406             }
407         }
408     }
409 }