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