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