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;
22 namespace Tizen.NUI.BaseComponents
25 /// Selector class, which is related by Control State, it is base class for other Selector.
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>
33 private readonly bool cloneable = typeof(ICloneable).IsAssignableFrom(typeof(T));
36 /// The list for adding <see cref="SelectorItem{T}"/>.
38 [EditorBrowsable(EditorBrowsableState.Never)]
39 List<SelectorItem<T>> SelectorItems { get; set; } = new List<SelectorItem<T>>();
42 /// Adds the specified state and value to the <see cref="SelectorItems"/>.
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)
49 SelectorItems.Add(new SelectorItem<T>(state, value));
54 /// Adds the specified state and value to the <see cref="SelectorItems"/>.
56 /// <param name="selectorItem">The selector item class that stores a state-value pair.</param>
57 [EditorBrowsable(EditorBrowsableState.Never)]
58 public void Add(SelectorItem<T> selectorItem)
60 // To prevent a state from having multiple values, remove existing state-value pair.
61 int index = SelectorItems.FindIndex(x => x.State == selectorItem.State);
63 SelectorItems.RemoveAt(index);
65 SelectorItems.Add(selectorItem);
68 /// <since_tizen> 6 </since_tizen>
69 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
70 public static implicit operator Selector<T>(T value)
72 return new Selector<T>(value);
75 /// Default Contructor
76 [EditorBrowsable(EditorBrowsableState.Never)]
82 [EditorBrowsable(EditorBrowsableState.Never)]
83 public Selector(T value) : this()
85 All = cloneable ? (T)((ICloneable)value)?.Clone() : value;
89 [EditorBrowsable(EditorBrowsableState.Never)]
90 public Selector(Selector<T> value) : this()
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)]
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)]
114 get => SelectorItems.Find(x => x.State == ControlState.Normal).Value;
115 set => Add(ControlState.Normal, value);
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)]
125 get => SelectorItems.Find(x => x.State == ControlState.Pressed).Value;
126 set => Add(ControlState.Pressed, value);
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)]
136 get => SelectorItems.Find(x => x.State == ControlState.Focused).Value;
137 set => Add(ControlState.Focused, value);
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 [EditorBrowsable(EditorBrowsableState.Never)]
147 get => SelectorItems.Find(x => x.State == ControlState.Selected).Value;
148 set => Add(ControlState.Selected, value);
153 /// <since_tizen> 6 </since_tizen>
154 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
155 [EditorBrowsable(EditorBrowsableState.Never)]
158 get => SelectorItems.Find(x => x.State == ControlState.Disabled).Value;
159 set => Add(ControlState.Disabled, value);
162 /// DisabledFocused State.
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 DisabledFocused
169 get => SelectorItems.Find(x => x.State == ControlState.DisabledFocused).Value;
170 set => Add(ControlState.DisabledFocused, value);
173 /// SelectedFocused State.
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 public T SelectedFocused
179 get => SelectorItems.Find(x => x.State == ControlState.SelectedFocused).Value;
180 set => Add(ControlState.SelectedFocused, value);
183 /// DisabledSelected State.
185 /// <since_tizen> 6 </since_tizen>
186 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
187 [EditorBrowsable(EditorBrowsableState.Never)]
188 public T DisabledSelected
190 get => SelectorItems.Find(x => x.State == ControlState.DisabledSelected).Value;
191 set => Add(ControlState.DisabledSelected, value);
197 /// <since_tizen> 6 </since_tizen>
198 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
199 [EditorBrowsable(EditorBrowsableState.Never)]
202 get => SelectorItems.Find(x => x.State == ControlState.Other).Value;
203 set => Add(ControlState.Other, value);
207 /// Gets the number of elements.
209 [EditorBrowsable(EditorBrowsableState.Never)]
210 public int Count => SelectorItems.Count;
213 /// Get value by State.
215 /// <exception cref="ArgumentNullException"> Thrown when state is null. </exception>
216 /// <since_tizen> 6 </since_tizen>
217 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
218 /// <returns>True if the selector has a given state value, false otherwise.</returns>
219 [EditorBrowsable(EditorBrowsableState.Never)]
220 public bool GetValue(ControlState state, out T result)
230 throw new ArgumentNullException(nameof(state));
234 int index = SelectorItems.FindIndex(x => x.State == state);
237 result = SelectorItems[index].Value;
243 throw new ArgumentNullException(nameof(state));
245 if (state.IsCombined)
247 index = SelectorItems.FindIndex(x => state.Contains(x.State));
250 result = SelectorItems[index].Value;
255 index = SelectorItems.FindIndex(x => x.State == ControlState.Other);
258 result = SelectorItems[index].Value;
266 /// Removes all elements.
268 [EditorBrowsable(EditorBrowsableState.Never)]
272 SelectorItems.Clear();
276 [EditorBrowsable(EditorBrowsableState.Never)]
277 public override string ToString()
279 string result = $"[All, {All}]";
281 foreach (var item in SelectorItems)
283 result += $", {item}";
291 /// If type T implements ICloneable, it calls Clone() method to clone values, otherwise use operator=.
293 /// <since_tizen> 6 </since_tizen>
294 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
295 [EditorBrowsable(EditorBrowsableState.Never)]
296 public Selector<T> Clone()
298 var cloned = new Selector<T>();
304 /// Clone with type converter.
306 /// <exception cref="ArgumentNullException"> Thrown when converter is null. </exception>
307 [EditorBrowsable(EditorBrowsableState.Never)]
308 public Selector<TOut> Clone<TOut>(Converter<T, TOut> converter)
310 if (converter == null) throw new ArgumentNullException(nameof(converter));
312 Selector<TOut> result = new Selector<TOut>();
313 result.All = converter(All);
314 result.SelectorItems = SelectorItems.ConvertAll<SelectorItem<TOut>>(m => new SelectorItem<TOut>(m.State, converter(m.Value)));
320 /// Copy values from other selector.
322 /// <exception cref="ArgumentNullException"> Thrown when other is null. </exception>
323 [EditorBrowsable(EditorBrowsableState.Never)]
324 public void Clone(Selector<T> other)
328 throw new ArgumentNullException(nameof(other));
333 All = (T)((ICloneable)other.All)?.Clone();
334 SelectorItems = other.SelectorItems.ConvertAll(m => new SelectorItem<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
339 SelectorItems = other.SelectorItems.ConvertAll(m => m);
343 internal bool HasMultiValue()
345 return SelectorItems.Count > 1;
350 /// This will be attached to a View to detect ControlState change.
352 [EditorBrowsable(EditorBrowsableState.Never)]
353 public class TriggerableSelector<T>
356 /// Create an TriggerableSelector.
358 [EditorBrowsable(EditorBrowsableState.Never)]
359 public delegate T ValueGetter(View view);
361 private readonly BindableProperty targetBindableProperty;
362 private readonly ValueGetter propertyGetter;
363 private bool dirty = true;
364 private Selector<T> selector;
367 /// Create an TriggerableSelector.
369 /// <param name="targetBindableProperty">The TriggerableSelector will change this bindable property value when the view's ControlState has changed.</param>
370 /// <param name="propertyGetter">It is optional value in case the target bindable property getter is not proper to use.</param>
371 [EditorBrowsable(EditorBrowsableState.Never)]
372 public TriggerableSelector(BindableProperty targetBindableProperty, ValueGetter propertyGetter = null)
374 this.targetBindableProperty = targetBindableProperty;
375 this.propertyGetter = propertyGetter;
379 /// Return the containing selector. It can be null.
381 /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
382 [EditorBrowsable(EditorBrowsableState.Never)]
383 public Selector<T> Get(View view)
385 if (!dirty) return selector;
388 throw new ArgumentNullException(nameof(view));
393 if (propertyGetter != null)
395 value = propertyGetter(view);
399 value = (T)view.GetValue(targetBindableProperty);
402 Selector<T> converted = value == null ? null : new Selector<T>(value);
403 Update(view, converted);
409 /// Update containing selector from the other selector.
411 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
412 /// <param name="otherSelector">The copy target.</param>
413 /// <param name="updateView">Whether it updates the target view after update the selector or not.</param>
414 /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
415 [EditorBrowsable(EditorBrowsableState.Never)]
416 public void Update(View view, Selector<T> otherSelector, bool updateView = false)
422 throw new ArgumentNullException(nameof(view));
425 if (otherSelector == null)
430 selector = otherSelector.Clone();
432 if (otherSelector.HasMultiValue())
434 view.ControlStateChangeEventInternal += OnViewControlState;
437 if (updateView && otherSelector.GetValue(view.ControlState, out var value))
439 view.SetValue(targetBindableProperty, value);
444 /// Update containing selector value from a single value.
445 /// Note that, it updates lazily if possible.
446 /// If you need to udpate directly, please use <seealso cref="Update" />.
448 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
449 /// <param name="value">The copy target.</param>
450 [EditorBrowsable(EditorBrowsableState.Never)]
451 public void UpdateIfNeeds(View view, T value)
453 if (selector != null && selector.HasMultiValue())
455 Selector<T> converted = value == null ? null : new Selector<T>(value);
456 Update(view, converted);
464 /// Reset selector and listeners.
466 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
467 [EditorBrowsable(EditorBrowsableState.Never)]
468 public void Reset(View view)
472 view.ControlStateChangeEventInternal -= OnViewControlState;
479 private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
481 View view = obj as View;
482 if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value))
484 view.SetValue(targetBindableProperty, value);
490 /// The selector item class that stores a state-value pair.
492 [EditorBrowsable(EditorBrowsableState.Never)]
493 public class SelectorItem<T>
496 /// The default constructor.
498 [EditorBrowsable(EditorBrowsableState.Never)]
499 public SelectorItem() {}
502 /// The constructor with the specified state and value.
504 /// <param name="state">The state</param>
505 /// <param name="value">The value associated with state.</param>
506 [EditorBrowsable(EditorBrowsableState.Never)]
507 public SelectorItem(ControlState state, T value)
516 [EditorBrowsable(EditorBrowsableState.Never)]
517 public ControlState State { get; set; }
520 /// The value associated with state.
522 [EditorBrowsable(EditorBrowsableState.Never)]
523 public T Value { get; set; }
526 [EditorBrowsable(EditorBrowsableState.Never)]
527 public override string ToString() => $"[{State}, {Value}]";
531 /// Extension class for <see cref="Selector{T}"/>.
533 [EditorBrowsable(EditorBrowsableState.Never)]
534 public static class SelectorExtensions
537 /// Adds the specified state and value to the <see cref="Selector{T}.SelectorItems"/>.
539 /// <param name="list">The list for adding state-value pair.</param>
540 /// <param name="state">The state.</param>
541 /// <param name="value">The value associated with state.</param>
542 /// <exception cref="ArgumentNullException"> Thrown when given list is null. </exception>
543 [EditorBrowsable(EditorBrowsableState.Never)]
544 public static void Add<T>(this IList<SelectorItem<T>> list, ControlState state, T value)
546 if (list == null) throw new ArgumentNullException(nameof(list));
548 list.Add(new SelectorItem<T>(state, value));