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