[NUI] Update 6.5 UX for CheckBox, Switch and RadioButton (#2773)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / ControlState.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Linq;
22
23 namespace Tizen.NUI.BaseComponents
24 {
25     /// <summary>
26     /// Class for describing the states of the view.
27     /// </summary>
28     [EditorBrowsable(EditorBrowsableState.Never)]
29     [Binding.TypeConverter(typeof(ControlStateTypeConverter))]
30     public class ControlState : IEquatable<ControlState>
31     {
32         private static readonly Dictionary<string, ControlState> stateDictionary = new Dictionary<string, ControlState>();
33         //Default States
34         /// <summary>
35         /// All State.
36         /// </summary>
37         [EditorBrowsable(EditorBrowsableState.Never)]
38         public static readonly ControlState All = Create("All");
39         /// <summary>
40         /// Normal State.
41         /// </summary>
42         [EditorBrowsable(EditorBrowsableState.Never)]
43         public static readonly ControlState Normal = Create("Normal");
44         /// <summary>
45         /// Focused State.
46         /// </summary>
47         [EditorBrowsable(EditorBrowsableState.Never)]
48         public static readonly ControlState Focused = Create("Focused");
49         /// <summary>
50         /// Pressed State.
51         /// </summary>
52         [EditorBrowsable(EditorBrowsableState.Never)]
53         public static readonly ControlState Pressed = Create("Pressed");
54         /// <summary>
55         /// Disabled State.
56         /// </summary>
57         [EditorBrowsable(EditorBrowsableState.Never)]
58         public static readonly ControlState Disabled = Create("Disabled");
59         /// <summary>
60         /// Selected State.
61         /// </summary>
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public static readonly ControlState Selected = Create("Selected");
64         /// <summary>
65         /// SelectedPressed State.
66         /// </summary>
67         [EditorBrowsable(EditorBrowsableState.Never)]
68         public static readonly ControlState SelectedPressed = Selected + Pressed;
69         /// <summary>
70         /// DisabledSelected State.
71         /// </summary>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public static readonly ControlState DisabledSelected = Disabled + Selected;
74         /// <summary>
75         /// DisabledFocused State.
76         /// </summary>
77         [EditorBrowsable(EditorBrowsableState.Never)]
78         public static readonly ControlState DisabledFocused = Disabled + Focused;
79         /// <summary>
80         /// SelectedFocused State.
81         /// </summary>
82         [EditorBrowsable(EditorBrowsableState.Never)]
83         public static readonly ControlState SelectedFocused = Selected + Focused;
84         /// <summary>
85         /// Other State.
86         /// </summary>
87         [EditorBrowsable(EditorBrowsableState.Never)]
88         public static readonly ControlState Other = Create("Other");
89
90         private List<ControlState> stateList = new List<ControlState>();
91         private readonly string name = "";
92
93         /// <summary>
94         /// Gets or sets a value indicating whether it has combined states.
95         /// </summary>
96         [EditorBrowsable(EditorBrowsableState.Never)]
97         public bool IsCombined => stateList.Count > 1;
98
99         private ControlState() { }
100
101         private ControlState(string name) : this() => this.name = name;
102
103         /// <summary>
104         /// Create an instance of the <see cref="ControlState"/> with state name.
105         /// </summary>
106         /// <param name="name">The state name.</param>
107         /// <returns>The <see cref="ControlState"/> instance which has single state.</returns>
108         [EditorBrowsable(EditorBrowsableState.Never)]
109         public static ControlState Create(string name)
110         {
111             if (name == null)
112                 throw new ArgumentNullException(nameof(name));
113             if (string.IsNullOrWhiteSpace(name))
114                 throw new ArgumentException("name cannot be empty string", nameof(name));
115
116             name = name.Trim();
117
118             if (stateDictionary.TryGetValue(name, out ControlState state))
119                 return state;
120
121             state = new ControlState(name);
122             state.stateList.Add(state);
123             stateDictionary.Add(name, state);
124             return state;
125         }
126
127         /// <summary>
128         /// Create an instance of the <see cref="ControlState"/> with combined states.
129         /// </summary>
130         /// <param name="states">The control state array.</param>
131         /// <returns>The <see cref="ControlState"/> instance which has combined state.</returns>
132         [EditorBrowsable(EditorBrowsableState.Never)]
133         public static ControlState Create(params ControlState[] states)
134         {
135             if (states.Length == 1)
136                 return states[0];
137
138             ControlState newState = new ControlState();
139             for (int i = 0; i < states.Length; i++)
140             {
141                 if (states[i] == Normal)
142                     continue;
143
144                 if (states[i] == All)
145                     return All;
146
147                 newState.stateList.AddRange(states[i].stateList);
148             }
149
150             if (newState.stateList.Count == 0)
151                 return Normal;
152
153             newState.stateList = newState.stateList.Distinct().ToList();
154
155             if (newState.stateList.Count == 1)
156             {
157                 return newState.stateList[0];
158             }
159
160             return newState;
161         }
162
163         /// <summary>
164         /// Determines whether a state contains a specified state.
165         /// </summary>
166         /// <param name="state">The state to search for</param>
167         /// <returns>true if the state contain a specified state, otherwise, false.</returns>
168         [EditorBrowsable(EditorBrowsableState.Never)]
169         public bool Contains(ControlState state)
170         {
171             if (state == null)
172                 throw new ArgumentNullException(nameof(state));
173
174             if (!IsCombined)
175                 return ReferenceEquals(this, state);
176
177             bool found;
178             for (int i = 0; i < state.stateList.Count; i++)
179             {
180                 found = false;
181                 for (int j = 0; j < stateList.Count; j++)
182                 {
183                     if (ReferenceEquals(state.stateList[i], stateList[j]))
184                     {
185                         found = true;
186                         break;
187                     }
188                 }
189                 if (!found) return false;
190             }
191
192             return true;
193         }
194
195         ///  <inheritdoc/>
196         [EditorBrowsable(EditorBrowsableState.Never)]
197         public bool Equals(ControlState other)
198         {
199             if (other is null || stateList.Count != other.stateList.Count)
200                 return false;
201
202             return Contains(other);
203         }
204
205         ///  <inheritdoc/>
206         [EditorBrowsable(EditorBrowsableState.Never)]
207         public override bool Equals(object obj) => this.Equals(obj as ControlState);
208
209         ///  <inheritdoc/>
210         [EditorBrowsable(EditorBrowsableState.Never)]
211         public override int GetHashCode() => (name.GetHashCode() * 397) ^ IsCombined.GetHashCode();
212
213         ///  <inheritdoc/>
214         [EditorBrowsable(EditorBrowsableState.Never)]
215         public override string ToString()
216         {
217             string name = "";
218             for (int i = 0; i < stateList.Count; i++)
219             {
220                 name += ((i == 0) ? "" : ", ") + stateList[i].name;
221             }
222             return name;
223         }
224
225         /// <summary>
226         /// Compares whether the two ControlStates are same or not.
227         /// </summary>
228         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
229         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
230         /// <returns>true if the ControlStates are equal; otherwise, false.</returns>
231         [EditorBrowsable(EditorBrowsableState.Never)]
232         public static bool operator ==(ControlState lhs, ControlState rhs)
233         {
234             // Check for null on left side.
235             if (lhs is null)
236             {
237                 if (rhs is null)
238                 {
239                     // null == null = true.
240                     return true;
241                 }
242
243                 // Only the left side is null.
244                 return false;
245             }
246             // Equals handles case of null on right side.
247             return lhs.Equals(rhs);
248         }
249
250         /// <summary>
251         /// Compares whether the two ControlStates are different or not.
252         /// </summary>
253         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
254         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
255         /// <returns>true if the ControlStates are not equal; otherwise, false.</returns>
256         [EditorBrowsable(EditorBrowsableState.Never)]
257         public static bool operator !=(ControlState lhs, ControlState rhs) => !(lhs == rhs);
258
259         /// <summary>
260         /// The addition operator.
261         /// </summary>
262         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
263         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
264         /// <returns>The <see cref="ControlState"/> containing the result of the addition.</returns>
265         [EditorBrowsable(EditorBrowsableState.Never)]
266         public static ControlState operator +(ControlState lhs, ControlState rhs) => Create(lhs, rhs);
267
268         /// <summary>
269         /// The substraction operator.
270         /// </summary>
271         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
272         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
273         /// <returns>The <see cref="ControlState"/> containing the result of the substraction.</returns>
274         /// <exception cref="ArgumentNullException"> Thrown when lhs or rhs is null. </exception>
275         [EditorBrowsable(EditorBrowsableState.Never)]
276         public static ControlState operator -(ControlState lhs, ControlState rhs)
277         {
278             if (null == lhs)
279             {
280                 throw new ArgumentNullException(nameof(lhs));
281             }
282             else if (null == rhs)
283             {
284                 throw new ArgumentNullException(nameof(rhs));
285             }
286
287             if (!lhs.IsCombined)
288             {
289                 return ReferenceEquals(lhs, rhs) ? Normal : lhs;
290             }
291
292             var rest = lhs.stateList.Except(rhs.stateList);
293             var count = rest.Count();
294
295             if (count == 0)
296             {
297                 return Normal;
298             }
299
300             if (count == 1)
301             {
302                 return rest.First();
303             }
304
305             ControlState newState = new ControlState();
306             newState.stateList.AddRange(rest);
307             return newState;
308         }
309
310         class ControlStateTypeConverter : Binding.TypeConverter
311         {
312             public override object ConvertFromInvariantString(string value)
313             {
314                 if (value != null)
315                 {
316                     value = value.Trim();
317
318                     ControlState convertedState = new ControlState();
319                     string[] parts = value.Split(',');
320                     foreach (string part in parts)
321                     {
322                         convertedState += Create(part);
323                     }
324                     return convertedState;
325                 }
326
327                 throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(ControlState)}");
328             }
329         }
330     }
331 }