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