/* * Copyright(c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Collections.Generic; using System.ComponentModel; using Tizen.NUI.Binding; namespace Tizen.NUI.BaseComponents { /// /// Selector class, which is related by Control State, it is base class for other Selector. /// /// The property type of the selector. if it's reference type, it should be of type that implement deep copy in . /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public class Selector { private readonly bool cloneable = typeof(ICloneable).IsAssignableFrom(typeof(T)); /// /// The list for adding . /// [EditorBrowsable(EditorBrowsableState.Never)] List> SelectorItems { get; set; } = new List>(); /// /// Adds the specified state and value to the . /// /// The state. /// The value associated with state. [EditorBrowsable(EditorBrowsableState.Never)] public void Add(ControlState state, T value) { SelectorItems.Add(new SelectorItem(state, value)); All = default; } /// /// Adds the specified state and value to the . /// /// The selector item class that stores a state-value pair. [EditorBrowsable(EditorBrowsableState.Never)] public void Add(SelectorItem selectorItem) { // To prevent a state from having multiple values, remove existing state-value pair. int index = SelectorItems.FindIndex(x => x.State == selectorItem.State); if (index != -1) SelectorItems.RemoveAt(index); SelectorItems.Add(selectorItem); } /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. public static implicit operator Selector(T value) { return new Selector(value); } /// Default Contructor [EditorBrowsable(EditorBrowsableState.Never)] public Selector() { } /// Contructor with T [EditorBrowsable(EditorBrowsableState.Never)] public Selector(T value) : this() { All = cloneable ? (T)((ICloneable)value)?.Clone() : value; } /// Copy constructor [EditorBrowsable(EditorBrowsableState.Never)] public Selector(Selector value) : this() { Clone(value); } /// /// All State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T All { get; set; } /// /// Normal State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Normal { get => SelectorItems.Find(x => x.State == ControlState.Normal).Value; set => Add(ControlState.Normal, value); } /// /// Pressed State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Pressed { get => SelectorItems.Find(x => x.State == ControlState.Pressed).Value; set => Add(ControlState.Pressed, value); } /// /// Focused State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Focused { get => SelectorItems.Find(x => x.State == ControlState.Focused).Value; set => Add(ControlState.Focused, value); } /// /// Selected State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Selected { get => SelectorItems.Find(x => x.State == ControlState.Selected).Value; set => Add(ControlState.Selected, value); } /// /// Disabled State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Disabled { get => SelectorItems.Find(x => x.State == ControlState.Disabled).Value; set => Add(ControlState.Disabled, value); } /// /// DisabledFocused State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T DisabledFocused { get => SelectorItems.Find(x => x.State == ControlState.DisabledFocused).Value; set => Add(ControlState.DisabledFocused, value); } /// /// SelectedFocused State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. public T SelectedFocused { get => SelectorItems.Find(x => x.State == ControlState.SelectedFocused).Value; set => Add(ControlState.SelectedFocused, value); } /// /// DisabledSelected State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T DisabledSelected { get => SelectorItems.Find(x => x.State == ControlState.DisabledSelected).Value; set => Add(ControlState.DisabledSelected, value); } /// /// Other State. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public T Other { get => SelectorItems.Find(x => x.State == ControlState.Other).Value; set => Add(ControlState.Other, value); } /// /// Gets the number of elements. /// [EditorBrowsable(EditorBrowsableState.Never)] public int Count => SelectorItems.Count; /// /// Get value by State. /// /// Thrown when state is null. /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. /// True if the selector has a given state value, false otherwise. [EditorBrowsable(EditorBrowsableState.Never)] public bool GetValue(ControlState state, out T result) { if (All != null) { result = All; return true; } if (state == null) throw new ArgumentNullException(nameof(state)); result = default; int index = SelectorItems.FindIndex(x => x.State == state); if (index >= 0) { result = SelectorItems[index].Value; return true; } if (null == state) { throw new ArgumentNullException(nameof(state)); } if (state.IsCombined) { index = SelectorItems.FindIndex(x => state.Contains(x.State)); if (index >= 0) { result = SelectorItems[index].Value; return true; } } index = SelectorItems.FindIndex(x => x.State == ControlState.Other); if (index >= 0) { result = SelectorItems[index].Value; return true; } return false; } /// /// Removes all elements. /// [EditorBrowsable(EditorBrowsableState.Never)] public void Clear() { All = default; SelectorItems.Clear(); } /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() { string result = $"[All, {All}]"; foreach (var item in SelectorItems) { result += $", {item}"; } return result; } /// /// Clone itself. /// If type T implements ICloneable, it calls Clone() method to clone values, otherwise use operator=. /// /// 6 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public Selector Clone() { var cloned = new Selector(); cloned.Clone(this); return cloned; } /// /// Clone with type converter. /// /// Thrown when converter is null. [EditorBrowsable(EditorBrowsableState.Never)] public Selector Clone(Converter converter) { if (converter == null) throw new ArgumentNullException(nameof(converter)); Selector result = new Selector(); result.All = converter(All); result.SelectorItems = SelectorItems.ConvertAll>(m => new SelectorItem(m.State, converter(m.Value))); return result; } /// /// Copy values from other selector. /// /// Thrown when other is null. [EditorBrowsable(EditorBrowsableState.Never)] public void Clone(Selector other) { if (null == other) { throw new ArgumentNullException(nameof(other)); } if (cloneable) { All = (T)((ICloneable)other.All)?.Clone(); SelectorItems = other.SelectorItems.ConvertAll(m => new SelectorItem(m.State, (T)((ICloneable)m.Value)?.Clone())); } else { All = other.All; SelectorItems = other.SelectorItems.ConvertAll(m => m); } } internal bool HasMultiValue() { return SelectorItems.Count > 1; } } /// /// This will be attached to a View to detect ControlState change. /// [EditorBrowsable(EditorBrowsableState.Never)] public class TriggerableSelector { /// /// Create an TriggerableSelector. /// [EditorBrowsable(EditorBrowsableState.Never)] public delegate T ValueGetter(View view); private readonly BindableProperty targetBindableProperty; private readonly ValueGetter propertyGetter; private bool dirty = true; private Selector selector; /// /// Create an TriggerableSelector. /// /// The TriggerableSelector will change this bindable property value when the view's ControlState has changed. /// It is optional value in case the target bindable property getter is not proper to use. [EditorBrowsable(EditorBrowsableState.Never)] public TriggerableSelector(BindableProperty targetBindableProperty, ValueGetter propertyGetter = null) { this.targetBindableProperty = targetBindableProperty; this.propertyGetter = propertyGetter; } /// /// Return the containing selector. It can be null. /// /// Thrown when view is null. [EditorBrowsable(EditorBrowsableState.Never)] public Selector Get(View view) { if (!dirty) return selector; if (null == view) { throw new ArgumentNullException(nameof(view)); } T value = default; if (propertyGetter != null) { value = propertyGetter(view); } else { value = (T)view.GetValue(targetBindableProperty); } Selector converted = value == null ? null : new Selector(value); Update(view, converted); return selector; } /// /// Update containing selector from the other selector. /// /// The View that is affected by this TriggerableSelector. /// The copy target. /// Whether it updates the target view after update the selector or not. /// Thrown when view is null. [EditorBrowsable(EditorBrowsableState.Never)] public void Update(View view, Selector otherSelector, bool updateView = false) { Reset(view); if (null == view) { throw new ArgumentNullException(nameof(view)); } if (otherSelector == null) { return; } selector = otherSelector.Clone(); if (otherSelector.HasMultiValue()) { view.ControlStateChangeEventInternal += OnViewControlState; } if (updateView && otherSelector.GetValue(view.ControlState, out var value)) { view.SetValue(targetBindableProperty, value); } } /// /// Update containing selector value from a single value. /// Note that, it updates lazily if possible. /// If you need to udpate directly, please use . /// /// The View that is affected by this TriggerableSelector. /// The copy target. [EditorBrowsable(EditorBrowsableState.Never)] public void UpdateIfNeeds(View view, T value) { if (selector != null && selector.HasMultiValue()) { Selector converted = value == null ? null : new Selector(value); Update(view, converted); return; } dirty = true; } /// /// Reset selector and listeners. /// /// The View that is affected by this TriggerableSelector. [EditorBrowsable(EditorBrowsableState.Never)] public void Reset(View view) { if (view != null) { view.ControlStateChangeEventInternal -= OnViewControlState; } selector?.Clear(); selector = null; dirty = false; } private void OnViewControlState(object obj, View.ControlStateChangedEventArgs controlStateChangedInfo) { View view = obj as View; if (null != view && selector.GetValue(controlStateChangedInfo.CurrentState, out var value)) { view.SetValue(targetBindableProperty, value); } } } /// /// The selector item class that stores a state-value pair. /// [EditorBrowsable(EditorBrowsableState.Never)] public class SelectorItem { /// /// The default constructor. /// [EditorBrowsable(EditorBrowsableState.Never)] public SelectorItem() {} /// /// The constructor with the specified state and value. /// /// The state /// The value associated with state. [EditorBrowsable(EditorBrowsableState.Never)] public SelectorItem(ControlState state, T value) { State = state; Value = value; } /// /// The state /// [EditorBrowsable(EditorBrowsableState.Never)] public ControlState State { get; set; } /// /// The value associated with state. /// [EditorBrowsable(EditorBrowsableState.Never)] public T Value { get; set; } /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() => $"[{State}, {Value}]"; } /// /// Extension class for . /// [EditorBrowsable(EditorBrowsableState.Never)] public static class SelectorExtensions { /// /// Adds the specified state and value to the . /// /// The list for adding state-value pair. /// The state. /// The value associated with state. /// Thrown when given list is null. [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(this IList> list, ControlState state, T value) { if (list == null) throw new ArgumentNullException(nameof(list)); list.Add(new SelectorItem(state, value)); } } }