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