[NUI] Fix size calculation for TextLabel with Margin in RelativeLayout
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Layouting / RelativeLayout.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using System.ComponentModel;
18 using Tizen.NUI.BaseComponents;
19 using Tizen.NUI.Binding;
20
21 namespace Tizen.NUI
22 {
23     /// <summary>
24     /// RelativeLayout calculates the size and position of all the children based on their relationship to each other.
25     /// </summary>
26     /// <since_tizen> 9 </since_tizen>
27     public partial class RelativeLayout : LayoutGroup
28     {
29         /// <summary>
30         /// LeftTargetProperty
31         /// </summary>
32         [EditorBrowsable(EditorBrowsableState.Never)]
33         public static readonly BindableProperty LeftTargetProperty = BindableProperty.CreateAttached("LeftTarget", typeof(View), typeof(RelativeLayout), null, propertyChanged: OnChildPropertyChanged);
34
35         /// <summary>
36         /// RightTargetProperty
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public static readonly BindableProperty RightTargetProperty = BindableProperty.CreateAttached("RightTarget", typeof(View), typeof(RelativeLayout), null, propertyChanged: OnChildPropertyChanged);
40
41         /// <summary>
42         /// TopTargetProperty
43         /// </summary>
44         [EditorBrowsable(EditorBrowsableState.Never)]
45         public static readonly BindableProperty TopTargetProperty = BindableProperty.CreateAttached("TopTarget", typeof(View), typeof(RelativeLayout), null, propertyChanged: OnChildPropertyChanged);
46
47         /// <summary>
48         /// BottomTargetProperty
49         /// </summary>
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public static readonly BindableProperty BottomTargetProperty = BindableProperty.CreateAttached("BottomTarget", typeof(View), typeof(RelativeLayout), null, propertyChanged: OnChildPropertyChanged);
52
53         /// <summary>
54         /// LeftRelativeOffsetProperty
55         /// </summary>
56         [EditorBrowsable(EditorBrowsableState.Never)]
57         public static readonly BindableProperty LeftRelativeOffsetProperty = BindableProperty.CreateAttached("LeftRelativeOffset", typeof(float), typeof(RelativeLayout), 0.0f, propertyChanged: OnChildPropertyChanged);
58
59         /// <summary>
60         /// RightRelativeOffsetProperty
61         /// </summary>
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public static readonly BindableProperty RightRelativeOffsetProperty = BindableProperty.CreateAttached("RightRelativeOffset", typeof(float), typeof(RelativeLayout), 1.0f, propertyChanged: OnChildPropertyChanged);
64
65         /// <summary>
66         /// TopRelativeOffsetProperty
67         /// </summary>
68         [EditorBrowsable(EditorBrowsableState.Never)]
69         public static readonly BindableProperty TopRelativeOffsetProperty = BindableProperty.CreateAttached("TopRelativeOffset", typeof(float), typeof(RelativeLayout), 0.0f, propertyChanged: OnChildPropertyChanged);
70
71         /// <summary>
72         /// BottomRelativeOffsetProperty
73         /// </summary>
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public static readonly BindableProperty BottomRelativeOffsetProperty = BindableProperty.CreateAttached("BottomRelativeOffset", typeof(float), typeof(RelativeLayout), 1.0f, propertyChanged: OnChildPropertyChanged);
76
77         /// <summary>
78         /// HorizontalAlignmentProperty
79         /// </summary>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         public static readonly BindableProperty HorizontalAlignmentProperty = BindableProperty.CreateAttached("HorizontalAlignment", typeof(Alignment), typeof(RelativeLayout), default(Alignment), propertyChanged: OnChildPropertyChanged);
82
83         /// <summary>
84         /// VerticalAlignmentProperty
85         /// </summary>
86         [EditorBrowsable(EditorBrowsableState.Never)]
87         public static readonly BindableProperty VerticalAlignmentProperty = BindableProperty.CreateAttached("VerticalAlignment", typeof(Alignment), typeof(RelativeLayout), default(Alignment), propertyChanged: OnChildPropertyChanged);
88
89         /// <summary>
90         /// FillHorizontalProperty
91         /// </summary>
92         [EditorBrowsable(EditorBrowsableState.Never)]
93         public static readonly BindableProperty FillHorizontalProperty = BindableProperty.CreateAttached("FillHorizontal", typeof(bool), typeof(RelativeLayout), false, propertyChanged: OnChildPropertyChanged);
94
95         /// <summary>
96         /// FillVerticalProperty
97         /// </summary>
98         [EditorBrowsable(EditorBrowsableState.Never)]
99         public static readonly BindableProperty FillVerticalProperty = BindableProperty.CreateAttached("FillVertical", typeof(bool), typeof(RelativeLayout), false, propertyChanged: OnChildPropertyChanged);
100
101         /// <summary>
102         /// Constructor
103         /// </summary>
104         /// <since_tizen> 9 </since_tizen>
105         public RelativeLayout() { }
106
107         /// <summary>
108         /// Gets left target object whose size and position is being used as reference.
109         /// </summary>
110         /// <param name="view">The child view whose size and position is being changed.</param>
111         /// <returns>The object whose size and position is being used as reference.</returns>
112         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
113         /// <since_tizen> 9 </since_tizen>
114         [Binding.TypeConverter(typeof(RelativeTargetConverter))]
115         public static View GetLeftTarget(BindableObject view) => GetAttachedValue<View>(view, LeftTargetProperty);
116
117         /// <summary>
118         /// Gets right target object whose size and position is being used as reference.
119         /// </summary>
120         /// <param name="view">The child view whose size and position is being changed.</param>
121         /// <returns>The object whose size and position is being used as reference.</returns>
122         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
123         /// <since_tizen> 9 </since_tizen>
124         [Binding.TypeConverter(typeof(RelativeTargetConverter))]
125         public static View GetRightTarget(BindableObject view) => GetAttachedValue<View>(view, RightTargetProperty);
126
127         /// <summary>
128         /// Gets top target object whose size and position is being used as reference.
129         /// </summary>
130         /// <param name="view">The child view whose size and position is being changed.</param>
131         /// <returns>The object whose size and position is being used as reference.</returns>
132         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
133         /// <since_tizen> 9 </since_tizen>
134         [Binding.TypeConverter(typeof(RelativeTargetConverter))]
135         public static View GetTopTarget(BindableObject view) => GetAttachedValue<View>(view, TopTargetProperty);
136
137         /// <summary>
138         /// Gets bottom target object whose size and position is being used as reference.
139         /// </summary>
140         /// <param name="view">The child view whose size and position is being changed.</param>
141         /// <returns>The object whose size and position is being used as reference.</returns>
142         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
143         /// <since_tizen> 9 </since_tizen>
144         [Binding.TypeConverter(typeof(RelativeTargetConverter))]
145         public static View GetBottomTarget(BindableObject view) => GetAttachedValue<View>(view, BottomTargetProperty);
146
147         /// <summary>
148         /// Gets left relative offset.
149         /// </summary>
150         /// <param name="view">The child view whose size and position is being changed.</param>
151         /// <returns>The ratio between left and right of the <seealso cref="LeftTargetProperty"/>.</returns>
152         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
153         /// <since_tizen> 9 </since_tizen>
154         public static float GetLeftRelativeOffset(View view) => GetAttachedValue<float>(view, LeftRelativeOffsetProperty);
155
156         /// <summary>
157         /// Gets right relative offset.
158         /// </summary>
159         /// <param name="view">The child view whose size and position is being changed.</param>
160         /// <returns>The ratio between left and right of the <seealso cref="RightTargetProperty"/>.</returns>
161         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
162         /// <since_tizen> 9 </since_tizen>
163         public static float GetRightRelativeOffset(View view) => GetAttachedValue<float>(view, RightRelativeOffsetProperty);
164
165         /// <summary>
166         /// Gets top relative offset.
167         /// </summary>
168         /// <param name="view">The child view whose size and position is being changed.</param>
169         /// <returns>The ratio between top and bottom of the <seealso cref="TopTargetProperty"/>.</returns>
170         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
171         /// <since_tizen> 9 </since_tizen>
172         public static float GetTopRelativeOffset(View view) => GetAttachedValue<float>(view, TopRelativeOffsetProperty);
173
174         /// <summary>
175         /// Gets bottom relative offset.
176         /// </summary>
177         /// <param name="view">The child view whose size and position is being changed.</param>
178         /// <returns>The ratio between top and bottom of the <seealso cref="BottomTargetProperty"/>.</returns>
179         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
180         /// <since_tizen> 9 </since_tizen>
181         public static float GetBottomRelativeOffset(View view) => GetAttachedValue<float>(view, BottomRelativeOffsetProperty);
182
183         /// <summary>
184         /// Gets the horizontal alignment
185         /// </summary>
186         /// <param name="view">The child view.</param>
187         /// <returns>The horizontal alignment of <paramref name="view"/>.</returns>
188         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
189         /// <since_tizen> 9 </since_tizen>
190         public static Alignment GetHorizontalAlignment(View view) => GetAttachedValue<Alignment>(view, HorizontalAlignmentProperty);
191
192         /// <summary>
193         /// Gets the vertical alignment
194         /// </summary>
195         /// <param name="view">The child view.</param>
196         /// <returns>The vertical alignment of <paramref name="view"/>.</returns>
197         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
198         /// <since_tizen> 9 </since_tizen>
199         public static Alignment GetVerticalAlignment(View view) => GetAttachedValue<Alignment>(view, VerticalAlignmentProperty);
200
201         /// <summary>
202         /// Gets the boolean value whether child fills its horizontal space.
203         /// </summary>
204         /// <param name="view">The child view.</param>
205         /// <returns>True if to fill the space, false otherwise.</returns>
206         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
207         /// <since_tizen> 9 </since_tizen>
208         public static bool GetFillHorizontal(View view) => GetAttachedValue<bool>(view, FillHorizontalProperty);
209
210         /// <summary>
211         /// Gets the boolean value whether child fills its vertical space.
212         /// </summary>
213         /// <param name="view">The child view.</param>
214         /// <returns>True if to fill the space, false otherwise.</returns>
215         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
216         /// <since_tizen> 9 </since_tizen>
217         public static bool GetFillVertical(View view) => GetAttachedValue<bool>(view, FillVerticalProperty);
218
219         /// <summary>
220         /// Specifies the left side edge of the child view relative to the target view. <br/>
221         /// null <paramref name="reference"/> means parent relative layout.
222         /// </summary>
223         /// <param name="view">The child view whose size and position is being changed.</param>
224         /// <param name="reference">The object whose size and position is being used as reference.</param>
225         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
226         /// <since_tizen> 9 </since_tizen>
227         public static void SetLeftTarget(View view, View reference) => SetAttachedValue(view, LeftTargetProperty, reference);
228
229         /// <summary>
230         /// Specifies the right side edge of the child view relative to the target view. <br/>
231         /// null <paramref name="reference"/> means parent relative layout.
232         /// </summary>
233         /// <param name="view">The child view whose size and position is being changed.</param>
234         /// <param name="reference">The object whose size and position is being used as reference.</param>
235         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
236         /// <since_tizen> 9 </since_tizen>
237         public static void SetRightTarget(View view, View reference) => SetAttachedValue(view, RightTargetProperty, reference);
238
239         /// <summary>
240         /// Specifies the top side edge of the child view relative to the target view. <br/>
241         /// null <paramref name="reference"/> means parent relative layout.
242         /// </summary>
243         /// <param name="view">The child view whose size and position is being changed.</param>
244         /// <param name="reference">The object whose size and position is being used as reference.</param>
245         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
246         /// <since_tizen> 9 </since_tizen>
247         public static void SetTopTarget(View view, View reference) => SetAttachedValue(view, TopTargetProperty, reference);
248
249         /// <summary>
250         /// Specifies the bottom side edge of the child view relative to the target view. <br/>
251         /// null <paramref name="reference"/> means parent relative layout.
252         /// </summary>
253         /// <param name="view">The child view whose size and position is being changed.</param>
254         /// <param name="reference">The object whose size and position is being used as reference.</param>
255         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
256         /// <since_tizen> 9 </since_tizen>
257         public static void SetBottomTarget(View view, View reference) => SetAttachedValue(view, BottomTargetProperty, reference);
258
259         /// <summary>
260         /// Sets the relative offset for left target.
261         /// When <paramref name="value"/> is 0 the left edges of the left target and <paramref name="view"/> are aligned.<br/>
262         /// When <paramref name="value"/> is 1 the left edge of the <paramref name="view"/> is aligned to the right edge of the left target.
263         /// </summary>
264         /// <param name="view">The child view whose size and position is being changed.</param>
265         /// <param name="value">The ratio between left and right of the <seealso cref="LeftTargetProperty"/>.</param>
266         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
267         /// <since_tizen> 9 </since_tizen>
268         public static void SetLeftRelativeOffset(View view, float value) => SetAttachedValue(view, LeftRelativeOffsetProperty, value);
269
270         /// <summary>
271         /// Sets the relative offset for right target.
272         /// When <paramref name="value"/> is 0 the right edge of the <paramref name="view"/> is aligned to the left edge of the right target.<br/>
273         /// When <paramref name="value"/> is 1 the right edges of the right target and <paramref name="view"/> are aligned.
274         /// </summary>
275         /// <param name="view">The child view whose size and position is being changed.</param>
276         /// <param name="value">The ratio between left and right of the <seealso cref="RightTargetProperty"/>.</param>
277         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
278         /// <since_tizen> 9 </since_tizen>
279         public static void SetRightRelativeOffset(View view, float value) => SetAttachedValue(view, RightRelativeOffsetProperty, value);
280
281         /// <summary>
282         /// Sets the relative offset for top target.
283         /// When <paramref name="value"/> is 0 the top edges of the top target and <paramref name="view"/> are aligned.<br/>
284         /// When <paramref name="value"/> is 1 the top edge of the <paramref name="view"/> is aligned to the bottom edge of the top target.
285         /// </summary>
286         /// <param name="view">The child view whose size and position is being changed.</param>
287         /// <param name="value">The ratio between left and right of the <seealso cref="TopTargetProperty"/>.</param>
288         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
289         /// <since_tizen> 9 </since_tizen>
290         public static void SetTopRelativeOffset(View view, float value) => SetAttachedValue(view, TopRelativeOffsetProperty, value);
291
292         /// <summary>
293         /// Sets the relative offset for bottom target.
294         /// When <paramref name="value"/> is 0 the bottom edge of the <paramref name="view"/> is aligned to the top edge of the bottom target.<br/>
295         /// When <paramref name="value"/> is 1 the bottom edges of the bottom target and <paramref name="view"/> are aligned.
296         /// </summary>
297         /// <param name="view">The child view whose size and position is being changed.</param>
298         /// <param name="value">The ratio between left and right of the <seealso cref="BottomTargetProperty"/>.</param>
299         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
300         /// <since_tizen> 9 </since_tizen>
301         public static void SetBottomRelativeOffset(View view, float value) => SetAttachedValue(view, BottomRelativeOffsetProperty, value);
302
303         /// <summary>
304         /// Sets the horizontal alignment of this child view.
305         /// </summary>
306         /// <param name="view">The child view.</param>
307         /// <param name="value">The horizontal alignment of <paramref name="view"/>.</param>
308         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
309         /// <since_tizen> 9 </since_tizen>
310         public static void SetHorizontalAlignment(View view, Alignment value) => SetAttachedValue(view, HorizontalAlignmentProperty, value);
311
312         /// <summary>
313         /// Sets the vertical alignment of this child view.
314         /// </summary>
315         /// <param name="view">The child view.</param>
316         /// <param name="value">The vertical alignment of <paramref name="view"/>.</param>
317         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
318         /// <since_tizen> 9 </since_tizen>
319         public static void SetVerticalAlignment(View view, Alignment value) => SetAttachedValue(view, VerticalAlignmentProperty, value);
320
321         /// <summary>
322         /// Sets the boolean value whether child fills its horizontal space.
323         /// </summary>
324         /// <param name="view">The child view.</param>
325         /// <param name="value">True if to fill the space, false otherwise.</param>
326         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
327         /// <since_tizen> 9 </since_tizen>
328         public static void SetFillHorizontal(View view, bool value) => SetAttachedValue(view, FillHorizontalProperty, value);
329
330         /// <summary>
331         /// Sets the boolean value whether child fills its vertical space.
332         /// </summary>
333         /// <param name="view">The child view.</param>
334         /// <param name="value">True if to fill the space, false otherwise.</param>
335         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
336         /// <since_tizen> 9 </since_tizen>
337         public static void SetFillVertical(View view, bool value) => SetAttachedValue(view, FillVerticalProperty, value);
338
339         /// <inheritdoc/>
340         /// <since_tizen> 9 </since_tizen>
341         protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
342         {
343             MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
344             MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
345
346             for (int i = 0; i < LayoutChildren.Count; i++)
347             {
348                 LayoutItem childLayout = LayoutChildren[i];
349                 if (childLayout != null)
350                 {
351                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
352
353                     if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
354                     {
355                         childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
356                     }
357                     if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
358                     {
359                         childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
360                     }
361                 }
362             }
363
364             (float childrenWidth, float childrenHeight) = CalculateChildrenSize(widthMeasureSpec.Size.AsDecimal(), heightMeasureSpec.Size.AsDecimal());
365             SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(childrenWidth), widthMeasureSpec, childWidthState),
366                                   ResolveSizeAndState(new LayoutLength(childrenHeight), heightMeasureSpec, childHeightState));
367
368             // There are 2 cases which require to calculate children's MeasuredWidth/Height as follows.
369             //
370             // 1. Text with Ellipsis true
371             //    TextLabel and TextField calculate MeasuredWidth/Height to cover their text string if they have WrapContent.
372             //    This causes children's Ellipsis cannot be displayed with RelativeLayout.
373             //    To resolve the above, RelativeLayout recalculates its children's MeasuredWidth/Height based on the children's space calculated by RelativeLayout APIs.
374             //
375             // 2. FillHorizontal/Vertical true
376             //    If children set FillHorizontal/Vertical true, then children's MeasuredWidth/Height are not correctly alculated.
377             //    Instead, children's size and position are correctly calculated in OnLayout().
378             //    This causes that the grand children's MeasuredWidth/Height are calculated incorrectly.
379             //    To resolve the above, RelativeLayout calculates its children's MeasuredWidth/Height based on the children's geometry calculated by RelativeLayout APIs.
380             //
381             //    e.g.
382             //    Let parent have RelativeLayout and parent's size be 1920x1080.
383             //    Let child have WrapContent with SetFillHorizontal/Vertical true.
384             //    Let grand child have MatchParent.
385             //    Then, child's size is 1920x1080 but child's MeasuredWidth/Height is 0x0.
386             //    Then, grand child's MeasuredWidth/Height is 0x0 and size is 0x0.
387             //
388             // TODO: Not to do duplicate operations in OnLayout() again.
389             bool needClearCache = false;
390
391             for (int i = 0; i < LayoutChildren.Count; i++)
392             {
393                 LayoutItem childLayout = LayoutChildren[i];
394                 if (childLayout != null)
395                 {
396                     bool ellipsisTextWidth = false;
397                     bool ellipsisTextHeight = false;
398                     bool needMeasuredWidth = false;
399                     bool needMeasuredHeight = false;
400
401                     if (((childLayout.Owner is TextLabel textLabel) && textLabel.Ellipsis) || ((childLayout.Owner is TextField textField) && textField.Ellipsis))
402                     {
403                         Geometry horizontalSpace = GetHorizontalSpace(childLayout.Owner);
404                         if (!horizontalSpace.Size.Equals(0))
405                         {
406                             ellipsisTextWidth = true;
407                             needClearCache = true;
408                         }
409
410                         Geometry verticalSpace = GetVerticalSpace(childLayout.Owner);
411                         if (!verticalSpace.Size.Equals(0))
412                         {
413                             ellipsisTextHeight = true;
414                             needClearCache = true;
415                         }
416                     }
417
418                     if (!ellipsisTextWidth && RelativeLayout.GetFillHorizontal(childLayout.Owner))
419                     {
420                         needMeasuredWidth = true;
421                         needClearCache = true;
422                     }
423
424                     if (!ellipsisTextHeight && RelativeLayout.GetFillVertical(childLayout.Owner))
425                     {
426                         needMeasuredHeight = true;
427                         needClearCache = true;
428                     }
429
430                     if ((ellipsisTextWidth == false) && (ellipsisTextHeight == false) && (needMeasuredWidth == false) && (needMeasuredHeight == false))
431                     {
432                         continue;
433                     }
434
435                     float width = childLayout.MeasuredWidth.Size.AsDecimal();
436                     float height = childLayout.MeasuredHeight.Size.AsDecimal();
437
438                     if (ellipsisTextWidth)
439                     {
440                         Geometry horizontalSpace = GetHorizontalSpace(childLayout.Owner);
441                         if (!horizontalSpace.Size.Equals(0))
442                         {
443                             if ((width > horizontalSpace.Size) || ((width < horizontalSpace.Size) && RelativeLayout.GetFillVertical(childLayout.Owner)))
444                             {
445                                 width = horizontalSpace.Size;
446                             }
447                         }
448                     }
449                     else if (needMeasuredWidth)
450                     {
451                         Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner);
452                         width = horizontalGeometry.Size;
453                     }
454
455                     if (ellipsisTextHeight)
456                     {
457                         Geometry verticalSpace = GetVerticalSpace(childLayout.Owner);
458                         if (!verticalSpace.Size.Equals(0))
459                         {
460                             if ((height > verticalSpace.Size) || ((height < verticalSpace.Size) && RelativeLayout.GetFillHorizontal(childLayout.Owner)))
461                             {
462                                 height = verticalSpace.Size;
463                             }
464                         }
465                     }
466                     else if (needMeasuredHeight)
467                     {
468                         Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner);
469                         height = verticalGeometry.Size;
470                     }
471
472                     // Padding sizes are added because Padding sizes will be subtracted in MeasureChild().
473                     MeasureSpecification childWidthMeasureSpec = new MeasureSpecification(new LayoutLength(width + Padding.Start + Padding.End), MeasureSpecification.ModeType.Exactly);
474                     MeasureSpecification childHeightMeasureSpec = new MeasureSpecification(new LayoutLength(height + Padding.Top + Padding.Bottom), MeasureSpecification.ModeType.Exactly);
475
476                     // To calculate the grand children's Measure() with the mode type Exactly,
477                     // children's Measure() is called with MatchParent if the children have WrapContent.
478                     //
479                     // i.e.
480                     // If children have Wrapcontent and the grand children have MatchParent,
481                     // then grand children's MeasuredWidth/Height do not fill the children
482                     // because the grand children's Measure() is called with the mode type AtMost.
483                     int origWidthSpecification = childLayout.Owner.WidthSpecification;
484                     int origHeightSpecification = childLayout.Owner.HeightSpecification;
485
486                     if (ellipsisTextWidth || needMeasuredWidth)
487                     {
488                         origWidthSpecification = childLayout.Owner.WidthSpecification;
489                         childLayout.Owner.WidthSpecification = LayoutParamPolicies.MatchParent;
490                     }
491                     if (ellipsisTextHeight || needMeasuredHeight)
492                     {
493                         origHeightSpecification = childLayout.Owner.HeightSpecification;
494                         childLayout.Owner.HeightSpecification = LayoutParamPolicies.MatchParent;
495                     }
496
497                     MeasureChild(childLayout, childWidthMeasureSpec, childHeightMeasureSpec);
498
499                     if (ellipsisTextWidth || needMeasuredWidth)
500                     {
501                         childLayout.Owner.WidthSpecification = origWidthSpecification;
502                     }
503                     if (ellipsisTextHeight || needMeasuredHeight)
504                     {
505                         childLayout.Owner.HeightSpecification = origHeightSpecification;
506                     }
507                 }
508             }
509
510             if (needClearCache)
511             {
512                 HorizontalRelativeCache.Clear();
513                 VerticalRelativeCache.Clear();
514             }
515         }
516
517         /// <inheritdoc/>
518         /// <since_tizen> 9 </since_tizen>
519         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
520         {
521             for (int i = 0; i < LayoutChildren.Count; i++)
522             {
523                 LayoutItem childLayout = LayoutChildren[i];
524                 if (childLayout != null)
525                 {
526                     Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner);
527                     Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner);
528
529                     LayoutLength childLeft = new LayoutLength(horizontalGeometry.Position + Padding.Start + childLayout.Margin.Start);
530                     LayoutLength childRight = new LayoutLength(horizontalGeometry.Position + horizontalGeometry.Size + Padding.Start - childLayout.Margin.End);
531                     LayoutLength childTop = new LayoutLength(verticalGeometry.Position + Padding.Top + childLayout.Margin.Top);
532                     LayoutLength childBottom = new LayoutLength(verticalGeometry.Position + verticalGeometry.Size + Padding.Top - childLayout.Margin.Bottom);
533
534                     childLayout.Layout(childLeft, childTop, childRight, childBottom);
535                 }
536             }
537             HorizontalRelativeCache.Clear();
538             VerticalRelativeCache.Clear();
539         }
540
541         /// <summary>The alignment of the relative layout child.</summary>
542         /// <since_tizen> 9 </since_tizen>
543         public enum Alignment
544         {
545             /// <summary>At the start of the container.</summary>
546             /// <since_tizen> 9 </since_tizen>
547             Start = 0,
548             /// <summary>At the center of the container.</summary>
549             /// <since_tizen> 9 </since_tizen>
550             Center = 1,
551             /// <summary>At the end of the container.</summary>
552             /// <since_tizen> 9 </since_tizen>
553             End = 2,
554         }
555     }
556
557     // Extension Method of RelativeLayout.Alignment.
558     internal static partial class RelativeLayoutAlignmentExtension
559     {
560         public static float ToFloat(this RelativeLayout.Alignment align)
561         {
562             return 0.5f * (float)align;
563         }
564     }
565 }