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