[NUI] Skip adding cell padding to last LinearLayout item (#1815)
[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             for (int i = 0; i < LayoutChildren.Count; i++)
290             {
291                 LayoutItem childLayout = LayoutChildren[i];
292                 int childDesiredHeight = childLayout.Owner.HeightSpecification;
293                 float childWeight = childLayout.Owner.Weight;
294                 Extents childMargin = childLayout.Margin;
295                 totalWeight += childWeight;
296
297                 bool useExcessSpace = (childLayout.Owner.WidthSpecification == 0 ) && (childWeight > 0);
298                 if( isExactly && useExcessSpace )
299                 {
300                     // Children to be laid out with excess space can be measured later
301                     _totalLength = Math.Max( _totalLength, (_totalLength + childMargin.Start + childMargin.End) );
302                 }
303                 else
304                 {
305                     if (useExcessSpace)
306                     {
307                         // Parent is not defiend!!!
308                         // The widthMode is either Unspecified or AtMost, and
309                         // this child is only laid out using excess space. Measure
310                         // using WrapContent so that we can find out the view's
311                         // optimal width.
312                         MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
313                                                 new MeasureSpecification(
314                                                     new LayoutLength(widthMeasureSpec.Size - (childLayout.Margin.Start + childLayout.Margin.End)),
315                                                     widthMeasureSpec.Mode),
316                                                 new LayoutLength(Padding.Start + Padding.End),
317                                                 new LayoutLength(LayoutParamPolicies.WrapContent));
318
319                         MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
320                                                 new MeasureSpecification(
321                                                     new LayoutLength(heightMeasureSpec.Size - (childLayout.Margin.Top + childLayout.Margin.Bottom)),
322                                                     heightMeasureSpec.Mode),
323                                                 new LayoutLength(Padding.Top + Padding.Bottom),
324                                                 new LayoutLength(childDesiredHeight));
325
326                         childLayout.Measure( childWidthMeasureSpec, childHeightMeasureSpec);
327                         usedExcessSpace += childLayout.MeasuredWidth.Size.AsDecimal();
328                     }
329                     else
330                     {
331                         MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
332                     }
333
334                     LayoutLength childWidth = childLayout.MeasuredWidth.Size;
335                     LayoutLength length = childWidth + childMargin.Start + childMargin.End;
336
337                     if (isExactly)
338                     {
339                         _totalLength += length.AsDecimal();
340                     }
341                     else
342                     {
343                         _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal() + (i < LayoutChildren.Count - 1 ? CellPadding.Width : 0));
344                     }
345                 }
346
347                 bool matchHeightLocally = false;
348                 if (heightMode != MeasureSpecification.ModeType.Exactly && childDesiredHeight == LayoutParamPolicies.MatchParent)
349                 {
350                     // A child has set to MatchParent on it's height.
351                     // Will have to re-measure at least this child when we know exact height.
352                     matchHeight = true;
353                     matchHeightLocally = true;
354                 }
355
356                 float marginHeight = childMargin.Top + childMargin.Bottom;
357                 float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight;
358
359                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
360                 {
361                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
362                 }
363                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
364                 {
365                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
366                 }
367
368                 maxHeight = Math.Max( maxHeight, childHeight);
369                 allFillParent = ( allFillParent && childDesiredHeight == LayoutParamPolicies.MatchParent);
370
371                 if (childWeight > 0)
372                 {
373                   // Heights of weighted Views are invalid if we end up remeasuring, so store them separately.
374                   weightedMaxHeight = Math.Max( weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight);
375                 }
376                 else
377                 {
378                   alternativeMaxHeight = Math.Max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
379                 }
380             } // foreach
381
382             float widthSize = _totalLength;
383             widthSize = Math.Max( widthSize, SuggestedMinimumWidth.AsDecimal());
384             MeasuredSize widthSizeAndState = ResolveSizeAndState( new LayoutLength(widthSize + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
385             widthSize = widthSizeAndState.Size.AsDecimal();
386
387             // 2nd phase:
388             // Expand children with weight to take up available space
389             // We cycle through weighted children now (children with weight > 0).
390             // The children are measured with exact size equal to their share of the available space based on their weights.
391             // _totalLength is updated to include weighted children measured sizes.
392             float remainingExcess = widthSize - _totalLength + usedExcessSpace - (Padding.Start + Padding.End);
393             if( remainingExcess != 0 && totalWeight > 0 )
394             {
395                 float remainingWeight = totalWeight;
396                 maxHeight = 0;
397                 _totalLength = 0;
398
399                 int numberOfChildren = LayoutChildren.Count;
400                 for( int i = 0; i < numberOfChildren; ++i )
401                 {
402                     LayoutItem childLayout = LayoutChildren[i];
403
404                     float desiredChildHeight = childLayout.Owner.HeightSpecification;
405
406                     float childWeight = childLayout.Owner.Weight;
407                     Extents childMargin = childLayout.Margin;
408
409                     if( childWeight > 0 )
410                     {
411                         MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight,
412                                              widthMeasureSpec, heightMeasureSpec, childState,
413                                              Orientation.Horizontal );
414                     }
415
416                     float length = childLayout.MeasuredWidth.Size.AsDecimal() + childMargin.Start + childMargin.End;
417                     float cellPadding = i < numberOfChildren - 1 ? CellPadding.Width : 0;
418                     if( isExactly )
419                     {
420                         _totalLength += length;
421                     }
422                     else
423                     {
424                         float totalLength = _totalLength;
425                         _totalLength = Math.Max( _totalLength, _totalLength + length + cellPadding );
426                     }
427
428                     bool matchHeightLocally = (heightMode != MeasureSpecification.ModeType.Exactly) && (desiredChildHeight == LayoutParamPolicies.MatchParent);
429                     float marginHeight = childMargin.Top + childMargin.Bottom;
430                     float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight;
431
432                     maxHeight = Math.Max( maxHeight, childHeight );
433                     alternativeMaxHeight = Math.Max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
434                     allFillParent = (allFillParent && desiredChildHeight == LayoutParamPolicies.MatchParent);
435                 } // for loop
436             }
437             else
438             {
439                 // No excess space or no weighted children
440                 alternativeMaxHeight = Math.Max( alternativeMaxHeight, weightedMaxHeight );
441             }
442
443             if (!allFillParent && heightMode != MeasureSpecification.ModeType.Exactly)
444             {
445                 maxHeight = alternativeMaxHeight;
446             }
447
448
449
450             // Padding should be concerned when specification is Wrapcontent.
451             maxHeight += (Owner.HeightSpecification == LayoutParamPolicies.WrapContent)?(Padding.Top + Padding.Bottom):0;
452             maxHeight = Math.Max( maxHeight, SuggestedMinimumHeight.AsRoundedValue() );
453
454             widthSizeAndState.State = childState.widthState;
455
456             SetMeasuredDimensions(widthSizeAndState,
457                                   ResolveSizeAndState( new LayoutLength(maxHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childState.heightState ));
458
459             if (matchHeight)
460             {
461                 ForceUniformHeight(widthMeasureSpec);
462             }
463         } // MeasureHorizontal
464
465         private void MeasureVertical( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
466         {
467             var widthMode = widthMeasureSpec.Mode;
468             var heightMode = heightMeasureSpec.Mode;
469             bool isExactly = ( heightMode == MeasureSpecification.ModeType.Exactly);
470             bool matchWidth = false;
471             bool allFillParent = true;
472             float maxWidth = 0.0f;
473             float alternativeMaxWidth = 0.0f;
474             float weightedMaxWidth = 0.0f;
475             float totalWeight = 0.0f;
476
477             // Reset total length
478             _totalLength = 0.0f;
479             float usedExcessSpace =0.0f;
480             HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
481                                                                      MeasuredSize.StateType.MeasuredSizeOK);
482
483
484             // measure children, and determine if further resolution is required
485
486             // 1st phase:
487             // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs
488             // to accumulate total used space in _totalLength.
489             // Weighted children are not measured in this phase.
490             // Available space for weighted children will be calculated in the phase 2 based on _totalLength value.
491             for (int i = 0; i < LayoutChildren.Count; i++)
492             {
493                 LayoutItem childLayout = LayoutChildren[i];
494                 int childDesiredWidth = childLayout.Owner.WidthSpecification;
495                 float childWeight = childLayout.Owner.Weight;
496                 Extents childMargin = childLayout.Margin;
497                 totalWeight += childWeight;
498
499                 bool useExcessSpace = (childLayout.Owner.HeightSpecification == 0) && (childWeight > 0);
500                 if( isExactly && useExcessSpace )
501                 {
502                    _totalLength = Math.Max( _totalLength, (_totalLength + childMargin.Top + childMargin.Bottom) );
503                 }
504                 else
505                 {
506                     if( useExcessSpace )
507                     {
508                         // The heightMode is either Unspecified or AtMost, and
509                         // this child is only laid out using excess space. Measure
510                         // using WrapContent so that we can find out the view's
511                         // optimal height.
512                         // We'll restore the original height of 0 after measurement.
513                         MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
514                                                 new MeasureSpecification(
515                                                     new LayoutLength(widthMeasureSpec.Size - (childLayout.Margin.Start + childLayout.Margin.End)),
516                                                     widthMeasureSpec.Mode),
517                                                 new LayoutLength(Padding.Start + Padding.End),
518                                                 new LayoutLength(childDesiredWidth));
519
520                         MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
521                                                 new MeasureSpecification(
522                                                     new LayoutLength(heightMeasureSpec.Size - (childLayout.Margin.Top + childLayout.Margin.Bottom)),
523                                                     heightMeasureSpec.Mode),
524                                                 new LayoutLength(Padding.Top + Padding.Bottom),
525                                                 new LayoutLength(LayoutParamPolicies.WrapContent));
526
527                         childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
528                         usedExcessSpace += childLayout.MeasuredHeight.Size.AsDecimal();
529                     }
530                     else
531                     {
532                         MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
533                     }
534
535                     LayoutLength childHeight = childLayout.MeasuredHeight.Size;
536                     LayoutLength length = childHeight + childMargin.Top + childMargin.Bottom;
537
538
539                     if (isExactly)
540                     {
541                         _totalLength += length.AsDecimal();
542                     }
543                     else
544                     {
545                         _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal() + (i < LayoutChildren.Count - 1 ? CellPadding.Height : 0));
546                     }
547                 }
548
549                 bool matchWidthLocally = false;
550                 if( widthMode != MeasureSpecification.ModeType.Exactly && childDesiredWidth ==  LayoutParamPolicies.MatchParent)
551                 {
552                     // Will have to re-measure at least this child when we know exact height.
553                     matchWidth = true;
554                     matchWidthLocally = true;
555                 }
556
557                 float marginWidth = childLayout.Margin.Start + childLayout.Margin.End;
558                 float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth;
559
560                 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
561                 {
562                     childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
563                 }
564                 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
565                 {
566                     childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
567                 }
568
569                 maxWidth = Math.Max( maxWidth, childWidth);
570                 allFillParent = ( allFillParent && childDesiredWidth == LayoutParamPolicies.MatchParent);
571
572                 if (childWeight > 0)
573                 {
574                     // Widths of weighted Views are bogus if we end up remeasuring, so keep them separate.
575                     weightedMaxWidth = Math.Max( weightedMaxWidth,  matchWidthLocally ? marginWidth : childWidth);
576                 }
577                 else
578                 {
579                     alternativeMaxWidth = Math.Max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth);
580                 }
581             } // foreach
582
583
584             float heightSize = _totalLength;
585             heightSize = Math.Max( heightSize, SuggestedMinimumHeight.AsDecimal());
586             MeasuredSize heightSizeAndState = ResolveSizeAndState( new LayoutLength(heightSize + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK );
587             heightSize = heightSizeAndState.Size.AsDecimal();
588
589             // 2nd phase:
590             // We cycle through weighted children now (children with weight > 0).
591             // The children are measured with exact size equal to their share of the available space based on their weights.
592             // _totalLength is updated to include weighted children measured sizes.
593             float remainingExcess = heightSize - _totalLength + usedExcessSpace - (Padding.Top + Padding.Bottom);
594             if( remainingExcess != 0 && totalWeight > 0.0f )
595             {
596                 float remainingWeight = totalWeight;
597                 maxWidth = 0;
598                 _totalLength = 0;
599
600                 int numberOfChildren = LayoutChildren.Count;
601                 for( int i = 0; i < numberOfChildren; ++i )
602                 {
603                     LayoutItem childLayout = LayoutChildren[i];
604
605                     float desiredChildWidth = childLayout.Owner.WidthSpecification;
606
607                     float childWeight = childLayout.Owner.Weight;
608                     Extents childMargin = childLayout.Margin;
609
610                     if( childWeight > 0 )
611                     {
612                         MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight,
613                                                 widthMeasureSpec, heightMeasureSpec, childState,
614                                                 Orientation.Vertical);
615                     }
616
617                     float length = childLayout.MeasuredHeight.Size.AsDecimal() + childMargin.Top + childMargin.Bottom;
618                     float cellPadding = i < numberOfChildren - 1 ? CellPadding.Height : 0;
619
620                     if( isExactly )
621                     {
622                         _totalLength += length;
623                     }
624                     else
625                     {
626                         float totalLength = _totalLength;
627                         _totalLength = Math.Max( _totalLength, _totalLength + length + cellPadding );
628                     }
629
630                     bool matchWidthLocally = (widthMode != MeasureSpecification.ModeType.Exactly) && (desiredChildWidth == LayoutParamPolicies.MatchParent);
631                     float marginWidth = childMargin.Start + childMargin.End;
632                     float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth;
633
634                     maxWidth = Math.Max( maxWidth, childWidth );
635                     alternativeMaxWidth = Math.Max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
636                     allFillParent = (allFillParent && desiredChildWidth == LayoutParamPolicies.MatchParent);
637                 } // for loop
638             }
639             else
640             {
641                 alternativeMaxWidth = Math.Max( alternativeMaxWidth, weightedMaxWidth );
642             }
643
644             if (!allFillParent && widthMode != MeasureSpecification.ModeType.Exactly)
645             {
646                 maxWidth = alternativeMaxWidth;
647             }
648
649             maxWidth += (Owner.WidthSpecification == LayoutParamPolicies.WrapContent)?(Padding.Start + Padding.End):0;
650             maxWidth = Math.Max( maxWidth, SuggestedMinimumWidth.AsRoundedValue());
651
652             heightSizeAndState.State = childState.heightState;
653
654             SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(maxWidth + Padding.Top + Padding.Bottom), widthMeasureSpec, childState.widthState ),
655                                   heightSizeAndState );
656
657             if (matchWidth)
658             {
659                 ForceUniformWidth(heightMeasureSpec );
660             }
661         } // MeasureVertical
662
663         private void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
664         {
665             bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
666
667             LayoutLength childTop = new LayoutLength(Padding.Top);
668             LayoutLength childLeft = new LayoutLength(Padding.Start);
669
670             // Where bottom of child should go
671             LayoutLength height = new LayoutLength(bottom - top);
672
673             // Space available for child
674             LayoutLength childSpace = new LayoutLength( height - Padding.Top - Padding.Bottom);
675
676             int count = LayoutChildren.Count;
677
678             switch (LinearAlignment)
679             {
680                 case Alignment.End:
681                     // totalLength contains the padding already
682                     // In case of RTL map END alignment to the left edge
683                     if (isLayoutRtl)
684                     {
685                         childLeft = new LayoutLength(Padding.Start);
686                     }
687                     else
688                     {
689                         childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - _totalLength);
690                     }
691                     break;
692                 case Alignment.CenterHorizontal: // FALL THROUGH
693                 case Alignment.Center:
694                     // totalLength contains the padding already
695                     childLeft = new LayoutLength(Padding.Start + (right.AsDecimal() - left.AsDecimal() - _totalLength) / 2.0f);
696                     break;
697                 case Alignment.Begin: // FALL THROUGH (default)
698                 default:
699                     // totalLength contains the padding already
700                     // In case of RTL map BEGIN alignment to the right edge
701                     if (isLayoutRtl)
702                     {
703                         childLeft = new LayoutLength(Padding.Start  + right.AsDecimal() - left.AsDecimal() - _totalLength);
704                     }
705                     else
706                     {
707                         childLeft = new LayoutLength(Padding.Start);
708                     }
709                     break;
710             }
711
712             int start = 0;
713             int dir = 1;
714
715             // In case of RTL, start drawing from the last child.
716             if (isLayoutRtl)
717             {
718                 start = count - 1;
719                 dir = -1;
720             }
721
722             for( int i = 0; i < count; i++)
723             {
724                 int childIndex = start + dir * i;
725                 // Get a reference to the childLayout at the given index
726                 LayoutItem childLayout = LayoutChildren[childIndex];
727                 if( childLayout != null )
728                 {
729                     LayoutLength childWidth = childLayout.MeasuredWidth.Size;
730                     LayoutLength childHeight = childLayout.MeasuredHeight.Size;
731                     Extents childMargin = childLayout.Margin;
732
733                     switch ( LinearAlignment )
734                     {
735                         case Alignment.Bottom:
736                             childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom);
737                             break;
738                         case Alignment.CenterVertical:
739                         case Alignment.Center: // FALLTHROUGH
740                             childTop = new LayoutLength(Padding.Top + ( ( childSpace - childHeight ).AsDecimal() / 2.0f ) + childMargin.Top - childMargin.Bottom);
741                             break;
742                         case Alignment.Top: // FALLTHROUGH default
743                         default:
744                             childTop = new LayoutLength(Padding.Top + childMargin.Top);
745                             break;
746                     }
747                     childLeft += childMargin.Start;
748                     childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
749                     childLeft += childWidth + childMargin.End + ((i < count - 1) ? CellPadding.Width : 0);
750                 }
751             }
752         } // LayoutHorizontally
753
754         private void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
755         {
756             LayoutLength childTop = new LayoutLength(Padding.Top);
757             LayoutLength childLeft = new LayoutLength(Padding.Start);
758
759             // Where end of child should go
760             LayoutLength width = new LayoutLength(right - left);
761
762             // Space available for child
763             LayoutLength childSpace = new LayoutLength( width - Padding.Start - Padding.End);
764
765             int count = LayoutChildren.Count;
766
767             switch (LinearAlignment)
768             {
769               case Alignment.Bottom:
770                 // totalLength contains the padding already
771                 childTop = new LayoutLength( Padding.Top + bottom.AsDecimal() - top.AsDecimal() - _totalLength);
772                 break;
773               case Alignment.CenterVertical: // FALL THROUGH
774               case Alignment.Center:
775                 // totalLength contains the padding already
776                 childTop = new LayoutLength(Padding.Top + ( bottom.AsDecimal() - top.AsDecimal() - _totalLength ) / 2.0f);
777                 break;
778               case Alignment.Top:  // FALL THROUGH (default)
779               default:
780                 // totalLength contains the padding already
781                 childTop = new LayoutLength( Padding.Top );
782                 break;
783             }
784
785             for( int i = 0; i < count; i++)
786             {
787                 LayoutItem childLayout = LayoutChildren[i];
788                 if( childLayout != null )
789                 {
790                     LayoutLength childWidth = childLayout.MeasuredWidth.Size;
791                     LayoutLength childHeight = childLayout.MeasuredHeight.Size;
792                     Extents childMargin = childLayout.Margin;
793
794                     childTop += childMargin.Top;
795                     switch ( LinearAlignment )
796                     {
797                       case Alignment.Begin:
798                       default:
799                       {
800                         childLeft = new LayoutLength(Padding.Start + childMargin.Start);
801                         break;
802                       }
803                       case Alignment.End:
804                       {
805                         childLeft = new LayoutLength(width - Padding.End - childWidth - childMargin.End);
806                         break;
807                       }
808                       case Alignment.CenterHorizontal:
809                       case Alignment.Center: // FALL THROUGH
810                       {
811                         childLeft = new LayoutLength(Padding.Start + (( childSpace - childWidth ).AsDecimal() / 2.0f) + childMargin.Start - childMargin.End);
812                         break;
813                       }
814                     }
815                     childLayout.Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
816                     childTop += childHeight + childMargin.Bottom + ((i < count - 1) ? CellPadding.Height : 0);
817                 }
818             }
819         } // LayoutVertical
820
821         private void ForceUniformHeight(MeasureSpecification widthMeasureSpec)
822         {
823           // Pretend that the linear layout has an exact size. This is the measured height of
824           // ourselves. The measured height should be the max height of the children, changed
825           // to accommodate the heightMeasureSpec from the parent
826           MeasureSpecification uniformMeasureSpec = new MeasureSpecification( MeasuredHeight.Size, MeasureSpecification.ModeType.Exactly);
827           foreach (LayoutItem childLayout in LayoutChildren)
828           {
829               int desiredChildHeight = childLayout.Owner.HeightSpecification;
830               int desiredChildWidth = childLayout.Owner.WidthSpecification;
831
832               if (desiredChildHeight == LayoutParamPolicies.MatchParent)
833               {
834                   // Temporarily force children to reuse their original measured width
835                   int originalWidth = desiredChildWidth;
836                   childLayout.Owner.WidthSpecification = (int)childLayout.MeasuredWidth.Size.AsRoundedValue();
837                   // Remeasure with new dimensions
838                   MeasureChildWithMargins( childLayout, widthMeasureSpec, new LayoutLength(0),
839                                            uniformMeasureSpec, new LayoutLength(0) );
840                   // Restore width specification
841                   childLayout.Owner.WidthSpecification =  originalWidth;
842               }
843           }
844         }
845
846         private void ForceUniformWidth(MeasureSpecification heightMeasureSpec)
847         {
848             // Pretend that the linear layout has an exact size.
849             MeasureSpecification uniformMeasureSpec = new MeasureSpecification( MeasuredWidth.Size, MeasureSpecification.ModeType.Exactly);
850             foreach (LayoutItem childLayout in LayoutChildren)
851             {
852                 int desiredChildWidth = childLayout.Owner.WidthSpecification;
853                 int desiredChildHeight = childLayout.Owner.WidthSpecification;
854
855                 if (desiredChildWidth  == LayoutParamPolicies.MatchParent)
856                 {
857                     // Temporarily force children to reuse their original measured height
858                     int originalHeight = desiredChildHeight;
859                     childLayout.Owner.HeightSpecification =  (int)childLayout.MeasuredHeight.Size.AsRoundedValue();
860
861                     // Remeasure with new dimensions
862                     MeasureChildWithMargins( childLayout, uniformMeasureSpec, new LayoutLength(0),
863                                              heightMeasureSpec, new LayoutLength(0));
864                     // Restore height specification
865                     childLayout.Owner.HeightSpecification = originalHeight;
866                 }
867             }
868         }
869     } //LinearLayout
870 } // namespace