f3a00719d66acc2c5ebba0624b2927029d15dfd6
[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 static new 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                 {
209                     return;
210                 }
211             }
212
213             var view = child as View;
214             if (view != null)
215             {
216                 //we can ignore the request if we are either fully constrained or when the size request changes and we were already fully constrainted
217                  if ((trigger == InvalidationTrigger.MeasureChanged) ||
218                      (trigger == InvalidationTrigger.SizeRequestChanged))
219                 {
220                     return;
221                 }
222             }
223
224             s_resolutionList.Add(new KeyValuePair<Layout, int>(this, GetElementDepth(this)));
225             if (!s_relayoutInProgress)
226             {
227                 s_relayoutInProgress = true;
228                 Device.BeginInvokeOnMainThread(() =>
229                 {
230                     // if thread safety mattered we would need to lock this and compareexchange above
231                     IList<KeyValuePair<Layout, int>> copy = s_resolutionList;
232                     s_resolutionList = new List<KeyValuePair<Layout, int>>();
233                     s_relayoutInProgress = false;
234                 });
235             }
236         }
237
238         static int GetElementDepth(Element view)
239         {
240             var result = 0;
241             while (view.Parent != null)
242             {
243                 result++;
244                 view = view.Parent;
245             }
246             return result;
247         }
248
249         void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
250         {
251             if (e.Action == NotifyCollectionChangedAction.Move)
252             {
253                 return;
254             }
255
256             if (e.OldItems != null)
257             {
258                 foreach (object item in e.OldItems)
259                 {
260                     var v = item as View;
261                     if (v == null)
262                         continue;
263
264                     OnInternalRemoved(v);
265                 }
266             }
267
268             if (e.NewItems != null)
269             {
270                 foreach (object item in e.NewItems)
271                 {
272                     var v = item as View;
273                     if (v == null)
274                         continue;
275
276                     if ((item as BaseHandle) == this)
277                         throw new InvalidOperationException("Can not add self to own child collection.");
278
279                     OnInternalAdded(v);
280                 }
281             }
282         }
283
284         void OnInternalAdded(View view)
285         {
286             var parent = view.GetParent() as Layout;
287             parent?.InternalChildren.Remove(view);
288
289             OnChildAdded(view);
290             if (ShouldInvalidateOnChildAdded(view))
291                 InvalidateLayout();
292         }
293
294         void OnInternalRemoved(View view)
295         {
296             OnChildRemoved(view);
297             if (ShouldInvalidateOnChildRemoved(view))
298                 InvalidateLayout();
299         }
300
301         bool ShouldLayoutChildren()
302         {
303             if ( !LogicalChildrenInternal.Any() )
304             {
305                 return false;
306             }
307
308             foreach (Element element in VisibleDescendants())
309             {
310                 var visual = element as BaseHandle;
311                 if (visual == null)
312                 {
313                     continue;
314                 }
315             }
316             return true;
317         }
318     }
319 }