[NUI] Fix to measure RelativeLayout's children sizes correctly
authorJaehyun Cho <jae_hyun.cho@samsung.com>
Sun, 18 Jul 2021 17:44:39 +0000 (02:44 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Mon, 19 Jul 2021 09:01:03 +0000 (18:01 +0900)
Previously, TextLabel and TextField's Ellipsis were not displayed in
RelativeLayout, because TextLabel and TextField's size always covers the full
text string.

Now, if TextLabel and TextField support Ellipsis, then RelativeLayout sets the
exact space size to the TextLabel and TextField. So if the assigned size does
not cover the full text string, then Ellipsis is displayed.

Previously, redundant Meausre() was called again in OnMeasure().

Now, Measure() is called again only for FillHorizontal/Vertical cases to set the
filled size to the MeasuredWidth/Height to calculate grand children's size
correctly.

src/Tizen.NUI/src/internal/Layouting/RelativeLayout.cs
src/Tizen.NUI/src/public/Layouting/RelativeLayout.cs

index 2b1b592..4f734c3 100644 (file)
@@ -233,6 +233,10 @@ namespace Tizen.NUI
             return (ChildrenWidth, ChildrenHeight);
         }
 
+        private Geometry GetHorizontalSpace(View view) => GetHorizontalRelative(view, MeasuredWidth.Size.AsDecimal() - (Padding.Start + Padding.End)).spaceGeometry;
+
+        private Geometry GetVerticalSpace(View view) => GetVerticalRelative(view, MeasuredHeight.Size.AsDecimal() - (Padding.Top + Padding.Bottom)).spaceGeometry;
+
         private Geometry GetHorizontalLayout(View view) => GetHorizontalRelative(view, MeasuredWidth.Size.AsDecimal() - (Padding.Start + Padding.End)).viewGeometry;
 
         private Geometry GetVerticalLayout(View view) => GetVerticalRelative(view, MeasuredHeight.Size.AsDecimal() - (Padding.Top + Padding.Bottom)).viewGeometry;
index 790a0a8..9771532 100755 (executable)
@@ -365,62 +365,100 @@ namespace Tizen.NUI
             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.
+            // There are 2 cases which require to calculate children's MeasuredWidth/Height as follows.
             //
-            // 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.
+            // 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.
             //
-            // TODO: Not to do the following operation in OnLayout() again.
+            // 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)
                 {
-                    LayoutLength childLeft;
-                    LayoutLength childRight;
-                    LayoutLength childTop;
-                    LayoutLength childBottom;
+                    bool ellipsisText = false;
+                    bool needMeasuredWidth = false;
+                    bool needMeasuredHeight = false;
 
-                    if (childLayout.Owner.WidthSpecification == LayoutParamPolicies.MatchParent)
+                    if (((childLayout.Owner is TextLabel textLabel) && textLabel.Ellipsis) || ((childLayout.Owner is TextField textField) && textField.Ellipsis))
                     {
-                        childLeft = new LayoutLength(childLayout.Margin.Start);
-                        childRight = new LayoutLength(MeasuredWidth.Size.AsDecimal() - childLayout.Margin.End);
+                        ellipsisText = true;
+                        needClearCache = true;
                     }
                     else
                     {
-                        Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner);
+                        if (RelativeLayout.GetFillHorizontal(childLayout.Owner))
+                        {
+                            needMeasuredWidth = true;
+                            needClearCache = true;
+                        }
+
+                        if (RelativeLayout.GetFillVertical(childLayout.Owner))
+                        {
+                            needMeasuredHeight = true;
+                            needClearCache = true;
+                        }
+                    }
 
-                        childLeft = new LayoutLength(horizontalGeometry.Position + Padding.Start + childLayout.Margin.Start);
-                        childRight = new LayoutLength(horizontalGeometry.Position + horizontalGeometry.Size + Padding.Start - childLayout.Margin.End);
+                    if ((ellipsisText == false) && (needMeasuredWidth == false) && (needMeasuredHeight == false))
+                    {
+                        continue;
                     }
 
-                    if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.MatchParent)
+                    float width = childLayout.MeasuredWidth.Size.AsDecimal();
+                    float height = childLayout.MeasuredWidth.Size.AsDecimal();
+
+                    if (ellipsisText)
                     {
-                        childTop = new LayoutLength(childLayout.Margin.Top);
-                        childBottom = new LayoutLength(MeasuredHeight.Size.AsDecimal() - childLayout.Margin.Bottom);
+                        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
                     {
-                        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);
+                        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(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;
+                    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.
@@ -429,22 +467,38 @@ namespace Tizen.NUI
                     // 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)
+                    int origWidthSpecification = childLayout.Owner.WidthSpecification;
+                    int origHeightSpecification = childLayout.Owner.HeightSpecification;
+
+                    if (ellipsisText || needMeasuredWidth)
                     {
+                        origWidthSpecification = childLayout.Owner.WidthSpecification;
                         childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent;
                     }
-                    if (childLayout.Owner.HeightSpecification == LayoutParamPolicies.WrapContent)
+                    if (ellipsisText || needMeasuredHeight)
                     {
+                        origHeightSpecification = childLayout.Owner.HeightSpecification;
                         childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent;
                     }
 
-                    // Use MeasureChild() because Margin sizes were already subtracted from childWidth/HeightMeasureSpec.
-                    MeasureChild(childLayout, childWidthMeasureSpec, childHeightMeasureSpec);
+                    MeasureChildWithMargins(childLayout, childWidthMeasureSpec, new LayoutLength(0), childHeightMeasureSpec, new LayoutLength(0));
 
-                    childLayout.Owner.WidthSpecification = origWidthSpecification;
-                    childLayout.Owner.HeightSpecification = origHeightSpecification;
+                    if (ellipsisText || needMeasuredWidth)
+                    {
+                        childLayout.Owner.WidthSpecification = origWidthSpecification;
+                    }
+                    if (ellipsisText || needMeasuredHeight)
+                    {
+                        childLayout.Owner.HeightSpecification = origHeightSpecification;
+                    }
                 }
             }
+
+            if (needClearCache)
+            {
+                HorizontalRelativeCache.Clear();
+                VerticalRelativeCache.Clear();
+            }
         }
 
         /// <inheritdoc/>