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