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