[NUI] Integreation from dalihub (#988)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / 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 System.Collections.Generic;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI
23 {
24     /// <summary>
25     /// [Draft] This class implements a grid layout
26     /// </summary>
27     internal class GridLayout : LayoutGroup
28     {
29         const int AUTO_FIT = -1;
30         private int _columns = 1;
31         private int _rows = 1;
32         private int _totalWidth;
33         private int _totalHeight;
34         private int _requestedColumnWidth = 1;
35         private int _numberOfRequestedColumns;
36         private GridLocations _locations;
37
38         /// <summary>
39         /// [draft] GridLayout Constructor/>
40         /// </summary>
41         /// <returns> New Grid object.</returns>
42         public GridLayout()
43         {
44             _locations = new GridLocations();
45         }
46
47         // <summary>
48         // [Draft] Get/Set the number of columns in the grid
49         // </summary>
50         public int Columns
51         {
52             get
53             {
54                 return GetColumns();
55             }
56             set
57             {
58                 SetColumns(value);
59             }
60         }
61
62
63         /// <summary>
64         /// [draft ] Sets the number of columns the GridLayout should have. />
65         /// </summary>
66         /// <param name="columns">The number of columns.</param>
67         internal void SetColumns(int columns)
68         {
69             _numberOfRequestedColumns = columns;
70             if( columns != _columns)
71             {
72                 _columns = Math.Max(1, _columns);
73                 _columns = columns;
74                 RequestLayout();
75             }
76         }
77
78         /// <summary>
79         /// [draft ] Gets the number of columns in the Grid />
80         /// </summary>
81         /// <returns>The number of columns in the Grid.</returns>
82         internal int GetColumns()
83         {
84             return _columns;
85         }
86
87         void DetermineNumberOfColumns( int availableSpace )
88         {
89             if( _numberOfRequestedColumns == AUTO_FIT )
90             {
91                 if( availableSpace > 0 )
92                 {
93                     // Can only calculate number of columns if a column width has been set
94                     _columns = ( _requestedColumnWidth > 0 ) ? ( availableSpace / _requestedColumnWidth ) : 1;
95                 }
96             }
97         }
98
99         protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
100         {
101             var gridWidthMode = widthMeasureSpec.Mode;
102             var gridHeightMode = heightMeasureSpec.Mode;
103             int widthSize = (int)widthMeasureSpec.Size.AsRoundedValue();
104             int heightSize = (int)heightMeasureSpec.Size.AsRoundedValue();
105
106             int availableContentWidth;
107             int availableContentHeight;
108
109             int desiredChildHeight;
110             int desiredChildWidth;
111
112             Extents gridLayoutPadding = Padding;
113
114             var childCount = _children.Count;
115
116             // WIDTH SPECIFICATIONS
117
118             // measure first child and use it's dimensions for layout measurement
119
120             if (childCount > 0)
121             {
122                 LayoutItem childLayoutItem = _children[0];
123                 View childOwner = childLayoutItem.Owner;
124
125                 MeasureChild( childLayoutItem, widthMeasureSpec, heightMeasureSpec );
126                 desiredChildHeight = (int)childLayoutItem.MeasuredHeight.Size.AsRoundedValue();
127                 desiredChildWidth = (int)childLayoutItem.MeasuredWidth.Size.AsRoundedValue();
128
129                 // If child has a margin then add it to desired size
130                 Extents childMargin = childLayoutItem.Margin;
131                 desiredChildHeight += childMargin.Top + childMargin.Bottom;
132                 desiredChildWidth += childMargin.Start + childMargin.End;
133
134                 _totalWidth = desiredChildWidth * _columns;
135
136                 // Include padding for max and min checks
137                 _totalWidth += gridLayoutPadding.Start + gridLayoutPadding.End;
138
139                 // Ensure width does not exceed specified at most width or less than mininum width
140                 _totalWidth = Math.Max( _totalWidth, (int)SuggestedMinimumWidth.AsRoundedValue() );
141
142                 // widthMode EXACTLY so grid must be the given width
143                 if( gridWidthMode == MeasureSpecification.ModeType.Exactly || gridWidthMode == MeasureSpecification.ModeType.AtMost )
144                 {
145                     // In the case of AT_MOST, widthSize is the max limit.
146                     _totalWidth = Math.Min( _totalWidth, widthSize );
147                 }
148
149                 availableContentWidth = _totalWidth - gridLayoutPadding.Start - gridLayoutPadding.End;
150                 widthSize = _totalWidth;
151
152                 // HEIGHT SPECIFICATIONS
153
154                 // heightMode EXACTLY so grid must be the given height
155                 if( gridHeightMode == MeasureSpecification.ModeType.Exactly || gridHeightMode == MeasureSpecification.ModeType.AtMost )
156                 {
157                     if( childCount > 0 )
158                     {
159                         _totalHeight = gridLayoutPadding.Top + gridLayoutPadding.Bottom;
160
161                         for( int i = 0; i < childCount; i += _columns )
162                         {
163                           _totalHeight += desiredChildHeight;
164                         }
165
166                         // Ensure ourHeight does not exceed specified at most height
167                         _totalHeight = Math.Min( _totalHeight, heightSize );
168                         _totalHeight = Math.Max( _totalHeight, (int)SuggestedMinimumHeight.AsRoundedValue() );
169
170                         heightSize = _totalHeight;
171                     } // Child exists
172
173                     // In the case of AT_MOST, availableContentHeight is the max limit.
174                     availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom;
175                 }
176                 else
177                 {
178                   // Grid expands to fit content
179
180                   // If number of columns AUTO_FIT then set to 1 column.
181                   _columns = ( _columns > 0 ) ? _columns : 1;
182                   // Calculate numbers of rows, round down result as later check for remainder.
183                   _rows = childCount / _columns;
184                   // If number of cells not cleanly dividable by columns, add another row to house remainder cells.
185                   _rows += ( childCount % _columns > 0 ) ? 1 : 0;
186
187                   availableContentHeight = desiredChildHeight * _rows;
188                 }
189
190             // If number of columns not defined
191             DetermineNumberOfColumns( availableContentWidth );
192
193             // Locations define the start, end,top and bottom of each cell.
194             _locations.CalculateLocations(_columns, availableContentWidth, availableContentHeight, childCount);
195
196             } // Children exists
197
198             SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK ),
199                                    ResolveSizeAndState( new LayoutLength(heightSize), heightMeasureSpec,  MeasuredSize.StateType.MeasuredSizeOK ) );
200         }
201
202         protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
203         {
204             List<GridLocations.Cell> locations = _locations.GetLocations();
205
206             Extents gridLayoutPadding = Padding;
207             Extents childMargins = new Extents();
208
209             // Margin for all children dependant on if set on first child
210             if( _children.Count > 0 )
211             {
212               childMargins = _children[0]?.Margin;
213             }
214
215             int index = 0;
216             foreach( LayoutItem childLayout in _children )
217             {
218                 // for each child
219                 if( childLayout != null )
220                 {
221                     // Get start and end position of child x1,x2
222                     int x1 = locations[ index ].Start;
223                     int x2 = locations[ index ].End;
224
225                     // Get top and bottom position of child y1,y2
226                     int y1 = locations[ index ].Top;
227                     int y2 = locations[ index ].Bottom;
228
229                     // Offset children by the grids padding if present
230                     x1 += gridLayoutPadding.Start;
231                     x2 += gridLayoutPadding.Start;
232                     y1 += gridLayoutPadding.Top;
233                     y2 += gridLayoutPadding.Top;
234
235                     // Offset children by the margin of the first child ( if required ).
236                     x1 += childMargins.Start;
237                     x2 -= childMargins.End;
238                     y1 += childMargins.Top;
239                     y2 -= childMargins.Bottom;
240
241                     childLayout.Layout( new LayoutLength(x1), new LayoutLength(y1),
242                                         new LayoutLength(x2), new LayoutLength(y2) );
243                     index++;
244                 }
245             }
246         }
247     }
248 }