01e42f36ca2a35abcb15fb1c2facf85aaeed6e18
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / 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(Element), 
22                 defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged, 
23                 defaultValueCreator: bindable => new VisualStateGroupList());
24
25         static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
26         {
27             GoToState((Element)bindable, CommonStates.Normal);
28         }
29
30         public static IList<VisualStateGroup> GetVisualStateGroups(Element visualElement)
31         {
32             return (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
33         }
34
35         public static void SetVisualStateGroups(Element visualElement, VisualStateGroupList value)
36         {
37             visualElement.SetValue(VisualStateGroupsProperty, value);
38         }
39
40         public static bool GoToState(Element 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 Element 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 }