2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
6 using Tizen.NUI.XamlBinding;
8 namespace Tizen.NUI.Xaml
10 internal static class VisualStateManager
12 internal class CommonStates
14 public const string Normal = "Normal";
15 public const string Disabled = "Disabled";
16 public const string Focused = "Focused";
19 public static readonly BindableProperty VisualStateGroupsProperty =
20 BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(Element),
21 defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged,
22 defaultValueCreator: bindable => new VisualStateGroupList());
24 static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
26 GoToState((Element)bindable, CommonStates.Normal);
29 public static IList<VisualStateGroup> GetVisualStateGroups(Element visualElement)
31 return (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
34 public static void SetVisualStateGroups(Element visualElement, VisualStateGroupList value)
36 visualElement.SetValue(VisualStateGroupsProperty, value);
39 public static bool GoToState(Element visualElement, string name)
41 if (!visualElement.IsSet(VisualStateGroupsProperty))
46 var groups = (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
48 foreach (VisualStateGroup group in groups)
50 if (group.CurrentState?.Name == name)
52 // We're already in the target state; nothing else to do
56 // See if this group contains the new state
57 var target = group.GetState(name);
63 // If we've got a new state to transition to, unapply the setters from the current state
64 if (group.CurrentState != null)
66 foreach (Setter setter in group.CurrentState.Setters)
68 setter.UnApply(visualElement);
72 // Update the current state
73 group.CurrentState = target;
75 // Apply the setters from the new state
76 foreach (Setter setter in target.Setters)
78 setter.Apply(visualElement);
87 public static bool HasVisualStateGroups(this Element element)
89 return element.IsSet(VisualStateGroupsProperty);
93 internal class VisualStateGroupList : IList<VisualStateGroup>
95 readonly IList<VisualStateGroup> _internalList;
97 void Validate(IList<VisualStateGroup> groups)
99 // If we have 1 group, no need to worry about duplicate group names
100 if (groups.Count > 1)
102 if (groups.GroupBy(vsg => vsg.Name).Any(g => g.Count() > 1))
104 throw new InvalidOperationException("VisualStateGroup Names must be unique");
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
111 if (groups.SelectMany(group => group.States)
112 .GroupBy(state => state.Name)
113 .Any(g => g.Count() > 1))
115 throw new InvalidOperationException("VisualState Names must be unique");
119 public VisualStateGroupList()
121 _internalList = new WatchAddList<VisualStateGroup>(Validate);
124 void ValidateOnStatesChanged(object sender, EventArgs eventArgs)
126 Validate(_internalList);
129 public IEnumerator<VisualStateGroup> GetEnumerator()
131 return _internalList.GetEnumerator();
134 IEnumerator IEnumerable.GetEnumerator()
136 return ((IEnumerable)_internalList).GetEnumerator();
139 public void Add(VisualStateGroup item)
141 _internalList.Add(item);
142 item.StatesChanged += ValidateOnStatesChanged;
147 foreach (var group in _internalList)
149 group.StatesChanged -= ValidateOnStatesChanged;
152 _internalList.Clear();
155 public bool Contains(VisualStateGroup item)
157 return _internalList.Contains(item);
160 public void CopyTo(VisualStateGroup[] array, int arrayIndex)
162 _internalList.CopyTo(array, arrayIndex);
165 public bool Remove(VisualStateGroup item)
167 item.StatesChanged -= ValidateOnStatesChanged;
168 return _internalList.Remove(item);
171 public int Count => _internalList.Count;
173 public bool IsReadOnly => false;
175 public int IndexOf(VisualStateGroup item)
177 return _internalList.IndexOf(item);
180 public void Insert(int index, VisualStateGroup item)
182 item.StatesChanged += ValidateOnStatesChanged;
183 _internalList.Insert(index, item);
186 public void RemoveAt(int index)
188 _internalList[index].StatesChanged -= ValidateOnStatesChanged;
189 _internalList.RemoveAt(index);
192 public VisualStateGroup this[int index]
194 get => _internalList[index];
195 set => _internalList[index] = value;
199 [RuntimeNameProperty(nameof(Name))]
200 [ContentProperty(nameof(States))]
201 internal sealed class VisualStateGroup
203 public VisualStateGroup()
205 States = new WatchAddList<VisualState>(OnStatesChanged);
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; }
213 internal VisualState GetState(string name)
215 foreach (VisualState state in States)
217 if (string.CompareOrdinal(state.Name, name) == 0)
226 internal VisualStateGroup Clone()
228 var clone = new VisualStateGroup {TargetType = TargetType, Name = Name, CurrentState = CurrentState};
229 foreach (VisualState state in States)
231 clone.States.Add(state.Clone());
237 internal event EventHandler StatesChanged;
239 void OnStatesChanged(IList<VisualState> list)
241 if (list.Any(state => string.IsNullOrEmpty(state.Name)))
243 throw new InvalidOperationException("State names may not be null or empty");
246 StatesChanged?.Invoke(this, EventArgs.Empty);
250 [RuntimeNameProperty(nameof(Name))]
251 internal sealed class VisualState
255 Setters = new ObservableCollection<Setter>();
258 public string Name { get; set; }
259 public IList<Setter> Setters { get;}
260 public Type TargetType { get; set; }
262 internal VisualState Clone()
264 var clone = new VisualState { Name = Name, TargetType = TargetType };
265 foreach (var setter in Setters)
267 clone.Setters.Add(setter);
274 internal static class VisualStateGroupListExtensions
276 internal static IList<VisualStateGroup> Clone(this IList<VisualStateGroup> groups)
278 var actual = new VisualStateGroupList();
279 foreach (var group in groups)
281 actual.Add(group.Clone());
288 internal class WatchAddList<T> : IList<T>
290 readonly Action<List<T>> _onAdd;
291 readonly List<T> _internalList;
293 public WatchAddList(Action<List<T>> onAdd)
296 _internalList = new List<T>();
299 public IEnumerator<T> GetEnumerator()
301 return _internalList.GetEnumerator();
304 IEnumerator IEnumerable.GetEnumerator()
306 return ((IEnumerable)_internalList).GetEnumerator();
309 public void Add(T item)
311 _internalList.Add(item);
312 _onAdd(_internalList);
317 _internalList.Clear();
320 public bool Contains(T item)
322 return _internalList.Contains(item);
325 public void CopyTo(T[] array, int arrayIndex)
327 _internalList.CopyTo(array, arrayIndex);
330 public bool Remove(T item)
332 return _internalList.Remove(item);
335 public int Count => _internalList.Count;
337 public bool IsReadOnly => false;
339 public int IndexOf(T item)
341 return _internalList.IndexOf(item);
344 public void Insert(int index, T item)
346 _internalList.Insert(index, item);
347 _onAdd(_internalList);
350 public void RemoveAt(int index)
352 _internalList.RemoveAt(index);
355 public T this[int index]
357 get => _internalList[index];
358 set => _internalList[index] = value;