[NUI] Support Layout property by ViewStyle
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Layouting / LinearLayout.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16
17 using System;
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using System.Collections.Generic;
21 using System.Linq;
22
23 namespace Tizen.NUI
24 {
25     /// <summary>
26     /// [Draft] This class implements a linear box layout, automatically handling right to left or left to right direction change.
27     /// </summary>
28     public class LinearLayout : LayoutGroup
29     {
30         private Alignment linearAlignment = Alignment.Top;
31
32         /// <summary>
33         /// [Draft] Enumeration for the direction in which the content is laid out
34         /// </summary>
35         /// <since_tizen> 6 </since_tizen>
36         public enum Orientation
37         {
38             /// <summary>
39             /// Horizontal (row)
40             /// </summary>
41             Horizontal,
42             /// <summary>
43             /// Vertical (column)
44             /// </summary>
45             Vertical
46         }
47
48         /// <summary>
49         /// [Draft] Enumeration for the alignment of the linear layout items
50         /// </summary>
51         /// <since_tizen> 6 </since_tizen>
52         [Obsolete("Deprecated in API9, will be removed in API11. Please use HorizontalAlignment and VerticalAlignment instead!")]
53         public enum Alignment
54         {
55             /// <summary>
56             /// At the left/right edge of the container (maps to LTR/RTL direction for horizontal orientation)
57             /// </summary>
58             Begin = 0x1,
59             /// <summary>
60             /// At the right/left edge of the container (maps to LTR/RTL direction for horizontal orientation)
61             /// </summary>
62             End = 0x2,
63             /// <summary>
64             /// At the horizontal center of the container
65             /// </summary>
66             CenterHorizontal = 0x4,
67             /// <summary>
68             /// At the top edge of the container
69             /// </summary>
70             Top = 0x8,
71             /// <summary>
72             /// At the bottom edge of the container
73             /// </summary>
74             Bottom = 0x10,
75             /// <summary>
76             /// At the vertical center of the container
77             /// </summary>
78             CenterVertical = 0x20,
79             /// <summary>
80             /// At the vertical and horizontal center of the container
81             /// </summary>
82             Center = 0x40
83         }
84
85         struct HeightAndWidthState
86         {
87             public MeasuredSize.StateType widthState;
88             public MeasuredSize.StateType heightState;
89
90             public HeightAndWidthState(MeasuredSize.StateType width, MeasuredSize.StateType height)
91             {
92                 widthState = width;
93                 heightState = height;
94             }
95         }
96
97         /// <summary>
98         /// [Draft] Get/Set the orientation in the layout
99         /// </summary>
100         /// <since_tizen> 6 </since_tizen>
101         public LinearLayout.Orientation LinearOrientation
102         {
103             get
104             {
105                 return linearOrientation;
106             }
107             set
108             {
109                 linearOrientation = value;
110                 RequestLayout();
111             }
112         }
113
114         /// <summary>
115         /// [Draft] Get/Set the padding between cells in the layout
116         /// </summary>
117         /// <since_tizen> 6 </since_tizen>
118         public Size2D CellPadding
119         {
120             get
121             {
122                 if (cellPadding == null)
123                 {
124                     cellPadding = new Size2D(0, 0);
125                 }
126
127                 return cellPadding;
128             }
129             set
130             {
131                 cellPadding = value;
132                 RequestLayout();
133             }
134         }
135
136
137         /// <summary>
138         /// [Draft] Get/Set the alignment in the layout
139         /// </summary>
140         /// <since_tizen> 6 </since_tizen>
141         [Obsolete("Deprecated in API9, will be removed in API11. Please use HorizontalAlignment and VerticalAlignment properties instead!")]
142         public LinearLayout.Alignment LinearAlignment
143         {
144             get
145             {
146                 return linearAlignment;
147             }
148
149             set
150             {
151                 if (linearAlignment == value)
152                 {
153                     return;
154                 }
155
156                 linearAlignment = value;
157
158                 switch (linearAlignment)
159                 {
160                     case Alignment.Begin:
161                         HorizontalAlignment = HorizontalAlignment.Begin;
162                         break;
163                     case Alignment.End:
164                         HorizontalAlignment = HorizontalAlignment.End;
165                         break;
166                     case Alignment.CenterHorizontal:
167                         HorizontalAlignment = HorizontalAlignment.Center;
168                         break;
169                     case Alignment.Top:
170                         VerticalAlignment = VerticalAlignment.Top;
171                         break;
172                     case Alignment.Bottom:
173                         VerticalAlignment = VerticalAlignment.Bottom;
174                         break;
175                     case Alignment.CenterVertical:
176                         VerticalAlignment = VerticalAlignment.Center;
177                         break;
178                     case Alignment.Center:
179                         HorizontalAlignment = HorizontalAlignment.Center;
180                         VerticalAlignment = VerticalAlignment.Center;
181                         break;
182                     default:
183                         break;
184                 }
185             }
186          }
187
188         /// <summary>
189         /// Get/Set the horizontal alignment in the layout
190         /// </summary>
191         /// <since_tizen> 9 </since_tizen>
192         public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Begin;
193
194         /// <summary>
195         /// Get/Set the vertical alignment in the layout
196         /// </summary>
197         /// <since_tizen> 9 </since_tizen>
198         public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Top;
199
200         private float totalLength = 0.0f;
201         private Size2D cellPadding = new Size2D(0, 0);
202         private Orientation linearOrientation = Orientation.Horizontal;
203
204         /// <summary>
205         /// [Draft] Constructor
206         /// </summary>
207         /// <since_tizen> 6 </since_tizen>
208         public LinearLayout()
209         {
210         }
211
212         /// <inheritdoc/>
213         [EditorBrowsable(EditorBrowsableState.Never)]
214         public override LayoutItem Clone()
215         {
216             var layout = new LinearLayout();
217
218             foreach (var prop in layout.GetType().GetProperties())
219             {
220                 if (prop.GetSetMethod() != null)
221                 {
222                     prop.SetValue(layout, this.GetType().GetProperty(prop.Name).GetValue(this));
223                 }
224             }
225
226             return layout;
227         }
228
229         /// <summary>
230         /// Measure the layout and its content to determine the measured width and the measured height.
231         /// </summary>
232         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
233         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
234         /// <since_tizen> 6 </since_tizen>
235         protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
236         {
237             if (linearOrientation == Orientation.Horizontal)
238             {
239                 MeasureHorizontal(widthMeasureSpec, heightMeasureSpec);
240             }
241             else
242             {
243                 MeasureVertical(widthMeasureSpec, heightMeasureSpec);
244             }
245         }
246
247         /// <summary>
248         /// Layout should assign a size and position to each of its children.<br />
249         /// </summary>
250         /// <param name="changed">This is a new size or position for this layout.</param>
251         /// <param name="left">Left position, relative to parent.</param>
252         /// <param name="top"> Top position, relative to parent.</param>
253         /// <param name="right">Right position, relative to parent.</param>
254         /// <param name="bottom">Bottom position, relative to parent.</param>
255         /// <since_tizen> 6 </since_tizen>
256         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
257         {
258             if (linearOrientation == Orientation.Horizontal)
259             {
260                 LayoutHorizontal(left, top, right, bottom);
261             }
262             else
263             {
264                 LayoutVertical(left, top, right, bottom);
265             }
266         }
267
268
269         private void MeasureWeightedChild(LayoutItem childLayout, float totalWeightLength, float totalWeight, float childWeight,
270                                            MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec,
271                                            HeightAndWidthState childState, Orientation orientation)
272         {
273             bool horizontal = false;
274             if (orientation == Orientation.Horizontal)
275             {
276                 horizontal = true;
277             }
278
279             float childsShare = totalWeightLength * (childWeight / totalWeight);
280             float desiredWidth = childLayout.Owner.WidthSpecification;
281             float desiredHeight = childLayout.Owner.HeightSpecification;
282
283             MeasureSpecification childWidthMeasureSpec;
284             MeasureSpecification childHeightMeasureSpec;
285
286             if (horizontal)
287             {
288                 childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childsShare - (childLayout.Margin.Start + childLayout.Margin.End)), MeasureSpecification.ModeType.Exactly);
289
290                 childHeightMeasureSpec = GetChildMeasureSpecification(
291                                             new MeasureSpecification(
292                                                 new LayoutLength(heightMeasureSpec.Size - (childLayout.Margin.Top + childLayout.Margin.Bottom)),
293                                                 heightMeasureSpec.Mode),
294                                             new LayoutLength(Padding.Top + Padding.Bottom),
295                                             new LayoutLength(desiredHeight));
296             }
297             else // vertical
298             {
299                 childWidthMeasureSpec = GetChildMeasureSpecification(
300                                             new MeasureSpecification(
301                                                 new LayoutLength(widthMeasureSpec.Size - (childLayout.Margin.Start + childLayout.Margin.End)),
302                                                 widthMeasureSpec.Mode),
303                                             new LayoutLength(Padding.Start + Padding.End),
304                                             new LayoutLength(desiredWidth));
305
306                 childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childsShare - (childLayout.Margin.Top + childLayout.Margin.Bottom)), MeasureSpecification.ModeType.Exactly);
307             }
308
309             childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
310
311             // Child may now not fit in horizontal dimension.
312             if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
313             {
314                 childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
315             }
316
317             // Child may now not fit in vertical dimension.
318             if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
319             {
320                 childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
321             }
322         }
323
324         private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
325         {
326             var widthMode = widthMeasureSpec.Mode;
327             var heightMode = heightMeasureSpec.Mode;
328             bool isWidthExactly = (widthMode == MeasureSpecification.ModeType.Exactly);
329             bool isHeightExactly = (heightMode == MeasureSpecification.ModeType.Exactly);
330             float maxHeight = 0.0f;
331             float totalWeight = 0.0f;
332             int childrenCount = IterateLayoutChildren().Count();
333
334             // Child layout, which wants to match its width to its parent's remaining width, is either following 1 or 2.
335             // 1. Child layout whose Owner.WidthSpecification is LayoutParamPolicies.MatchParent.
336             // 2. Child layout whose Owner.WidthSpecification is 0 and Owner.Weight is greater than 0.
337             // The number of child layout which wants to match its width to its parent's remaining width.
338             int childrenMatchParentCount = 0;
339
340             // Reset measure variable
341             totalLength = 0.0f;
342
343             HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
344                                                                      MeasuredSize.StateType.MeasuredSizeOK);
345
346             // 1ST PHASE:
347             //
348             // We measure all children whose width specification policy is WrapContent without weight.
349             // After 1st phase, remaining width of parent is accumulated to calculate width of children
350             // whose width specification policy is MatchParent.
351             foreach (var childLayout in LayoutChildren)
352             {
353                 if (!childLayout.SetPositionByLayout)
354                 {
355                     continue;
356                 }
357                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
358                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
359                 float childWeight = childLayout.Owner.Weight;
360                 Extents childMargin = childLayout.Margin;
361                 float childMarginWidth = childMargin.Start + childMargin.End;
362                 bool useRemainingWidth = (childDesiredWidth == 0) && (childWeight > 0);
363
364                 if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
365                 {
366                     totalWeight += childWeight;
367                     childrenMatchParentCount++;
368                 }
369
370                 // MatchParent child layout's margin is not added to totalLength.
371                 // Consequently, MatchParent child layout's margin is added to remaining size,
372                 // so the margin is not shared with other MatchParent child layouts.
373                 // e.g.
374                 // LinearLayout has size 100.
375                 // Child layout1 is MatchParent and its margin is 20. (This margin is not ad
376                 // Child layout2 is MatchParent and its margin is 0.
377                 // Then, child layout1's size is 30 and child layout2's size is 50.
378                 if ((childDesiredWidth == LayoutParamPolicies.WrapContent) || ((childDesiredWidth >= 0) && (!useRemainingWidth)))
379                 {
380                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
381
382                     float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
383
384                     if (childMeasuredWidth < 0)
385                     {
386                         totalLength = Math.Max(totalLength, totalLength + childMarginWidth);
387                     }
388                     else
389                     {
390                         totalLength = Math.Max(totalLength, totalLength + childMeasuredWidth + childMarginWidth);
391                     }
392                 }
393
394                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
395                 {
396                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
397                 }
398                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
399                 {
400                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
401                 }
402             } // 1ST PHASE foreach
403
404             totalLength = Math.Max(totalLength, totalLength + CellPadding.Width * (childrenCount - 1) + Padding.Start + Padding.End);
405             float widthSize = Math.Max(totalLength, SuggestedMinimumWidth.AsDecimal());
406             MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
407             widthSize = widthSizeAndState.Size.AsDecimal();
408
409             float remainingWidth = widthSize - totalLength;
410             float totalWeightLength = 0.0f;
411
412             // Up to now, only WrapContent children's sizes are added to the totalLength.
413             // Since the totalLength is used in OnLayout as the sum of all children's sizes,
414             // the layout size is assigned to the totalLength if MatchParent child exists.
415             if (childrenMatchParentCount > 0)
416             {
417                 totalLength = widthSize;
418             }
419
420             // 2ND PHASE:
421             //
422             // We measure all children whose width specification policy is MatchParent without weight.
423             // After 2nd phase, all children's widths are calculated without considering weight.
424             // And the widths of all weighted children are accumulated to calculate weighted width.
425             foreach (var childLayout in LayoutChildren)
426             {
427                 if (!childLayout.SetPositionByLayout)
428                 {
429                     continue;
430                 }
431                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
432                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
433                 float childWeight = childLayout.Owner.Weight;
434                 Extents childMargin = childLayout.Margin;
435                 bool useRemainingWidth = (childDesiredWidth == 0) && (childWeight > 0);
436                 bool needToMeasure = false;
437
438                 if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
439                 {
440                     if (isHeightExactly)
441                     {
442                         needToMeasure = true;
443                     }
444                     // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
445                     // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
446                     // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
447                     //
448                     // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
449                     else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
450                     {
451                         if (childDesiredHeight == LayoutParamPolicies.MatchParent)
452                         {
453                             Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is MatchParent!");
454                         }
455                         else
456                         {
457                             Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is 0 with positive weight!");
458                         }
459                     }
460                 }
461
462                 if (remainingWidth > 0)
463                 {
464                     if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
465                     {
466                         if (isWidthExactly)
467                         {
468                             // In MeasureChildWithMargins(), it is assumed that widthMeasureSpec includes Padding.Start and Padding.End.
469                             // Therefore, Padding.Start and Padding.End are added to widthMeasureSpec.Size before it is passed to MeasureChildWithMargins().
470                             widthMeasureSpec.SetSize(new LayoutLength((int)(remainingWidth / childrenMatchParentCount) + Padding.Start + Padding.End));
471                             needToMeasure = true;
472                         }
473                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
474                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
475                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
476                         //
477                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
478                         else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
479                         {
480                             if (childDesiredWidth == LayoutParamPolicies.MatchParent)
481                             {
482                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is MatchParent!");
483                             }
484                             else
485                             {
486                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is 0 with positive weight!");
487                             }
488                         }
489                     }
490                 }
491
492                 if (needToMeasure == true)
493                 {
494                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
495                 }
496
497                 if ((childWeight > 0) && ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (childDesiredWidth == 0)))
498                 {
499                     float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
500
501                     if (childMeasuredWidth < 0)
502                     {
503                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMargin.Start + childMargin.End);
504                     }
505                     else
506                     {
507                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMeasuredWidth + childMargin.Start + childMargin.End);
508                     }
509                 }
510             } // 2ND PHASE foreach
511
512             // 3RD PHASE:
513             //
514             // We measure all weighted children whose owner has weight greater than 0.
515             // After 3rd phase, all weighted children has width which is proportional to their weights
516             // in remaining width of parent.
517             if (totalWeight > 0.0f)
518             {
519                 foreach (LayoutItem childLayout in LayoutChildren)
520                 {
521                     if (!childLayout.SetPositionByLayout)
522                     {
523                         continue;
524                     }
525                     int childDesiredWidth = childLayout.Owner.WidthSpecification;
526                     float childWeight = childLayout.Owner.Weight;
527
528                     if ((childWeight > 0) && ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (childDesiredWidth == 0)))
529                     {
530                         if (isWidthExactly)
531                         {
532                             MeasureWeightedChild(childLayout, totalWeightLength, totalWeight, childWeight,
533                                                  widthMeasureSpec, heightMeasureSpec, childState,
534                                                  Orientation.Horizontal);
535                         }
536                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
537                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
538                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
539                         //
540                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
541                         else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
542                         {
543                             if (childDesiredWidth == LayoutParamPolicies.MatchParent)
544                             {
545                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is MatchParent!");
546                             }
547                             else
548                             {
549                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is 0 with positive weight!");
550                             }
551                         }
552                     }
553                 }
554             }
555             // 3RD PHASE foreach
556
557             // Decide the max height among children.
558             foreach (var childLayout in LayoutChildren)
559             {
560                 if (!childLayout.SetPositionByLayout)
561                 {
562                     continue;
563                 }
564
565                 Extents childMargin = childLayout.Margin;
566                 float childMarginHeight = childMargin.Top + childMargin.Bottom;
567                 float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
568
569                 if (childMeasuredHeight < 0)
570                 {
571                     maxHeight = Math.Max(maxHeight, childMarginHeight);
572                 }
573                 else
574                 {
575                     maxHeight = Math.Max(maxHeight, childMeasuredHeight + childMarginHeight);
576                 }
577             }
578
579             // Decide the max height compared with paddings and its suggested height.
580             maxHeight = Math.Max(maxHeight, maxHeight + (Padding.Top + Padding.Bottom));
581             maxHeight = Math.Max(maxHeight, SuggestedMinimumHeight.AsRoundedValue());
582
583             widthSizeAndState.State = childState.widthState;
584
585             SetMeasuredDimensions(widthSizeAndState,
586                                   ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childState.heightState));
587         } // MeasureHorizontal
588
589         private void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
590         {
591             var widthMode = widthMeasureSpec.Mode;
592             var heightMode = heightMeasureSpec.Mode;
593             bool isWidthExactly = (widthMode == MeasureSpecification.ModeType.Exactly);
594             bool isHeightExactly = (heightMode == MeasureSpecification.ModeType.Exactly);
595             float maxWidth = 0.0f;
596             float totalWeight = 0.0f;
597             int childrenCount = IterateLayoutChildren().Count();
598
599             // Child layout, which wants to match its height to its parent's remaining height, is either following 1 or 2.
600             // 1. Child layout whose Owner.HeightSpecification is LayoutParamPolicies.MatchParent.
601             // 2. Child layout whose Owner.HeightSpecification is 0 and Owner.Weight is greater than 0.
602             // The number of child layout which wants to match its height to its parent's remaining height.
603             int childrenMatchParentCount = 0;
604
605             // Reset measure variable
606             totalLength = 0.0f;
607
608             HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
609                                                                      MeasuredSize.StateType.MeasuredSizeOK);
610
611             // 1ST PHASE:
612             //
613             // We measure all children whose height specification policy is WrapContent without weight.
614             // After 1st phase, remaining height of parent is accumulated to calculate height of children
615             // whose height specification policy is MatchParent.
616             foreach (var childLayout in LayoutChildren)
617             {
618                 if (!childLayout.SetPositionByLayout)
619                 {
620                     continue;
621                 }
622                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
623                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
624                 float childWeight = childLayout.Owner.Weight;
625                 Extents childMargin = childLayout.Margin;
626                 float childMarginHeight = childMargin.Top + childMargin.Bottom;
627                 bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0);
628
629                 if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
630                 {
631                     totalWeight += childWeight;
632                     childrenMatchParentCount++;
633                 }
634
635                 // MatchParent child layout's margin is not added to totalLength.
636                 // Consequently, MatchParent child layout's margin is added to remaining size,
637                 // so the margin is not shared with other MatchParent child layouts.
638                 // e.g.
639                 // LinearLayout has size 100.
640                 // Child layout1 is MatchParent and its margin is 20. (This margin is not ad
641                 // Child layout2 is MatchParent and its margin is 0.
642                 // Then, child layout1's size is 30 and child layout2's size is 50.
643                 if ((childDesiredHeight == LayoutParamPolicies.WrapContent) || ((childDesiredHeight > 0) && (!useRemainingHeight)))
644                 {
645                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
646
647                     float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
648
649                     if (childMeasuredHeight < 0)
650                     {
651                         totalLength = Math.Max(totalLength, totalLength + childMarginHeight);
652                     }
653                     else
654                     {
655                         totalLength = Math.Max(totalLength, totalLength + childMeasuredHeight + childMarginHeight);
656                     }
657                 }
658
659                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
660                 {
661                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
662                 }
663                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
664                 {
665                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
666                 }
667             } // 1ST PHASE foreach
668
669             totalLength = Math.Max(totalLength, totalLength + CellPadding.Height * (childrenCount - 1) + Padding.Top + Padding.Bottom);
670             float heightSize = Math.Max(totalLength, SuggestedMinimumHeight.AsDecimal());
671             MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
672             heightSize = heightSizeAndState.Size.AsDecimal();
673
674             float remainingHeight = heightSize - totalLength;
675             float totalWeightLength = 0.0f;
676
677             // Up to now, only WrapContent children's sizes are added to the totalLength.
678             // Since the totalLength is used in OnLayout as the sum of all children's sizes,
679             // the layout size is assigned to the totalLength if MatchParent child exists.
680             if (childrenMatchParentCount > 0)
681             {
682                 totalLength = heightSize;
683             }
684
685             // 2ND PHASE:
686             //
687             // We measure all children whose height specification policy is MatchParent without weight.
688             // After 2nd phase, all children's heights are calculated without considering weight.
689             // And the heights of all weighted children are accumulated to calculate weighted height.
690             foreach (var childLayout in LayoutChildren)
691             {
692                 if (!childLayout.SetPositionByLayout)
693                 {
694                     continue;
695                 }
696                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
697                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
698                 float childWeight = childLayout.Owner.Weight;
699                 Extents childMargin = childLayout.Margin;
700                 bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0);
701                 bool needToMeasure = false;
702
703                 if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
704                 {
705                     if (isWidthExactly)
706                     {
707                         needToMeasure = true;
708                     }
709                     // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
710                     // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
711                     // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
712                     //
713                     // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
714                     else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
715                     {
716                         if (childDesiredWidth == LayoutParamPolicies.MatchParent)
717                         {
718                             Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is MatchParent!");
719                         }
720                         else
721                         {
722                             Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s WidthSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s WidthSpecification is 0 with positive weight!");
723                         }
724                     }
725                 }
726
727                 if (remainingHeight > 0)
728                 {
729                     if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
730                     {
731                         if (isHeightExactly)
732                         {
733                             // In MeasureChildWithMargins(), it is assumed that heightMeasureSpec includes Padding.Top and Padding.Bottom.
734                             // Therefore, Padding.Top and Padding.Bottom are added to heightMeasureSpec.Size before it is passed to MeasureChildWithMargins().
735                             heightMeasureSpec.SetSize(new LayoutLength((int)(remainingHeight / childrenMatchParentCount) + Padding.Top + Padding.Bottom));
736                             needToMeasure = true;
737                         }
738                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
739                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
740                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
741                         //
742                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
743                         else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
744                         {
745                             if (childDesiredHeight == LayoutParamPolicies.MatchParent)
746                             {
747                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is MatchParent!");
748                             }
749                             else
750                             {
751                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is 0 with positive weight!");
752                             }
753                         }
754                     }
755                 }
756
757                 if (needToMeasure == true)
758                 {
759                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
760                 }
761
762                 if ((childWeight > 0) && ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (childDesiredHeight == 0)))
763                 {
764                     float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
765
766                     if (childMeasuredHeight < 0)
767                     {
768                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMargin.Top + childMargin.Bottom);
769                     }
770                     else
771                     {
772                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMeasuredHeight + childMargin.Top + childMargin.Bottom);
773                     }
774                 }
775             } // 2ND PHASE foreach
776
777             // 3RD PHASE:
778             //
779             // We measure all weighted children whose owner has weight greater than 0.
780             // After 3rd phase, all weighted children has height which is proportional to their weights
781             // in remaining height of parent.
782             if (totalWeight > 0)
783             {
784                 foreach (var childLayout in LayoutChildren)
785                 {
786                     if (!childLayout.SetPositionByLayout)
787                     {
788                         continue;
789                     }
790                     int childDesiredHeight = childLayout.Owner.HeightSpecification;
791                     float childWeight = childLayout.Owner.Weight;
792
793                     if ((childWeight > 0) && ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (childDesiredHeight == 0)))
794                     {
795                         if (isHeightExactly)
796                         {
797                             MeasureWeightedChild(childLayout, totalWeightLength, totalWeight, childWeight,
798                                                  widthMeasureSpec, heightMeasureSpec, childState,
799                                                  Orientation.Vertical);
800                         }
801                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
802                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
803                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
804                         //
805                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
806                         else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
807                         {
808                             if (childDesiredHeight == LayoutParamPolicies.MatchParent)
809                             {
810                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is MatchParent!");
811                             }
812                             else
813                             {
814                                 Tizen.Log.Error("NUI", "There is a recursive reference! Parent layout(Owner: " + Owner + ")'s HeightSpecification is WrapContent and child layout(Owner: " + childLayout.Owner + ")'s HeightSpecification is 0 with positive weight!");
815                             }
816                         }
817                     }
818                 }
819             } // 3RD PHASE foreach
820
821             // Decide the max width among children.
822             foreach (var childLayout in LayoutChildren)
823             {
824                 if (!childLayout.SetPositionByLayout)
825                 {
826                     continue;
827                 }
828
829                 Extents childMargin = childLayout.Margin;
830                 float childMarginWidth = childMargin.Start + childMargin.End;
831                 float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
832
833                 if (childMeasuredWidth < 0)
834                 {
835                     maxWidth = Math.Max(maxWidth, childMarginWidth);
836                 }
837                 else
838                 {
839                     maxWidth = Math.Max(maxWidth, childMeasuredWidth + childMarginWidth);
840                 }
841             }
842
843             // Decide the max width compared with paddings and its suggested width.
844             maxWidth = Math.Max(maxWidth, maxWidth + (Padding.Start + Padding.End));
845             maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue());
846
847             heightSizeAndState.State = childState.heightState;
848
849             SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState),
850                                   heightSizeAndState);
851         } // MeasureVertical
852
853         private void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
854         {
855             bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
856
857             LayoutLength childTop = new LayoutLength(Padding.Top);
858             LayoutLength childLeft = new LayoutLength(Padding.Start);
859
860             // Where bottom of child should go
861             LayoutLength height = new LayoutLength(bottom - top);
862
863             // Space available for child
864             LayoutLength childSpace = new LayoutLength(height - Padding.Top - Padding.Bottom);
865
866             var LinearChildren = IterateLayoutChildren();
867             int count = LinearChildren.Count<LayoutItem>();
868
869             switch (HorizontalAlignment)
870             {
871                 case HorizontalAlignment.End:
872                     // totalLength contains the padding already
873                     // In case of RTL map END alignment to the left edge
874                     if (isLayoutRtl)
875                     {
876                         childLeft = new LayoutLength(Padding.Start);
877                     }
878                     else
879                     {
880                         childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - totalLength);
881                     }
882                     break;
883                 case HorizontalAlignment.Center:
884                     // totalLength contains the padding already
885                     childLeft = new LayoutLength(Padding.Start + (right.AsDecimal() - left.AsDecimal() - totalLength) / 2.0f);
886                     break;
887                 case HorizontalAlignment.Begin: // FALL THROUGH (default)
888                 default:
889                     // totalLength contains the padding already
890                     // In case of RTL map BEGIN alignment to the right edge
891                     if (isLayoutRtl)
892                     {
893                         childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - totalLength);
894                     }
895                     else
896                     {
897                         childLeft = new LayoutLength(Padding.Start);
898                     }
899                     break;
900             }
901
902             int start = 0;
903             int dir = 1;
904
905             // In case of RTL, start drawing from the last child.
906             if (isLayoutRtl)
907             {
908                 start = count - 1;
909                 dir = -1;
910             }
911
912             for (int i = 0; i < count; i++)
913             {
914                 int childIndex = start + dir * i;
915                 // Get a reference to the childLayout at the given index
916                 LayoutItem childLayout = LinearChildren.ElementAt<LayoutItem>(i);
917
918                 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
919                 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
920                 Extents childMargin = childLayout.Margin;
921
922                 switch (VerticalAlignment)
923                 {
924                     case VerticalAlignment.Bottom:
925                         childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom);
926                         break;
927                     case VerticalAlignment.Center:
928                         childTop = new LayoutLength(Padding.Top + ((childSpace - childHeight).AsDecimal() / 2.0f) + childMargin.Top - childMargin.Bottom);
929                         break;
930                     case VerticalAlignment.Top: // FALLTHROUGH default
931                     default:
932                         childTop = new LayoutLength(Padding.Top + childMargin.Top);
933                         break;
934                 }
935                 childLeft += (isLayoutRtl ? childMargin.End : childMargin.Start);
936                 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
937                 childLeft += childWidth + (isLayoutRtl ? childMargin.Start : childMargin.End) + ((i < count - 1) ? CellPadding.Width : 0);
938             }
939         } // LayoutHorizontally
940
941         private void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
942         {
943             LayoutLength childTop = new LayoutLength(Padding.Top);
944             LayoutLength childLeft = new LayoutLength(Padding.Start);
945
946             // Where end of child should go
947             LayoutLength width = new LayoutLength(right - left);
948
949             // Space available for child
950             LayoutLength childSpace = new LayoutLength(width - Padding.Start - Padding.End);
951
952             var LinearChildren = IterateLayoutChildren();
953             int count = LinearChildren.Count<LayoutItem>();
954
955             switch (VerticalAlignment)
956             {
957                 case VerticalAlignment.Bottom:
958                     // totalLength contains the padding already
959                     childTop = new LayoutLength(Padding.Top + bottom.AsDecimal() - top.AsDecimal() - totalLength);
960                     break;
961                 case VerticalAlignment.Center:
962                     // totalLength contains the padding already
963                     childTop = new LayoutLength(Padding.Top + (bottom.AsDecimal() - top.AsDecimal() - totalLength) / 2.0f);
964                     break;
965                 case VerticalAlignment.Top:  // FALL THROUGH (default)
966                 default:
967                     // totalLength contains the padding already
968                     childTop = new LayoutLength(Padding.Top);
969                     break;
970             }
971
972             for (int i = 0; i < count; i++)
973             {
974                 LayoutItem childLayout = LinearChildren.ElementAt<LayoutItem>(i);
975
976                 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
977                 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
978                 Extents childMargin = childLayout.Margin;
979
980                 childTop += childMargin.Top;
981                 switch (HorizontalAlignment)
982                 {
983                     case HorizontalAlignment.Begin:
984                     default:
985                         {
986                             childLeft = new LayoutLength(Padding.Start + childMargin.Start);
987                             break;
988                         }
989                     case HorizontalAlignment.End:
990                         {
991                             childLeft = new LayoutLength(width - Padding.End - childWidth - childMargin.End);
992                             break;
993                         }
994                     case HorizontalAlignment.Center:
995                         {
996                             childLeft = new LayoutLength(Padding.Start + ((childSpace - childWidth).AsDecimal() / 2.0f) + childMargin.Start - childMargin.End);
997                             break;
998                         }
999                 }
1000                 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
1001                 childTop += childHeight + childMargin.Bottom + ((i < count - 1) ? CellPadding.Height : 0);
1002             }
1003         } // LayoutVertical
1004
1005         private void ForceUniformHeight(MeasureSpecification widthMeasureSpec)
1006         {
1007             // Pretend that the linear layout has an exact size. This is the measured height of
1008             // ourselves. The measured height should be the max height of the children, changed
1009             // to accommodate the heightMeasureSpec from the parent
1010             MeasureSpecification uniformMeasureSpec = new MeasureSpecification(MeasuredHeight.Size, MeasureSpecification.ModeType.Exactly);
1011             foreach (var childLayout in LayoutChildren)
1012             {
1013                 if (!childLayout.SetPositionByLayout)
1014                 {
1015                     continue;
1016                 }
1017                 int desiredChildHeight = childLayout.Owner.HeightSpecification;
1018                 int desiredChildWidth = childLayout.Owner.WidthSpecification;
1019
1020                 if (desiredChildHeight == LayoutParamPolicies.MatchParent)
1021                 {
1022                     // Temporarily force children to reuse their original measured width
1023                     int originalWidth = desiredChildWidth;
1024                     childLayout.Owner.WidthSpecification = (int)childLayout.MeasuredWidth.Size.AsRoundedValue();
1025                     // Remeasure with new dimensions
1026                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0),
1027                                              uniformMeasureSpec, new LayoutLength(0));
1028                     // Restore width specification
1029                     childLayout.Owner.WidthSpecification = originalWidth;
1030                 }
1031             }
1032         }
1033     } //LinearLayout
1034 } // namespace