[NUI] add ControlState class to support combined and custom state (#1762)
authorYeongJong Lee <cleanlyj@naver.com>
Fri, 3 Jul 2020 02:05:20 +0000 (11:05 +0900)
committerGitHub <noreply@github.com>
Fri, 3 Jul 2020 02:05:20 +0000 (11:05 +0900)
### Custom State

You can define your own control states. first, declare ControlState variable
and initialize using  `ControlState.Create`.
```
public class MyButton : Button {
   ...
   ControlState MyState = ControlState.Create("MyState");
   ...
}
```
And assign to `View.ControlState`.
```
void OnStateChanged(...) {
    ControlState = MyState;
}
```

If you want to assign the value of custom state to `Selector`, use
`Add(ControlState state, object value)`.
```
Selector<string> textSelector = new Selector<string>();
textSelector.Add(ControlState.Pressed, "Pressed!");
textSelector.Add(MyState, "MyText");
```
or
```
Selector<string> textSelector = new Selector<string>()
{
    { ControlState.Pressed, "Pressed!" },
    { MyState, "MyText" }
};
```

 ### Combined State
To implement your own combined control states, you can use the `+` or `+=`
operators.
For example, `MyCombinedStateX` are all the same.
```
ControlState MyCombinedStateA =
   ControlState.Create(ControlState.Pressed, ControlState.Focused);

ControlState MyCombinedStateB = ControlState.Pressed + ControlState.Focused;
```

Note that `Normal` and `All` state cannot be combined with other states.
`Normal` state is ignored. however, `All` state will ignore other states.
```
ControlState.Create(ControlState.Pressed, ControlState.Focused, ControlState.Normal) ==
   ControlState.Create(ControlState.Pressed, ControlState.Focused)

ControlState.Create(ControlState.All, ControlState.Pressed, ControlState.Focused) ==
   ControlState.Create(ControlState.All)
```

 ### ControlState in Xaml

It will support new initialization syntax.
Legacy:
```
<c:TextLabelStyle.Text>
    <c:Selector x:TypeArguments="x:String" Normal="button" Pressed="pressed!!"/>
</c:TextLabelStyle.Text>
```

New:
```
<c:TextLabelStyle.Text>
    <c:StateValuePair x:TypeArguments="x:String" State="Normal" Value="button"/>
    <c:StateValuePair x:TypeArguments="x:String" State="Pressed" Value="pressed!!" />
    <c:StateValuePair x:TypeArguments="x:String" State="MyState" Value="my state!" />
    <c:StateValuePair x:TypeArguments="x:String" State="MyState,Normal,Focused" Value="my combined state!" />
</c:TextLabelStyle.Text>
```

However, it won't work because there is no converter of  `ControlState`.
The next step would be to implement `ControlState` converter.

14 files changed:
src/Tizen.NUI.Components/Controls/Button.Internal.cs
src/Tizen.NUI.Components/Controls/Control.cs
src/Tizen.NUI.Components/Controls/DropDown.DropDownItemView.cs
src/Tizen.NUI.Components/Controls/Extension/LottieButtonExtension.cs
src/Tizen.NUI.Components/Controls/Slider.cs
src/Tizen.NUI.Components/Controls/Tab.cs
src/Tizen.NUI.Components/PreloadStyle/OverlayAnimationButtonStyle.cs
src/Tizen.NUI.Wearable/src/public/CircularProgress.cs
src/Tizen.NUI/src/public/BaseComponents/ControlState.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/BaseComponents/Style/Constants.cs
src/Tizen.NUI/src/public/BaseComponents/Style/Selector.cs
src/Tizen.NUI/src/public/BaseComponents/Style/StateValueCollection.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/BaseComponents/View.cs
src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs

index 64c1504..68b7160 100755 (executable)
@@ -104,47 +104,48 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected void UpdateState()
         {
-            ControlStates sourceState = ControlState;
-            ControlStates targetState;
+            ControlState sourceState = ControlState;
+            ControlState targetState;
 
             if (isEnabled)
             {
                 if (isPressed)
                 {
                     // Pressed
-                    targetState = ControlStates.Pressed;
+                    targetState = ControlState.Pressed;
                 }
                 else
                 {
                     // Normal
-                    targetState = ControlStates.Normal;
+                    targetState = ControlState.Normal;
 
                     // Selected
-                    targetState |= (IsSelected ? ControlStates.Selected : 0);
+                    if (IsSelected) targetState += ControlState.Selected;
 
                     // Focused, SelectedFocused
-                    targetState |= (IsFocused ? ControlStates.Focused : 0);
+                    if (IsFocused) targetState += ControlState.Focused;
                 }
             }
             else
             {
                 // Disabled
-                targetState = ControlStates.Disabled;
+                targetState = ControlState.Disabled;
 
-                // DisabledSelected, DisabledFocused
-                targetState |= (IsSelected ? ControlStates.Selected : (IsFocused ? ControlStates.Focused : 0));
+                // DisabledSelected
+                if (IsSelected) targetState += ControlState.Selected;
+                // DisabledFocused
+                else if (IsFocused) targetState += ControlState.Focused;
             }
 
             if (sourceState != targetState)
             {
                 ControlState = targetState;
-
                 OnUpdate();
 
                 StateChangedEventArgs e = new StateChangedEventArgs
                 {
-                    PreviousState = sourceState,
-                    CurrentState = targetState
+                    PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
+                    CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
                 };
                 stateChangeHander?.Invoke(this, e);
 
@@ -356,14 +357,14 @@ namespace Tizen.NUI.Components
         {
             base.OnControlStateChanged(controlStateChangedInfo);
 
-            var stateEnabled = !((controlStateChangedInfo.CurrentState & ControlStates.Disabled) == ControlStates.Disabled);
+            var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
 
             if (isEnabled != stateEnabled)
             {
                 isEnabled = stateEnabled;
             }
 
-            var statePressed = (controlStateChangedInfo.CurrentState & ControlStates.Pressed) == ControlStates.Pressed;
+            var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
 
             if (isPressed != statePressed)
             {
index 7fcde28..54e86c8 100755 (executable)
@@ -256,13 +256,13 @@ namespace Tizen.NUI.Components
             switch(state)
             {
                 case PointStateType.Down:
-                    ControlState = ControlStates.Pressed;
+                    ControlState = ControlState.Pressed;
                     break;
                 case PointStateType.Interrupted:
                 case PointStateType.Up:
-                    if (ControlState == ControlStates.Pressed)
+                    if (ControlState == ControlState.Pressed)
                     {
-                        ControlState = ControlStates.Normal;
+                        ControlState = ControlState.Normal;
                     }
                     break;
                 default:
@@ -304,7 +304,7 @@ namespace Tizen.NUI.Components
 
         private void Initialize()
         {
-            ControlState = ControlStates.Normal;
+            ControlState = ControlState.Normal;
 
             RegisterDetectionOfSubstyleChanges();
 
index b9b95ab..62268a9 100755 (executable)
@@ -199,12 +199,12 @@ namespace Tizen.NUI.Components
                     CreateCheckImage();
                     if (value)
                     {
-                        ControlState = ControlStates.Selected;
+                        ControlState = ControlState.Selected;
                         mCheck.Show();
                     }
                     else
                     {
-                        ControlState = ControlStates.Normal;
+                        ControlState = ControlState.Normal;
                         mCheck.Hide();
                     }
                 }
index e885c1d..26d3ee6 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -70,12 +70,12 @@ namespace Tizen.NUI.Components.Extension
             var lottieStyle = (ILottieButtonStyle)button.Style;
             lottieView.URL = lottieStyle.LottieUrl;
             lottieView.StopBehavior = LottieAnimationView.StopBehaviorType.MaximumFrame;
-            lottieStyle.PlayRange?.GetValue(ControlStates.Normal)?.Show(lottieView, true);
+            lottieStyle.PlayRange?.GetValue(ControlState.Normal)?.Show(lottieView, true);
         }
 
-        internal static void UpdateLottieView(Button button, ControlStates previousState, LottieAnimationView lottieView)
+        internal static void UpdateLottieView(Button button, ControlState previousState, LottieAnimationView lottieView)
         {
-            ((ILottieButtonStyle)button.Style).PlayRange?.GetValue(button.ControlState)?.Show(lottieView, ((int)previousState & (int)ControlStates.Pressed) == 0);
+            ((ILottieButtonStyle)button.Style).PlayRange?.GetValue(button.ControlState)?.Show(lottieView, !previousState.Contains(ControlState.Pressed));
         }
     }
 }
index 07e7cf8..30b6f18 100755 (executable)
@@ -876,7 +876,7 @@ namespace Tizen.NUI.Components
 
             if (!isFocused && !isPressed)
             {
-                ControlState = ControlStates.Normal;
+                ControlState = ControlState.Normal;
                 if (stateChangedHandler != null)
                 {
                     StateChangedArgs args = new StateChangedArgs();
@@ -886,7 +886,7 @@ namespace Tizen.NUI.Components
             }
             else if (isPressed)
             {
-                ControlState = ControlStates.Pressed;
+                ControlState = ControlState.Pressed;
 
                 if (stateChangedHandler != null)
                 {
@@ -897,7 +897,7 @@ namespace Tizen.NUI.Components
             }
             else if (!isPressed && isFocused)
             {
-                ControlState = ControlStates.Focused;
+                ControlState = ControlState.Focused;
 
                 if (stateChangedHandler != null)
                 {
index 276b14e..a48d3ed 100755 (executable)
@@ -758,7 +758,7 @@ namespace Tizen.NUI.Components
                 }
                 set
                 {
-                    ControlState = value ? ControlStates.Selected : ControlStates.Normal;
+                    ControlState = value ? ControlState.Selected : ControlState.Normal;
                     isSelected = value;
                 }
             }
index 0cf4924..9961a77 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -104,7 +104,7 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override void OnControlStateChanged(Button button, View.ControlStateChangedEventArgs args)
         {
-            if (button.ControlState != ControlStates.Pressed)
+            if (button.ControlState != ControlState.Pressed)
             {
                 return;
             }
@@ -162,4 +162,4 @@ namespace Tizen.NUI.Components
                 PressAnimation = null;
         }
     }
-}
\ No newline at end of file
+}
index 631b561..efc0da1 100755 (executable)
@@ -345,13 +345,13 @@ namespace Tizen.NUI.Wearable
                 isEnabled = value;
                 if (isEnabled)
                 {
-                    ControlState = ControlStates.Normal;
+                    ControlState = ControlState.Normal;
 
                     UpdateTrackVisualColor(new Color(0.0f, 0.16f, 0.30f, 1.0f)); // #002A4D
                 }
                 else
                 {
-                    ControlState = ControlStates.Disabled;
+                    ControlState = ControlState.Disabled;
 
                     UpdateTrackVisualColor(new Color(0.25f, 0.25f, 0.25f, 1.0f)); // #404040
                 }
@@ -421,7 +421,7 @@ namespace Tizen.NUI.Wearable
         {
             base.OnControlStateChanged(controlStateChangedInfo);
 
-            var stateEnabled = !((controlStateChangedInfo.CurrentState & ControlStates.Disabled) == ControlStates.Disabled);
+            var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
 
             if (isEnabled != stateEnabled)
             {
diff --git a/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs b/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs
new file mode 100644 (file)
index 0000000..06aa976
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright(c) 2020 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 System.Linq;
+
+namespace Tizen.NUI.BaseComponents
+{
+    /// <summary>
+    /// Class for describing the states of the view.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ControlState : IEquatable<ControlState>
+    {
+        private static readonly Dictionary<string, ControlState> stateDictionary = new Dictionary<string, ControlState>();
+        //Default States
+        /// <summary>
+        /// All State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState All = Create("All");
+        /// <summary>
+        /// Normal State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Normal = Create("Normal");
+        /// <summary>
+        /// Focused State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Focused = Create("Focused");
+        /// <summary>
+        /// Pressed State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Pressed = Create("Pressed");
+        /// <summary>
+        /// Disabled State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Disabled = Create("Disabled");
+        /// <summary>
+        /// Selected State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Selected = Create("Selected");
+        /// <summary>
+        /// DisabledSelected State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState DisabledSelected = Disabled + Selected;
+        /// <summary>
+        /// DisabledFocused State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState DisabledFocused = Disabled + Focused;
+        /// <summary>
+        /// SelectedFocused State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState SelectedFocused = Selected + Focused;
+        /// <summary>
+        /// Other State.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly ControlState Other = Create("Other");
+
+        private List<ControlState> stateList = new List<ControlState>();
+        private readonly string name = "";
+
+        /// <summary>
+        /// Gets or sets a value indicating whether it has combined states.
+        /// </summary>
+        [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(string name) : this() => this.name = name;
+
+        /// <summary>
+        /// Create an instance of the <see cref="ControlState"/> with state name.
+        /// </summary>
+        /// <param name="name">The state name.</param>
+        /// <returns>The <see cref="ControlState"/> instance which has single state.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static ControlState Create(string name)
+        {
+            if (name == null)
+                throw new ArgumentNullException(nameof(name));
+            if (string.IsNullOrWhiteSpace(name))
+                throw new ArgumentException("name cannot be empty string", nameof(name));
+
+            if (stateDictionary.TryGetValue(name, out ControlState state))
+                return state;
+
+            state = new ControlState(name);
+            state.stateList.Add(state);
+            stateDictionary.Add(name, state);
+            return state;
+        }
+
+        /// <summary>
+        /// Create an instance of the <see cref="ControlState"/> with combined states.
+        /// </summary>
+        /// <param name="states">The control state array.</param>
+        /// <returns>The <see cref="ControlState"/> instance which has combined state.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static ControlState Create(params ControlState[] states)
+        {
+            if (states.Length == 1)
+                return states[0];
+
+            ControlState newState = new ControlState();
+            for (int i = 0; i < states.Length; i++)
+            {
+                if (states[i] == Normal)
+                    continue;
+
+                if (states[i] == All)
+                    return All;
+
+                newState.stateList.AddRange(states[i].stateList);
+            }
+
+            if (newState.stateList.Count == 0)
+                return Normal;
+
+            newState.stateList = newState.stateList.Distinct().ToList();
+
+            return newState;
+        }
+
+        /// <summary>
+        /// Determines whether a state contains a specified state.
+        /// </summary>
+        /// <param name="state">The state to search for</param>
+        /// <returns>true if the state contain a specified state, otherwise, false.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Contains(ControlState state)
+        {
+            if (state == null)
+                throw new ArgumentNullException(nameof(state));
+
+            if (!IsCombined)
+                return ReferenceEquals(this, state);
+
+            bool found;
+            for (int i = 0; i < state.stateList.Count; i++)
+            {
+                found = false;
+                for (int j = 0; j < stateList.Count; j++)
+                {
+                    if (ReferenceEquals(state.stateList[i], stateList[j]))
+                    {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) return false;
+            }
+
+            return true;
+        }
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Equals(ControlState other)
+        {
+            if (other is null || stateList.Count != other.stateList.Count)
+                return false;
+
+            return Contains(other);
+        }
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override bool Equals(object obj) => this.Equals(obj as ControlState);
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override int GetHashCode() => (name.GetHashCode() * 397) ^ IsCombined.GetHashCode();
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override string ToString()
+        {
+            string name = "";
+            for (int i = 0; i < stateList.Count; i++)
+            {
+                name += ((i == 0) ? "" : ", ") + stateList[i].name;
+            }
+            return name;
+        }
+
+        /// <summary>
+        /// Compares whether the two ControlStates are same or not.
+        /// </summary>
+        /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
+        /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
+        /// <returns>true if the ControlStates are equal; otherwise, false.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool operator ==(ControlState lhs, ControlState rhs) => lhs.Equals(rhs);
+
+        /// <summary>
+        /// Compares whether the two ControlStates are different or not.
+        /// </summary>
+        /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
+        /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
+        /// <returns>true if the ControlStates are not equal; otherwise, false.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool operator !=(ControlState lhs, ControlState rhs) => !lhs.Equals(rhs);
+
+        /// <summary>
+        /// The addition operator.
+        /// </summary>
+        /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
+        /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
+        /// <returns>The <see cref="ControlState"/> containing the result of the addition.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static ControlState operator +(ControlState lhs, ControlState rhs) => Create(lhs, rhs);
+    }
+
+    /// <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)
+        {
+            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);
+
+        ///  <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override bool Equals(object obj)
+        {
+            if (!(obj is StateValuePair<T>))
+                return false;
+
+            return Equals((StateValuePair<T>)obj);
+        }
+
+        ///  <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 6d04a79..068967a 100755 (executable)
@@ -16,6 +16,7 @@
  */
 
 using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
 
 namespace Tizen.NUI.Components
 {
@@ -84,4 +85,32 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         DisabledSelected = Disabled | Selected,
     }
+
+    //FIXME: Please remove this Extension class when ControlStates is removed.
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    internal static class ControlStatesExtension
+    {
+        public static ControlStates FromControlStateClass(ControlState controlState)
+        {
+            if (controlState == ControlState.Normal)
+                return ControlStates.Normal;
+            if (controlState == ControlState.Focused)
+                return ControlStates.Focused;
+            if (controlState == ControlState.Disabled)
+                return ControlStates.Disabled;
+            if (controlState == ControlState.Selected)
+                return ControlStates.Selected;
+            if (controlState == ControlState.Pressed)
+                return ControlStates.Pressed;
+            if (controlState == ControlState.DisabledFocused)
+                return ControlStates.DisabledFocused;
+            if (controlState == ControlState.SelectedFocused)
+                return ControlStates.SelectedFocused;
+            if (controlState == ControlState.DisabledSelected)
+                return ControlStates.DisabledSelected;
+
+            return ControlStates.Normal;
+        }
+    }
 }
+
index a804baf..7a486b5 100755 (executable)
@@ -27,7 +27,7 @@ namespace Tizen.NUI.BaseComponents
     /// <since_tizen> 6 </since_tizen>
     /// 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<T> : BindableObject
+    public class Selector<T> : StateValueCollection<T>
     {
         /// <since_tizen> 6 </since_tizen>
         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
@@ -70,8 +70,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Normal
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Normal).Value;
+            set => Add(ControlState.Normal, value);
         }
         /// <summary>
         /// Pressed State.
@@ -81,8 +81,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Pressed
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Pressed).Value;
+            set => Add(ControlState.Pressed, value);
         }
         /// <summary>
         /// Focused State.
@@ -92,8 +92,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Focused
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Focused).Value;
+            set => Add(ControlState.Focused, value);
         }
         /// <summary>
         /// Selected State.
@@ -103,8 +103,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Selected
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Selected).Value;
+            set => Add(ControlState.Selected, value);
         }
         /// <summary>
         /// Disabled State.
@@ -114,8 +114,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Disabled
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Disabled).Value;
+            set => Add(ControlState.Disabled, value);
         }
         /// <summary>
         /// DisabledFocused State.
@@ -125,8 +125,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T DisabledFocused
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.DisabledFocused).Value;
+            set => Add(ControlState.DisabledFocused, value);
         }
         /// <summary>
         /// SelectedFocused State.
@@ -135,8 +135,8 @@ 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;
-            set;
+            get => Find(x => x.State == ControlState.SelectedFocused).Value;
+            set => Add(ControlState.SelectedFocused, value);
         }
         /// <summary>
         /// DisabledSelected State.
@@ -146,8 +146,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T DisabledSelected
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.DisabledSelected).Value;
+            set => Add(ControlState.DisabledSelected, value);
         }
 
         /// <summary>
@@ -158,8 +158,8 @@ namespace Tizen.NUI.BaseComponents
         [EditorBrowsable(EditorBrowsableState.Never)]
         public T Other
         {
-            get;
-            set;
+            get => Find(x => x.State == ControlState.Other).Value;
+            set => Add(ControlState.Other, value);
         }
         /// <summary>
         /// Get value by State.
@@ -167,7 +167,7 @@ namespace Tizen.NUI.BaseComponents
         /// <since_tizen> 6 </since_tizen>
         /// 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 GetValue(ControlStates state)
+        public T GetValue(ControlState state)
         {
             if(All != null)
             {
@@ -175,25 +175,32 @@ namespace Tizen.NUI.BaseComponents
             }
             switch(state)
             {
-                case ControlStates.Normal:
+                case null:
+                case var s when s == ControlState.Normal:
                     return Normal != null? Normal : Other;
-                case ControlStates.Focused:
+                case var s when s == ControlState.Focused:
                     return Focused != null? Focused : Other;
-                case ControlStates.Pressed:
+                case var s when s == ControlState.Pressed:
                     return Pressed != null? Pressed : Other;
-                case ControlStates.Disabled:
+                case var s when s == ControlState.Disabled:
                     return Disabled != null? Disabled : Other;
-                case ControlStates.Selected:
-                    return Selected != null? Selected : Other;
-                case ControlStates.DisabledFocused:
+                case var s when s == ControlState.Selected:
+                   return Selected != null? Selected : Other;
+                case var s when s == ControlState.DisabledFocused:
                     return DisabledFocused != null? DisabledFocused : (Disabled != null ? Disabled : Other);
-                case ControlStates.DisabledSelected:
+                case var s when s == ControlState.DisabledSelected:
                     return DisabledSelected != null ? DisabledSelected : (Disabled != null ? Disabled : Other);
-                case ControlStates.SelectedFocused:
+                case var s when s == ControlState.SelectedFocused:
                     return SelectedFocused != null ? SelectedFocused : (Selected != null ? Selected : Other);
                 default:
                 {
-                    // TODO Handle combined states
+                    StateValuePair<T> value = Find(x => x.State == state);
+                    if (value.Value != null)
+                        return value.Value;
+
+                    value = Find(x => state.Contains(x.State));
+                    if (value.Value != null)
+                        return value.Value;
                     return Other;
                 }
             }
diff --git a/src/Tizen.NUI/src/public/BaseComponents/Style/StateValueCollection.cs b/src/Tizen.NUI/src/public/BaseComponents/Style/StateValueCollection.cs
new file mode 100644 (file)
index 0000000..b2e005e
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright(c) 2020 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;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Tizen.NUI.BaseComponents
+{
+    /// <summary>
+    /// The StateValueCollection class, which is related by <see cref="ControlState"/>, it is abstract class for <see cref="Selector{T}"/>.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public abstract class StateValueCollection<T> : ICollection<StateValuePair<T>>
+    {
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal List<StateValuePair<T>> StateValueList { get; } = new List<StateValuePair<T>>();
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int Count => StateValueList.Count;
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool IsReadOnly => ((ICollection<StateValuePair<T>>)StateValueList).IsReadOnly;
+
+        /// <summary>
+        /// Add a <see cref="StateValuePair{T}"/> with state and value.
+        /// </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));
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Add(StateValuePair<T> item)
+        {
+            // To prevent a state from having multiple values, remove existing state-value pair.
+            int index = StateValueList.FindIndex(x => x.State == item.State);
+            if (index != -1)
+                StateValueList.RemoveAt(index);
+
+            StateValueList.Add(item);
+        }
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Clear() => StateValueList.Clear();
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Contains(StateValuePair<T> item) => StateValueList.Contains(item);
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void CopyTo(StateValuePair<T>[] array, int arrayIndex) => StateValueList.CopyTo(array, arrayIndex);
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Remove(StateValuePair<T> item) => StateValueList.Remove(item);
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public IEnumerator<StateValuePair<T>> GetEnumerator() => StateValueList.GetEnumerator();
+
+        IEnumerator IEnumerable.GetEnumerator() => StateValueList.GetEnumerator();
+
+        /// <summary>
+        /// Searches for a StateValuePair that matches the conditions defined by the specified
+        /// predicate, and returns the first occurrence within the entire <see cref="StateValueList"/>
+        /// </summary>
+        /// <param name="match">The <see cref="Predicate{T}"/> delegate that defines the conditions of the element to search for.</param>
+        /// <returns>The first element that matches the conditions defined by the specified predicate,
+        /// if found; otherwise, the default value for type <see cref="StateValuePair{T}"/>.</returns>
+        public StateValuePair<T> Find(Predicate<StateValuePair<T>> match) => StateValueList.Find(match);
+    }
+}
\ No newline at end of file
index 401b48d..621ae79 100755 (executable)
@@ -171,14 +171,13 @@ namespace Tizen.NUI.BaseComponents
 
         internal event EventHandler<ControlStateChangedEventArgs> ControlStateChangeEventInternal;
 
-        private ControlStates controlStates;
+        private ControlState controlStates = ControlState.Normal;
         /// <summary>
         /// Get/Set the control state.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
         /// 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 ControlStates ControlState
+        public ControlState ControlState
         {
             get
             {
index fb27aaa..e587b2b 100755 (executable)
@@ -1068,7 +1068,7 @@ namespace Tizen.NUI.BaseComponents
             /// <param name="previousState">The previous control state.</param>
             /// <param name="currentState">The current control state.</param>
             [EditorBrowsable(EditorBrowsableState.Never)]
-            public ControlStateChangedEventArgs(ControlStates previousState, ControlStates currentState)
+            public ControlStateChangedEventArgs(ControlState previousState, ControlState currentState)
             {
                 PreviousState = previousState;
                 CurrentState = currentState;
@@ -1078,13 +1078,13 @@ namespace Tizen.NUI.BaseComponents
             /// The previous control state.
             /// </summary>
             [EditorBrowsable(EditorBrowsableState.Never)]
-            public ControlStates PreviousState { get; }
+            public ControlState PreviousState { get; }
 
             /// <summary>
             /// The current control state.
             /// </summary>
             [EditorBrowsable(EditorBrowsableState.Never)]
-            public ControlStates CurrentState { get; }
+            public ControlState CurrentState { get; }
         }
 
         private EventHandlerWithReturnType<object, WheelEventArgs, bool> WindowWheelEventHandler;