04f639e6feced2ffedc556cd120e693a60ab1e37
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Layouting / GridLayout.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
17 using System;
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using Tizen.NUI.Binding;
21
22 namespace Tizen.NUI
23 {
24     /// <summary>
25     /// GridLayout is a 2D grid pattern layout that consists of a set of rows and columns.
26     /// </summary>
27     public partial class GridLayout : LayoutGroup
28     {
29         /// <summary>
30         /// ColumnProperty
31         /// </summary>
32         [EditorBrowsable(EditorBrowsableState.Never)]
33         public static readonly BindableProperty ColumnProperty = BindableProperty.CreateAttached("Column", typeof(int), typeof(GridLayout), CellUndefined, validateValue: (bindable, value) => (int)value >= 0, propertyChanged: OnChildPropertyChanged);
34
35         /// <summary>
36         /// ColumnSpanProperty
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public static readonly BindableProperty ColumnSpanProperty = BindableProperty.CreateAttached("ColumnSpan", typeof(int), typeof(GridLayout), 1, validateValue: (bindable, value) => (int)value >= 1, propertyChanged: OnChildPropertyChanged);
40
41         /// <summary>
42         /// RowProperty
43         /// </summary>
44         [EditorBrowsable(EditorBrowsableState.Never)]
45         public static readonly BindableProperty RowProperty = BindableProperty.CreateAttached("Row", typeof(int), typeof(GridLayout), CellUndefined, validateValue: (bindable, value) => (int)value >= 0, propertyChanged: OnChildPropertyChanged);
46
47         /// <summary>
48         /// RowSpanProperty
49         /// </summary>
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public static readonly BindableProperty RowSpanProperty = BindableProperty.CreateAttached("RowSpan", typeof(int), typeof(GridLayout), 1, validateValue: (bindable, value) => (int)value >= 1, propertyChanged: OnChildPropertyChanged);
52
53         /// <summary>
54         /// HorizontalStretchProperty
55         /// </summary>
56         [EditorBrowsable(EditorBrowsableState.Never)]
57         public static readonly BindableProperty HorizontalStretchProperty = BindableProperty.CreateAttached("HorizontalStretch", typeof(StretchFlags), typeof(GridLayout), default(StretchFlags), validateValue: ValidateEnum((int)StretchFlags.None, (int)StretchFlags.ExpandAndFill), propertyChanged: OnChildPropertyChanged);
58
59         /// <summary>
60         /// VerticalStretchProperty
61         /// </summary>
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public static readonly BindableProperty VerticalStretchProperty = BindableProperty.CreateAttached("VerticalStretch", typeof(StretchFlags), typeof(GridLayout), default(StretchFlags), validateValue: ValidateEnum((int)StretchFlags.None, (int)StretchFlags.ExpandAndFill), propertyChanged: OnChildPropertyChanged);
64
65         /// <summary>
66         /// HorizontalAlignmentProperty
67         /// </summary>
68         [EditorBrowsable(EditorBrowsableState.Never)]
69         public static readonly BindableProperty HorizontalAlignmentProperty = BindableProperty.CreateAttached("HorizontalAlignment", typeof(Alignment), typeof(GridLayout), Alignment.Start, validateValue: ValidateEnum((int)Alignment.Start, (int)Alignment.End), propertyChanged: OnChildPropertyChanged);
70
71         /// <summary>
72         /// VerticalAlignmentProperty
73         /// </summary>
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public static readonly BindableProperty VerticalAlignmentProperty = BindableProperty.CreateAttached("VerticalAlignment", typeof(Alignment), typeof(GridLayout), Alignment.Start, validateValue: ValidateEnum((int)Alignment.Start, (int)Alignment.End), propertyChanged: OnChildPropertyChanged);
76
77         private const int CellUndefined = -1;
78         private Orientation gridOrientation = Orientation.Horizontal;
79         private int columns = 1;
80         private int rows = 1;
81         private float columnSpacing = 0;
82         private float rowSpacing = 0;
83
84         /// <summary>
85         /// Enumeration for the direction in which the content is laid out
86         /// </summary>
87         /// <since_tizen> 8 </since_tizen>
88         public enum Orientation
89         {
90             /// <summary>
91             /// Horizontal (row)
92             /// </summary>
93             Horizontal,
94             /// <summary>
95             /// Vertical (column)
96             /// </summary>
97             Vertical
98         }
99
100         /// <summary>
101         /// Gets the column index.
102         /// </summary>
103         /// <param name="view">The child view.</param>
104         /// <returns>The column index of <paramref name="view"/>.</returns>
105         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
106         /// <since_tizen> 8 </since_tizen>
107         public static int GetColumn(View view) => GetAttachedValue<int>(view, ColumnProperty);
108
109         /// <summary>
110         /// Gets the column span.
111         /// </summary>
112         /// <param name="view">The child view.</param>
113         /// <returns>The column span of <paramref name="view"/>.</returns>
114         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
115         /// <since_tizen> 8 </since_tizen>
116         public static int GetColumnSpan(View view) => GetAttachedValue<int>(view, ColumnSpanProperty);
117
118         /// <summary>
119         /// Gets the row index.
120         /// </summary>
121         /// <param name="view">The child view.</param>
122         /// <returns>The row index of <paramref name="view"/>.</returns>
123         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
124         /// <since_tizen> 8 </since_tizen>
125         public static int GetRow(View view) => GetAttachedValue<int>(view, RowProperty);
126
127         /// <summary>
128         /// Gets the row span.
129         /// </summary>
130         /// <param name="view">The child view.</param>
131         /// <returns>The row span of <paramref name="view"/>.</returns>
132         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
133         /// <since_tizen> 8 </since_tizen>
134         public static int GetRowSpan(View view) => GetAttachedValue<int>(view, RowSpanProperty);
135
136         /// <summary>
137         /// Gets the value how child is resized within its horizontal space.
138         /// </summary>
139         /// <param name="view">The child view.</param>
140         /// <returns>The horizontal stretch flag of <paramref name="view"/>.</returns>
141         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
142         /// <since_tizen> 8 </since_tizen>
143         public static StretchFlags GetHorizontalStretch(View view) => GetAttachedValue<StretchFlags>(view, HorizontalStretchProperty);
144
145         /// <summary>
146         /// Gets the value how child is resized within its vertical space.
147         /// </summary>
148         /// <param name="view">The child view.</param>
149         /// <returns>The vertical stretch flag of <paramref name="view"/>.</returns>
150         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
151         /// <since_tizen> 8 </since_tizen>
152         public static StretchFlags GetVerticalStretch(View view) => GetAttachedValue<StretchFlags>(view, VerticalStretchProperty);
153
154         /// <summary>
155         /// Gets the horizontal alignment of this child.
156         /// </summary>
157         /// <param name="view">The child view.</param>
158         /// <returns>The horizontal alignment of <paramref name="view"/>.</returns>
159         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
160         /// <since_tizen> 8 </since_tizen>
161         public static Alignment GetHorizontalAlignment(View view) => GetAttachedValue<Alignment>(view, HorizontalAlignmentProperty);
162
163         /// <summary>
164         /// Gets the vertical alignment of this child.
165         /// </summary>
166         /// <param name="view">The child view.</param>
167         /// <returns>The vertical alignment of <paramref name="view"/>.</returns>
168         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
169         /// <since_tizen> 8 </since_tizen>
170         public static Alignment GetVerticalAlignment(View view) => GetAttachedValue<Alignment>(view, VerticalAlignmentProperty);
171
172         /// <summary>
173         /// Sets the column index the child occupies. the default value is -1.
174         /// </summary>
175         /// <param name="view">The child view.</param>
176         /// <param name="value">The column index of <paramref name="view"/>.</param>
177         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
178         /// <exception cref="ArgumentException">The <paramref name="value"/> cannot be less than 0.</exception>
179         /// <since_tizen> 8 </since_tizen>
180         public static void SetColumn(View view, int value) => SetAttachedValue(view, ColumnProperty, value);
181
182         /// <summary>
183         /// Sets the column span the child occupies. the default value is 1.
184         /// </summary>
185         /// <param name="view">The child view.</param>
186         /// <param name="value">The column span of <paramref name="view"/>.</param>
187         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
188         /// <exception cref="ArgumentException">The <paramref name="value"/> cannot be less than 1.</exception>
189         /// <since_tizen> 8 </since_tizen>
190         public static void SetColumnSpan(View view, int value) => SetAttachedValue(view, ColumnSpanProperty, value);
191
192         /// <summary>
193         /// Sets the row index the child occupies. the default value is -1.
194         /// </summary>
195         /// <param name="view">The child view.</param>
196         /// <param name="value">The row index of <paramref name="view"/>.</param>
197         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
198         /// <exception cref="ArgumentException">The <paramref name="value"/> cannot be less than 0.</exception>
199         /// <since_tizen> 8 </since_tizen>
200         public static void SetRow(View view, int value) => SetAttachedValue(view, RowProperty, value);
201
202         /// <summary>
203         /// Sets the row span the child occupies. the default value is 1.
204         /// </summary>
205         /// <param name="view">The child view.</param>
206         /// <param name="value">The row span of <paramref name="view"/>.</param>
207         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
208         /// <exception cref="ArgumentException">The <paramref name="value"/> cannot be less than 1.</exception>
209         /// <since_tizen> 8 </since_tizen>
210         public static void SetRowSpan(View view, int value) => SetAttachedValue(view, RowSpanProperty, value);
211
212         /// <summary>
213         /// Sets the value how child is resized within its horizontal space. <see cref="StretchFlags.None"/> by default.
214         /// </summary>
215         /// <param name="view">The child view.</param>
216         /// <param name="value">The horizontal stretch flag of <paramref name="view"/>.</param>
217         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
218         /// <exception cref="ArgumentException">The <paramref name="value"/> should be <see cref="StretchFlags"/>.</exception>
219         /// <since_tizen> 8 </since_tizen>
220         public static void SetHorizontalStretch(View view, StretchFlags value) => SetAttachedValue(view, HorizontalStretchProperty, value);
221
222         /// <summary>
223         /// Set the value how child is resized within its vertical space. <see cref="StretchFlags.None"/> by default.
224         /// </summary>
225         /// <param name="view">The child view.</param>
226         /// <param name="value">The vertical stretch flag of <paramref name="view"/>.</param>
227         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
228         /// <exception cref="ArgumentException">The <paramref name="value"/> should be <see cref="StretchFlags"/>.</exception>
229         /// <since_tizen> 8 </since_tizen>
230         public static void SetVerticalStretch(View view, StretchFlags value) => SetAttachedValue(view, VerticalStretchProperty, value);
231
232         /// <summary>
233         /// Set the horizontal alignment of this child inside the cells. <see cref="Alignment.Start"/> by default.
234         /// </summary>
235         /// <param name="view">The child view.</param>
236         /// <param name="value">The horizontal alignment flag of <paramref name="view"/>.</param>
237         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
238         /// <exception cref="ArgumentException">The <paramref name="value"/> should be <see cref="Alignment"/>.</exception>
239         /// <since_tizen> 8 </since_tizen>
240         public static void SetHorizontalAlignment(View view, Alignment value) => SetAttachedValue(view, HorizontalAlignmentProperty, value);
241
242         /// <summary>
243         /// Set the vertical alignment of this child inside the cells.
244         /// </summary>
245         /// <param name="view">The child view.</param>
246         /// <param name="value">The vertical alignment flag of <paramref name="view"/>.</param>
247         /// <exception cref="ArgumentNullException">The <paramref name="view"/> cannot be null.</exception>
248         /// <exception cref="ArgumentException">The <paramref name="value"/> should be <see cref="Alignment"/>.</exception>
249         /// <since_tizen> 8 </since_tizen>
250         public static void SetVerticalAlignment(View view, Alignment value) => SetAttachedValue(view, VerticalAlignmentProperty, value);
251
252         /// <summary>
253         /// The distance between columns.
254         /// </summary>
255         /// <since_tizen> 8 </since_tizen>
256         public float ColumnSpacing
257         {
258             get => columnSpacing;
259             set
260             {
261                 if (columnSpacing == value) return;
262                 columnSpacing = value > 0 ? value : 0;
263
264                 RequestLayout();
265             }
266         }
267
268         /// <summary>
269         /// The distance between rows.
270         /// </summary>
271         /// <since_tizen> 8 </since_tizen>
272         public float RowSpacing
273         {
274             get => rowSpacing;
275             set
276             {
277                 if (rowSpacing == value) return;
278                 rowSpacing = value > 0 ? value : 0;
279
280                 RequestLayout();
281             }
282         }
283
284         /// <summary>
285         /// Get/Set the orientation in the layout
286         /// </summary>
287         /// <exception cref="InvalidEnumArgumentException">Thrown when using invalid arguments that are enumerators.</exception>
288         /// <since_tizen> 8 </since_tizen>
289         public Orientation GridOrientation
290         {
291             get => gridOrientation;
292             set
293             {
294                 if (gridOrientation == value) return;
295                 if (value != Orientation.Horizontal && value != Orientation.Vertical)
296                     throw new InvalidEnumArgumentException(nameof(GridOrientation));
297
298                 gridOrientation = value;
299                 RequestLayout();
300             }
301         }
302
303         /// <summary>
304         /// GridLayout Constructor.
305         /// </summary>
306         /// <returns> New Grid object.</returns>
307         /// <since_tizen> 6 </since_tizen>
308         public GridLayout()
309         {
310         }
311
312         /// <summary>
313         /// Gets or Sets the number of columns in the grid.
314         /// </summary>
315         /// <since_tizen> 6 </since_tizen>
316         public int Columns
317         {
318             get => columns;
319             set
320             {
321                 if (value == columns) return;
322
323                 if (value < 1) value = 1;
324                 columns = value;
325                 RequestLayout();
326             }
327         }
328
329         /// <summary>
330         /// Gets or Sets the number of rows in the grid.
331         /// </summary>
332         /// <since_tizen> 8 </since_tizen>
333         public int Rows
334         {
335             get => rows;
336             set
337             {
338                 if (value == rows) return;
339
340                 if (value < 1) value = 1;
341                 rows = value;
342                 RequestLayout();
343             }
344         }
345
346         /// <summary>
347         /// Measure the layout and its content to determine the measured width and the measured height.<br />
348         /// </summary>
349         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
350         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
351         /// <since_tizen> 6 </since_tizen>
352         protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
353         {
354             int widthSize;
355             int heightSize;
356             var widthMode = widthMeasureSpec.Mode;
357             var heightMode = heightMeasureSpec.Mode;
358
359             InitChildren(widthMeasureSpec, heightMeasureSpec);
360
361             if (widthMode == MeasureSpecification.ModeType.Exactly)
362                 widthSize = (int)widthMeasureSpec.Size.AsRoundedValue();
363             else
364                 widthSize = (int)(hLocations[maxColumnConut] - hLocations[0] - columnSpacing);
365
366             if (heightMode == MeasureSpecification.ModeType.Exactly)
367                 heightSize = (int)heightMeasureSpec.Size.AsRoundedValue();
368             else
369                 heightSize = (int)(vLocations[maxRowCount] - vLocations[0] - rowSpacing);
370
371             LayoutLength widthLength = new LayoutLength(widthSize + Padding.Start + Padding.End);
372             LayoutLength heightLenght = new LayoutLength(heightSize + Padding.Top + Padding.Bottom);
373
374             MeasuredSize widthMeasuredSize = ResolveSizeAndState(widthLength, widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
375             MeasuredSize heightMeasuredSize = ResolveSizeAndState(heightLenght, heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
376
377             SetMeasuredDimensions(widthMeasuredSize, heightMeasuredSize);
378         }
379
380         /// <summary>
381         /// Assign a size and position to each of its children.<br />
382         /// </summary>
383         /// <param name="changed">This is a new size or position for this layout.</param>
384         /// <param name="left">Left position, relative to parent.</param>
385         /// <param name="top"> Top position, relative to parent.</param>
386         /// <param name="right">Right position, relative to parent.</param>
387         /// <param name="bottom">Bottom position, relative to parent.</param>
388         /// <since_tizen> 6 </since_tizen>
389         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
390         {
391             InitChildrenWithExpand(MeasuredWidth.Size - Padding.Start - Padding.End, MeasuredHeight.Size - Padding.Top - Padding.Bottom);
392
393             for (int i = 0; i < gridChildren.Length; i++)
394             {
395                 GridChild child = gridChildren[i];
396                 View view = child.LayoutItem?.Owner;
397
398                 if (view == null) continue;
399
400                 Alignment halign = GetHorizontalAlignment(view);
401                 Alignment valign = GetVerticalAlignment(view);
402
403                 int column = child.Column.Start;
404                 int row = child.Row.Start;
405                 int columnEnd = child.Column.End;
406                 int rowEnd = child.Row.End;
407                 float l = hLocations[column] + Padding.Start + view.Margin.Start;
408                 float t = vLocations[row] + Padding.Top + view.Margin.Top;
409                 float width = hLocations[columnEnd] - hLocations[column] - ColumnSpacing - view.Margin.Start - view.Margin.End;
410                 float height = vLocations[rowEnd] - vLocations[row] - RowSpacing - view.Margin.Top - view.Margin.Bottom;
411
412                 if (!child.Column.Stretch.HasFlag(StretchFlags.Fill))
413                 {
414                     l += (width - child.LayoutItem.MeasuredWidth.Size.AsDecimal()) * halign.ToFloat();
415                     width = child.LayoutItem.MeasuredWidth.Size.AsDecimal();
416                 }
417
418                 if (!child.Row.Stretch.HasFlag(StretchFlags.Fill))
419                 {
420                     t += (height - child.LayoutItem.MeasuredHeight.Size.AsDecimal()) * valign.ToFloat();
421                     height = child.LayoutItem.MeasuredHeight.Size.AsDecimal();
422                 }
423
424                 child.LayoutItem.Layout(new LayoutLength(l), new LayoutLength(t), new LayoutLength(l + width), new LayoutLength(t + height));
425             }
426
427             LayoutForIndependentChild();
428         }
429
430         /// <summary>
431         /// The value how child is resized within its space.
432         /// </summary>
433         /// <since_tizen> 8 </since_tizen>
434         [Flags]
435         public enum StretchFlags
436         {
437             /// <summary>
438             /// Respect mesured size of the child.
439             /// </summary>
440             /// <since_tizen> 8 </since_tizen>
441             None = 0,
442             /// <summary>
443             /// Resize to completely fill the space.
444             /// </summary>
445             /// <since_tizen> 8 </since_tizen>
446             Fill = 1,
447             /// <summary>
448             /// Expand to share available space in GridLayout.
449             /// </summary>
450             /// <since_tizen> 8 </since_tizen>
451             Expand = 2,
452             /// <summary>
453             /// Expand to share available space in GridLayout and fill the space.
454             /// </summary>
455             /// <since_tizen> 8 </since_tizen>
456             ExpandAndFill = Fill + Expand,
457         }
458
459         /// <summary>
460         /// The alignment of the grid layout child.
461         /// </summary>
462         /// <since_tizen> 8 </since_tizen>
463         public enum Alignment
464         {
465             /// <summary>
466             /// At the start of the container.
467             /// </summary>
468             /// <since_tizen> 8 </since_tizen>
469             Start = 0,
470             /// <summary>
471             /// At the center of the container
472             /// </summary>
473             /// <since_tizen> 8 </since_tizen>
474             Center = 1,
475             /// <summary>
476             /// At the end of the container.
477             /// </summary>
478             /// <since_tizen> 8 </since_tizen>
479             End = 2,
480         }
481     }
482
483     // Extension Method of GridLayout.Alignment.
484     internal static class AlignmentExtension
485     {
486         public static float ToFloat(this GridLayout.Alignment align)
487         {
488             return 0.5f * (float)align;
489         }
490     }
491 }