[NUI] Add ControlStateTypeConverter for xaml (#2185)
authorYeongJong Lee <cleanlyj@naver.com>
Fri, 27 Nov 2020 05:09:53 +0000 (14:09 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Tue, 1 Dec 2020 08:56:39 +0000 (17:56 +0900)
Custom ConstrolState is now available in xaml.

 ### Sample
XamlPage.xaml
```xaml
<View x:Class="NUIXamlTemplate1.XamlPage"
      xmlns="http://tizen.org/Tizen.NUI/2018/XAML"
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:c="clr-namespace:Tizen.NUI.Components;assembly=Tizen.NUI.Components"
      xmlns:local="clr-namespace:NUIXamlTemplate1">
    <local:MyButton x:Name="MyButton">
        <x:Arguments>
            <c:ButtonStyle Size="100, 100">
                <c:ButtonStyle.BackgroundColor>
                    <Selector x:TypeArguments="Color" Normal="1, 1, 0, 1" Pressed="0.77, 0.88, 1, 1" Disabled="0.88, 0.88, 0.88, 1">
                        <SelectorItem x:TypeArguments="Color" State="MyState" Value="0,0,1,1" />
                        <SelectorItem x:TypeArguments="Color" State="MyState,Focused" Value="0,1,1,1" />
                    </Selector>
                </c:ButtonStyle.BackgroundColor>
            </c:ButtonStyle>
        </x:Arguments>
    </local:MyButton>
</View>
```

MyButton.cs
```cs
public class MyButton : Button
{
    public MyButton(ButtonStyle buttonStyle) : base(buttonStyle) {}
    public void SetControlState(ControlState s) => ControlState = s;
}
```
Main.cs
```cs
protected override void OnCreate()
{
    base.OnCreate();
    Window.Instance.BackgroundColor = new Color(227 / 255f, 255 / 255f, 227 / 255f, 1.0f);
    Window.Instance.KeyEvent += OnKeyEvent;

    View root = new View();
    root.WidthSpecification = LayoutParamPolicies.MatchParent;
    root.HeightSpecification = LayoutParamPolicies.MatchParent;
    root.Layout = new AbsoluteLayout();
    Window.Instance.GetDefaultLayer().Add(root);

    XamlPage xamlPage = new XamlPage();
    root.Add(xamlPage);

    ControlState MyState = ControlState.Create("MyState");

    Button button = new Button() { Position2D = new Position2D(100, 100) };
    bool flag = false;
    button.Clicked += (object sender, ClickedEventArgs e) =>
    {
        if (!flag)
        {
            xamlPage.MyButton.SetControlState(MyState);
            flag = true;
        }
        else
        {
            xamlPage.MyButton.SetControlState(ControlState.Focused + MyState);
            flag = false;
        }
    };
    root.Add(button);
}
```

src/Tizen.NUI/src/public/BaseComponents/ControlState.cs
src/Tizen.NUI/src/public/BaseComponents/Style/Selector.cs

index 14a7de2..205b88d 100755 (executable)
@@ -26,6 +26,7 @@ namespace Tizen.NUI.BaseComponents
     /// Class for describing the states of the view.
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
+    [Binding.TypeConverter(typeof(ControlStateTypeConverter))]
     public class ControlState : IEquatable<ControlState>
     {
         private static readonly Dictionary<string, ControlState> stateDictionary = new Dictionary<string, ControlState>();
@@ -90,12 +91,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public bool IsCombined => stateList.Count > 1;
 
-        /// <summary>
-        /// Default Contructor. Please use <see cref="Create(string)"/> or <see cref="Create(ControlState[])"/> instead.
-        /// </summary>
-        // Do not open this constructor. This is only for xaml support.
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public ControlState() { }
+        private ControlState() { }
 
         private ControlState(string name) : this() => this.name = name;
 
@@ -112,6 +108,8 @@ namespace Tizen.NUI.BaseComponents
             if (string.IsNullOrWhiteSpace(name))
                 throw new ArgumentException("name cannot be empty string", nameof(name));
 
+            name = name.Trim();
+
             if (stateDictionary.TryGetValue(name, out ControlState state))
                 return state;
 
@@ -302,76 +300,26 @@ namespace Tizen.NUI.BaseComponents
             newState.stateList.AddRange(rest);
             return newState;
         }
-    }
 
-    /// <summary>
-    /// The Key/Value pair structure. this is mutable to support for xaml.
-    /// </summary>
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public struct StateValuePair<T> : IEquatable<StateValuePair<T>>
-    {
-        /// <summary>
-        /// The constructor with the specified state and value.
-        /// </summary>
-        /// <param name="state">The state</param>
-        /// <param name="value">The value associated with state.</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public StateValuePair(ControlState state, T value)
+        class ControlStateTypeConverter : Binding.TypeConverter
         {
-            State = state;
-            Value = value;
-        }
-
-        /// <summary>
-        /// The state
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public ControlState State { get; set; }
-        /// <summary>
-        /// The value associated with state.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public T Value { get; set; }
-
-        ///  <inheritdoc/>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public bool Equals(StateValuePair<T> other) => (Value.Equals(other.Value)) && (State == other.State);
+            public override object ConvertFromInvariantString(string value)
+            {
+                if (value != null)
+                {
+                    value = value.Trim();
 
-        ///  <inheritdoc/>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override bool Equals(object obj)
-        {
-            if (!(obj is StateValuePair<T>))
-                return false;
+                    ControlState convertedState = new ControlState();
+                    string[] parts = value.Split(',');
+                    foreach (string part in parts)
+                    {
+                        convertedState += Create(part);
+                    }
+                    return convertedState;
+                }
 
-            return Equals((StateValuePair<T>)obj);
+                throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(ControlState)}");
+            }
         }
-
-        ///  <inheritdoc/>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override int GetHashCode() => (State.GetHashCode() * 397) ^ Value.GetHashCode();
-
-
-        /// <summary>
-        /// Compares whether the two StateValuePair are different or not.
-        /// </summary>
-        /// <param name="lhs">A <see cref="StateValuePair{T}"/> on the left hand side.</param>
-        /// <param name="rhs">A <see cref="StateValuePair{T}"/> on the right hand side.</param>
-        /// <returns>true if the StateValuePair are equal; otherwise, false.</returns>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public static bool operator ==(StateValuePair<T> lhs, StateValuePair<T> rhs) => lhs.Equals(rhs);
-
-        /// <summary>
-        /// Compares whether the two StateValuePair are same or not.
-        /// </summary>
-        /// <param name="lhs">A <see cref="StateValuePair{T}"/> on the left hand side.</param>
-        /// <param name="rhs">A <see cref="StateValuePair{T}"/> on the right hand side.</param>
-        /// <returns>true if the StateValuePair are not equal; otherwise, false.</returns>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public static bool operator !=(StateValuePair<T> lhs, StateValuePair<T> rhs) => !(lhs == rhs);
-
-        ///  <inheritdoc/>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override string ToString() => $"[{State}, {Value}]";
     }
 }
\ No newline at end of file
index 07baed1..b6f04dd 100755 (executable)
@@ -34,32 +34,32 @@ namespace Tizen.NUI.BaseComponents
         private readonly bool cloneable = typeof(ICloneable).IsAssignableFrom(typeof(T));
 
         /// <summary>
-        /// The list for adding state-value pair.
+        /// The list for adding <see cref="SelectorItem{T}"/>.
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public IList<StateValuePair<T>> StateValueList { get; set; } = new List<StateValuePair<T>>();
+        public IList<SelectorItem<T>> SelectorItems { get; set; } = new List<SelectorItem<T>>();
 
         /// <summary>
-        /// Adds the specified state and value.
+        /// Adds the specified state and value to the <see cref="SelectorItems"/>.
         /// </summary>
         /// <param name="state">The state.</param>
         /// <param name="value">The value associated with state.</param>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Add(ControlState state, T value) => Add(new StateValuePair<T>(state, value));
+        public void Add(ControlState state, T value) => SelectorItems.Add(new SelectorItem<T>(state, value));
 
         /// <summary>
-        /// Adds the specified state and value.
+        /// Adds the specified state and value to the <see cref="SelectorItems"/>.
         /// </summary>
-        /// <param name="stateValuePair"></param>
+        /// <param name="selectorItem">The selector item class that stores a state-value pair.</param>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Add(StateValuePair<T> stateValuePair)
+        public void Add(SelectorItem<T> selectorItem)
         {
             // To prevent a state from having multiple values, remove existing state-value pair.
-            int index = ((List<StateValuePair<T>>)StateValueList).FindIndex(x => x.State == stateValuePair.State);
+            int index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == selectorItem.State);
             if (index != -1)
-                StateValueList.RemoveAt(index);
+                SelectorItems.RemoveAt(index);
 
-            StateValueList.Add(stateValuePair);
+            SelectorItems.Add(selectorItem);
         }
 
         /// <since_tizen> 6 </since_tizen>
@@ -108,7 +108,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Normal
         {
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Normal).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Normal).Value;
             set => Add(ControlState.Normal, value);
         }
         /// <summary>
@@ -119,8 +119,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Pressed
         {
-
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Pressed).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Pressed).Value;
             set => Add(ControlState.Pressed, value);
         }
         /// <summary>
@@ -131,7 +130,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Focused
         {
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Focused).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Focused).Value;
             set => Add(ControlState.Focused, value);
         }
         /// <summary>
@@ -142,7 +141,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Selected
         {
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Selected).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Selected).Value;
             set => Add(ControlState.Selected, value);
         }
         /// <summary>
@@ -153,8 +152,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Disabled
         {
-
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Disabled).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Disabled).Value;
             set => Add(ControlState.Disabled, value);
         }
         /// <summary>
@@ -165,7 +163,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T DisabledFocused
         {
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.DisabledFocused).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.DisabledFocused).Value;
             set => Add(ControlState.DisabledFocused, value);
         }
         /// <summary>
@@ -175,7 +173,7 @@ namespace Tizen.NUI.BaseComponents
         /// 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<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.SelectedFocused).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.SelectedFocused).Value;
             set => Add(ControlState.SelectedFocused, value);
         }
         /// <summary>
@@ -186,8 +184,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T DisabledSelected
         {
-
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.DisabledSelected).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.DisabledSelected).Value;
             set => Add(ControlState.DisabledSelected, value);
         }
 
@@ -199,7 +196,7 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Other
         {
-            get => ((List<StateValuePair<T>>)StateValueList).Find(x => x.State == ControlState.Other).Value;
+            get => ((List<SelectorItem<T>>)SelectorItems).Find(x => x.State == ControlState.Other).Value;
             set => Add(ControlState.Other, value);
         }
 
@@ -207,7 +204,7 @@ namespace Tizen.NUI.BaseComponents
         /// Gets the number of elements.
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public int Count => StateValueList.Count;
+        public int Count => SelectorItems.Count;
 
         /// <summary>
         /// Get value by State.
@@ -226,12 +223,15 @@ namespace Tizen.NUI.BaseComponents
                 return true;
             }
 
+            if (state == null)
+                throw new ArgumentNullException(nameof(state));
+
             result = default;
 
-            int index = ((List<StateValuePair<T>>)StateValueList).FindIndex(x => x.State == state);
+            int index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == state);
             if (index >= 0)
             {
-                result = StateValueList[index].Value;
+                result = SelectorItems[index].Value;
                 return true;
             }
 
@@ -241,18 +241,18 @@ namespace Tizen.NUI.BaseComponents
             }
             if (state.IsCombined)
             {
-                index = ((List<StateValuePair<T>>)StateValueList).FindIndex(x => state.Contains(x.State));
+                index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => state.Contains(x.State));
                 if (index >= 0)
                 {
-                    result = StateValueList[index].Value;
+                    result = SelectorItems[index].Value;
                     return true;
                 }
             }
 
-            index = ((List<StateValuePair<T>>)StateValueList).FindIndex(x => x.State == ControlState.Other);
+            index = ((List<SelectorItem<T>>)SelectorItems).FindIndex(x => x.State == ControlState.Other);
             if (index >= 0)
             {
-                result = StateValueList[index].Value;
+                result = SelectorItems[index].Value;
                 return true;
             }
 
@@ -266,7 +266,7 @@ namespace Tizen.NUI.BaseComponents
         public void Clear()
         {
             All = default;
-            StateValueList.Clear();
+            SelectorItems.Clear();
         }
 
         /// <inheritdoc/>
@@ -275,7 +275,7 @@ namespace Tizen.NUI.BaseComponents
         {
             string result = $"[All, {All}]";
 
-            foreach (var item in StateValueList)
+            foreach (var item in SelectorItems)
             {
                 result += $", {item}";
             }
@@ -312,20 +312,19 @@ namespace Tizen.NUI.BaseComponents
             if (cloneable)
             {
                 All = (T)((ICloneable)other.All)?.Clone();
-                StateValueList = ((List<StateValuePair<T>>)other.StateValueList).ConvertAll(m => new StateValuePair<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
+                SelectorItems = ((List<SelectorItem<T>>)other.SelectorItems).ConvertAll(m => new SelectorItem<T>(m.State, (T)((ICloneable)m.Value)?.Clone()));
             }
             else
             {
                 All = other.All;
-                StateValueList = ((List<StateValuePair<T>>)other.StateValueList).ConvertAll(m => m);
+                SelectorItems = ((List<SelectorItem<T>>)other.SelectorItems).ConvertAll(m => m);
             }
         }
 
         internal bool HasMultiValue()
         {
-            return StateValueList.Count > 1;
+            return SelectorItems.Count > 1;
         }
-
     }
 
     /// <summary>
@@ -467,4 +466,64 @@ namespace Tizen.NUI.BaseComponents
             }
         }
     }
+
+    /// <summary>
+    /// The selector item class that stores a state-value pair.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class SelectorItem<T>
+    {
+        /// <summary>
+        /// The default constructor.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public SelectorItem() {}
+
+        /// <summary>
+        /// The constructor with the specified state and value.
+        /// </summary>
+        /// <param name="state">The state</param>
+        /// <param name="value">The value associated with state.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public SelectorItem(ControlState state, T value)
+        {
+            State = state;
+            Value = value;
+        }
+
+        /// <summary>
+        /// The state
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ControlState State { get; set; }
+
+        /// <summary>
+        /// The value associated with state.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public T Value { get; set; }
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override string ToString() => $"[{State}, {Value}]";
+    }
+
+    /// <summary>
+    /// Extension class for <see cref="Selector{T}"/>.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public static class SelectorExtensions
+    {
+        /// <summary>
+        /// Adds the specified state and value to the <see cref="Selector{T}.SelectorItems"/>.
+        /// </summary>
+        /// <param name="list">The list for adding state-value pair.</param>
+        /// <param name="state">The state.</param>
+        /// <param name="value">The value associated with state.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void Add<T>(this IList<SelectorItem<T>> list, ControlState state, T value)
+        {
+            list.Add(new SelectorItem<T>(state, value));
+        }
+    }
 }