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