[NUI]Add xaml support for nui and nui xaml test sample (#230)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Xaml / VisualStateManager.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
5 using System.Linq;
6 using Tizen.NUI;
7 using Tizen.NUI.Binding;
8
9 namespace Tizen.NUI.Xaml
10 {
11         internal static class VisualStateManager
12         {
13                 internal class CommonStates
14                 {
15                         public const string Normal = "Normal";
16                         public const string Disabled = "Disabled";
17                         public const string Focused = "Focused";
18                 }
19
20                 public static readonly BindableProperty VisualStateGroupsProperty =
21                         BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(/*VisualElement*/BaseHandle), 
22                                 defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged, 
23                                 defaultValueCreator: bindable => new VisualStateGroupList());
24
25                 static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
26                 {
27                         GoToState((/*VisualElement*/BaseHandle)bindable, CommonStates.Normal);
28                 }
29
30                 public static IList<VisualStateGroup> GetVisualStateGroups(/*VisualElement*/BaseHandle visualElement)
31                 {
32                         return (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
33                 }
34
35                 public static void SetVisualStateGroups(/*VisualElement*/BaseHandle visualElement, VisualStateGroupList value)
36                 {
37                         visualElement.SetValue(VisualStateGroupsProperty, value);
38                 }
39
40                 public static bool GoToState(/*VisualElement*/BaseHandle visualElement, string name)
41                 {
42                         if (!visualElement.IsSet(VisualStateGroupsProperty))
43                         {
44                                 return false;
45                         }
46
47                         var groups = (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
48
49                         foreach (VisualStateGroup group in groups)
50                         {
51                                 if (group.CurrentState?.Name == name)
52                                 {
53                                         // We're already in the target state; nothing else to do
54                                         return true;
55                                 }
56
57                                 // See if this group contains the new state
58                                 var target = group.GetState(name);
59                                 if (target == null)
60                                 {
61                                         continue;
62                                 }
63
64                                 // If we've got a new state to transition to, unapply the setters from the current state
65                                 if (group.CurrentState != null)
66                                 {
67                                         foreach (Setter setter in group.CurrentState.Setters)
68                                         {
69                                                 setter.UnApply(visualElement);
70                                         }
71                                 }
72
73                                 // Update the current state
74                                 group.CurrentState = target;
75
76                                 // Apply the setters from the new state
77                                 foreach (Setter setter in target.Setters)
78                                 {
79                                         setter.Apply(visualElement);
80                                 }
81
82                                 return true;
83                         }
84
85                         return false;
86                 }
87
88                 public static bool HasVisualStateGroups(this /*VisualElement*/BaseHandle element)
89                 {
90                         return element.IsSet(VisualStateGroupsProperty);
91                 }
92         }
93
94         internal class VisualStateGroupList : IList<VisualStateGroup>
95         {
96                 readonly IList<VisualStateGroup> _internalList;
97
98                 void Validate(IList<VisualStateGroup> groups)
99                 { 
100                         // If we have 1 group, no need to worry about duplicate group names
101                         if (groups.Count > 1)
102                         {
103                                 if (groups.GroupBy(vsg => vsg.Name).Any(g => g.Count() > 1))
104                                 {
105                                         throw new InvalidOperationException("VisualStateGroup Names must be unique");
106                                 }
107                         }
108
109                         // State names must be unique within this group list, so pull in all 
110                         // the states in all the groups, group them by name, and see if we have
111                         // and duplicates
112                         if (groups.SelectMany(group => group.States)
113                                 .GroupBy(state => state.Name)
114                                 .Any(g => g.Count() > 1))
115                         {
116                                 throw new InvalidOperationException("VisualState Names must be unique");
117                         }
118                 }
119
120                 public VisualStateGroupList() 
121                 {
122                         _internalList = new WatchAddList<VisualStateGroup>(Validate);
123                 }
124
125                 void ValidateOnStatesChanged(object sender, EventArgs eventArgs)
126                 {
127                         Validate(_internalList);
128                 }
129
130                 public IEnumerator<VisualStateGroup> GetEnumerator()
131                 {
132                         return _internalList.GetEnumerator();
133                 }
134
135                 IEnumerator IEnumerable.GetEnumerator()
136                 {
137                         return ((IEnumerable)_internalList).GetEnumerator();
138                 }
139
140                 public void Add(VisualStateGroup item)
141                 {
142                         _internalList.Add(item);
143                         item.StatesChanged += ValidateOnStatesChanged;
144                 }
145
146                 public void Clear()
147                 {
148                         foreach (var group in _internalList)
149                         {
150                                 group.StatesChanged -= ValidateOnStatesChanged;
151                         }
152
153                         _internalList.Clear();
154                 }
155
156                 public bool Contains(VisualStateGroup item)
157                 {
158                         return _internalList.Contains(item);
159                 }
160
161                 public void CopyTo(VisualStateGroup[] array, int arrayIndex)
162                 {
163                         _internalList.CopyTo(array, arrayIndex);
164                 }
165
166                 public bool Remove(VisualStateGroup item)
167                 {
168                         item.StatesChanged -= ValidateOnStatesChanged;
169                         return _internalList.Remove(item);
170                 }
171
172                 public int Count => _internalList.Count;
173
174                 public bool IsReadOnly => false;
175
176                 public int IndexOf(VisualStateGroup item)
177                 {
178                         return _internalList.IndexOf(item);
179                 }
180
181                 public void Insert(int index, VisualStateGroup item)
182                 {
183                         item.StatesChanged += ValidateOnStatesChanged;
184                         _internalList.Insert(index, item);
185                 }
186
187                 public void RemoveAt(int index)
188                 {
189                         _internalList[index].StatesChanged -= ValidateOnStatesChanged;
190                         _internalList.RemoveAt(index);
191                 }
192
193                 public VisualStateGroup this[int index]
194                 {
195                         get => _internalList[index];
196                         set => _internalList[index] = value;
197                 }
198         }
199
200         [RuntimeNameProperty(nameof(Name))]
201         [ContentProperty(nameof(States))]
202         internal sealed class VisualStateGroup 
203         {
204                 public VisualStateGroup()
205                 {
206                         States = new WatchAddList<VisualState>(OnStatesChanged);
207                 }
208
209                 public Type TargetType { get; set; }
210                 public string Name { get; set; }
211                 public IList<VisualState> States { get; }
212                 public VisualState CurrentState { get; internal set; }
213
214                 internal VisualState GetState(string name)
215                 {
216                         foreach (VisualState state in States)
217                         {
218                                 if (string.CompareOrdinal(state.Name, name) == 0)
219                                 {
220                                         return state;
221                                 }
222                         }
223
224                         return null;
225                 }
226
227                 internal VisualStateGroup Clone()
228                 {
229                         var clone =  new VisualStateGroup {TargetType = TargetType, Name = Name, CurrentState = CurrentState};
230                         foreach (VisualState state in States)
231                         {
232                                 clone.States.Add(state.Clone());
233                         }
234
235                         return clone;
236                 }
237
238                 internal event EventHandler StatesChanged;
239
240                 void OnStatesChanged(IList<VisualState> list)
241                 {
242                         if (list.Any(state => string.IsNullOrEmpty(state.Name)))
243                         {
244                                 throw new InvalidOperationException("State names may not be null or empty");
245                         }
246
247                         StatesChanged?.Invoke(this, EventArgs.Empty);
248                 }
249         }
250
251         [RuntimeNameProperty(nameof(Name))]
252         internal sealed class VisualState 
253         {
254                 public VisualState()
255                 {
256                         Setters = new ObservableCollection<Setter>();
257                 }
258
259                 public string Name { get; set; }
260                 public IList<Setter> Setters { get;}
261                 public Type TargetType { get; set; }
262
263                 internal VisualState Clone()
264                 {
265                         var clone = new VisualState { Name = Name, TargetType = TargetType };
266                         foreach (var setter in Setters)
267                         {
268                                 clone.Setters.Add(setter);
269                         }
270
271                         return clone;
272                 }
273         }
274
275         internal static class VisualStateGroupListExtensions
276         {
277                 internal static IList<VisualStateGroup> Clone(this IList<VisualStateGroup> groups)
278                 {
279                         var actual = new VisualStateGroupList();
280                         foreach (var group in groups)
281                         {
282                                 actual.Add(group.Clone());
283                         }
284
285                         return actual;
286                 }
287         }
288
289         internal class WatchAddList<T> : IList<T>
290         {
291                 readonly Action<List<T>> _onAdd;
292                 readonly List<T> _internalList;
293
294                 public WatchAddList(Action<List<T>> onAdd)
295                 {
296                         _onAdd = onAdd;
297                         _internalList = new List<T>();
298                 }
299
300                 public IEnumerator<T> GetEnumerator()
301                 {
302                         return _internalList.GetEnumerator();
303                 }
304
305                 IEnumerator IEnumerable.GetEnumerator()
306                 {
307                         return ((IEnumerable)_internalList).GetEnumerator();
308                 }
309
310                 public void Add(T item)
311                 {
312                         _internalList.Add(item);
313                         _onAdd(_internalList);
314                 }
315
316                 public void Clear()
317                 {
318                         _internalList.Clear();
319                 }
320
321                 public bool Contains(T item)
322                 {
323                         return _internalList.Contains(item);
324                 }
325
326                 public void CopyTo(T[] array, int arrayIndex)
327                 {
328                         _internalList.CopyTo(array, arrayIndex);
329                 }
330
331                 public bool Remove(T item)
332                 {
333                         return _internalList.Remove(item);
334                 }
335
336                 public int Count => _internalList.Count;
337
338                 public bool IsReadOnly => false;
339
340                 public int IndexOf(T item)
341                 {
342                         return _internalList.IndexOf(item);
343                 }
344
345                 public void Insert(int index, T item)
346                 {
347                         _internalList.Insert(index, item);
348                         _onAdd(_internalList);
349                 }
350
351                 public void RemoveAt(int index)
352                 {
353                         _internalList.RemoveAt(index);
354                 }
355
356                 public T this[int index]
357                 {
358                         get => _internalList[index];
359                         set => _internalList[index] = value;
360                 }
361         }
362 }