e04e3bd8ccf3963c376474648e5d527ddedc2571
[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), 0.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), 0.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 right 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
369         /// <inheritdoc/>
370         /// <since_tizen> 9 </since_tizen>
371         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
372         {
373             for (int i = 0; i < LayoutChildren.Count; i++)
374             {
375                 LayoutItem childLayout = LayoutChildren[i];
376                 if (childLayout != null)
377                 {
378                     Geometry horizontalGeometry = GetHorizontalLayout(childLayout.Owner);
379                     Geometry verticalGeometry = GetVerticalLayout(childLayout.Owner);
380
381                     LayoutLength childLeft =  new LayoutLength(horizontalGeometry.Position);
382                     LayoutLength childRight = new LayoutLength(horizontalGeometry.Position + horizontalGeometry.Size);
383                     LayoutLength childTop = new LayoutLength(verticalGeometry.Position);
384                     LayoutLength childBottom = new LayoutLength(verticalGeometry.Position + verticalGeometry.Size);
385
386                     childLayout.Layout(childLeft, childTop, childRight, childBottom);
387                 }
388             }
389             HorizontalRelativeCache.Clear();
390             VerticalRelativeCache.Clear();
391         }
392
393         /// <summary>The alignment of the relative layout child.</summary>
394         /// <since_tizen> 9 </since_tizen>
395         public enum Alignment
396         {
397             /// <summary>At the start of the container.</summary>
398             /// <since_tizen> 9 </since_tizen>
399             Start = 0,
400             /// <summary>At the center of the container.</summary>
401             /// <since_tizen> 9 </since_tizen>
402             Center = 1,
403             /// <summary>At the end of the container.</summary>
404             /// <since_tizen> 9 </since_tizen>
405             End = 2,
406         }
407     }
408
409     // Extension Method of RelativeLayout.Alignment.
410     internal static partial class RelativeLayoutAlignmentExtension
411     {
412         public static float ToFloat(this RelativeLayout.Alignment align)
413         {
414             return 0.5f * (float)align;
415         }
416     }
417 }