[NUI]Revert temporary patch that fix DA build error (#2284)
[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.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.Binding;
21 using Tizen.NUI.Components;
22
23 namespace Tizen.NUI.BaseComponents
24 {
25     /// <summary>
26     /// Selector class, which is related by Control State, it is base class for other Selector.
27     /// </summary>
28     /// <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>
29     /// <since_tizen> 6 </since_tizen>
30     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
31     [EditorBrowsable(EditorBrowsableState.Never)]
32     public class Selector<T>
33     {
34         private readonly bool cloneable = typeof(ICloneable).IsAssignableFrom(typeof(T));
35
36         /// <summary>
37         /// The list for adding <see cref="SelectorItem{T}"/>.
38         /// </summary>
39         [EditorBrowsable(EditorBrowsableState.Never)]
40         List<SelectorItem<T>> SelectorItems { get; set; } = new List<SelectorItem<T>>();
41
42         /// <summary>
43         /// Adds the specified state and value to the <see cref="SelectorItems"/>.
44         /// </summary>
45         /// <param name="state">The state.</param>
46         /// <param name="value">The value associated with state.</param>
47         [EditorBrowsable(EditorBrowsableState.Never)]
48         public void Add(ControlState state, T value) => SelectorItems.Add(new SelectorItem<T>(state, value));
49
50         /// <summary>
51         /// Adds the specified state and value to the <see cref="SelectorItems"/>.
52         /// </summary>
53         /// <param name="selectorItem">The selector item class that stores a state-value pair.</param>
54         [EditorBrowsable(EditorBrowsableState.Never)]
55         public void Add(SelectorItem<T> selectorItem)
56         {
57             // To prevent a state from having multiple values, remove existing state-value pair.
58             int index = SelectorItems.FindIndex(x => x.State == selectorItem.State);
59             if (index != -1)
60                 SelectorItems.RemoveAt(index);
61
62             SelectorItems.Add(selectorItem);
63         }
64
65         /// <since_tizen> 6 </since_tizen>
66         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
67         public static implicit operator Selector<T>(T value)
68         {
69             return new Selector<T>(value);
70         }
71
72         /// Default Contructor
73         [EditorBrowsable(EditorBrowsableState.Never)]
74         public Selector()
75         {
76         }
77
78         /// Contructor with T
79         [EditorBrowsable(EditorBrowsableState.Never)]
80         public Selector(T value) : this()
81         {
82             All = cloneable ? (T)((ICloneable)value)?.Clone() : value;
83         }
84
85         /// Copy constructor
86         [EditorBrowsable(EditorBrowsableState.Never)]
87         public Selector(Selector<T> value) : this()
88         {
89             Clone(value);
90         }
91
92         /// <summary>
93         /// All State.
94         /// </summary>
95         /// <since_tizen> 6 </since_tizen>
96         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
97         [EditorBrowsable(EditorBrowsableState.Never)]
98         public T All
99         {
100             get;
101             set;
102         }
103         /// <summary>
104         /// Normal State.
105         /// </summary>
106         /// <since_tizen> 6 </since_tizen>
107         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
108         [EditorBrowsable(EditorBrowsableState.Never)]
109         public T Normal
110         {
111             get => SelectorItems.Find(x => x.State == ControlState.Normal).Value;
112             set => Add(ControlState.Normal, value);
113         }
114         /// <summary>
115         /// Pressed State.
116         /// </summary>
117         /// <since_tizen> 6 </since_tizen>
118         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
119         [EditorBrowsable(EditorBrowsableState.Never)]
120         public T Pressed
121         {
122             get => SelectorItems.Find(x => x.State == ControlState.Pressed).Value;
123             set => Add(ControlState.Pressed, value);
124         }
125         /// <summary>
126         /// Focused State.
127         /// </summary>
128         /// <since_tizen> 6 </since_tizen>
129         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
130         [EditorBrowsable(EditorBrowsableState.Never)]
131         public T Focused
132         {
133             get => SelectorItems.Find(x => x.State == ControlState.Focused).Value;
134             set => Add(ControlState.Focused, value);
135         }
136         /// <summary>
137         /// Selected State.
138         /// </summary>
139         /// <since_tizen> 6 </since_tizen>
140         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
141         [EditorBrowsable(EditorBrowsableState.Never)]
142         public T Selected
143         {
144             get => SelectorItems.Find(x => x.State == ControlState.Selected).Value;
145             set => Add(ControlState.Selected, value);
146         }
147         /// <summary>
148         /// Disabled State.
149         /// </summary>
150         /// <since_tizen> 6 </since_tizen>
151         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
152         [EditorBrowsable(EditorBrowsableState.Never)]
153         public T Disabled
154         {
155             get => SelectorItems.Find(x => x.State == ControlState.Disabled).Value;
156             set => Add(ControlState.Disabled, value);
157         }
158         /// <summary>
159         /// DisabledFocused State.
160         /// </summary>
161         /// <since_tizen> 6 </since_tizen>
162         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
163         [EditorBrowsable(EditorBrowsableState.Never)]
164         public T DisabledFocused
165         {
166             get => SelectorItems.Find(x => x.State == ControlState.DisabledFocused).Value;
167             set => Add(ControlState.DisabledFocused, value);
168         }
169         /// <summary>
170         /// SelectedFocused State.
171         /// </summary>
172         /// <since_tizen> 6 </since_tizen>
173         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
174         public T SelectedFocused
175         {
176             get => SelectorItems.Find(x => x.State == ControlState.SelectedFocused).Value;
177             set => Add(ControlState.SelectedFocused, value);
178         }
179         /// <summary>
180         /// DisabledSelected State.
181         /// </summary>
182         /// <since_tizen> 6 </since_tizen>
183         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
184         [EditorBrowsable(EditorBrowsableState.Never)]
185         public T DisabledSelected
186         {
187             get => SelectorItems.Find(x => x.State == ControlState.DisabledSelected).Value;
188             set => Add(ControlState.DisabledSelected, value);
189         }
190
191         /// <summary>
192         /// Other State.
193         /// </summary>
194         /// <since_tizen> 6 </since_tizen>
195         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
196         [EditorBrowsable(EditorBrowsableState.Never)]
197         public T Other
198         {
199             get => SelectorItems.Find(x => x.State == ControlState.Other).Value;
200             set => Add(ControlState.Other, value);
201         }
202
203         /// <summary>
204         /// Gets the number of elements.
205         /// </summary>
206         [EditorBrowsable(EditorBrowsableState.Never)]
207         public int Count => SelectorItems.Count;
208
209         /// <summary>
210         /// Get value by State.
211         /// </summary>
212         /// <exception cref="ArgumentNullException"> Thrown when state is null. </exception>
213         /// <since_tizen> 6 </since_tizen>
214         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
215         /// <returns>True if the selector has a given state value, false otherwise.</returns>
216         [EditorBrowsable(EditorBrowsableState.Never)]
217         public bool GetValue(ControlState state, out T result)
218         {
219             if (All != null)
220             {
221                 result = All;
222
223                 return true;
224             }
225
226             if (state == null)
227                 throw new ArgumentNullException(nameof(state));
228
229             result = default;
230
231             int index = SelectorItems.FindIndex(x => x.State == state);
232             if (index >= 0)
233             {
234                 result = SelectorItems[index].Value;
235                 return true;
236             }
237
238             if (null == state)
239             {
240                 throw new ArgumentNullException(nameof(state));
241             }
242             if (state.IsCombined)
243             {
244                 index = SelectorItems.FindIndex(x => state.Contains(x.State));
245                 if (index >= 0)
246                 {
247                     result = SelectorItems[index].Value;
248                     return true;
249                 }
250             }
251
252             index = SelectorItems.FindIndex(x => x.State == ControlState.Other);
253             if (index >= 0)
254             {
255                 result = SelectorItems[index].Value;
256                 return true;
257             }
258
259             return false;
260         }
261
262         /// <summary>
263         /// Removes all elements.
264         /// </summary>
265         [EditorBrowsable(EditorBrowsableState.Never)]
266         public void Clear()
267         {
268             All = default;
269             SelectorItems.Clear();
270         }
271
272         /// <inheritdoc/>
273         [EditorBrowsable(EditorBrowsableState.Never)]
274         public override string ToString()
275         {
276             string result = $"[All, {All}]";
277
278             foreach (var item in SelectorItems)
279             {
280                 result += $", {item}";
281             }
282
283             return result;
284         }
285
286         /// <summary>
287         /// Clone itself.
288         /// If type T implements ICloneable, it calls Clone() method to clone values, otherwise use operator=.
289         /// </summary>
290         /// <since_tizen> 6 </since_tizen>
291         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
292         [EditorBrowsable(EditorBrowsableState.Never)]
293         public Selector<T> Clone()
294         {
295             var cloned = new Selector<T>();
296             cloned.Clone(this);
297             return cloned;
298         }
299
300         /// <summary>
301         /// Copy values from other selector.
302         /// </summary>
303         /// <exception cref="ArgumentNullException"> Thrown when other is null. </exception>
304         [EditorBrowsable(EditorBrowsableState.Never)]
305         public void Clone(Selector<T> other)
306         {
307             if (null == other)
308             {
309                 throw new ArgumentNullException(nameof(other));
310             }
311
312             if (cloneable)
313             {
314                 All = (T)((ICloneable)other.All)?.Clone();
315                 SelectorItems = other.SelectorItems.ConvertAll(m => new SelectorItem<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
316             }
317             else
318             {
319                 All = other.All;
320                 SelectorItems = other.SelectorItems.ConvertAll(m => m);
321             }
322         }
323
324         internal bool HasMultiValue()
325         {
326             return SelectorItems.Count > 1;
327         }
328     }
329
330     /// <summary>
331     /// This will be attached to a View to detect ControlState change.
332     /// </summary>
333     [EditorBrowsable(EditorBrowsableState.Never)]
334     public class TriggerableSelector<T>
335     {
336         /// <summary>
337         /// Create an TriggerableSelector.
338         /// </summary>
339         [EditorBrowsable(EditorBrowsableState.Never)]
340         public delegate T ValueGetter(View view);
341
342         private readonly BindableProperty targetBindableProperty;
343         private readonly ValueGetter propertyGetter;
344         private bool dirty = true;
345         private Selector<T> selector;
346
347         /// <summary>
348         /// Create an TriggerableSelector.
349         /// </summary>
350         /// <param name="targetBindableProperty">The TriggerableSelector will change this bindable property value when the view's ControlState has changed.</param>
351         /// <param name="propertyGetter">It is optional value in case the target bindable property getter is not proper to use.</param>
352         [EditorBrowsable(EditorBrowsableState.Never)]
353         public TriggerableSelector(BindableProperty targetBindableProperty, ValueGetter propertyGetter = null)
354         {
355             this.targetBindableProperty = targetBindableProperty;
356             this.propertyGetter = propertyGetter;
357         }
358
359         /// <summary>
360         /// Return the containing selector. It can be null.
361         /// </summary>
362         /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
363         [EditorBrowsable(EditorBrowsableState.Never)]
364         public Selector<T> Get(View view)
365         {
366             if (!dirty) return selector;
367             if (null == view)
368             {
369                 throw new ArgumentNullException(nameof(view));
370             }
371
372             T value = default;
373
374             if (propertyGetter != null)
375             {
376                 value = propertyGetter(view);
377             }
378             else
379             {
380                 value = (T)view.GetValue(targetBindableProperty);
381             }
382
383             Selector<T> converted = value == null ? null : new Selector<T>(value);
384             Update(view, converted);
385
386             return selector;
387         }
388
389         /// <summary>
390         /// Update containing selector from the other selector.
391         /// </summary>
392         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
393         /// <param name="otherSelector">The copy target.</param>
394         /// <param name="updateView">Whether it updates the target view after update the selector or not.</param>
395         /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
396         [EditorBrowsable(EditorBrowsableState.Never)]
397         public void Update(View view, Selector<T> otherSelector, bool updateView = false)
398         {
399             Reset(view);
400
401             if (null == view)
402             {
403                 throw new ArgumentNullException(nameof(view));
404             }
405
406             if (otherSelector == null)
407             {
408                 return;
409             }
410
411             selector = otherSelector.Clone();
412
413             if (otherSelector.HasMultiValue())
414             {
415                 view.ControlStateChangeEventInternal += OnViewControlState;
416             }
417
418             if (updateView && otherSelector.GetValue(view.ControlState, out var value))
419             {
420                 view.SetValue(targetBindableProperty, value);
421             }
422         }
423
424         /// <summary>
425         /// Update containing selector value from a single value.
426         /// Note that, it updates lazily if possible.
427         /// If you need to udpate directly, please use <seealso cref="Update" />.
428         /// </summary>
429         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
430         /// <param name="value">The copy target.</param>
431         [EditorBrowsable(EditorBrowsableState.Never)]
432         public void UpdateIfNeeds(View view, T value)
433         {
434             if (selector != null && selector.HasMultiValue())
435             {
436                 Selector<T> converted = value == null ? null : new Selector<T>(value);
437                 Update(view, converted);
438                 return;
439             }
440
441             dirty = true;
442         }
443
444         /// <summary>
445         /// Reset selector and listeners.
446         /// </summary>
447         /// <param name="view">The View that is affected by this TriggerableSelector.</param>
448         [EditorBrowsable(EditorBrowsableState.Never)]
449         public void Reset(View view)
450         {
451             if (view != null)
452             {
453                 view.ControlStateChangeEventInternal -= OnViewControlState;
454             }
455             selector?.Clear();
456             selector = null;
457             dirty = false;
458         }
459
460         private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
461         {
462             View view = obj as View;
463             if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value))
464             {
465                 view.SetValue(targetBindableProperty, value);
466             }
467         }
468     }
469
470     /// <summary>
471     /// The selector item class that stores a state-value pair.
472     /// </summary>
473     [EditorBrowsable(EditorBrowsableState.Never)]
474     public class SelectorItem<T>
475     {
476         /// <summary>
477         /// The default constructor.
478         /// </summary>
479         [EditorBrowsable(EditorBrowsableState.Never)]
480         public SelectorItem() {}
481
482         /// <summary>
483         /// The constructor with the specified state and value.
484         /// </summary>
485         /// <param name="state">The state</param>
486         /// <param name="value">The value associated with state.</param>
487         [EditorBrowsable(EditorBrowsableState.Never)]
488         public SelectorItem(ControlState state, T value)
489         {
490             State = state;
491             Value = value;
492         }
493
494         /// <summary>
495         /// The state
496         /// </summary>
497         [EditorBrowsable(EditorBrowsableState.Never)]
498         public ControlState State { get; set; }
499
500         /// <summary>
501         /// The value associated with state.
502         /// </summary>
503         [EditorBrowsable(EditorBrowsableState.Never)]
504         public T Value { get; set; }
505
506         ///  <inheritdoc/>
507         [EditorBrowsable(EditorBrowsableState.Never)]
508         public override string ToString() => $"[{State}, {Value}]";
509     }
510
511     /// <summary>
512     /// Extension class for <see cref="Selector{T}"/>.
513     /// </summary>
514     [EditorBrowsable(EditorBrowsableState.Never)]
515     public static class SelectorExtensions
516     {
517         /// <summary>
518         /// Adds the specified state and value to the <see cref="Selector{T}.SelectorItems"/>.
519         /// </summary>
520         /// <param name="list">The list for adding state-value pair.</param>
521         /// <param name="state">The state.</param>
522         /// <param name="value">The value associated with state.</param>
523         [EditorBrowsable(EditorBrowsableState.Never)]
524         public static void Add<T>(this IList<SelectorItem<T>> list, ControlState state, T value)
525         {
526             list.Add(new SelectorItem<T>(state, value));
527         }
528     }
529 }