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