-/** Copyright (c) 2018 Samsung Electronics Co., Ltd.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-*/
-using System.ComponentModel;
+/* Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using Tizen.NUI.BaseComponents;
+using System.Collections.Generic;
namespace Tizen.NUI
{
/// <summary>
/// [Draft] This class implements a linear box layout, automatically handling right to left or left to right direction change.
/// </summary>
- internal class LinearLayout : LayoutGroupWrapper
+ internal class LinearLayout : LayoutGroup
{
- private global::System.Runtime.InteropServices.HandleRef swigCPtr;
-
- internal LinearLayout(global::System.IntPtr cPtr, bool cMemoryOwn) : base(LayoutPINVOKE.LinearLayout_SWIGUpcast(cPtr), cMemoryOwn)
+ /// <summary>
+ /// [Draft] Enumeration for the direction in which the content is laid out
+ /// </summary>
+ public enum Orientation
{
- swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
+ /// <summary>
+ /// Horizontal (row)
+ /// </summary>
+ Horizontal,
+ /// <summary>
+ /// Vertical (column)
+ /// </summary>
+ Vertical
}
- internal static global::System.Runtime.InteropServices.HandleRef getCPtr(LinearLayout obj)
+ /// <summary>
+ /// [Draft] Enumeration for the alignment of the linear layout items
+ /// </summary>
+ public enum Alignment
{
- return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
+ /// <summary>
+ /// At the left/right edge of the container (maps to LTR/RTL direction for horizontal orientation)
+ /// </summary>
+ Begin = 0x1,
+ /// <summary>
+ /// At the right/left edge of the container (maps to LTR/RTL direction for horizontal orientation)
+ /// </summary>
+ End = 0x2,
+ /// <summary>
+ /// At the horizontal center of the container
+ /// </summary>
+ CenterHorizontal = 0x4,
+ /// <summary>
+ /// At the top edge of the container
+ /// </summary>
+ Top = 0x8,
+ /// <summary>
+ /// At the bottom edge of the container
+ /// </summary>
+ Bottom = 0x10,
+ /// <summary>
+ /// At the vertical center of the container
+ /// </summary>
+ CenterVertical = 0x20
}
- protected override void Dispose(DisposeTypes type)
+ struct HeightAndWidthState
{
- if (disposed)
+ public MeasuredSize.StateType widthState;
+ public MeasuredSize.StateType heightState;
+
+ public HeightAndWidthState( MeasuredSize.StateType width, MeasuredSize.StateType height)
{
- return;
+ widthState = width;
+ heightState = height;
}
+ }
- if (type == DisposeTypes.Explicit)
+ /// <summary>
+ /// [Draft] Get/Set the orientation in the layout
+ /// </summary>
+ public LinearLayout.Orientation LinearOrientation
+ {
+ get
{
- //Called by User
- //Release your own managed resources here.
- //You should release all of your own disposable objects here.
-
+ return _linearOrientation;
}
-
- //Release your own unmanaged resources here.
- //You should not access any managed member here except static instance.
- //because the execution order of Finalizes is non-deterministic.
- if (swigCPtr.Handle != global::System.IntPtr.Zero)
+ set
{
- if (swigCMemOwn)
- {
- swigCMemOwn = false;
- LayoutPINVOKE.delete_LinearLayout(swigCPtr);
- }
- swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
+ _linearOrientation = value;
+ RequestLayout();
}
- base.Dispose(type);
}
- internal static class ChildProperty
- {
- internal static readonly int WEIGHT = LayoutPINVOKE.LinearLayout_ChildProperty_WEIGHT_get();
- }
- public LinearLayout() : this(LayoutPINVOKE.LinearLayout_New(), true)
+ /// <summary>
+ /// [Draft] Get/Set the padding between cells in the layout
+ /// </summary>
+ public Size2D CellPadding
{
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ get
+ {
+ return _cellPadding;
+ }
+ set
+ {
+ _cellPadding = value;
+ RequestLayout();
+ }
}
- public new static LinearLayout DownCast(BaseHandle handle)
- {
- LinearLayout ret = new LinearLayout(LayoutPINVOKE.LinearLayout_DownCast(BaseHandle.getCPtr(handle)), true);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
- }
- internal LinearLayout(LinearLayout other) : this(LayoutPINVOKE.new_LinearLayout__SWIG_1(LinearLayout.getCPtr(other)), true)
+ /// <summary>
+ /// [Draft] Get/Set the alignment in the layout
+ /// </summary>
+ public LinearLayout.Alignment LinearAlignment{ get; set; } = Alignment.CenterVertical;
+
+ private float _totalLength = 0.0f;
+ private Size2D _cellPadding = new Size2D(0,0);
+ private Orientation _linearOrientation = Orientation.Horizontal;
+
+ /// <summary>
+ /// [Draft] Constructor
+ /// </summary>
+ public LinearLayout()
{
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
- internal LinearLayout Assign(LinearLayout other)
+ protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
{
- LinearLayout ret = new LinearLayout(LayoutPINVOKE.LinearLayout_Assign(swigCPtr, LinearLayout.getCPtr(other)), false);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ if (_linearOrientation == Orientation.Horizontal)
+ {
+ MeasureHorizontal(widthMeasureSpec, heightMeasureSpec);
+ }
+ else
+ {
+ MeasureVertical(widthMeasureSpec, heightMeasureSpec);
+ }
}
- internal void SetCellPadding(LayoutSize size)
+ protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
{
- LayoutPINVOKE.LinearLayout_SetCellPadding(swigCPtr, LayoutSize.getCPtr(size));
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ if (_linearOrientation == Orientation.Horizontal)
+ {
+ LayoutHorizontal(left, top, right, bottom);
+ }
+ else
+ {
+ LayoutVertical(left, top, right, bottom);
+ }
}
- internal LayoutSize GetCellPadding()
+ private void MeasureHorizontal(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
{
- LayoutSize ret = new LayoutSize(LayoutPINVOKE.LinearLayout_GetCellPadding(swigCPtr), true);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
- }
+ 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 total length
+ _totalLength = 0.0f;
+ LayoutLength usedExcessSpace = new LayoutLength(0);
- internal void SetOrientation(LinearLayout.Orientation orientation)
- {
- LayoutPINVOKE.LinearLayout_SetOrientation(swigCPtr, (int)orientation);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- }
+ HeightAndWidthState childState = new HeightAndWidthState(MeasuredSize.StateType.MeasuredSizeOK,
+ MeasuredSize.StateType.MeasuredSizeOK);
- internal LinearLayout.Orientation GetOrientation()
- {
- LinearLayout.Orientation ret = (LinearLayout.Orientation)LayoutPINVOKE.LinearLayout_GetOrientation(swigCPtr);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
- }
+ // Measure children, and determine if further resolution is required
- internal void SetAlignment(LinearLayout.Alignment alignment)
- {
- LayoutPINVOKE.LinearLayout_SetAlignment(swigCPtr, (uint)alignment);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- }
+ // 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.
- internal LinearLayout.Alignment GetAlignment()
- {
- LinearLayout.Alignment ret = (LinearLayout.Alignment)LayoutPINVOKE.LinearLayout_GetAlignment(swigCPtr);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
- }
+ foreach( LayoutItem childLayout in _children )
+ {
+ LayoutLength childDesiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification);
+ LayoutLength childDesiredHeight = new LayoutLength(childLayout.Owner.HeightSpecification);
+ float childWeight = childLayout.Owner.Weight;
+ Extents childMargin = childLayout.Margin;
+ totalWeight += childWeight;
- internal enum PropertyRange
- {
- CHILD_PROPERTY_START_INDEX = PropertyRanges.CHILD_PROPERTY_REGISTRATION_START_INDEX,
- CHILD_PROPERTY_END_INDEX = PropertyRanges.CHILD_PROPERTY_REGISTRATION_START_INDEX + 1000
- }
+ bool useExcessSpace = (childDesiredWidth.AsRoundedValue() == 0 ) && (childWeight > 0);
+ if( isExactly && useExcessSpace )
+ {
+ _totalLength += childMargin.Start + childMargin.End;
+ }
+ else
+ {
+ if( useExcessSpace )
+ {
+ // The widthMode is either UNSPECIFIED or AT_MOST, and
+ // this child is only laid out using excess space. Measure
+ // using WRAP_CONTENT 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),
+ childDesiredHeight);
+ childLayout.Measure( childWidthMeasureSpec, childHeightMeasureSpec);
+ usedExcessSpace += childLayout.MeasuredWidth.Size;
+ }
+ else
+ {
+ MeasureChild(childLayout, widthMeasureSpec, heightMeasureSpec);
+ }
+ LayoutLength childWidth = childLayout.MeasuredWidth.Size;
- /// <summary>
- /// [Draft] Get/Set the orientation in the layout
- /// </summary>
- public LinearLayout.Orientation LinearOrientation
+ 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 && (int)childDesiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent)
+ {
+ // Will have to re-measure at least this child when we know exact height.
+ matchHeight = true;
+ matchHeightLocally = true;
+ }
+
+ LayoutLength marginHeight = new LayoutLength(childMargin.Top + childMargin.Bottom);
+ LayoutLength childHeight = childLayout.MeasuredHeight.Size + marginHeight;
+
+ if( childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall )
+ {
+ childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
+ }
+ if( childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
+ {
+ childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
+ }
+
+ maxHeight = Math.Max( maxHeight, childHeight.AsDecimal());
+ allFillParent = ( allFillParent && childDesiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent );
+
+ if( childWeight > 0 )
+ {
+ /*
+ * Heights of weighted Views are bogus if we end up
+ * remeasuring, so keep them separate.
+ */
+ weightedMaxHeight = Math.Max( weightedMaxHeight, matchHeightLocally ? marginHeight.AsDecimal() : childHeight.AsDecimal());
+ }
+ else
+ {
+ alternativeMaxHeight = Math.Max( alternativeMaxHeight, matchHeightLocally ? marginHeight.AsDecimal() : childHeight.AsDecimal() );
+ }
+ } // foreach
+
+ Extents padding = Padding;
+ _totalLength += padding.Start + padding.End;
+ LayoutLength widthSize = new LayoutLength(_totalLength);
+ widthSize = new LayoutLength( Math.Max( widthSize.AsDecimal(), SuggestedMinimumWidth.AsDecimal()));
+ MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+ widthSize = widthSizeAndState.Size;
+
+ 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( _children.Count, widthMeasureSpec );
+ }
+ } // MeasureHorizontally
+
+ void MeasureVertical( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
{
- get
+ 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;
+ LayoutLength usedExcessSpace = new LayoutLength(0);
+
+ 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 mTotalLength 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 mTotalLength value.
+ uint index = 0;
+ foreach( LayoutItem childLayout in _children )
{
- return GetOrientation();
+ LayoutLength childDesiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification);
+ LayoutLength childDesiredHeight = new LayoutLength(childLayout.Owner.HeightSpecification);
+ float childWeight = childLayout.Owner.Weight;
+ Extents childMargin = childLayout.Margin;
+ totalWeight += childWeight;
+
+ bool useExcessSpace = (childDesiredHeight.AsRoundedValue() == 0) && (childWeight > 0);
+
+ if( isExactly && useExcessSpace )
+ {
+ LayoutLength totalLength = new LayoutLength(_totalLength);
+ _totalLength = Math.Max( totalLength.AsDecimal(), (totalLength + childMargin.Top + childMargin.Bottom).AsDecimal() );
+ }
+ else
+ {
+ LayoutLength childHeight = new LayoutLength(0);
+ 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),
+ 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;
+ usedExcessSpace += childHeight;
+ }
+ else
+ {
+ MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
+ childHeight = childLayout.MeasuredHeight.Size;
+ }
+
+ float length = (childHeight + childMargin.Top + childMargin.Bottom).AsDecimal();
+ float cellPadding = CellPadding.Height;
+ // No need to add cell padding to the end of last item.
+ if (index>=_children.Count-1)
+ {
+ cellPadding = 0.0f;
+ }
+ _totalLength = Math.Max( _totalLength, _totalLength + length + cellPadding );
+ }
+
+ bool matchWidthLocally = false;
+ if( widthMode != MeasureSpecification.ModeType.Exactly && childDesiredWidth == new LayoutLength(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);
+ LayoutLength childWidth = new LayoutLength(childLayout.MeasuredWidth.Size.AsDecimal() + marginWidth);
+
+ // was combineMeasuredStates()
+ if (childLayout.MeasuredWidthAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
+ {
+ childState.widthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
+ }
+ if (childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
+ {
+ childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
+ }
+
+ maxWidth = (Math.Max( maxWidth, childWidth.AsDecimal()));
+ allFillParent = (allFillParent && (childDesiredWidth == new LayoutLength(LayoutParamPolicies.MatchParent)));
+
+ float widthforWeight = childWidth.AsDecimal();
+ 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);
+ }
+ } // 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;
+
+ // Either expand children with weight to take up available space or
+ // shrink them if they extend beyond our current bounds. If we skipped
+ // measurement on any children, we need to measure them now.
+
+ // 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.AsDecimal();
+
+ if( remainingExcess != 0 && totalWeight > 0.0f )
+ {
+ float remainingWeightSum = totalWeight;
+
+ _totalLength = 0;
+
+ foreach( LayoutItem childLayout in _children )
+ {
+ int desiredWidth = childLayout.Owner.WidthSpecification;
+ int desiredHeight = childLayout.Owner.HeightSpecification;
+ float childWeight = childLayout.Owner.Weight;
+ Extents childMargin = childLayout.Margin;
+
+ float childHeight = 0;
+ if( childWeight > 0 )
+ {
+ float share = ( childWeight * remainingExcess ) / remainingWeightSum;
+ remainingExcess -= share;
+ remainingWeightSum -= childWeight;
+
+ // Always lay out weighted elements with intrinsic size regardless of the parent spec
+ // for consistency between specs.
+ if( desiredHeight == 0 )
+ {
+ // This child needs to be laid out from scratch using
+ // only its share of excess space.
+ childHeight = share;
+ }
+ else
+ {
+ // This child had some intrinsic width to which we
+ // need to add its share of excess space.
+ childHeight = childLayout.MeasuredHeight.Size.AsDecimal() + share;
+ }
+
+ MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( widthMeasureSpec,
+ new LayoutLength(Padding.Start + Padding.End),
+ new LayoutLength(desiredWidth) );
+
+ MeasureSpecification childHeightMeasureSpec = new MeasureSpecification( new LayoutLength(childHeight), MeasureSpecification.ModeType.Exactly);
+ childLayout.Measure( childWidthMeasureSpec, childHeightMeasureSpec );
+
+ // Child may now not fit in vertical dimension.
+ if( childLayout.MeasuredHeightAndState.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
+ {
+ childState.heightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
+ }
+ }
+
+ bool matchWidthLocally = false;
+ if( widthMode != MeasureSpecification.ModeType.Exactly && desiredWidth == (int)ChildLayoutData.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 && desiredWidth == (int)ChildLayoutData.MatchParent;
+
+
+ alternativeMaxWidth = Math.Max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
+
+ 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>=_children.Count-1)
+ {
+ cellPadding = 0.0f;
+ }
+ _totalLength = Math.Max( _totalLength, _totalLength + length + cellPadding );
+ } // foreach
+
+ // Add in our padding
+ _totalLength += Padding.Top + Padding.Bottom;
}
- set
+ else
{
- SetOrientation(value);
+ alternativeMaxWidth = Math.Max( alternativeMaxWidth, weightedMaxWidth );
}
- }
- /// <summary>
- /// [Draft] Get/Set the padding between cells in the layout
- /// </summary>
- public LayoutSize CellPadding
+ 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( _children.Count, heightMeasureSpec );
+ }
+ } // MeasureVertically
+
+ void LayoutHorizontal(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
{
- get
+ 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 = _children.Count;
+
+ switch (LinearAlignment)
{
- return GetCellPadding();
+ case Alignment.Begin:
+ 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;
+ 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:
+ // totalLength contains the padding already
+ childLeft = new LayoutLength(Padding.Start + (right.AsDecimal() - left.AsDecimal() - _totalLength) / 2.0f);
+ break;
}
- set
+
+ int start = 0;
+ int dir = 1;
+
+ // In case of RTL, start drawing from the last child.
+ if (isLayoutRtl)
{
- SetCellPadding(value);
+ start = count - 1;
+ dir = -1;
}
- }
- /// <summary>
- /// [Draft] Get/Set the alignment in the layout
- /// </summary>
- public LinearLayout.Alignment LinearAlignment
+ for( int i = 0; i < count; i++)
+ {
+ int childIndex = start + dir * i;
+ // Get a reference to the childLayout at the given index
+ LayoutItem childLayout = _children[childIndex];
+ if( childLayout != null )
+ {
+ LayoutLength childWidth = childLayout.MeasuredWidth.Size;
+ LayoutLength childHeight = childLayout.MeasuredHeight.Size;
+ Extents childMargin = childLayout.Margin;
+
+ switch ( LinearAlignment )
+ {
+ case Alignment.Top:
+ childTop = new LayoutLength(Padding.Top + childMargin.Top);
+ break;
+ case Alignment.Bottom:
+ childTop = new LayoutLength(height - Padding.Bottom - childHeight - childMargin.Bottom);
+ break;
+ case Alignment.CenterVertical: // FALLTHROUGH
+ default:
+ childTop = new LayoutLength(Padding.Top + ( ( childSpace - childHeight ).AsDecimal() / 2.0f ) + childMargin.Top - childMargin.Bottom);
+ break;
+ }
+ childLeft += childMargin.Start;
+ childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
+ childLeft += childWidth + childMargin.End + CellPadding.Width;
+ }
+ }
+ } // LayoutHorizontally
+
+ void LayoutVertical(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
{
- get
+ 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 = _children.Count;
+
+ switch (LinearAlignment)
{
- return GetAlignment();
+ case Alignment.Top:
+ // totalLength contains the padding already
+ childTop = new LayoutLength( Padding.Top );
+ break;
+ case Alignment.Bottom:
+ // totalLength contains the padding already
+ childTop = new LayoutLength( Padding.Top + bottom.AsDecimal() - top.AsDecimal() - _totalLength);
+ break;
+ case Alignment.CenterVertical:
+ default:
+ // totalLength contains the padding already
+ childTop = new LayoutLength(Padding.Top + ( bottom.AsDecimal() - top.AsDecimal() - _totalLength ) / 2.0f);
+ break;
}
- set
+
+ for( int i = 0; i < count; i++)
{
- SetAlignment(value);
+ LayoutItem childLayout = _children[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:
+ {
+ 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>
- /// [Draft] Enumeration for the direction in which the content is laid out
- /// </summary>
- public enum Orientation
+ void ForceUniformHeight( int count, MeasureSpecification widthMeasureSpec )
{
- /// <summary>
- /// Horizontal (row)
- /// </summary>
- Horizontal,
- /// <summary>
- /// Vertical (column)
- /// </summary>
- Vertical
+ // Pretend that the linear layout has an exact size. This is the measured height of
+ // ourselves. The measured height should be the max height of the children, changed
+ // to accommodate the heightMeasureSpec from the parent
+ MeasureSpecification uniformMeasureSpec = new MeasureSpecification( MeasuredHeight.Size, MeasureSpecification.ModeType.Exactly);
+ for (int i = 0; i < count; ++i)
+ {
+ LayoutItem childLayout = _children[i];
+ if (childLayout != null)
+ {
+ LayoutLength desiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification);
+ LayoutLength desiredHeight = new LayoutLength(Owner.WidthSpecification );
+
+ if (desiredHeight.AsRoundedValue() == LayoutParamPolicies.MatchParent)
+ {
+ // Temporarily force children to reuse their old measured width
+ LayoutLength oldWidth = desiredWidth;
+ childLayout.Owner.WidthSpecification = (int)childLayout.MeasuredWidth.Size.AsRoundedValue();
+ // Remeasure with new dimensions
+ MeasureChildWithMargins( childLayout, widthMeasureSpec, new LayoutLength(0),
+ uniformMeasureSpec, new LayoutLength(0) );
+
+ childLayout.Owner.WidthSpecification = (int)oldWidth.AsRoundedValue();
+ }
+ }
+ }
}
- /// <summary>
- /// [Draft] Enumeration for the alignment of the linear layout items
- /// </summary>
- public enum Alignment
+ void ForceUniformWidth( int count, MeasureSpecification heightMeasureSpec )
{
- /// <summary>
- /// At the left/right edge of the container (maps to LTR/RTL direction for horizontal orientation)
- /// </summary>
- Begin = 0x1,
- /// <summary>
- /// At the right/left edge of the container (maps to LTR/RTL direction for horizontal orientation)
- /// </summary>
- End = 0x2,
- /// <summary>
- /// At the horizontal center of the container
- /// </summary>
- CenterHorizontal = 0x4,
- /// <summary>
- /// At the top edge of the container
- /// </summary>
- Top = 0x8,
- /// <summary>
- /// At the bottom edge of the container
- /// </summary>
- Bottom = 0x10,
- /// <summary>
- /// At the vertical center of the container
- /// </summary>
- CenterVertical = 0x20
+ // Pretend that the linear layout has an exact size.
+ MeasureSpecification uniformMeasureSpec = new MeasureSpecification( MeasuredWidth.Size, MeasureSpecification.ModeType.Exactly);
+ for (int i = 0; i < count; ++i)
+ {
+ LayoutItem childLayout = _children[i];
+ if (childLayout != null)
+ {
+ LayoutLength desiredWidth = new LayoutLength(childLayout.Owner.WidthSpecification);
+ LayoutLength desiredHeight = new LayoutLength(Owner.WidthSpecification );
+
+ if( desiredWidth.AsRoundedValue() == LayoutParamPolicies.MatchParent)
+ {
+ // Temporarily force children to reuse their old measured height
+ LayoutLength oldHeight = desiredHeight;
+ childLayout.Owner.HeightSpecification = (int)childLayout.MeasuredHeight.Size.AsRoundedValue();
+
+ // Remeasure with new dimensions
+ MeasureChildWithMargins( childLayout, uniformMeasureSpec, new LayoutLength(0),
+ heightMeasureSpec, new LayoutLength(0));
+
+ childLayout.Owner.HeightSpecification = (int)oldHeight.AsRoundedValue();
+ }
+ }
+ }
}
- }
+ } //LinearLayout
-}
+} // namespace