[NUI] Fix WrapContent View's LinearLayout size calculation
[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         /// <summary>
213         /// Measure the layout and its content to determine the measured width and the measured height.
214         /// </summary>
215         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
216         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
217         /// <since_tizen> 6 </since_tizen>
218         protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
219         {
220             if (linearOrientation == Orientation.Horizontal)
221             {
222                 MeasureHorizontal(widthMeasureSpec, heightMeasureSpec);
223             }
224             else
225             {
226                 MeasureVertical(widthMeasureSpec, heightMeasureSpec);
227             }
228         }
229
230         /// <summary>
231         /// Layout should assign a size and position to each of its children.<br />
232         /// </summary>
233         /// <param name="changed">This is a new size or position for this layout.</param>
234         /// <param name="left">Left position, relative to parent.</param>
235         /// <param name="top"> Top position, relative to parent.</param>
236         /// <param name="right">Right position, relative to parent.</param>
237         /// <param name="bottom">Bottom position, relative to parent.</param>
238         /// <since_tizen> 6 </since_tizen>
239         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
240         {
241             if (linearOrientation == Orientation.Horizontal)
242             {
243                 LayoutHorizontal(left, top, right, bottom);
244             }
245             else
246             {
247                 LayoutVertical(left, top, right, bottom);
248             }
249         }
250
251
252         private void MeasureWeightedChild(LayoutItem childLayout, float totalWeightLength, float totalWeight, float childWeight,
253                                            MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec,
254                                            HeightAndWidthState childState, Orientation orientation)
255         {
256             bool horizontal = false;
257             if (orientation == Orientation.Horizontal)
258             {
259                 horizontal = true;
260             }
261
262             float childsShare = totalWeightLength * (childWeight / totalWeight);
263             float desiredWidth = childLayout.Owner.WidthSpecification;
264             float desiredHeight = childLayout.Owner.HeightSpecification;
265
266             MeasureSpecification childWidthMeasureSpec;
267             MeasureSpecification childHeightMeasureSpec;
268
269             if (horizontal)
270             {
271                 childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childsShare - (childLayout.Margin.Start + childLayout.Margin.End)), MeasureSpecification.ModeType.Exactly);
272
273                 childHeightMeasureSpec = GetChildMeasureSpecification(
274                                             new MeasureSpecification(
275                                                 new LayoutLength(heightMeasureSpec.Size - (childLayout.Margin.Top + childLayout.Margin.Bottom)),
276                                                 heightMeasureSpec.Mode),
277                                             new LayoutLength(Padding.Top + Padding.Bottom),
278                                             new LayoutLength(desiredHeight));
279             }
280             else // vertical
281             {
282                 childWidthMeasureSpec = GetChildMeasureSpecification(
283                                             new MeasureSpecification(
284                                                 new LayoutLength(widthMeasureSpec.Size - (childLayout.Margin.Start + childLayout.Margin.End)),
285                                                 widthMeasureSpec.Mode),
286                                             new LayoutLength(Padding.Start + Padding.End),
287                                             new LayoutLength(desiredWidth));
288
289                 childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childsShare - (childLayout.Margin.Top + childLayout.Margin.Bottom)), MeasureSpecification.ModeType.Exactly);
290             }
291
292             childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
293
294             // Child may now not fit in horizontal dimension.
295             if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
296             {
297                 childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
298             }
299
300             // Child may now not fit in vertical dimension.
301             if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
302             {
303                 childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
304             }
305         }
306
307         private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
308         {
309             var widthMode = widthMeasureSpec.Mode;
310             var heightMode = heightMeasureSpec.Mode;
311             bool isWidthExactly = (widthMode == MeasureSpecification.ModeType.Exactly);
312             bool isHeightExactly = (heightMode == MeasureSpecification.ModeType.Exactly);
313             float maxHeight = 0.0f;
314             float totalWeight = 0.0f;
315             int childrenCount = IterateLayoutChildren().Count();
316
317             // Child layout, which wants to match its width to its parent's remaining width, is either following 1 or 2.
318             // 1. Child layout whose Owner.WidthSpecification is LayoutParamPolicies.MatchParent.
319             // 2. Child layout whose Owner.WidthSpecification is 0 and Owner.Weight is greater than 0.
320             // The number of child layout which wants to match its width to its parent's remaining width.
321             int childrenMatchParentCount = 0;
322
323             // Reset measure variable
324             totalLength = 0.0f;
325
326             HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
327                                                                      MeasuredSize.StateType.MeasuredSizeOK);
328
329             // 1ST PHASE:
330             //
331             // We measure all children whose width specification policy is WrapContent without weight.
332             // After 1st phase, remaining width of parent is accumulated to calculate width of children
333             // whose width specification policy is MatchParent.
334             foreach (var childLayout in LayoutChildren)
335             {
336                 if (!childLayout.SetPositionByLayout)
337                 {
338                     continue;
339                 }
340                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
341                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
342                 float childWeight = childLayout.Owner.Weight;
343                 Extents childMargin = childLayout.Margin;
344                 float childMarginWidth = childMargin.Start + childMargin.End;
345                 float childMarginHeight = childMargin.Top + childMargin.Bottom;
346                 bool useRemainingWidth = (childDesiredWidth == 0) && (childWeight > 0);
347
348                 if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
349                 {
350                     totalWeight += childWeight;
351                     childrenMatchParentCount++;
352                 }
353
354                 // MatchParent child layout's margin is not added to totalLength.
355                 // Consequently, MatchParent child layout's margin is added to remaining size,
356                 // so the margin is not shared with other MatchParent child layouts.
357                 // e.g.
358                 // LinearLayout has size 100.
359                 // Child layout1 is MatchParent and its margin is 20. (This margin is not ad
360                 // Child layout2 is MatchParent and its margin is 0.
361                 // Then, child layout1's size is 30 and child layout2's size is 50.
362                 if ((childDesiredWidth == LayoutParamPolicies.WrapContent) || ((childDesiredWidth >= 0) && (!useRemainingWidth)))
363                 {
364                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
365
366                     float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
367
368                     if (childMeasuredWidth < 0)
369                     {
370                         totalLength = Math.Max(totalLength, totalLength + childMarginWidth);
371                     }
372                     else
373                     {
374                         totalLength = Math.Max(totalLength, totalLength + childMeasuredWidth + childMarginWidth);
375                     }
376                 }
377
378                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
379                 {
380                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
381                 }
382                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
383                 {
384                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
385                 }
386
387                 float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
388                 if (childMeasuredHeight < 0)
389                 {
390                     maxHeight = Math.Max(maxHeight, childMarginHeight);
391                 }
392                 else
393                 {
394                     maxHeight = Math.Max(maxHeight, childMeasuredHeight + childMarginHeight);
395                 }
396             } // 1ST PHASE foreach
397
398             totalLength = Math.Max(totalLength, totalLength + CellPadding.Width * (childrenCount - 1) + Padding.Start + Padding.End);
399             float widthSize = Math.Max(totalLength, SuggestedMinimumWidth.AsDecimal());
400             MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
401             widthSize = widthSizeAndState.Size.AsDecimal();
402
403             float remainingWidth = widthSize - totalLength;
404             float totalWeightLength = 0.0f;
405
406             // Up to now, only WrapContent children's sizes are added to the totalLength.
407             // Since the totalLength is used in OnLayout as the sum of all children's sizes,
408             // the layout size is assigned to the totalLength if MatchParent child exists.
409             if (childrenMatchParentCount > 0)
410             {
411                 totalLength = widthSize;
412             }
413
414             // 2ND PHASE:
415             //
416             // We measure all children whose width specification policy is MatchParent without weight.
417             // After 2nd phase, all children's widths are calculated without considering weight.
418             // And the widths of all weighted children are accumulated to calculate weighted width.
419             foreach (var childLayout in LayoutChildren)
420             {
421                 if (!childLayout.SetPositionByLayout)
422                 {
423                     continue;
424                 }
425                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
426                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
427                 float childWeight = childLayout.Owner.Weight;
428                 Extents childMargin = childLayout.Margin;
429                 float childMarginHeight = childMargin.Top + childMargin.Bottom;
430                 bool useRemainingWidth = (childDesiredWidth == 0) && (childWeight > 0);
431                 bool needToMeasure = false;
432
433                 if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
434                 {
435                     if (isHeightExactly)
436                     {
437                         needToMeasure = true;
438                     }
439                     // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
440                     // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
441                     // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
442                     //
443                     // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
444                     else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
445                     {
446                         if (childDesiredHeight == LayoutParamPolicies.MatchParent)
447                         {
448                             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!");
449                         }
450                         else
451                         {
452                             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!");
453                         }
454                     }
455                 }
456
457                 if (remainingWidth > 0)
458                 {
459                     if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingWidth))
460                     {
461                         if (isWidthExactly)
462                         {
463                             // In MeasureChildWithMargins(), it is assumed that widthMeasureSpec includes Padding.Start and Padding.End.
464                             // Therefore, Padding.Start and Padding.End are added to widthMeasureSpec.Size before it is passed to MeasureChildWithMargins().
465                             widthMeasureSpec.SetSize(new LayoutLength((int)(remainingWidth / childrenMatchParentCount) + Padding.Start + Padding.End));
466                             needToMeasure = true;
467                         }
468                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
469                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
470                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
471                         //
472                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
473                         else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
474                         {
475                             if (childDesiredWidth == LayoutParamPolicies.MatchParent)
476                             {
477                                 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!");
478                             }
479                             else
480                             {
481                                 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!");
482                             }
483                         }
484                     }
485                 }
486
487                 if (needToMeasure == true)
488                 {
489                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
490
491                     float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
492                     if (childMeasuredHeight < 0)
493                     {
494                         maxHeight = Math.Max(maxHeight, childMarginHeight);
495                     }
496                     else
497                     {
498                         maxHeight = Math.Max(maxHeight, childMeasuredHeight + childMarginHeight);
499                     }
500                 }
501
502                 if ((childWeight > 0) && ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (childDesiredWidth == 0)))
503                 {
504                     float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
505
506                     if (childMeasuredWidth < 0)
507                     {
508                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMargin.Start + childMargin.End);
509                     }
510                     else
511                     {
512                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMeasuredWidth + childMargin.Start + childMargin.End);
513                     }
514                 }
515             } // 2ND PHASE foreach
516
517             // 3RD PHASE:
518             //
519             // We measure all weighted children whose owner has weight greater than 0.
520             // After 3rd phase, all weighted children has width which is proportional to their weights
521             // in remaining width of parent.
522             if (totalWeight > 0.0f)
523             {
524                 foreach (LayoutItem childLayout in LayoutChildren)
525                 {
526                     if (!childLayout.SetPositionByLayout)
527                     {
528                         continue;
529                     }
530                     int childDesiredWidth = childLayout.Owner.WidthSpecification;
531                     float childWeight = childLayout.Owner.Weight;
532
533                     if ((childWeight > 0) && ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (childDesiredWidth == 0)))
534                     {
535                         if (isWidthExactly)
536                         {
537                             MeasureWeightedChild(childLayout, totalWeightLength, totalWeight, childWeight,
538                                                  widthMeasureSpec, heightMeasureSpec, childState,
539                                                  Orientation.Horizontal);
540                         }
541                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
542                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
543                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
544                         //
545                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
546                         else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
547                         {
548                             if (childDesiredWidth == LayoutParamPolicies.MatchParent)
549                             {
550                                 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!");
551                             }
552                             else
553                             {
554                                 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!");
555                             }
556                         }
557                     }
558                 } // 3RD PHASE foreach
559             }
560
561             maxHeight = Math.Max(maxHeight, maxHeight + (Padding.Top + Padding.Bottom));
562             maxHeight = Math.Max(maxHeight, SuggestedMinimumHeight.AsRoundedValue());
563
564             widthSizeAndState.State = childState.widthState;
565
566             SetMeasuredDimensions(widthSizeAndState,
567                                   ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childState.heightState));
568         } // MeasureHorizontal
569
570         private void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
571         {
572             var widthMode = widthMeasureSpec.Mode;
573             var heightMode = heightMeasureSpec.Mode;
574             bool isWidthExactly = (widthMode == MeasureSpecification.ModeType.Exactly);
575             bool isHeightExactly = (heightMode == MeasureSpecification.ModeType.Exactly);
576             float maxWidth = 0.0f;
577             float totalWeight = 0.0f;
578             int childrenCount = IterateLayoutChildren().Count();
579
580             // Child layout, which wants to match its height to its parent's remaining height, is either following 1 or 2.
581             // 1. Child layout whose Owner.HeightSpecification is LayoutParamPolicies.MatchParent.
582             // 2. Child layout whose Owner.HeightSpecification is 0 and Owner.Weight is greater than 0.
583             // The number of child layout which wants to match its height to its parent's remaining height.
584             int childrenMatchParentCount = 0;
585
586             // Reset measure variable
587             totalLength = 0.0f;
588
589             HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
590                                                                      MeasuredSize.StateType.MeasuredSizeOK);
591
592             // 1ST PHASE:
593             //
594             // We measure all children whose height specification policy is WrapContent without weight.
595             // After 1st phase, remaining height of parent is accumulated to calculate height of children
596             // whose height specification policy is MatchParent.
597             foreach (var childLayout in LayoutChildren)
598             {
599                 if (!childLayout.SetPositionByLayout)
600                 {
601                     continue;
602                 }
603                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
604                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
605                 float childWeight = childLayout.Owner.Weight;
606                 Extents childMargin = childLayout.Margin;
607                 float childMarginWidth = childMargin.Start + childMargin.End;
608                 float childMarginHeight = childMargin.Top + childMargin.Bottom;
609                 bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0);
610
611                 if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
612                 {
613                     totalWeight += childWeight;
614                     childrenMatchParentCount++;
615                 }
616
617                 // MatchParent child layout's margin is not added to totalLength.
618                 // Consequently, MatchParent child layout's margin is added to remaining size,
619                 // so the margin is not shared with other MatchParent child layouts.
620                 // e.g.
621                 // LinearLayout has size 100.
622                 // Child layout1 is MatchParent and its margin is 20. (This margin is not ad
623                 // Child layout2 is MatchParent and its margin is 0.
624                 // Then, child layout1's size is 30 and child layout2's size is 50.
625                 if ((childDesiredHeight == LayoutParamPolicies.WrapContent) || ((childDesiredHeight > 0) && (!useRemainingHeight)))
626                 {
627                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
628
629                     float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
630
631                     if (childMeasuredHeight < 0)
632                     {
633                         totalLength = Math.Max(totalLength, totalLength + childMarginHeight);
634                     }
635                     else
636                     {
637                         totalLength = Math.Max(totalLength, totalLength + childMeasuredHeight + childMarginHeight);
638                     }
639                 }
640
641                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
642                 {
643                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
644                 }
645                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
646                 {
647                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
648                 }
649
650                 float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
651                 if (childMeasuredWidth < 0)
652                 {
653                     maxWidth = Math.Max(maxWidth, childMarginWidth);
654                 }
655                 else
656                 {
657                     maxWidth = Math.Max(maxWidth, childMeasuredWidth + childMarginWidth);
658                 }
659             } // 1ST PHASE foreach
660
661             totalLength = Math.Max(totalLength, totalLength + CellPadding.Height * (childrenCount - 1) + Padding.Top + Padding.Bottom);
662             float heightSize = Math.Max(totalLength, SuggestedMinimumHeight.AsDecimal());
663             MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
664             heightSize = heightSizeAndState.Size.AsDecimal();
665
666             float remainingHeight = heightSize - totalLength;
667             float totalWeightLength = 0.0f;
668
669             // Up to now, only WrapContent children's sizes are added to the totalLength.
670             // Since the totalLength is used in OnLayout as the sum of all children's sizes,
671             // the layout size is assigned to the totalLength if MatchParent child exists.
672             if (childrenMatchParentCount > 0)
673             {
674                 totalLength = heightSize;
675             }
676
677             // 2ND PHASE:
678             //
679             // We measure all children whose height specification policy is MatchParent without weight.
680             // After 2nd phase, all children's heights are calculated without considering weight.
681             // And the heights of all weighted children are accumulated to calculate weighted height.
682             foreach (var childLayout in LayoutChildren)
683             {
684                 if (!childLayout.SetPositionByLayout)
685                 {
686                     continue;
687                 }
688                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
689                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
690                 float childWeight = childLayout.Owner.Weight;
691                 Extents childMargin = childLayout.Margin;
692                 float childMarginWidth = childMargin.Start + childMargin.End;
693                 bool useRemainingHeight = (childDesiredHeight == 0) && (childWeight > 0);
694                 bool needToMeasure = false;
695
696                 if ((childDesiredWidth == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
697                 {
698                     if (isWidthExactly)
699                     {
700                         needToMeasure = true;
701                     }
702                     // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
703                     // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
704                     // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
705                     //
706                     // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
707                     else if (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)
708                     {
709                         if (childDesiredWidth == LayoutParamPolicies.MatchParent)
710                         {
711                             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!");
712                         }
713                         else
714                         {
715                             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!");
716                         }
717                     }
718                 }
719
720                 if (remainingHeight > 0)
721                 {
722                     if ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (useRemainingHeight))
723                     {
724                         if (isHeightExactly)
725                         {
726                             // In MeasureChildWithMargins(), it is assumed that heightMeasureSpec includes Padding.Top and Padding.Bottom.
727                             // Therefore, Padding.Top and Padding.Bottom are added to heightMeasureSpec.Size before it is passed to MeasureChildWithMargins().
728                             heightMeasureSpec.SetSize(new LayoutLength((int)(remainingHeight / childrenMatchParentCount) + Padding.Top + Padding.Bottom));
729                             needToMeasure = true;
730                         }
731                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
732                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
733                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
734                         //
735                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
736                         else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
737                         {
738                             if (childDesiredHeight == LayoutParamPolicies.MatchParent)
739                             {
740                                 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!");
741                             }
742                             else
743                             {
744                                 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!");
745                             }
746                         }
747                     }
748                 }
749
750                 if (needToMeasure == true)
751                 {
752                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
753
754                     float childMeasuredWidth = childLayout.MeasuredWidth.Size.AsDecimal();
755                     if (childMeasuredWidth < 0)
756                     {
757                         maxWidth = Math.Max(maxWidth, childMarginWidth);
758                     }
759                     else
760                     {
761                         maxWidth = Math.Max(maxWidth, childMeasuredWidth + childMarginWidth);
762                     }
763                 }
764
765                 if ((childWeight > 0) && ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (childDesiredHeight == 0)))
766                 {
767                     float childMeasuredHeight = childLayout.MeasuredHeight.Size.AsDecimal();
768
769                     if (childMeasuredHeight < 0)
770                     {
771                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMargin.Top + childMargin.Bottom);
772                     }
773                     else
774                     {
775                         totalWeightLength = Math.Max(totalWeightLength, totalWeightLength + childMeasuredHeight + childMargin.Top + childMargin.Bottom);
776                     }
777                 }
778             } // 2ND PHASE foreach
779
780             // 3RD PHASE:
781             //
782             // We measure all weighted children whose owner has weight greater than 0.
783             // After 3rd phase, all weighted children has height which is proportional to their weights
784             // in remaining height of parent.
785             if (totalWeight > 0)
786             {
787                 foreach (var childLayout in LayoutChildren)
788                 {
789                     if (!childLayout.SetPositionByLayout)
790                     {
791                         continue;
792                     }
793                     int childDesiredHeight = childLayout.Owner.HeightSpecification;
794                     float childWeight = childLayout.Owner.Weight;
795
796                     if ((childWeight > 0) && ((childDesiredHeight == LayoutParamPolicies.MatchParent) || (childDesiredHeight == 0)))
797                     {
798                         if (isHeightExactly)
799                         {
800                             MeasureWeightedChild(childLayout, totalWeightLength, totalWeight, childWeight,
801                                                  widthMeasureSpec, heightMeasureSpec, childState,
802                                                  Orientation.Vertical);
803                         }
804                         // RelativeLayout's MatchParent children should not fill to the RelativeLayout.
805                         // Because the children's sizes and positions are calculated by RelativeLayout's APIs.
806                         // Therefore, not to fill the RelativeLayout, the mode is changed from Exactly to AtMost.
807                         //
808                         // Not to print the recursive reference error message for this case, Specification is checked if it is WrapContent.
809                         else if (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
810                         {
811                             if (childDesiredHeight == LayoutParamPolicies.MatchParent)
812                             {
813                                 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!");
814                             }
815                             else
816                             {
817                                 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!");
818                             }
819                         }
820                     }
821                 } // 3RD PHASE foreach
822             }
823
824             maxWidth = Math.Max(maxWidth, maxWidth + (Padding.Start + Padding.End));
825             maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue());
826
827             heightSizeAndState.State = childState.heightState;
828
829             SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState),
830                                   heightSizeAndState);
831         } // MeasureVertical
832
833         private void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
834         {
835             bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
836
837             LayoutLength childTop = new LayoutLength(Padding.Top);
838             LayoutLength childLeft = new LayoutLength(Padding.Start);
839
840             // Where bottom of child should go
841             LayoutLength height = new LayoutLength(bottom - top);
842
843             // Space available for child
844             LayoutLength childSpace = new LayoutLength(height - Padding.Top - Padding.Bottom);
845
846             var LinearChildren = IterateLayoutChildren();
847             int count = LinearChildren.Count<LayoutItem>();
848
849             switch (HorizontalAlignment)
850             {
851                 case HorizontalAlignment.End:
852                     // totalLength contains the padding already
853                     // In case of RTL map END alignment to the left edge
854                     if (isLayoutRtl)
855                     {
856                         childLeft = new LayoutLength(Padding.Start);
857                     }
858                     else
859                     {
860                         childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - totalLength);
861                     }
862                     break;
863                 case HorizontalAlignment.Center:
864                     // totalLength contains the padding already
865                     childLeft = new LayoutLength(Padding.Start + (right.AsDecimal() - left.AsDecimal() - totalLength) / 2.0f);
866                     break;
867                 case HorizontalAlignment.Begin: // FALL THROUGH (default)
868                 default:
869                     // totalLength contains the padding already
870                     // In case of RTL map BEGIN alignment to the right edge
871                     if (isLayoutRtl)
872                     {
873                         childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - totalLength);
874                     }
875                     else
876                     {
877                         childLeft = new LayoutLength(Padding.Start);
878                     }
879                     break;
880             }
881
882             int start = 0;
883             int dir = 1;
884
885             // In case of RTL, start drawing from the last child.
886             if (isLayoutRtl)
887             {
888                 start = count - 1;
889                 dir = -1;
890             }
891
892             for (int i = 0; i < count; i++)
893             {
894                 int childIndex = start + dir * i;
895                 // Get a reference to the childLayout at the given index
896                 LayoutItem childLayout = LinearChildren.ElementAt<LayoutItem>(i);
897
898                 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
899                 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
900                 Extents childMargin = childLayout.Margin;
901
902                 switch (VerticalAlignment)
903                 {
904                     case VerticalAlignment.Bottom:
905                         childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom);
906                         break;
907                     case VerticalAlignment.Center:
908                         childTop = new LayoutLength(Padding.Top + ((childSpace - childHeight).AsDecimal() / 2.0f) + childMargin.Top - childMargin.Bottom);
909                         break;
910                     case VerticalAlignment.Top: // FALLTHROUGH default
911                     default:
912                         childTop = new LayoutLength(Padding.Top + childMargin.Top);
913                         break;
914                 }
915                 childLeft += (isLayoutRtl ? childMargin.End : childMargin.Start);
916                 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
917                 childLeft += childWidth + (isLayoutRtl ? childMargin.Start : childMargin.End) + ((i < count - 1) ? CellPadding.Width : 0);
918             }
919         } // LayoutHorizontally
920
921         private void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
922         {
923             LayoutLength childTop = new LayoutLength(Padding.Top);
924             LayoutLength childLeft = new LayoutLength(Padding.Start);
925
926             // Where end of child should go
927             LayoutLength width = new LayoutLength(right - left);
928
929             // Space available for child
930             LayoutLength childSpace = new LayoutLength(width - Padding.Start - Padding.End);
931
932             var LinearChildren = IterateLayoutChildren();
933             int count = LinearChildren.Count<LayoutItem>();
934
935             switch (VerticalAlignment)
936             {
937                 case VerticalAlignment.Bottom:
938                     // totalLength contains the padding already
939                     childTop = new LayoutLength(Padding.Top + bottom.AsDecimal() - top.AsDecimal() - totalLength);
940                     break;
941                 case VerticalAlignment.Center:
942                     // totalLength contains the padding already
943                     childTop = new LayoutLength(Padding.Top + (bottom.AsDecimal() - top.AsDecimal() - totalLength) / 2.0f);
944                     break;
945                 case VerticalAlignment.Top:  // FALL THROUGH (default)
946                 default:
947                     // totalLength contains the padding already
948                     childTop = new LayoutLength(Padding.Top);
949                     break;
950             }
951
952             for (int i = 0; i < count; i++)
953             {
954                 LayoutItem childLayout = LinearChildren.ElementAt<LayoutItem>(i);
955
956                 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
957                 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
958                 Extents childMargin = childLayout.Margin;
959
960                 childTop += childMargin.Top;
961                 switch (HorizontalAlignment)
962                 {
963                     case HorizontalAlignment.Begin:
964                     default:
965                         {
966                             childLeft = new LayoutLength(Padding.Start + childMargin.Start);
967                             break;
968                         }
969                     case HorizontalAlignment.End:
970                         {
971                             childLeft = new LayoutLength(width - Padding.End - childWidth - childMargin.End);
972                             break;
973                         }
974                     case HorizontalAlignment.Center:
975                         {
976                             childLeft = new LayoutLength(Padding.Start + ((childSpace - childWidth).AsDecimal() / 2.0f) + childMargin.Start - childMargin.End);
977                             break;
978                         }
979                 }
980                 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
981                 childTop += childHeight + childMargin.Bottom + ((i < count - 1) ? CellPadding.Height : 0);
982             }
983         } // LayoutVertical
984
985         private void ForceUniformHeight(MeasureSpecification widthMeasureSpec)
986         {
987             // Pretend that the linear layout has an exact size. This is the measured height of
988             // ourselves. The measured height should be the max height of the children, changed
989             // to accommodate the heightMeasureSpec from the parent
990             MeasureSpecification uniformMeasureSpec = new MeasureSpecification(MeasuredHeight.Size, MeasureSpecification.ModeType.Exactly);
991             foreach (var childLayout in LayoutChildren)
992             {
993                 if (!childLayout.SetPositionByLayout)
994                 {
995                     continue;
996                 }
997                 int desiredChildHeight = childLayout.Owner.HeightSpecification;
998                 int desiredChildWidth = childLayout.Owner.WidthSpecification;
999
1000                 if (desiredChildHeight == LayoutParamPolicies.MatchParent)
1001                 {
1002                     // Temporarily force children to reuse their original measured width
1003                     int originalWidth = desiredChildWidth;
1004                     childLayout.Owner.WidthSpecification = (int)childLayout.MeasuredWidth.Size.AsRoundedValue();
1005                     // Remeasure with new dimensions
1006                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0),
1007                                              uniformMeasureSpec, new LayoutLength(0));
1008                     // Restore width specification
1009                     childLayout.Owner.WidthSpecification = originalWidth;
1010                 }
1011             }
1012         }
1013     } //LinearLayout
1014 } // namespace