/*
* 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));
}
}
}