[NUI] Sync dalihub & Fix VD build error (#824)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Layouting / LayoutItem.cs
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI
23 {
24
25     [FlagsAttribute]
26     enum LayoutFlags : short
27     {
28       None = 0,
29       ForceLayout = 1,
30       LayoutRequired = 2,
31       MeasuredDimensionSet = 4
32     };
33
34     /// <summary>
35     /// [Draft] Base class for layouts. It is used to layout a View
36     /// It can be laid out by a LayoutGroup.
37     /// </summary>
38     internal class LayoutItem
39     {
40         private MeasureSpecification OldWidthMeasureSpec; // Store measure specification to compare against later
41         private MeasureSpecification OldHeightMeasureSpec;// Store measure specification to compare against later
42
43         private LayoutFlags Flags = LayoutFlags.None;
44
45         private ILayoutParent Parent;
46
47         private LayoutLength _left;
48         private LayoutLength _right;
49         private LayoutLength _top;
50         private LayoutLength _bottom;
51         private LayoutData _layoutData;
52
53         private Extents _padding;
54         private Extents _margin;
55
56         /// <summary>
57         /// [Draft] The View that this Layout has been assigned to.
58         /// </summary>
59         public View Owner{get; set;}  // Should not keep a View alive.
60
61         /// <summary>
62         /// [Draft] Margin for this LayoutItem
63         /// </summary>
64         public Extents Margin
65         {
66             get
67             {
68                 return _margin;
69             }
70             set
71             {
72                 _margin = value;
73                 RequestLayout();
74             }
75         }
76
77         /// <summary>
78         /// [Draft] Padding for this LayoutItem
79         /// </summary>
80         public Extents Padding
81         {
82             get
83             {
84                 return _padding;
85             }
86             set
87             {
88                 _padding = value;
89                 RequestLayout();
90             }
91         }
92
93         /// <summary>
94         /// [Draft] Constructor
95         /// </summary>
96         public LayoutItem()
97         {
98             Initialize();
99         }
100
101         /// <summary>
102         /// [Draft] Constructor setting the owner of this LayoutItem.
103         /// </summary>
104         /// <param name="owner">Owning View of this layout, currently a View but may be extending for Windows/Layers.</param>
105         public LayoutItem(View owner)
106         {
107             Owner = owner;
108             Initialize();
109         }
110
111         /// <summary>
112         /// [Draft] Set parent to this layout.
113         /// </summary>
114         /// <param name="parent">Parent to set on this Layout.</param>
115         public void SetParent( ILayoutParent parent)
116         {
117             Parent = parent as LayoutGroup;
118         }
119
120         /// <summary>
121         /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
122         /// </summary>
123         public void Unparent()
124         {
125             // Enable directly derived types to first remove children
126             OnUnparent();
127
128             // Remove myself from parent
129             Parent?.Remove( this );
130
131             // Remove parent reference
132             Parent = null;
133
134             // Lastly, clear layout from owning View.
135             Owner?.ResetLayout();
136         }
137
138         private void Initialize()
139         {
140             _layoutData = new LayoutData();
141             _left = new LayoutLength(0);
142             _top = new LayoutLength(0);
143             _right = new LayoutLength(0);
144             _bottom = new LayoutLength(0);
145             _padding = new Extents(0,0,0,0);
146             _margin = new Extents(0,0,0,0);
147         }
148
149         /// <summary>
150         /// Get the View owning this LayoutItem
151         /// </summary>
152         internal View GetOwner()
153         {
154             return Owner;
155         }
156
157         /// <summary>
158         /// Initialize the layout and allow derived classes to also perform any operations
159         /// </summary>
160         /// <param name="owner">Owner of this Layout.</param>
161         internal void AttachToOwner(View owner)
162         {
163             // Assign the layout owner.
164             Owner = owner;
165             OnAttachedToOwner();
166             // Add layout to parent layout if a layout container
167             View parent = Owner.GetParent() as View;
168             (parent?.Layout as LayoutGroup)?.Add( this );
169         }
170
171         /// <summary>
172         /// This is called to find out how big a layout should be. <br />
173         /// The parent supplies constraint information in the width and height parameters. <br />
174         /// The actual measurement work of a layout is performed in OnMeasure called by this
175         /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
176         /// </summary>
177         /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
178         /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
179         internal void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
180         {
181             // Check if relayouting is required.
182             bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
183                                (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
184                                (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
185                                (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
186
187             bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
188                                  (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
189
190             bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
191                                    (MeasuredHeight.Size == heightMeasureSpec.Size);
192
193             bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize);
194             needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
195
196             if (needsLayout)
197             {
198                 OnMeasure(widthMeasureSpec, heightMeasureSpec);
199                 Flags = Flags | LayoutFlags.LayoutRequired;
200                 Flags &= ~LayoutFlags.ForceLayout;
201             }
202             OldWidthMeasureSpec = widthMeasureSpec;
203             OldHeightMeasureSpec = heightMeasureSpec;
204         }
205
206         /// <summary>
207         /// Assign a size and position to a layout and all of its descendants. <br />
208         /// This is the second phase of the layout mechanism.  (The first is measuring). In this phase, each parent
209         /// calls layout on all of its children to position them.  This is typically done using the child<br />
210         /// measurements that were stored in the measure pass.<br />
211         /// </summary>
212         /// <param name="left">Left position, relative to parent.</param>
213         /// <param name="top">Top position, relative to parent.</param>
214         /// <param name="right">Right position, relative to parent.</param>
215         /// <param name="bottom">Bottom position, relative to parent.</param>
216         public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
217         {
218             bool changed = SetFrame(left, top, right, bottom);
219
220             // Check if Measure needed before Layouting
221             if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
222             {
223                 OnLayout(changed, left, top, right, bottom);
224                 // Clear flag
225                 Flags &= ~LayoutFlags.LayoutRequired;
226             }
227         }
228
229         /// <summary>
230         /// Utility to return a default size.<br />
231         /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
232         /// MeasureSpecification.<br />
233         /// </summary>
234         /// <param name="size"> Default size for this layout.</param>
235         /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
236         /// <returns>The size this layout should be.</returns>
237         public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
238         {
239             LayoutLength result = size;
240             MeasureSpecification.ModeType specMode = measureSpecification.Mode;
241             LayoutLength specSize = measureSpecification.Size;
242
243             switch (specMode)
244             {
245                 case MeasureSpecification.ModeType.Unspecified:
246                 {
247                     result = size;
248                     break;
249                 }
250                 case MeasureSpecification.ModeType.AtMost:
251                 {
252                     // Ensure the default size does not exceed the spec size unless the default size is 0.
253                     // Another container could provide a default size of 0.
254
255                     // Do not set size to 0, use specSize in this case as could be a legacy container
256                     if( ( size.AsDecimal() < specSize.AsDecimal()) && ( size.AsDecimal() >  0) )
257                     {
258                         result = size;
259                     }
260                     else
261                     {
262                         result = specSize;
263                     }
264                     break;
265                 }
266                 case MeasureSpecification.ModeType.Exactly:
267                 {
268                     result = specSize;
269                     break;
270                 }
271             }
272
273             return result;
274         }
275
276         public ILayoutParent GetParent()
277         {
278             return Parent;
279         }
280
281         /// <summary>
282         /// Request that this layout is re-laid out.<br />
283         /// This will make this layout and all it's parent layouts dirty.<br />
284         /// </summary>
285         public void RequestLayout()
286         {
287             Flags = Flags | LayoutFlags.ForceLayout;
288             Window.Instance.LayoutController.RequestLayout(this);
289         }
290
291         /// <summary>
292         /// Predicate to determine if this layout has been requested to re-layout.<br />
293         /// </summary>
294         public bool LayoutRequested
295         {
296             get
297             {
298                 return ( Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
299             }
300         }
301
302         /// <summary>
303         /// Get the measured width (without any measurement flags).<br />
304         /// This method should be used only during measurement and layout calculations.<br />
305         /// </summary>
306         public MeasuredSize MeasuredWidth{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
307
308         /// <summary>
309         /// Get the measured height (without any measurement flags).<br />
310         /// This method should be used only during measurement and layout calculations.<br />
311         /// </summary>
312         public MeasuredSize MeasuredHeight{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
313
314         /// <summary>
315         /// Get the measured width and state.<br />
316         /// This method should be used only during measurement and layout calculations.<br />
317         /// </summary>
318         public MeasuredSize MeasuredWidthAndState
319         {
320             get
321             {
322                 return MeasuredWidth; // Not bitmasking State unless proven to be required.
323             }
324         }
325
326
327         /// <summary>
328         /// Get the measured height and state.<br />
329         /// This method should be used only during measurement and layout calculations.<br />
330         /// </summary>
331         public MeasuredSize MeasuredHeightAndState
332         {
333             get
334             {
335                 return MeasuredHeight;  // Not bitmasking State unless proven to be required.
336             }
337         }
338
339         /// <summary>
340         /// Returns the suggested minimum width that the layout should use.<br />
341         /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
342         /// </summary>
343         public LayoutLength SuggestedMinimumWidth
344         {
345             get
346             {
347                 int naturalWidth = Owner.NaturalSize2D.Width;
348                 return new LayoutLength(Math.Max( MinimumWidth.AsDecimal(), naturalWidth ));
349             }
350         }
351
352         /// <summary>
353         /// Returns the suggested minimum height that the layout should use.<br />
354         /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
355         /// </summary>
356         public LayoutLength SuggestedMinimumHeight
357         {
358             get
359             {
360                 int naturalHeight = Owner.NaturalSize2D.Height;
361                 return new LayoutLength(Math.Max( MinimumHeight.AsDecimal(), naturalHeight ));
362             }
363         }
364
365         /// <summary>
366         /// Sets the minimum width of the layout.<br />
367         /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
368         /// layout constrains it with less available width).<br />
369         /// 1. if the owner's View.LayoutWidthSpecification has exact value, then that value overrides the minimum size.<br />
370         /// 2. If the owner's View.LayoutWidthSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's width is set based on the suggested minimum width. (@see GetSuggestedMinimumWidth()).<br />
371         /// 3. If the owner's View.LayoutWidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
372         /// </summary>
373         public LayoutLength MinimumWidth {get; set;}
374
375         /// <summary>
376         /// Sets the minimum height of the layout.<br />
377         /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
378         /// layout constrains it with less available height).<br />
379         /// 1. if the owner's View.LayoutHeightSpecification has exact value, then that value overrides the minimum size.<br />
380         /// 2. If the owner's View.LayoutHeightSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's height is set based on the suggested minimum height. (@see GetSuggestedMinimumHeight()).<br />
381         /// 3. If the owner's View.LayoutHeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
382         /// </summary>
383         public LayoutLength MinimumHeight {get; set;}
384
385         ///<summary>
386         /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
387         ///</summary>
388         /// <param name="size"> How big the layout wants to be.</param>
389         /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
390         /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
391         /// <returns> A measured size, which may indicate that it is too small. </returns>
392         protected MeasuredSize ResolveSizeAndState( LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState )
393         {
394             var specMode = measureSpecification.Mode;
395             LayoutLength specSize = measureSpecification.Size;
396             MeasuredSize result = new MeasuredSize( size, childMeasuredState);
397
398             switch( specMode )
399             {
400                 case MeasureSpecification.ModeType.AtMost:
401                 {
402                     if (specSize.AsRoundedValue() < size.AsRoundedValue())
403                     {
404                         result = new MeasuredSize( specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
405                     }
406                     break;
407                 }
408
409                 case MeasureSpecification.ModeType.Exactly:
410                 {
411                     result.Size = specSize;
412                     break;
413                 }
414
415                 case MeasureSpecification.ModeType.Unspecified:
416                 default:
417                 {
418                     break;
419                 }
420             }
421             return result;
422         }
423
424         /// <summary>
425         /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
426         /// </summary>
427         /// <param name="measuredWidth">The measured width of this layout.</param>
428         /// <param name="measuredHeight">The measured height of this layout.</param>
429         protected void SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
430         {
431             MeasuredWidth = measuredWidth;
432             MeasuredHeight = measuredHeight;
433             Flags = Flags | LayoutFlags.MeasuredDimensionSet;
434         }
435
436         /// <summary>
437         /// Measure the layout and its content to determine the measured width and the
438         /// measured height.<br />
439         /// The base class implementation of measure defaults to the background size,
440         /// unless a larger size is allowed by the MeasureSpec. Subclasses should
441         /// override to provide better measurements of their content.<br />
442         /// If this method is overridden, it is the subclass's responsibility to make sure the
443         /// measured height and width are at least the layout's minimum height and width.<br />
444         /// </summary>
445         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
446         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
447         protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
448         {
449             // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
450             SetMeasuredDimensions( GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec ),
451                                    GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec ) );
452         }
453
454         /// <summary>
455         /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
456         /// Derived classes with children should override this method and call Layout() on each of their children. <br />
457         /// </summary>
458         /// <param name="changed">This is a new size or position for this layout.</param>
459         /// <param name="left">Left position, relative to parent.</param>
460         /// <param name="top">Top position, relative to parent.</param>
461         /// <param name="right">Right position, relative to parent.</param>
462         /// <param name="bottom">Bottom position, relative to parent.</param>
463         protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
464         {
465         }
466
467         /// <summary>
468         /// Virtual method to inform derived classes when the layout size changed. <br />
469         /// </summary>
470         /// <param name="newSize">The new size of the layout.</param>
471         /// <param name="oldSize">The old size of the layout.</param>
472         protected virtual void OnSizeChanged(LayoutSize newSize, LayoutSize oldSize)
473         {
474         }
475
476         /// <summary>
477         /// Virtual method to allow derived classes to remove any children before it is removed from
478         /// its parent.
479         /// </summary>
480         public virtual void OnUnparent()
481         {
482         }
483
484         /// <summary>
485         /// Virtual method called when this Layout is attached to it's owner.
486         /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
487         /// </summary>
488         protected virtual void OnAttachedToOwner()
489         {
490         }
491
492         private bool SetFrame(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
493         {
494             bool changed = false;
495
496             if( _left != left || _right != right || _top != top || _bottom != bottom  )
497             {
498                 changed = true;
499             }
500
501             LayoutLength oldWidth = _right - _left;
502             LayoutLength oldHeight = _bottom - _top;
503             LayoutLength newWidth = right - left;
504             LayoutLength newHeight = bottom - top;
505             bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
506
507             _left = left;
508             _top = top;
509             _right = right;
510             _bottom = bottom;
511
512             // Set actual positions of View.
513             Owner.SetX(_left.AsRoundedValue());
514             Owner.SetY(_top.AsRoundedValue());
515             Owner.SetSize((int)newWidth.AsRoundedValue(), (int)newHeight.AsRoundedValue());
516
517             return changed;
518         }
519
520     }
521 }