1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using Tizen.NUI.BaseComponents;
19 using System.Collections.Generic;
21 using System.ComponentModel;
26 /// [Draft] This class implements a linear box layout, automatically handling right to left or left to right direction change.
28 public class LinearLayout : LayoutGroup
31 /// [Draft] Enumeration for the direction in which the content is laid out
33 /// <since_tizen> 6 </since_tizen>
34 public enum Orientation
47 /// [Draft] Enumeration for the alignment of the linear layout items
49 /// <since_tizen> 6 </since_tizen>
53 /// At the left/right edge of the container (maps to LTR/RTL direction for horizontal orientation)
57 /// At the right/left edge of the container (maps to LTR/RTL direction for horizontal orientation)
61 /// At the horizontal center of the container
63 CenterHorizontal = 0x4,
65 /// At the top edge of the container
69 /// At the bottom edge of the container
73 /// At the vertical center of the container
75 CenterVertical = 0x20,
77 /// At the vertical and horizontal center of the container
82 struct HeightAndWidthState
84 public MeasuredSize.StateType widthState;
85 public MeasuredSize.StateType heightState;
87 public HeightAndWidthState(MeasuredSize.StateType width, MeasuredSize.StateType height)
95 /// [Draft] Get/Set the orientation in the layout
97 /// <since_tizen> 6 </since_tizen>
98 public LinearLayout.Orientation LinearOrientation
102 return _linearOrientation;
106 _linearOrientation = value;
112 /// [Draft] Get/Set the padding between cells in the layout
114 /// <since_tizen> 6 </since_tizen>
115 public Size2D CellPadding
123 _cellPadding = value;
130 /// [Draft] Get/Set the alignment in the layout
132 /// <since_tizen> 6 </since_tizen>
133 public LinearLayout.Alignment LinearAlignment { get; set; } = Alignment.Top;
135 private float _totalLength = 0.0f;
136 private Size2D _cellPadding = new Size2D(0, 0);
137 private Orientation _linearOrientation = Orientation.Horizontal;
140 /// [Draft] Constructor
142 /// <since_tizen> 6 </since_tizen>
143 public LinearLayout()
148 /// Measure the layout and its content to determine the measured width and the measured height.
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)
155 if (_linearOrientation == Orientation.Horizontal)
157 MeasureHorizontal(widthMeasureSpec, heightMeasureSpec);
161 MeasureVertical(widthMeasureSpec, heightMeasureSpec);
166 /// Layout should assign a size and position to each of its children.<br />
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)
176 if (_linearOrientation == Orientation.Horizontal)
178 LayoutHorizontal(left, top, right, bottom);
182 LayoutVertical(left, top, right, bottom);
187 private void MeasureWeightedChild(LayoutItem childLayout, float remainingExcess, float remainingWeight, float childWeight,
188 MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec,
189 HeightAndWidthState childState, Orientation orientation)
191 bool horizontal = false;
192 if (orientation == Orientation.Horizontal)
197 float childsShare = (childWeight * remainingExcess) / remainingWeight;
198 remainingExcess -= childsShare;
199 remainingWeight -= childWeight;
201 float desiredWidth = childLayout.Owner.WidthSpecification;
202 float desiredHeight = childLayout.Owner.HeightSpecification;
203 float childLength = 0;
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)))
209 // This child needs to be laid out from scratch using
210 // only its share of excess space.
211 childLength = childsShare;
215 // This child had some intrinsic width to which we
216 // need to add its share of excess space.
219 childLength = childLayout.MeasuredWidth.Size.AsDecimal() + childsShare;
223 childLength = childLayout.MeasuredHeight.Size.AsDecimal() + childsShare;
227 MeasureSpecification childWidthMeasureSpec;
228 MeasureSpecification childHeightMeasureSpec;
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));
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));
249 childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childLength), MeasureSpecification.ModeType.Exactly);
252 childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
254 // Child may now not fit in horizontal dimension.
255 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
257 childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
260 // Child may now not fit in vertical dimension.
261 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
263 childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
267 private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
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();
280 // Reset measure variables
282 float usedExcessSpace = 0.0f;
283 HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
284 MeasuredSize.StateType.MeasuredSizeOK);
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())
294 int childDesiredHeight = childLayout.Owner.HeightSpecification;
295 float childWeight = childLayout.Owner.Weight;
296 Extents childMargin = childLayout.Margin;
297 totalWeight += childWeight;
299 bool useExcessSpace = (childLayout.Owner.WidthSpecification == 0) && (childWeight > 0);
300 if (isExactly && useExcessSpace)
302 // Children to be laid out with excess space can be measured later
303 _totalLength = Math.Max(_totalLength, (_totalLength + childMargin.Start + childMargin.End));
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
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));
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));
328 childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
329 usedExcessSpace += childLayout.MeasuredWidth.Size.AsDecimal();
333 MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
336 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
337 LayoutLength length = childWidth + childMargin.Start + childMargin.End;
339 _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal());
342 bool matchHeightLocally = false;
343 if (heightMode != MeasureSpecification.ModeType.Exactly && childDesiredHeight == LayoutParamPolicies.MatchParent)
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.
348 matchHeightLocally = true;
351 float marginHeight = childMargin.Top + childMargin.Bottom;
352 float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight;
354 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
356 childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
358 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
360 childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
363 maxHeight = Math.Max(maxHeight, childHeight);
364 allFillParent = (allFillParent && childDesiredHeight == LayoutParamPolicies.MatchParent);
368 // Heights of weighted Views are invalid if we end up remeasuring, so store them separately.
369 weightedMaxHeight = Math.Max(weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight);
373 alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight);
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();
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)
391 float remainingWeight = totalWeight;
395 foreach (LayoutItem childLayout in IterateLayoutChildren())
397 float desiredChildHeight = childLayout.Owner.HeightSpecification;
399 float childWeight = childLayout.Owner.Weight;
400 Extents childMargin = childLayout.Margin;
404 MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight,
405 widthMeasureSpec, heightMeasureSpec, childState,
406 Orientation.Horizontal);
409 float length = childLayout.MeasuredWidth.Size.AsDecimal() + childMargin.Start + childMargin.End;
410 _totalLength += length;
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;
416 maxHeight = Math.Max(maxHeight, childHeight);
417 alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight);
418 allFillParent = (allFillParent && desiredChildHeight == LayoutParamPolicies.MatchParent);
420 _totalLength = Math.Max(_totalLength, _totalLength + CellPadding.Width * (childrenCount - 1));
424 // No excess space or no weighted children
425 alternativeMaxHeight = Math.Max(alternativeMaxHeight, weightedMaxHeight);
428 if (!allFillParent && heightMode != MeasureSpecification.ModeType.Exactly)
430 maxHeight = alternativeMaxHeight;
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());
439 widthSizeAndState.State = childState.widthState;
441 SetMeasuredDimensions(widthSizeAndState,
442 ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childState.heightState));
446 ForceUniformHeight(widthMeasureSpec);
448 } // MeasureHorizontal
450 private void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
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();
463 // Reset total length
465 float usedExcessSpace = 0.0f;
466 HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
467 MeasuredSize.StateType.MeasuredSizeOK);
470 // measure children, and determine if further resolution is required
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())
480 int childDesiredWidth = childLayout.Owner.WidthSpecification;
481 float childWeight = childLayout.Owner.Weight;
482 Extents childMargin = childLayout.Margin;
483 totalWeight += childWeight;
485 bool useExcessSpace = (childLayout.Owner.HeightSpecification == 0) && (childWeight > 0);
486 if (isExactly && useExcessSpace)
488 _totalLength = Math.Max(_totalLength, (_totalLength + childMargin.Top + childMargin.Bottom));
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
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));
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));
513 childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
514 usedExcessSpace += childLayout.MeasuredHeight.Size.AsDecimal();
518 MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
521 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
522 LayoutLength length = childHeight + childMargin.Top + childMargin.Bottom;
524 _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal());
527 bool matchWidthLocally = false;
528 if (widthMode != MeasureSpecification.ModeType.Exactly && childDesiredWidth == LayoutParamPolicies.MatchParent)
530 // Will have to re-measure at least this child when we know exact height.
532 matchWidthLocally = true;
535 float marginWidth = childLayout.Margin.Start + childLayout.Margin.End;
536 float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth;
538 if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
540 childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
542 if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
544 childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
547 maxWidth = Math.Max(maxWidth, childWidth);
548 allFillParent = (allFillParent && childDesiredWidth == LayoutParamPolicies.MatchParent);
552 // Widths of weighted Views are bogus if we end up remeasuring, so keep them separate.
553 weightedMaxWidth = Math.Max(weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth);
557 alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth);
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();
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)
574 float remainingWeight = totalWeight;
578 foreach (LayoutItem childLayout in IterateLayoutChildren())
580 float desiredChildWidth = childLayout.Owner.WidthSpecification;
582 float childWeight = childLayout.Owner.Weight;
583 Extents childMargin = childLayout.Margin;
587 MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight,
588 widthMeasureSpec, heightMeasureSpec, childState,
589 Orientation.Vertical);
592 float length = childLayout.MeasuredHeight.Size.AsDecimal() + childMargin.Top + childMargin.Bottom;
593 _totalLength += length;
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;
599 maxWidth = Math.Max(maxWidth, childWidth);
600 alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth);
601 allFillParent = (allFillParent && desiredChildWidth == LayoutParamPolicies.MatchParent);
603 _totalLength = Math.Max(_totalLength, _totalLength + CellPadding.Height * (childrenCount - 1));
607 alternativeMaxWidth = Math.Max(alternativeMaxWidth, weightedMaxWidth);
610 if (!allFillParent && widthMode != MeasureSpecification.ModeType.Exactly)
612 maxWidth = alternativeMaxWidth;
615 maxWidth += (Owner.WidthSpecification == LayoutParamPolicies.WrapContent) ? (Padding.Start + Padding.End) : 0;
616 maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue());
618 heightSizeAndState.State = childState.heightState;
620 SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState),
625 ForceUniformWidth(heightMeasureSpec);
629 private void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
631 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
633 LayoutLength childTop = new LayoutLength(Padding.Top);
634 LayoutLength childLeft = new LayoutLength(Padding.Start);
636 // Where bottom of child should go
637 LayoutLength height = new LayoutLength(bottom - top);
639 // Space available for child
640 LayoutLength childSpace = new LayoutLength(height - Padding.Top - Padding.Bottom);
642 List<LayoutItem> LinearChildren = IterateLayoutChildren().ToList();
643 int count = LinearChildren.Count;
645 switch (LinearAlignment)
648 // totalLength contains the padding already
649 // In case of RTL map END alignment to the left edge
652 childLeft = new LayoutLength(Padding.Start);
656 childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - _totalLength);
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);
664 case Alignment.Begin: // FALL THROUGH (default)
666 // totalLength contains the padding already
667 // In case of RTL map BEGIN alignment to the right edge
670 childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - _totalLength);
674 childLeft = new LayoutLength(Padding.Start);
682 // In case of RTL, start drawing from the last child.
689 for (int i = 0; i < count; i++)
691 int childIndex = start + dir * i;
692 // Get a reference to the childLayout at the given index
693 LayoutItem childLayout = LinearChildren[childIndex];
695 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
696 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
697 Extents childMargin = childLayout.Margin;
699 switch (LinearAlignment)
701 case Alignment.Bottom:
702 childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom);
704 case Alignment.CenterVertical:
705 case Alignment.Center: // FALLTHROUGH
706 childTop = new LayoutLength(Padding.Top + ((childSpace - childHeight).AsDecimal() / 2.0f) + childMargin.Top - childMargin.Bottom);
708 case Alignment.Top: // FALLTHROUGH default
710 childTop = new LayoutLength(Padding.Top + childMargin.Top);
713 childLeft += childMargin.Start;
714 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
715 childLeft += childWidth + childMargin.End + ((i < count - 1) ? CellPadding.Width : 0);
717 } // LayoutHorizontally
719 private void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
721 LayoutLength childTop = new LayoutLength(Padding.Top);
722 LayoutLength childLeft = new LayoutLength(Padding.Start);
724 // Where end of child should go
725 LayoutLength width = new LayoutLength(right - left);
727 // Space available for child
728 LayoutLength childSpace = new LayoutLength(width - Padding.Start - Padding.End);
730 List<LayoutItem> LinearChildren = IterateLayoutChildren().ToList();
731 int count = LinearChildren.Count;
733 switch (LinearAlignment)
735 case Alignment.Bottom:
736 // totalLength contains the padding already
737 childTop = new LayoutLength(Padding.Top + bottom.AsDecimal() - top.AsDecimal() - _totalLength);
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);
744 case Alignment.Top: // FALL THROUGH (default)
746 // totalLength contains the padding already
747 childTop = new LayoutLength(Padding.Top);
751 for (int i = 0; i < count; i++)
753 LayoutItem childLayout = LinearChildren[i];
755 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
756 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
757 Extents childMargin = childLayout.Margin;
759 childTop += childMargin.Top;
760 switch (LinearAlignment)
762 case Alignment.Begin:
765 childLeft = new LayoutLength(Padding.Start + childMargin.Start);
770 childLeft = new LayoutLength(width - Padding.End - childWidth - childMargin.End);
773 case Alignment.CenterHorizontal:
774 case Alignment.Center: // FALL THROUGH
776 childLeft = new LayoutLength(Padding.Start + ((childSpace - childWidth).AsDecimal() / 2.0f) + childMargin.Start - childMargin.End);
780 childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
781 childTop += childHeight + childMargin.Bottom + ((i < count - 1) ? CellPadding.Height : 0);
785 private void ForceUniformHeight(MeasureSpecification widthMeasureSpec)
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())
793 int desiredChildHeight = childLayout.Owner.HeightSpecification;
794 int desiredChildWidth = childLayout.Owner.WidthSpecification;
796 if (desiredChildHeight == LayoutParamPolicies.MatchParent)
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;
810 private void ForceUniformWidth(MeasureSpecification heightMeasureSpec)
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())
816 int desiredChildWidth = childLayout.Owner.WidthSpecification;
817 int desiredChildHeight = childLayout.Owner.WidthSpecification;
819 if (desiredChildWidth == LayoutParamPolicies.MatchParent)
821 // Temporarily force children to reuse their original measured height
822 int originalHeight = desiredChildHeight;
823 childLayout.Owner.HeightSpecification = (int)childLayout.MeasuredHeight.Size.AsRoundedValue();
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;
834 [EditorBrowsable(EditorBrowsableState.Never)]
835 protected override void Dispose(bool disposing)
839 // Dispose managed resources.
840 _cellPadding?.Dispose();
842 // Free native resources.
843 base.Dispose(disposing);