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