/* * Copyright(c) 2019 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; using Tizen.NUI.Components; namespace Tizen.NUI.BaseComponents { /// /// Selector class, which is related by Control State, it is base class for other Selector. /// /// 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(T).IsAssignableFrom(typeof(ICloneable)); /// /// The list for adding state-value pair. /// [EditorBrowsable(EditorBrowsableState.Never)] public IList> StateValueList { get; private 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) => StateValueList.Add(new StateValuePair(state, value)); /// /// Adds the specified state and value to the . /// /// [EditorBrowsable(EditorBrowsableState.Never)] public void Add(StateValuePair stateValuePair) => StateValueList.Add(stateValuePair); /// 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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(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 => ((List>)StateValueList).FindLast(x => x.State == ControlState.Other).Value; set => Add(ControlState.Other, value); } /// /// Get value by State. /// /// 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; } result = default; int index = ((List>)StateValueList).FindLastIndex(x => x.State == state); if (index >= 0) { result = StateValueList[index].Value; return true; } if (state.IsCombined) { index = ((List>)StateValueList).FindLastIndex(x => state.Contains(x.State)); if (index >= 0) { result = StateValueList[index].Value; return true; } } index = ((List>)StateValueList).FindLastIndex(x => x.State == ControlState.Other); if (index >= 0) { result = StateValueList[index].Value; return true; } return false; } /// /// Removes all elements from /// [EditorBrowsable(EditorBrowsableState.Never)] public void Clear() { All = default; StateValueList.Clear(); } /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() { string result = $"[All, {All}]"; foreach (var item in StateValueList) { 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; } /// /// Copy values from other selector. /// [EditorBrowsable(EditorBrowsableState.Never)] public void Clone(Selector other) { if (cloneable) { All = (T)((ICloneable)other.All)?.Clone(); StateValueList = ((List>)other.StateValueList).ConvertAll(m => new StateValuePair(m.State, (T)((ICloneable)m.Value)?.Clone())); } else { All = other.All; StateValueList = ((List>)other.StateValueList).ConvertAll(m => m); } } internal bool HasMultiValue() { return StateValueList.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. /// [EditorBrowsable(EditorBrowsableState.Never)] public Selector Get(View view) { if (!dirty) return selector; 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. [EditorBrowsable(EditorBrowsableState.Never)] public void Update(View view, Selector otherSelector, bool updateView = false) { Reset(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) { 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); } } } /// /// 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. [EditorBrowsable(EditorBrowsableState.Never)] public static void Add(this IList> list, ControlState state, T value) { list.Add(new StateValuePair(state, value)); } } }