[NUI] Sync with dalihub & API5 branch (#631)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / XamlBinding / Layout.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Collections.ObjectModel;
4 using System.Collections.Specialized;
5 using System.ComponentModel;
6 using System.Linq;
7 using Tizen.NUI.Binding.Internals;
8 using Tizen.NUI.BaseComponents;
9
10 namespace Tizen.NUI.Binding
11 {
12     [ContentProperty("Children")]
13     internal abstract class Layout<T> : Layout, IViewContainer<T> where T : View
14     {
15         readonly ElementCollection<T> _children;
16
17         protected Layout()
18         {
19             _children = new ElementCollection<T>(InternalChildren);
20         }
21
22         public new IList<T> Children
23         {
24             get { return _children; }
25         }
26
27         protected virtual void OnAdded(T view)
28         {
29         }
30
31         protected override void OnChildAdded(Element child)
32         {
33             base.OnChildAdded(child);
34
35             var typedChild = child as T;
36             if (typedChild != null)
37                 OnAdded(typedChild);
38         }
39
40         protected override void OnChildRemoved(Element child)
41         {
42             base.OnChildRemoved(child);
43
44             var typedChild = child as T;
45             if (typedChild != null)
46                 OnRemoved(typedChild);
47         }
48
49         protected virtual void OnRemoved(T view)
50         {
51         }
52     }
53
54     internal abstract class Layout : View, ILayout, ILayoutController, IPaddingElement
55     {
56         public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create("IsClippedToBounds", typeof(bool), typeof(Layout), false);
57
58         public static readonly BindableProperty CascadeInputTransparentProperty = BindableProperty.Create(
59             nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true);
60
61         public new static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;
62
63         static IList<KeyValuePair<Layout, int>> s_resolutionList = new List<KeyValuePair<Layout, int>>();
64         static bool s_relayoutInProgress;
65
66         bool _hasDoneLayout;
67         Size _lastLayoutSize = new Size(-1, -1, 0);
68
69         ReadOnlyCollection<Element> _logicalChildren;
70
71         protected Layout()
72         {
73             //if things were added in base ctor (through implicit styles), the items added aren't properly parented
74             if (InternalChildren.Count > 0)
75                 InternalChildrenOnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, InternalChildren));
76
77             InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
78         }
79
80         public bool IsClippedToBounds
81         {
82             get { return (bool)GetValue(IsClippedToBoundsProperty); }
83             set { SetValue(IsClippedToBoundsProperty, value); }
84         }
85
86         public new Thickness Padding
87         {
88             get { return (Thickness)GetValue(PaddingElement.PaddingProperty); }
89             set { SetValue(PaddingElement.PaddingProperty, value); }
90         }
91
92         public bool CascadeInputTransparent
93         {
94             get { return (bool)GetValue(CascadeInputTransparentProperty); }
95             set { SetValue(CascadeInputTransparentProperty, value); }
96         }
97
98         Thickness IPaddingElement.PaddingDefaultValueCreator()
99         {
100             return default(Thickness);
101         }
102
103         void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue)
104         {
105             UpdateChildrenLayout();
106         }
107
108         internal ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
109
110         internal override ReadOnlyCollection<Element> LogicalChildrenInternal
111         {
112             get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren)); }
113         }
114
115         /// <summary>
116         /// Raised when the layout of the Page has changed.
117         /// </summary>
118         public event EventHandler LayoutChanged;
119
120         [EditorBrowsable(EditorBrowsableState.Never)]
121         public new IReadOnlyList<Element> Children
122         {
123             get { return InternalChildren; }
124         }
125
126         public void ForceLayout()
127         {
128         }
129
130
131         public static void LayoutChildIntoBoundingRegion(BaseHandle child, Rectangle region)
132         {
133             var view = child as View;
134             if (view == null)
135             {
136                 return;
137             }
138         }
139
140         public void LowerChild(View view)
141         {
142             if (!InternalChildren.Contains(view) || (InternalChildren.First() as BaseHandle) == view)
143                 return;
144
145             InternalChildren.Move(InternalChildren.IndexOf(view), 0);
146         }
147
148         public void RaiseChild(View view)
149         {
150             if (!InternalChildren.Contains(view) || (InternalChildren.Last() as BaseHandle) == view)
151                 return;
152
153             InternalChildren.Move(InternalChildren.IndexOf(view), InternalChildren.Count - 1);
154         }
155
156         protected virtual void InvalidateLayout()
157         {
158             _hasDoneLayout = false;
159             if (!_hasDoneLayout)
160                 ForceLayout();
161         }
162
163         protected abstract void LayoutChildren(double x, double y, double width, double height);
164
165         protected void OnChildMeasureInvalidated(object sender, EventArgs e)
166         {
167             InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
168             OnChildMeasureInvalidated((BaseHandle)sender, trigger);
169             OnChildMeasureInvalidated();
170         }
171
172         protected virtual void OnChildMeasureInvalidated()
173         {
174         }
175
176         protected virtual bool ShouldInvalidateOnChildAdded(View child)
177         {
178             return true;
179         }
180
181         protected virtual bool ShouldInvalidateOnChildRemoved(View child)
182         {
183             return true;
184         }
185
186         protected void UpdateChildrenLayout()
187         {
188             _hasDoneLayout = true;
189
190             if (!ShouldLayoutChildren())
191                 return;
192
193             LayoutChanged?.Invoke(this, EventArgs.Empty);
194         }
195
196         internal static void LayoutChildIntoBoundingRegion(View child, Rectangle region, SizeRequest childSizeRequest)
197         {
198         }
199
200         internal virtual void OnChildMeasureInvalidated(BaseHandle child, InvalidationTrigger trigger)
201         {
202             ReadOnlyCollection<Element> children = LogicalChildrenInternal;
203             int count = children.Count;
204             for (var index = 0; index < count; index++)
205             {
206                 var v = LogicalChildrenInternal[index] as BaseHandle;
207                 if (v != null)
208                     return;
209             }
210
211             var view = child as View;
212             if (view != null)
213             {
214                 //we can ignore the request if we are either fully constrained or when the size request changes and we were already fully constrainted
215                  if ((trigger == InvalidationTrigger.MeasureChanged) ||
216                      (trigger == InvalidationTrigger.SizeRequestChanged))
217                 {
218                     return;
219                 }
220             }
221
222             s_resolutionList.Add(new KeyValuePair<Layout, int>(this, GetElementDepth(this)));
223             if (!s_relayoutInProgress)
224             {
225                 s_relayoutInProgress = true;
226                 Device.BeginInvokeOnMainThread(() =>
227                 {
228                     // if thread safety mattered we would need to lock this and compareexchange above
229                     IList<KeyValuePair<Layout, int>> copy = s_resolutionList;
230                     s_resolutionList = new List<KeyValuePair<Layout, int>>();
231                     s_relayoutInProgress = false;
232                 });
233             }
234         }
235
236         static int GetElementDepth(Element view)
237         {
238             var result = 0;
239             while (view.Parent != null)
240             {
241                 result++;
242                 view = view.Parent;
243             }
244             return result;
245         }
246
247         void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
248         {
249             if (e.Action == NotifyCollectionChangedAction.Move)
250             {
251                 return;
252             }
253
254             if (e.OldItems != null)
255             {
256                 foreach (object item in e.OldItems)
257                 {
258                     var v = item as View;
259                     if (v == null)
260                         continue;
261
262                     OnInternalRemoved(v);
263                 }
264             }
265
266             if (e.NewItems != null)
267             {
268                 foreach (object item in e.NewItems)
269                 {
270                     var v = item as View;
271                     if (v == null)
272                         continue;
273
274                     if ((item as BaseHandle) == this)
275                         throw new InvalidOperationException("Can not add self to own child collection.");
276
277                     OnInternalAdded(v);
278                 }
279             }
280         }
281
282         void OnInternalAdded(View view)
283         {
284             var parent = view.GetParent() as Layout;
285             parent?.InternalChildren.Remove(view);
286
287             OnChildAdded(view);
288             if (ShouldInvalidateOnChildAdded(view))
289                 InvalidateLayout();
290         }
291
292         void OnInternalRemoved(View view)
293         {
294             OnChildRemoved(view);
295             if (ShouldInvalidateOnChildRemoved(view))
296                 InvalidateLayout();
297         }
298
299         bool ShouldLayoutChildren()
300         {
301             if ( !LogicalChildrenInternal.Any() )
302                 return false;
303
304             foreach (Element element in VisibleDescendants())
305             {
306                 var visual = element as BaseHandle;
307                 if (visual == null)
308                     continue;
309             }
310             return true;
311         }
312     }
313 }