2 * Copyright(c) 2019 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 public IList<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) => SelectorItems.Add(new SelectorItem<T>(state, value));
51 /// Adds the specified state and value to the <see cref="SelectorItems"/>.
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)
57 // To prevent a state from having multiple values, remove existing state-value pair.
58 int index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == selectorItem.State);
60 SelectorItems.RemoveAt(index);
62 SelectorItems.Add(selectorItem);
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)
69 return new Selector<T>(value);
72 /// Default Contructor
73 [EditorBrowsable(EditorBrowsableState.Never)]
79 [EditorBrowsable(EditorBrowsableState.Never)]
80 public Selector(T value) : this()
82 All = cloneable ? (T)((ICloneable)value)?.Clone() : value;
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 public Selector(Selector<T> value) : this()
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)]
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)]
111 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Normal).Value;
112 set => Add(ControlState.Normal, value);
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)]
122 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Pressed).Value;
123 set => Add(ControlState.Pressed, value);
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)]
133 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Focused).Value;
134 set => Add(ControlState.Focused, value);
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)]
144 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Selected).Value;
145 set => Add(ControlState.Selected, value);
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)]
155 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Disabled).Value;
156 set => Add(ControlState.Disabled, value);
159 /// DisabledFocused State.
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
166 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.DisabledFocused).Value;
167 set => Add(ControlState.DisabledFocused, value);
170 /// SelectedFocused State.
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
176 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.SelectedFocused).Value;
177 set => Add(ControlState.SelectedFocused, value);
180 /// DisabledSelected State.
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
187 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.DisabledSelected).Value;
188 set => Add(ControlState.DisabledSelected, value);
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)]
199 get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Other).Value;
200 set => Add(ControlState.Other, value);
204 /// Gets the number of elements.
206 [EditorBrowsable(EditorBrowsableState.Never)]
207 public int Count => SelectorItems.Count;
210 /// Get value by State.
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)
227 throw new ArgumentNullException(nameof(state));
231 int index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == state);
234 result = SelectorItems[index].Value;
240 throw new ArgumentNullException(nameof(state));
242 if (state.IsCombined)
244 index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => state.Contains(x.State));
247 result = SelectorItems[index].Value;
252 index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == ControlState.Other);
255 result = SelectorItems[index].Value;
263 /// Removes all elements.
265 [EditorBrowsable(EditorBrowsableState.Never)]
269 SelectorItems.Clear();
273 [EditorBrowsable(EditorBrowsableState.Never)]
274 public override string ToString()
276 string result = $"[All, {All}]";
278 foreach (var item in SelectorItems)
280 result += $", {item}";
288 /// If type T implements ICloneable, it calls Clone() method to clone values, otherwise use operator=.
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()
295 var cloned = new Selector<T>();
301 /// Copy values from other selector.
303 /// <exception cref="ArgumentNullException"> Thrown when other is null. </exception>
304 [EditorBrowsable(EditorBrowsableState.Never)]
305 public void Clone(Selector<T> other)
309 throw new ArgumentNullException(nameof(other));
314 All = (T)((ICloneable)other.All)?.Clone();
315 SelectorItems = ((List<SelectorItem<T>>)other.SelectorItems).ConvertAll(m => new SelectorItem<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
320 SelectorItems = ((List<SelectorItem<T>>)other.SelectorItems).ConvertAll(m => m);
324 internal bool HasMultiValue()
326 return SelectorItems.Count > 1;
331 /// This will be attached to a View to detect ControlState change.
333 [EditorBrowsable(EditorBrowsableState.Never)]
334 public class TriggerableSelector<T>
337 /// Create an TriggerableSelector.
339 [EditorBrowsable(EditorBrowsableState.Never)]
340 public delegate T ValueGetter(View view);
342 private readonly BindableProperty targetBindableProperty;
343 private readonly ValueGetter propertyGetter;
344 private bool dirty = true;
345 private Selector<T> selector;
348 /// Create an TriggerableSelector.
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)
355 this.targetBindableProperty = targetBindableProperty;
356 this.propertyGetter = propertyGetter;
360 /// Return the containing selector. It can be null.
362 /// <exception cref="ArgumentNullException"> Thrown when view is null. </exception>
363 [EditorBrowsable(EditorBrowsableState.Never)]
364 public Selector<T> Get(View view)
366 if (!dirty) return selector;
369 throw new ArgumentNullException(nameof(view));
374 if (propertyGetter != null)
376 value = propertyGetter(view);
380 value = (T)view.GetValue(targetBindableProperty);
383 Selector<T> converted = value == null ? null : new Selector<T>(value);
384 Update(view, converted);
390 /// Update containing selector from the other selector.
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)
403 throw new ArgumentNullException(nameof(view));
406 if (otherSelector == null)
411 selector = otherSelector.Clone();
413 if (otherSelector.HasMultiValue())
415 view.ControlStateChangeEventInternal += OnViewControlState;
418 if (updateView && otherSelector.GetValue(view.ControlState, out var value))
420 view.SetValue(targetBindableProperty, value);
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" />.
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)
434 if (selector != null && selector.HasMultiValue())
436 Selector<T> converted = value == null ? null : new Selector<T>(value);
437 Update(view, converted);
445 /// Reset selector and listeners.
447 /// <param name="view">The View that is affected by this TriggerableSelector.</param>
448 [EditorBrowsable(EditorBrowsableState.Never)]
449 public void Reset(View view)
453 view.ControlStateChangeEventInternal -= OnViewControlState;
460 private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo)
462 View view = obj as View;
463 if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value))
465 view.SetValue(targetBindableProperty, value);
471 /// The selector item class that stores a state-value pair.
473 [EditorBrowsable(EditorBrowsableState.Never)]
474 public class SelectorItem<T>
477 /// The default constructor.
479 [EditorBrowsable(EditorBrowsableState.Never)]
480 public SelectorItem() {}
483 /// The constructor with the specified state and value.
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)
497 [EditorBrowsable(EditorBrowsableState.Never)]
498 public ControlState State { get; set; }
501 /// The value associated with state.
503 [EditorBrowsable(EditorBrowsableState.Never)]
504 public T Value { get; set; }
507 [EditorBrowsable(EditorBrowsableState.Never)]
508 public override string ToString() => $"[{State}, {Value}]";
512 /// Extension class for <see cref="Selector{T}"/>.
514 [EditorBrowsable(EditorBrowsableState.Never)]
515 public static class SelectorExtensions
518 /// Adds the specified state and value to the <see cref="Selector{T}.SelectorItems"/>.
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)
526 list.Add(new SelectorItem<T>(state, value));