2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.Binding;
21 using Tizen.NUI.Components;
23 namespace Tizen.NUI.BaseComponents
26 /// Selector class, which is related by Control State, it is base class for other Selector.
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>
34 private readonly bool cloneable = typeof(ICloneable).IsAssignableFrom(typeof(T));
37 /// The list for adding <see cref="SelectorItem{T}"/>.
39 [EditorBrowsable(EditorBrowsableState.Never)]
40 List<SelectorItem<T>> SelectorItems { get; set; } = new List<SelectorItem<T>>();
43 /// Adds the specified state and value to the <see cref="SelectorItems"/>.
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)
50 SelectorItems.Add(new SelectorItem<T>(state, value));
55 /// Adds the specified state and value to the <see cref="SelectorItems"/>.
57 /// <param name="selectorItem">The selector item class that stores a state-value pair.</param>
58 [EditorBrowsable(EditorBrowsableState.Never)]
59 public void Add(SelectorItem<T> selectorItem)
61 // To prevent a state from having multiple values, remove existing state-value pair.
62 int index = SelectorItems.FindIndex(x => x.State == selectorItem.State);
64 SelectorItems.RemoveAt(index);
66 SelectorItems.Add(selectorItem);
69 /// <since_tizen> 6 </since_tizen>
70 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
71 public static implicit operator Selector<T>(T value)
73 return new Selector<T>(value);
76 /// Default Contructor
77 [EditorBrowsable(EditorBrowsableState.Never)]
83 [EditorBrowsable(EditorBrowsableState.Never)]
84 public Selector(T value) : this()
86 All = cloneable ? (T)((ICloneable)value)?.Clone() : value;
90 [EditorBrowsable(EditorBrowsableState.Never)]
91 public Selector(Selector<T> value) : this()
99 /// <since_tizen> 6 </since_tizen>
100 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
101 [EditorBrowsable(EditorBrowsableState.Never)]
110 /// <since_tizen> 6 </since_tizen>
111 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
112 [EditorBrowsable(EditorBrowsableState.Never)]
115 get => SelectorItems.Find(x => x.State == ControlState.Normal).Value;
116 set => Add(ControlState.Normal, value);
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)]
126 get => SelectorItems.Find(x => x.State == ControlState.Pressed).Value;
127 set => Add(ControlState.Pressed, value);
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)]
137 get => SelectorItems.Find(x => x.State == ControlState.Focused).Value;
138 set => Add(ControlState.Focused, value);
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)]
148 get => SelectorItems.Find(x => x.State == ControlState.Selected).Value;
149 set => Add(ControlState.Selected, value);
154 /// <since_tizen> 6 </since_tizen>
155 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
156 [EditorBrowsable(EditorBrowsableState.Never)]
159 get => SelectorItems.Find(x => x.State == ControlState.Disabled).Value;
160 set => Add(ControlState.Disabled, value);
163 /// DisabledFocused State.
165 /// <since_tizen> 6 </since_tizen>
166 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
167 [EditorBrowsable(EditorBrowsableState.Never)]
168 public T DisabledFocused
170 get => SelectorItems.Find(x => x.State == ControlState.DisabledFocused).Value;
171 set => Add(ControlState.DisabledFocused, value);
174 /// SelectedFocused State.
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 public T SelectedFocused
180 get => SelectorItems.Find(x => x.State == ControlState.SelectedFocused).Value;
181 set => Add(ControlState.SelectedFocused, value);
184 /// DisabledSelected State.
186 /// <since_tizen> 6 </since_tizen>
187 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
188 [EditorBrowsable(EditorBrowsableState.Never)]
189 public T DisabledSelected
191 get => SelectorItems.Find(x => x.State == ControlState.DisabledSelected).Value;
192 set => Add(ControlState.DisabledSelected, value);
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)]
203 get => SelectorItems.Find(x => x.State == ControlState.Other).Value;
204 set => Add(ControlState.Other, value);
208 /// Gets the number of elements.
210 [EditorBrowsable(EditorBrowsableState.Never)]
211 public int Count => SelectorItems.Count;
214 /// Get value by State.
216 /// <exception cref="ArgumentNullException"> Thrown when state is null. </exception>
217 /// <since_tizen> 6 </since_tizen>
218 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
219 /// <returns>True if the selector has a given state value, false otherwise.</returns>
220 [EditorBrowsable(EditorBrowsableState.Never)]
221 public bool GetValue(ControlState state, out T result)
231 throw new ArgumentNullException(nameof(state));
235 int index = SelectorItems.FindIndex(x => x.State == state);
238 result = SelectorItems[index].Value;
244 throw new ArgumentNullException(nameof(state));
246 if (state.IsCombined)
248 index = SelectorItems.FindIndex(x => state.Contains(x.State));
251 result = SelectorItems[index].Value;
256 index = SelectorItems.FindIndex(x => x.State == ControlState.Other);
259 result = SelectorItems[index].Value;
267 /// Removes all elements.
269 [EditorBrowsable(EditorBrowsableState.Never)]
273 SelectorItems.Clear();
277 [EditorBrowsable(EditorBrowsableState.Never)]
278 public override string ToString()
280 string result = $"[All, {All}]";
282 foreach (var item in SelectorItems)
284 result += $", {item}";
292 /// If type T implements ICloneable, it calls Clone() method to clone values, otherwise use operator=.
294 /// <since_tizen> 6 </since_tizen>
295 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
296 [EditorBrowsable(EditorBrowsableState.Never)]
297 public Selector<T> Clone()
299 var cloned = new Selector<T>();
305 /// Clone with type converter.
307 /// <exception cref="ArgumentNullException"> Thrown when converter is null. </exception>
308 [EditorBrowsable(EditorBrowsableState.Never)]
309 public Selector<TOut> Clone<TOut>(Converter<T, TOut> converter)
311 if (converter == null) throw new ArgumentNullException(nameof(converter));
313 Selector<TOut> result = new Selector<TOut>();
314 result.All = converter(All);
315 result.SelectorItems = SelectorItems.ConvertAll<SelectorItem<TOut>>(m => new SelectorItem<TOut>(m.State, converter(m.Value)));
321 /// Copy values from other selector.
323 /// <exception cref="ArgumentNullException"> Thrown when other is null. </exception>
324 [EditorBrowsable(EditorBrowsableState.Never)]
325 public void Clone(Selector<T> other)
329 throw new ArgumentNullException(nameof(other));
334 All = (T)((ICloneable)other.All)?.Clone();
335 SelectorItems = other.SelectorItems.ConvertAll(m => new SelectorItem<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
340 SelectorItems = other.SelectorItems.ConvertAll(m => m);
344 internal bool HasMultiValue()
346 return SelectorItems.Count > 1;
351 /// This will be attached to a View to detect ControlState change.
353 [EditorBrowsable(EditorBrowsableState.Never)]
354 public class TriggerableSelector<T>
357 /// Create an TriggerableSelector.
359 [EditorBrowsable(EditorBrowsableState.Never)]
360 public delegate T ValueGetter(View view);
362 private readonly BindableProperty targetBindableProperty;
363 private readonly ValueGetter propertyGetter;
364 private bool dirty = true;
365 private Selector<T> selector;
368 /// Create an TriggerableSelector.
370 /// <param name="targetBindableProperty">The TriggerableSelector will change this bindable property value when the view's ControlState has changed.</param>
371 /// <param name="propertyGetter">It is optional value in case the target bindable property getter is not proper to use.</param>
372 [EditorBrowsable(EditorBrowsableState.Never)]
373 public TriggerableSelector(BindableProperty targetBindableProperty, ValueGetter propertyGetter = null)
375 this.targetBindableProperty = targetBindableProperty;
376 this.propertyGetter = propertyGetter;
380 /// Return the containing selector. It can be null.
382 /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
383 [EditorBrowsable(EditorBrowsableState.Never)]
384 public Selector<T> Get(View view)
386 if (!dirty) return selector;
389 throw new ArgumentNullException(nameof(view));
394 if (propertyGetter != null)
396 value = propertyGetter(view);
400 value = (T)view.GetValue(targetBindableProperty);
403 Selector<T> converted = value == null ? null : new Selector<T>(value);
404 Update(view, converted);
410 /// Update containing selector from the other selector.
412 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
413 /// <param name="otherSelector">The copy target.</param>
414 /// <param name="updateView">Whether it updates the target view after update the selector or not.</param>
415 /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
416 [EditorBrowsable(EditorBrowsableState.Never)]
417 public void Update(View view, Selector<T> otherSelector, bool updateView = false)
423 throw new ArgumentNullException(nameof(view));
426 if (otherSelector == null)
431 selector = otherSelector.Clone();
433 if (otherSelector.HasMultiValue())
435 view.ControlStateChangeEventInternal += OnViewControlState;
438 if (updateView && otherSelector.GetValue(view.ControlState, out var value))
440 view.SetValue(targetBindableProperty, value);
445 /// Update containing selector value from a single value.
446 /// Note that, it updates lazily if possible.
447 /// If you need to udpate directly, please use <seealso cref="Update" />.
449 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
450 /// <param name="value">The copy target.</param>
451 [EditorBrowsable(EditorBrowsableState.Never)]
452 public void UpdateIfNeeds(View view, T value)
454 if (selector != null && selector.HasMultiValue())
456 Selector<T> converted = value == null ? null : new Selector<T>(value);
457 Update(view, converted);
465 /// Reset selector and listeners.
467 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
468 [EditorBrowsable(EditorBrowsableState.Never)]
469 public void Reset(View view)
473 view.ControlStateChangeEventInternal -= OnViewControlState;
480 private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
482 View view = obj as View;
483 if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value))
485 view.SetValue(targetBindableProperty, value);
491 /// The selector item class that stores a state-value pair.
493 [EditorBrowsable(EditorBrowsableState.Never)]
494 public class SelectorItem<T>
497 /// The default constructor.
499 [EditorBrowsable(EditorBrowsableState.Never)]
500 public SelectorItem() {}
503 /// The constructor with the specified state and value.
505 /// <param name="state">The state</param>
506 /// <param name="value">The value associated with state.</param>
507 [EditorBrowsable(EditorBrowsableState.Never)]
508 public SelectorItem(ControlState state, T value)
517 [EditorBrowsable(EditorBrowsableState.Never)]
518 public ControlState State { get; set; }
521 /// The value associated with state.
523 [EditorBrowsable(EditorBrowsableState.Never)]
524 public T Value { get; set; }
527 [EditorBrowsable(EditorBrowsableState.Never)]
528 public override string ToString() => $"[{State}, {Value}]";
532 /// Extension class for <see cref="Selector{T}"/>.
534 [EditorBrowsable(EditorBrowsableState.Never)]
535 public static class SelectorExtensions
538 /// Adds the specified state and value to the <see cref="Selector{T}.SelectorItems"/>.
540 /// <param name="list">The list for adding state-value pair.</param>
541 /// <param name="state">The state.</param>
542 /// <param name="value">The value associated with state.</param>
543 /// <exception cref="ArgumentNullException"> Thrown when given list is null. </exception>
544 [EditorBrowsable(EditorBrowsableState.Never)]
545 public static void Add<T>(this IList<SelectorItem<T>> list, ControlState state, T value)
547 if (list == null) throw new ArgumentNullException(nameof(list));
549 list.Add(new SelectorItem<T>(state, value));