void InsertChild( LayoutItem child ) { // Store created node for child Interop.FlexLayout.FlexLayout_AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count-1); }
/// <summary> /// Callback when child is added to container.<br /> /// Derived classes can use this to set their own child properties on the child layout's owner.<br /> /// </summary> /// <param name="child">The Layout child.</param> /// <since_tizen> 6 </since_tizen> protected override void OnChildAdd(LayoutItem child) { InsertChild(child); }
// Virtual Methods that can be overridden by derived classes. /// <summary> /// Callback when child is added to container.<br /> /// Derived classes can use this to set their own child properties on the child layout's owner.<br /> /// </summary> /// <param name="child">The Layout child.</param> /// <since_tizen> 6 </since_tizen> protected virtual void OnChildAdd(LayoutItem child) { }
/// <summary> /// Callback when child is removed from container.<br /> /// </summary> /// <param name="child">The Layout child.</param> /// <since_tizen> 6 </since_tizen> protected virtual void OnChildRemove(LayoutItem child) { }
} // MeasureVertical private void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom) { bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL; LayoutLength childTop = new LayoutLength(Padding.Top); LayoutLength childLeft = new LayoutLength(Padding.Start); // Where bottom of child should go LayoutLength height = new LayoutLength(bottom - top); // Space available for child LayoutLength childSpace = new LayoutLength(height - Padding.Top - Padding.Bottom); int count = LayoutChildren.Count; switch (LinearAlignment) { case Alignment.End: // totalLength contains the padding already // In case of RTL map END alignment to the left edge if (isLayoutRtl) { childLeft = new LayoutLength(Padding.Start); } else { childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - _totalLength); } break; case Alignment.CenterHorizontal: // FALL THROUGH case Alignment.Center: // totalLength contains the padding already childLeft = new LayoutLength(Padding.Start + (right.AsDecimal() - left.AsDecimal() - _totalLength) / 2.0f); break; case Alignment.Begin: // FALL THROUGH (default) default: // totalLength contains the padding already // In case of RTL map BEGIN alignment to the right edge if (isLayoutRtl) { childLeft = new LayoutLength(Padding.Start + right.AsDecimal() - left.AsDecimal() - _totalLength); } else { childLeft = new LayoutLength(Padding.Start); } break; } int start = 0; int dir = 1; // In case of RTL, start drawing from the last child. if (isLayoutRtl) { start = count - 1; dir = -1; } for (int i = 0; i < count; i++) { int childIndex = start + dir * i; // Get a reference to the childLayout at the given index LayoutItem childLayout = LayoutChildren[childIndex]; if (childLayout != null) { LayoutLength childWidth = childLayout.MeasuredWidth.Size; LayoutLength childHeight = childLayout.MeasuredHeight.Size; Extents childMargin = childLayout.Margin; switch (LinearAlignment) { case Alignment.Bottom: childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom); break; case Alignment.CenterVertical: case Alignment.Center: // FALLTHROUGH childTop = new LayoutLength(Padding.Top + ((childSpace - childHeight).AsDecimal() / 2.0f) + childMargin.Top - childMargin.Bottom); break; case Alignment.Top: // FALLTHROUGH default default: childTop = new LayoutLength(Padding.Top + childMargin.Top); break; } childLeft += childMargin.Start; childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); childLeft += childWidth + childMargin.End + CellPadding.Width; } } } // LayoutHorizontally
/// <inheritdoc/> /// <since_tizen> 9 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } } (float childrenWidth, float childrenHeight) = CalculateChildrenSize(widthMeasureSpec.Size.AsDecimal(), heightMeasureSpec.Size.AsDecimal()); SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(childrenWidth), widthMeasureSpec, childWidthState), ResolveSizeAndState(new LayoutLength(childrenHeight), heightMeasureSpec, childHeightState)); // There are 2 cases which require to calculate children's MeasuredWidth/Height as follows. // // 1. Text with Ellipsis true // TextLabel and TextField calculate MeasuredWidth/Height to cover their text string if they have WrapContent. // This causes children's Ellipsis cannot be displayed with RelativeLayout. // To resolve the above, RelativeLayout recalculates its children's MeasuredWidth/Height based on the children's space calculated by RelativeLayout APIs. // // 2. FillHorizontal/Vertical true // If children set FillHorizontal/Vertical true, then children's MeasuredWidth/Height are not correctly alculated. // Instead, children's size and position are correctly calculated in OnLayout(). // This causes that the grand children's MeasuredWidth/Height are calculated incorrectly. // To resolve the above, RelativeLayout calculates its children's MeasuredWidth/Height based on the children's geometry calculated by RelativeLayout APIs. // // e.g. // Let parent have RelativeLayout and parent's size be 1920x1080. // Let child have WrapContent with SetFillHorizontal/Vertical true. // Let grand child have MatchParent. // Then, child's size is 1920x1080 but child's MeasuredWidth/Height is 0x0. // Then, grand child's MeasuredWidth/Height is 0x0 and size is 0x0. // // TODO: Not to do duplicate operations in OnLayout() again. bool needClearCache = false; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { bool ellipsisText = false; bool needMeasuredWidth = false; bool needMeasuredHeight = false; if (((childLayout.Owner is TextLabel textLabel) && textLabel.Ellipsis) || ((childLayout.Owner is TextField textField) && textField.Ellipsis)) { ellipsisText = true; needClearCache = true; } else { if (RelativeLayout.GetFillHorizontal(childLayout.Owner)) { needMeasuredWidth = true; needClearCache = true; } if (RelativeLayout.GetFillVertical(childLayout.Owner)) { needMeasuredHeight = true; needClearCache = true; } } if ((ellipsisText == false) && (needMeasuredWidth == false) && (needMeasuredHeight == false)) { continue; } float width = childLayout.MeasuredWidth.Size.AsDecimal(); float height = childLayout.MeasuredWidth.Size.AsDecimal(); if (ellipsisText) { Geometry horizontalSpace = GetHorizontalSpace(childLayout.Owner); if ((width > horizontalSpace.Size) || ((width < horizontalSpace.Size) && RelativeLayout.GetFillVertical(childLayout.Owner))) { width = horizontalSpace.Size; } Geometry verticalSpace = GetVerticalSpace(childLayout.Owner); if ((height > verticalSpace.Size) || ((height < verticalSpace.Size) && RelativeLayout.GetFillHorizontal(childLayout.Owner))) { height = verticalSpace.Size; } } else { if (needMeasuredWidth) { Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner); width = horizontalGeometry.Size; } if (needMeasuredHeight) { Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner); height = verticalGeometry.Size; } } // Padding sizes are added because Padding sizes will be subtracted in MeasureChild(). MeasureSpecification childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(width + Padding.Start + Padding.End), MeasureSpecification.ModeType.Exactly); MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(height + Padding.Top + Padding.Bottom), MeasureSpecification.ModeType.Exactly); // To calculate the grand children's Measure() with the mode type Exactly, // children's Measure() is called with MatchParent if the children have WrapContent. // // i.e. // If children have Wrapcontent and the grand children have MatchParent, // then grand children's MeasuredWidth/Height do not fill the children // because the grand children's Measure() is called with the mode type AtMost. int origWidthSpecification = childLayout.Owner.WidthSpecification; int origHeightSpecification = childLayout.Owner.HeightSpecification; if (ellipsisText || needMeasuredWidth) { origWidthSpecification = childLayout.Owner.WidthSpecification; childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent; } if (ellipsisText || needMeasuredHeight) { origHeightSpecification = childLayout.Owner.HeightSpecification; childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent; } MeasureChildWithMargins(childLayout, childWidthMeasureSpec, new LayoutLength(0), childHeightMeasureSpec, new LayoutLength(0)); if (ellipsisText || needMeasuredWidth) { childLayout.Owner.WidthSpecification = origWidthSpecification; } if (ellipsisText || needMeasuredHeight) { childLayout.Owner.HeightSpecification = origHeightSpecification; } } }
public GridChild(LayoutItem layoutItem, Node column, Node row) { LayoutItem = layoutItem; Column = column; Row = row; }
private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var widthMode = widthMeasureSpec.Mode; var heightMode = heightMeasureSpec.Mode; bool isExactly = (widthMode == MeasureSpecification.ModeType.Exactly); bool matchHeight = false; bool allFillParent = true; float maxHeight = 0.0f; float alternativeMaxHeight = 0.0f; float weightedMaxHeight = 0.0f; float totalWeight = 0.0f; // Reset measure variables _totalLength = 0.0f; float usedExcessSpace = 0.0f; HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeOK); // 1st phase: // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs // to accumulate total used space in totalLength based on measured sizes and margins. // Weighted children are not measured at this phase. // Available space for weighted children will be calculated in the phase 2 based on totalLength value. // Max height of children is stored. foreach (LayoutItem childLayout in LayoutChildren) { int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childLayout.Owner.WidthSpecification == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { // Children to be laid out with excess space can be measured later _totalLength += childMargin.Start + childMargin.End; } else { if (useExcessSpace) { // The widthMode is either Unspecified or AtMost, and // this child is only laid out using excess space. Measure // using WrapContent so that we can find out the view's // optimal width. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(childLayout.Padding.Start + childLayout.Padding.End), new LayoutLength(LayoutParamPolicies.WrapContent)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(heightMeasureSpec, new LayoutLength(childLayout.Padding.Top + childLayout.Padding.Bottom), new LayoutLength(childDesiredHeight)); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); usedExcessSpace += childLayout.MeasuredWidth.Size.AsDecimal(); } else { MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); } LayoutLength childWidth = childLayout.MeasuredWidth.Size; LayoutLength length = childWidth + childMargin.Start + childMargin.End; if (isExactly) { _totalLength += length.AsDecimal(); } else { _totalLength = Math.Max(_totalLength, _totalLength + length.AsDecimal() + CellPadding.Width); } } bool matchHeightLocally = false; if (heightMode != MeasureSpecification.ModeType.Exactly && childDesiredHeight == LayoutParamPolicies.MatchParent) { // A child has set to MatchParent on it's height. // Will have to re-measure at least this child when we know exact height. matchHeight = true; matchHeightLocally = true; } float marginHeight = childMargin.Top + childMargin.Bottom; float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight; if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxHeight = Math.Max(maxHeight, childHeight); allFillParent = (allFillParent && childDesiredHeight == LayoutParamPolicies.MatchParent); if (childWeight > 0) { // Heights of weighted Views are invalid if we end up remeasuring, so store them separately. weightedMaxHeight = Math.Max(weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight); } else { alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight); } } // foreach Extents padding = Padding; _totalLength += padding.Start + padding.End; float widthSize = _totalLength; widthSize = Math.Max(widthSize, SuggestedMinimumWidth.AsDecimal()); MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); widthSize = widthSizeAndState.Size.AsDecimal(); // 2nd phase: // Expand children with weight to take up available space // We cycle through weighted children now (children with weight > 0). // The children are measured with exact size equal to their share of the available space based on their weights. // _totalLength is updated to include weighted children measured sizes. float remainingExcess = widthSize - _totalLength + usedExcessSpace; if (remainingExcess != 0 && totalWeight > 0) { float remainingWeight = totalWeight; maxHeight = 0; _totalLength = 0; int numberOfChildren = LayoutChildren.Count; for (int i = 0; i < numberOfChildren; ++i) { LayoutItem childLayout = LayoutChildren[i]; float desiredChildHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; if (childWeight > 0) { MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight, widthMeasureSpec, heightMeasureSpec, childState, Orientation.Horizontal); } float length = childLayout.MeasuredWidth.Size.AsDecimal() + childMargin.Start + childMargin.End; float cellPadding = i < numberOfChildren - 1 ? CellPadding.Width : 0; if (isExactly) { _totalLength += length; } else { float totalLength = _totalLength; _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } bool matchHeightLocally = (heightMode != MeasureSpecification.ModeType.Exactly) && (desiredChildHeight == LayoutParamPolicies.MatchParent); float marginHeight = childMargin.Top + childMargin.Bottom; float childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + marginHeight; maxHeight = Math.Max(maxHeight, childHeight); alternativeMaxHeight = Math.Max(alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight); allFillParent = (allFillParent && desiredChildHeight == LayoutParamPolicies.MatchParent); _totalLength += padding.Start + padding.End; } // for loop } else { // No excess space or no weighted children alternativeMaxHeight = Math.Max(alternativeMaxHeight, weightedMaxHeight); } if (!allFillParent && heightMode != MeasureSpecification.ModeType.Exactly) { maxHeight = alternativeMaxHeight; } maxHeight += padding.Top + padding.Bottom; maxHeight = Math.Max(maxHeight, SuggestedMinimumHeight.AsRoundedValue()); widthSizeAndState.State = childState.widthState; SetMeasuredDimensions(widthSizeAndState, ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childState.heightState)); if (matchHeight) { ForceUniformHeight(widthMeasureSpec); } } // MeasureHorizontal
protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var gridWidthMode = widthMeasureSpec.Mode; var gridHeightMode = heightMeasureSpec.Mode; int widthSize = (int)widthMeasureSpec.Size.AsRoundedValue(); int heightSize = (int)heightMeasureSpec.Size.AsRoundedValue(); int availableContentWidth; int availableContentHeight; int desiredChildHeight; int desiredChildWidth; Extents gridLayoutPadding = Padding; var childCount = _children.Count; // WIDTH SPECIFICATIONS // measure first child and use it's dimensions for layout measurement if (childCount > 0) { LayoutItem childLayoutItem = _children[0]; View childOwner = childLayoutItem.Owner; MeasureChild(childLayoutItem, widthMeasureSpec, heightMeasureSpec); desiredChildHeight = (int)childLayoutItem.MeasuredHeight.Size.AsRoundedValue(); desiredChildWidth = (int)childLayoutItem.MeasuredWidth.Size.AsRoundedValue(); // If child has a margin then add it to desired size Extents childMargin = childLayoutItem.Margin; desiredChildHeight += childMargin.Top + childMargin.Bottom; desiredChildWidth += childMargin.Start + childMargin.End; _totalWidth = desiredChildWidth * _columns; // Include padding for max and min checks _totalWidth += gridLayoutPadding.Start + gridLayoutPadding.End; // Ensure width does not exceed specified at most width or less than mininum width _totalWidth = Math.Max(_totalWidth, (int)SuggestedMinimumWidth.AsRoundedValue()); // widthMode EXACTLY so grid must be the given width if (gridWidthMode == MeasureSpecification.ModeType.Exactly || gridWidthMode == MeasureSpecification.ModeType.AtMost) { // In the case of AT_MOST, widthSize is the max limit. _totalWidth = Math.Min(_totalWidth, widthSize); } availableContentWidth = _totalWidth - gridLayoutPadding.Start - gridLayoutPadding.End; widthSize = _totalWidth; // HEIGHT SPECIFICATIONS // heightMode EXACTLY so grid must be the given height if (gridHeightMode == MeasureSpecification.ModeType.Exactly || gridHeightMode == MeasureSpecification.ModeType.AtMost) { if (childCount > 0) { _totalHeight = gridLayoutPadding.Top + gridLayoutPadding.Bottom; for (int i = 0; i < childCount; i += _columns) { _totalHeight += desiredChildHeight; } // Ensure ourHeight does not exceed specified at most height _totalHeight = Math.Min(_totalHeight, heightSize); _totalHeight = Math.Max(_totalHeight, (int)SuggestedMinimumHeight.AsRoundedValue()); heightSize = _totalHeight; } // Child exists // In the case of AT_MOST, availableContentHeight is the max limit. availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom; } else { // Grid expands to fit content // If number of columns AUTO_FIT then set to 1 column. _columns = (_columns > 0) ? _columns : 1; // Calculate numbers of rows, round down result as later check for remainder. _rows = childCount / _columns; // If number of cells not cleanly dividable by columns, add another row to house remainder cells. _rows += (childCount % _columns > 0) ? 1 : 0; availableContentHeight = desiredChildHeight * _rows; } // If number of columns not defined DetermineNumberOfColumns(availableContentWidth); // Locations define the start, end,top and bottom of each cell. _locations.CalculateLocations(_columns, availableContentWidth, availableContentHeight, childCount); } // Children exists SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK), ResolveSizeAndState(new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK)); }
private void InitChildrenData(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { int childCount = LayoutChildren.Count; bool isHorizontal = (GridOrientation == Orientation.Horizontal); int mainPivot = 0, subPivot = 0; int[] pivotStack = new int[isHorizontal ? Columns : Rows]; vLocations = hLocations = null; vEdgeList = hEdgeList = null; gridChildren = new GridChild[childCount]; maxColumnConut = Columns; maxRowCount = Rows; totalVerticalExpand = 0; totalHorizontalExpand = 0; for (int i = 0; i < childCount; i++) { LayoutItem item = LayoutChildren[i]; View view = item?.Owner; if (view == null) { continue; } int column, columnSpan, row, rowSpan; StretchFlags verticalStretch, horizontalStretch; column = GetColumn(view); columnSpan = GetColumnSpan(view); row = GetRow(view); rowSpan = GetRowSpan(view); verticalStretch = GetVerticalStretch(view); horizontalStretch = GetHorizontalStretch(view); if (column + columnSpan > Columns || row + rowSpan > Rows) { if (column + columnSpan > Columns) { Tizen.Log.Error("NUI", "Column + ColumnSapn exceeds Grid Columns. Column + ColumnSpan (" + column + " + " + columnSpan + ") > Grid Columns(" + Columns + ")"); } else { Tizen.Log.Error("NUI", "Row + RowSapn exceeds Grid Rows. Row + RowSapn (" + row + " + " + rowSpan + ") > Grid Rows(" + Rows + ")"); } gridChildren[i] = new GridChild(null, new Node(0, 1, 0, 0), new Node(0, 1, 0, 0)); continue; } if (horizontalStretch.HasFlag(StretchFlags.Expand)) { totalHorizontalExpand++; } if (verticalStretch.HasFlag(StretchFlags.Expand)) { totalVerticalExpand++; } // assign column/row depending on GridOrientation. The main axis count(Columns on Horizontal, Rows otherwise) won't be exceeded // explicit column(row) count which is assigned by Columns(Rows). but, cross axis count(Rows(Columns)) can be increased by sub axis count. if (column == CellUndefined || row == CellUndefined) { (int point, int span)mainAxis = isHorizontal ? (column, columnSpan) : (row, rowSpan); (int point, int span)subAxis = isHorizontal ? (row, rowSpan) : (column, columnSpan); if (subAxis.point != CellUndefined) { subPivot = subAxis.point; } if (mainAxis.point != CellUndefined) { mainPivot = mainAxis.point; } if (mainPivot + mainAxis.span > pivotStack.Length) { mainPivot = 0; subPivot++; } for (int n = mainPivot + mainAxis.span - 1; n >= mainPivot; n--) { if (pivotStack[n] > subPivot) { mainPivot = n + 1; n = mainPivot + mainAxis.span; if (n > pivotStack.Length) { if (mainAxis.point != CellUndefined) { mainPivot = mainAxis.point; } else { mainPivot = 0; } n = mainPivot + mainAxis.span; subPivot++; } } } if (isHorizontal) { column = mainPivot; row = subPivot; } else { column = subPivot; row = mainPivot; } for (int start = mainPivot, end = mainPivot + mainAxis.span; start < end; start++) { pivotStack[start] = subPivot + subAxis.span; } mainPivot += mainAxis.span; } if (maxColumnConut < column + columnSpan) { maxColumnConut = column + columnSpan; } if (maxRowCount < row + rowSpan) { maxRowCount = row + rowSpan; } MeasureChildWithMargins(item, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); gridChildren[i] = new GridChild(item, new Node(column, columnSpan, item.MeasuredWidth.Size.AsDecimal(), horizontalStretch), new Node(row, rowSpan, item.MeasuredHeight.Size.AsDecimal(), verticalStretch)); } }
/// <summary> /// Measure the layout and its content to determine the measured width and the measured height.<br /> /// </summary> /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param> /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param> /// <since_tizen> 6 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL; Extents padding = Owner.Padding; Extents margin = Owner.Margin; Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin)); Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding)); float width = FlexUndefined; // Behaves as WrapContent (Flex Auto) float height = FlexUndefined; // Behaves as WrapContent (Flex Auto) if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost) { width = widthMeasureSpec.Size.AsDecimal(); } if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost) { height = heightMeasureSpec.Size.AsDecimal(); } // Save measureSpec to measure children. // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't // because measurChild function is called by native callback. parentMeasureSpecificationWidth = widthMeasureSpec; parentMeasureSpecificationHeight = heightMeasureSpec; // Assign child properties for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem layoutItem = LayoutChildren[i]; View Child = layoutItem?.Owner; HandleRef childHandleRef = (HandleRef)Child.GetValue(FlexItemProperty); if (childHandleRef.Handle == IntPtr.Zero || Child == null) { continue; } AlignmentType flexAlignemnt = GetFlexAlignmentSelf(Child); PositionType flexPosition = GetFlexPositionType(Child); float flexAspectRatio = GetFlexAspectRatio(Child); float flexBasis = GetFlexBasis(Child); float flexShrink = GetFlexShrink(Child); float flexGrow = GetFlexGrow(Child); Interop.FlexLayout.FlexLayout_SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt); Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)flexPosition); Interop.FlexLayout.FlexLayout_SetFlexAspectRatio(childHandleRef, flexAspectRatio); Interop.FlexLayout.FlexLayout_SetFlexBasis(childHandleRef, flexBasis); Interop.FlexLayout.FlexLayout_SetFlexShrink(childHandleRef, flexShrink); Interop.FlexLayout.FlexLayout_SetFlexGrow(childHandleRef, flexGrow); } Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width, height, isLayoutRtl); LayoutLength flexLayoutWidth = new LayoutLength(Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr)); LayoutLength flexLayoutHeight = new LayoutLength(Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr)); Debug.WriteLineIf(LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue() + " height:" + flexLayoutHeight.AsRoundedValue()); SetMeasuredDimensions(GetDefaultSize(flexLayoutWidth, widthMeasureSpec), GetDefaultSize(flexLayoutHeight, heightMeasureSpec)); }
/// <inheritdoc/> /// <since_tizen> 9 </since_tizen> protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } } } (float childrenWidth, float childrenHeight) = CalculateChildrenSize(widthMeasureSpec.Size.AsDecimal(), heightMeasureSpec.Size.AsDecimal()); SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(childrenWidth), widthMeasureSpec, childWidthState), ResolveSizeAndState(new LayoutLength(childrenHeight), heightMeasureSpec, childHeightState)); // RelativeLayout sets its children's size in OnLayout(). // Therefore, the children's MeasuredWidth/Height are not the same with the children's size. // This causes that the grand children's MeasuredWidth/Height are calculated incorrectly. // To resolve the above, RelativeLayout updates its children's MeasuredWidth/Height after // the RelativeLayout's MeasuredWidth/Height are calculated. // // e.g. // Let parent have RelativeLayout and parent's size be 1920x1080. // Let child have WrapContent with SetFillHorizontal/Vertical true. // Let grand child have MatchParent. // Then, child's size is 1920x1080 but child's MeasuredWidth/Height is 0x0. // Then, grand child's MeasuredWidth/Height is 0x0 and size is 0x0. // // TODO: Not to do the following operation in OnLayout() again. for (int i = 0; i < LayoutChildren.Count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { LayoutLength childLeft; LayoutLength childRight; LayoutLength childTop; LayoutLength childBottom; if (childLayout.Owner.WidthSpecification == LayoutParamPolicies.MatchParent) { childLeft = new LayoutLength(childLayout.Margin.Start); childRight = new LayoutLength(MeasuredWidth.Size.AsDecimal() - childLayout.Margin.End); } else { Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner); childLeft = new LayoutLength(horizontalGeometry.Position + Padding.Start + childLayout.Margin.Start); childRight = new LayoutLength(horizontalGeometry.Position + horizontalGeometry.Size + Padding.Start - childLayout.Margin.End); } if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.MatchParent) { childTop = new LayoutLength(childLayout.Margin.Top); childBottom = new LayoutLength(MeasuredHeight.Size.AsDecimal() - childLayout.Margin.Bottom); } else { Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner); childTop = new LayoutLength(verticalGeometry.Position + Padding.Top + childLayout.Margin.Top); childBottom = new LayoutLength(verticalGeometry.Position + verticalGeometry.Size + Padding.Top - childLayout.Margin.Bottom); } // Padding sizes are added because Padding sizes will be subtracted in MeasureChild(). MeasureSpecification childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childRight.AsDecimal() - childLeft.AsDecimal() + Padding.Start + Padding.End), MeasureSpecification.ModeType.Exactly); MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childBottom.AsDecimal() - childTop.AsDecimal() + Padding.Top + Padding.Bottom), MeasureSpecification.ModeType.Exactly); int origWidthSpecification = childLayout.Owner.WidthSpecification; int origHeightSpecification = childLayout.Owner.HeightSpecification; // To calculate the grand children's Measure() with the mode type Exactly, // children's Measure() is called with MatchParent if the children have WrapContent. // // i.e. // If children have Wrapcontent and the grand children have MatchParent, // then grand children's MeasuredWidth/Height do not fill the children // because the grand children's Measure() is called with the mode type AtMost. if (childLayout.Owner.WidthSpecification == LayoutParamPolicies.WrapContent) { childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent; } if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.WrapContent) { childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent; } // Use MeasureChild() because Margin sizes were already subtracted from childWidth/HeightMeasureSpec. MeasureChild(childLayout, childWidthMeasureSpec, childHeightMeasureSpec); childLayout.Owner.WidthSpecification = origWidthSpecification; childLayout.Owner.HeightSpecification = origHeightSpecification; } } }
internal void MeasureChildWithMarginsNative(LayoutItem child, LayoutMeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed, LayoutMeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed) { LayoutPINVOKE.LayoutGroupWrapperImpl_MeasureChildWithMarginsSwigExplicitLayoutGroupWrapperImpl(swigCPtr, LayoutItem.getCPtr(child), LayoutMeasureSpec.getCPtr(parentWidthMeasureSpec), LayoutLength.getCPtr(widthUsed), LayoutMeasureSpec.getCPtr(parentHeightMeasureSpec), LayoutLength.getCPtr(heightUsed)); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
/// <summary> /// Callback when child is removed from container.<br /> /// </summary> /// <param name="child">The Layout child.</param> /// <since_tizen> 6 </since_tizen> protected override void OnChildRemove(LayoutItem child) { // When child View is removed from it's parent View (that is a Layout) then remove it from the layout too. // FlexLayout refers to the child as a View not LayoutItem. Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child); }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// The heavy lifting is done in GetChildMeasureSpec.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> protected virtual void MeasureChild(LayoutItem child, LayoutMeasureSpec parentWidthMeasureSpec, LayoutMeasureSpec parentHeightMeasureSpec) { layoutGroupWrapperImpl.MeasureChildNative(child, parentWidthMeasureSpec, parentHeightMeasureSpec); }
private void MeasureWeightedChild(LayoutItem childLayout, float remainingExcess, float remainingWeight, float childWeight, MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec, HeightAndWidthState childState, Orientation orientation) { bool horizontal = false; if (orientation == Orientation.Horizontal) { horizontal = true; } float childsShare = (childWeight * remainingExcess) / remainingWeight; remainingExcess -= childsShare; remainingWeight -= childWeight; float desiredWidth = childLayout.Owner.WidthSpecification; float desiredHeight = childLayout.Owner.HeightSpecification; float childLength = 0; Extents layoutPadding = Padding; // Always lay out weighted elements with intrinsic size regardless of the parent spec. // for consistency between specs. if ((horizontal && (desiredWidth == 0)) || (!horizontal && (desiredHeight == 0))) { // This child needs to be laid out from scratch using // only its share of excess space. childLength = childsShare; } else { // This child had some intrinsic width to which we // need to add its share of excess space. if (horizontal) { childLength = childLayout.MeasuredWidth.Size.AsDecimal() + childsShare; } else { childLength = childLayout.MeasuredHeight.Size.AsDecimal() + childsShare; } } MeasureSpecification childWidthMeasureSpec; MeasureSpecification childHeightMeasureSpec; if (horizontal) { childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(childLength), MeasureSpecification.ModeType.Exactly); childHeightMeasureSpec = GetChildMeasureSpecification(heightMeasureSpec, new LayoutLength(layoutPadding.Top + layoutPadding.Bottom), new LayoutLength(desiredHeight)); } else // vertical { childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(Padding.Start + Padding.End), new LayoutLength(desiredWidth)); childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(childLength), MeasureSpecification.ModeType.Exactly); } childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); // Child may now not fit in horizontal dimension. if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } // Child may now not fit in vertical dimension. if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } }
/// <summary> /// Ask one of the children of this view to measure itself, taking into /// account both the MeasureSpec requirements for this view and its padding.<br /> /// and margins. The child must have MarginLayoutParams The heavy lifting is /// done in GetChildMeasureSpec.<br /> /// </summary> /// <param name="child">The child to measure.</param> /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param> /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param> /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param> /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param> protected virtual void MeasureChildWithMargins(LayoutItem child, LayoutMeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed, LayoutMeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed) { layoutGroupWrapperImpl.MeasureChildWithMarginsNative(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); }
} // MeasureHorizontal private void MeasureVertical(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { var widthMode = widthMeasureSpec.Mode; var heightMode = heightMeasureSpec.Mode; bool isExactly = (heightMode == MeasureSpecification.ModeType.Exactly); bool matchWidth = false; bool allFillParent = true; float maxWidth = 0.0f; float alternativeMaxWidth = 0.0f; float weightedMaxWidth = 0.0f; float totalWeight = 0.0f; // Reset total length _totalLength = 0.0f; float usedExcessSpace = 0.0f; HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK, MeasuredSize.StateType.MeasuredSizeTooSmall); // measure children, and determine if further resolution is required // 1st phase: // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs // to accumulate total used space in _totalLength. // Weighted children are not measured in this phase. // Available space for weighted children will be calculated in the phase 2 based on _totalLength value. uint index = 0; foreach (LayoutItem childLayout in LayoutChildren) { int childDesiredWidth = childLayout.Owner.WidthSpecification; int childDesiredHeight = childLayout.Owner.HeightSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; totalWeight += childWeight; bool useExcessSpace = (childDesiredHeight == 0) && (childWeight > 0); if (isExactly && useExcessSpace) { _totalLength = Math.Max(_totalLength, (_totalLength + childMargin.Top + childMargin.Bottom)); } else { float childHeight = 0.0f; if (useExcessSpace) { // The heightMode is either Unspecified or AtMost, and // this child is only laid out using excess space. Measure // using WrapContent so that we can find out the view's // optimal height. // We'll restore the original height of 0 after measurement. MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(widthMeasureSpec, new LayoutLength(childLayout.Padding.Start + childLayout.Padding.End), new LayoutLength(childDesiredWidth)); MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(heightMeasureSpec, new LayoutLength(childLayout.Padding.Top + childLayout.Padding.Bottom), new LayoutLength(LayoutParamPolicies.WrapContent)); childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec); childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); usedExcessSpace += childHeight; } else { MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec); childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); } float length = childHeight + childMargin.Top + childMargin.Bottom; float cellPadding = CellPadding.Height; // No need to add cell padding to the end of last item. if (index >= LayoutChildren.Count - 1) { cellPadding = 0.0f; } _totalLength = Math.Max(_totalLength, _totalLength + length + cellPadding); } bool matchWidthLocally = false; if (widthMode != MeasureSpecification.ModeType.Exactly && (childDesiredWidth == LayoutParamPolicies.MatchParent)) { // Will have to re-measure at least this child when we know exact height. matchWidth = true; matchWidthLocally = true; } float marginWidth = (childLayout.Margin.Start) + (childLayout.Margin.End); float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth; if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall; } if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall) { childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall; } maxWidth = Math.Max(maxWidth, childWidth); allFillParent = (allFillParent && (childDesiredWidth == LayoutParamPolicies.MatchParent)); float widthforWeight = childWidth; if (matchWidthLocally) { widthforWeight = marginWidth; } if (childWeight > 0) { // Widths of weighted Views are bogus if we end up remeasuring, so keep them separate. weightedMaxWidth = Math.Max(weightedMaxWidth, widthforWeight); } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, widthforWeight); } index++; } // foreach Extents padding = Padding; _totalLength += padding.Top + padding.Bottom; LayoutLength heightSize = new LayoutLength(_totalLength); heightSize = new LayoutLength(Math.Max(heightSize.AsDecimal(), SuggestedMinimumHeight.AsDecimal())); MeasuredSize heightSizeAndState = ResolveSizeAndState(heightSize, heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); heightSize = heightSizeAndState.Size; // 2nd phase: // We cycle through weighted children now (children with weight > 0). // The children are measured with exact size equal to their share of the available space based on their weights. // _totalLength is updated to include weighted children measured sizes. float remainingExcess = heightSize.AsDecimal() - _totalLength + usedExcessSpace; if (remainingExcess != 0 && totalWeight > 0.0f) { float remainingWeight = totalWeight; _totalLength = 0; int numberOfChildren = LayoutChildren.Count; for (int i = 0; i < numberOfChildren; ++i) { LayoutItem childLayout = LayoutChildren[i]; float desiredChildWidth = childLayout.Owner.WidthSpecification; float childWeight = childLayout.Owner.Weight; Extents childMargin = childLayout.Margin; if (childWeight > 0) { MeasureWeightedChild(childLayout, remainingExcess, remainingWeight, childWeight, widthMeasureSpec, heightMeasureSpec, childState, Orientation.Vertical); } bool matchWidthLocally = false; if (widthMode != MeasureSpecification.ModeType.Exactly && desiredChildWidth == LayoutParamPolicies.MatchParent) { // Will have to re-measure at least this child when we know exact height. matchWidth = true; matchWidthLocally = true; } float marginWidth = childMargin.Start + childMargin.End; float childWidth = childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth; maxWidth = Math.Max(maxWidth, childWidth); allFillParent = allFillParent && desiredChildWidth == LayoutParamPolicies.MatchParent; float childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); float childLength = childHeight + childMargin.Top + childMargin.Bottom; float cellPadding = i < numberOfChildren - 1 ? CellPadding.Height : 0.0f; _totalLength = _totalLength + childLength + cellPadding; alternativeMaxWidth = Math.Max(alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth); } // for loop // Add in our padding _totalLength += padding.Top + padding.Bottom; } else { alternativeMaxWidth = Math.Max(alternativeMaxWidth, weightedMaxWidth); } if (!allFillParent && widthMode != MeasureSpecification.ModeType.Exactly) { maxWidth = alternativeMaxWidth; } maxWidth += padding.Start + padding.End; maxWidth = Math.Max(maxWidth, SuggestedMinimumWidth.AsRoundedValue()); heightSizeAndState.State = childState.heightState; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childState.widthState), heightSizeAndState); if (matchWidth) { ForceUniformWidth(heightMeasureSpec); } } // MeasureVertical
public static extern global::System.IntPtr FlexLayout_RemoveChild(global::System.Runtime.InteropServices.HandleRef jarg1, LayoutItem jarg2);
} // LayoutHorizontally private void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom) { LayoutLength childTop = new LayoutLength(Padding.Top); LayoutLength childLeft = new LayoutLength(Padding.Start); // Where end of child should go LayoutLength width = new LayoutLength(right - left); // Space available for child LayoutLength childSpace = new LayoutLength(width - Padding.Start - Padding.End); int count = LayoutChildren.Count; switch (LinearAlignment) { case Alignment.Bottom: // totalLength contains the padding already childTop = new LayoutLength(Padding.Top + bottom.AsDecimal() - top.AsDecimal() - _totalLength); break; case Alignment.CenterVertical: // FALL THROUGH case Alignment.Center: // totalLength contains the padding already childTop = new LayoutLength(Padding.Top + (bottom.AsDecimal() - top.AsDecimal() - _totalLength) / 2.0f); break; case Alignment.Top: // FALL THROUGH (default) default: // totalLength contains the padding already childTop = new LayoutLength(Padding.Top); break; } for (int i = 0; i < count; i++) { LayoutItem childLayout = LayoutChildren[i]; if (childLayout != null) { LayoutLength childWidth = childLayout.MeasuredWidth.Size; LayoutLength childHeight = childLayout.MeasuredHeight.Size; Extents childMargin = childLayout.Margin; childTop += childMargin.Top; switch (LinearAlignment) { case Alignment.Begin: default: { childLeft = new LayoutLength(Padding.Start + childMargin.Start); break; } case Alignment.End: { childLeft = new LayoutLength(width - Padding.End - childWidth - childMargin.End); break; } case Alignment.CenterHorizontal: case Alignment.Center: // FALL THROUGH { childLeft = new LayoutLength(Padding.Start + ((childSpace - childWidth).AsDecimal() / 2.0f) + childMargin.Start - childMargin.End); break; } } childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); childTop += childHeight + childMargin.Bottom + CellPadding.Height; } } } // LayoutVertical
/// <summary> /// Add LayoutItem to a removal stack for removal after transitions finish. /// </summary> /// <param name="itemToRemove">LayoutItem to remove.</param> internal void AddToRemovalStack(LayoutItem itemToRemove) { transitionManager.AddToRemovalStack(itemToRemove); }