[NUI] Add ControlStateTypeConverter for xaml (#2002)
[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         /// DisabledSelected State.
66         /// </summary>
67         [EditorBrowsable(EditorBrowsableState.Never)]
68         public static readonly ControlState DisabledSelected = Disabled + Selected;
69         /// <summary>
70         /// DisabledFocused State.
71         /// </summary>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public static readonly ControlState DisabledFocused = Disabled + Focused;
74         /// <summary>
75         /// SelectedFocused State.
76         /// </summary>
77         [EditorBrowsable(EditorBrowsableState.Never)]
78         public static readonly ControlState SelectedFocused = Selected + Focused;
79         /// <summary>
80         /// Other State.
81         /// </summary>
82         [EditorBrowsable(EditorBrowsableState.Never)]
83         public static readonly ControlState Other = Create("Other");
84
85         private List<ControlState> stateList = new List<ControlState>();
86         private readonly string name = "";
87
88         /// <summary>
89         /// Gets or sets a value indicating whether it has combined states.
90         /// </summary>
91         [EditorBrowsable(EditorBrowsableState.Never)]
92         public bool IsCombined => stateList.Count > 1;
93
94         private ControlState() { }
95
96         private ControlState(string name) : this() => this.name = name;
97
98         /// <summary>
99         /// Create an instance of the <see cref="ControlState"/> with state name.
100         /// </summary>
101         /// <param name="name">The state name.</param>
102         /// <returns>The <see cref="ControlState"/> instance which has single state.</returns>
103         [EditorBrowsable(EditorBrowsableState.Never)]
104         public static ControlState Create(string name)
105         {
106             if (name == null)
107                 throw new ArgumentNullException(nameof(name));
108             if (string.IsNullOrWhiteSpace(name))
109                 throw new ArgumentException("name cannot be empty string", nameof(name));
110
111             if (stateDictionary.TryGetValue(name, out ControlState state))
112                 return state;
113
114             state = new ControlState(name);
115             state.stateList.Add(state);
116             stateDictionary.Add(name, state);
117             return state;
118         }
119
120         /// <summary>
121         /// Create an instance of the <see cref="ControlState"/> with combined states.
122         /// </summary>
123         /// <param name="states">The control state array.</param>
124         /// <returns>The <see cref="ControlState"/> instance which has combined state.</returns>
125         [EditorBrowsable(EditorBrowsableState.Never)]
126         public static ControlState Create(params ControlState[] states)
127         {
128             if (states.Length == 1)
129                 return states[0];
130
131             ControlState newState = new ControlState();
132             for (int i = 0; i < states.Length; i++)
133             {
134                 if (states[i] == Normal)
135                     continue;
136
137                 if (states[i] == All)
138                     return All;
139
140                 newState.stateList.AddRange(states[i].stateList);
141             }
142
143             if (newState.stateList.Count == 0)
144                 return Normal;
145
146             newState.stateList = newState.stateList.Distinct().ToList();
147
148             if (newState.stateList.Count == 1)
149             {
150                 return newState.stateList[0];
151             }
152
153             return newState;
154         }
155
156         /// <summary>
157         /// Determines whether a state contains a specified state.
158         /// </summary>
159         /// <param name="state">The state to search for</param>
160         /// <returns>true if the state contain a specified state, otherwise, false.</returns>
161         [EditorBrowsable(EditorBrowsableState.Never)]
162         public bool Contains(ControlState state)
163         {
164             if (state == null)
165                 throw new ArgumentNullException(nameof(state));
166
167             if (!IsCombined)
168                 return ReferenceEquals(this, state);
169
170             bool found;
171             for (int i = 0; i < state.stateList.Count; i++)
172             {
173                 found = false;
174                 for (int j = 0; j < stateList.Count; j++)
175                 {
176                     if (ReferenceEquals(state.stateList[i], stateList[j]))
177                     {
178                         found = true;
179                         break;
180                     }
181                 }
182                 if (!found) return false;
183             }
184
185             return true;
186         }
187
188         ///  <inheritdoc/>
189         [EditorBrowsable(EditorBrowsableState.Never)]
190         public bool Equals(ControlState other)
191         {
192             if (other is null || stateList.Count != other.stateList.Count)
193                 return false;
194
195             return Contains(other);
196         }
197
198         ///  <inheritdoc/>
199         [EditorBrowsable(EditorBrowsableState.Never)]
200         public override bool Equals(object obj) => this.Equals(obj as ControlState);
201
202         ///  <inheritdoc/>
203         [EditorBrowsable(EditorBrowsableState.Never)]
204         public override int GetHashCode() => (name.GetHashCode() * 397) ^ IsCombined.GetHashCode();
205
206         ///  <inheritdoc/>
207         [EditorBrowsable(EditorBrowsableState.Never)]
208         public override string ToString()
209         {
210             string name = "";
211             for (int i = 0; i < stateList.Count; i++)
212             {
213                 name += ((i == 0) ? "" : ", ") + stateList[i].name;
214             }
215             return name;
216         }
217
218         /// <summary>
219         /// Compares whether the two ControlStates are same or not.
220         /// </summary>
221         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
222         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
223         /// <returns>true if the ControlStates are equal; otherwise, false.</returns>
224         [EditorBrowsable(EditorBrowsableState.Never)]
225         public static bool operator ==(ControlState lhs, ControlState rhs) => lhs.Equals(rhs);
226
227         /// <summary>
228         /// Compares whether the two ControlStates are different or not.
229         /// </summary>
230         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
231         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
232         /// <returns>true if the ControlStates are not equal; otherwise, false.</returns>
233         [EditorBrowsable(EditorBrowsableState.Never)]
234         public static bool operator !=(ControlState lhs, ControlState rhs) => !lhs.Equals(rhs);
235
236         /// <summary>
237         /// The addition operator.
238         /// </summary>
239         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
240         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
241         /// <returns>The <see cref="ControlState"/> containing the result of the addition.</returns>
242         [EditorBrowsable(EditorBrowsableState.Never)]
243         public static ControlState operator +(ControlState lhs, ControlState rhs) => Create(lhs, rhs);
244
245         /// <summary>
246         /// The substraction operator.
247         /// </summary>
248         /// <param name="lhs">A <see cref="ControlState"/> on the left hand side.</param>
249         /// <param name="rhs">A <see cref="ControlState"/> on the right hand side.</param>
250         /// <returns>The <see cref="ControlState"/> containing the result of the substraction.</returns>
251         [EditorBrowsable(EditorBrowsableState.Never)]
252         public static ControlState operator -(ControlState lhs, ControlState rhs)
253         {
254             if (!lhs.IsCombined)
255             {
256                 return ReferenceEquals(lhs, rhs) ? Normal : lhs;
257             }
258
259             var rest = lhs.stateList.Except(rhs.stateList);
260
261             if (rest.Count() == 0)
262             {
263                 return Normal;
264             }
265
266             if (rest.Count() == 1)
267             {
268                 return rest.First();
269             }
270
271             ControlState newState = new ControlState();
272             newState.stateList.AddRange(rest);
273             return newState;
274         }
275
276         class ControlStateTypeConverter : Binding.TypeConverter
277         {
278             public override object ConvertFromInvariantString(string value)
279             {
280                 if (value != null)
281                 {
282                     value = value.Trim();
283
284                     ControlState convertedState = new ControlState();
285                     string[] parts = value.Split(',');
286                     foreach (string part in parts)
287                     {
288                         convertedState += Create(part);
289                     }
290                     return convertedState;
291                 }
292
293                 throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(ControlState)}");
294             }
295         }
296     }
297 }