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