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