1 /* Copyright (c) 2019 Samsung Electronics Co., Ltd.
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
7 .* http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.ComponentModel;
19 using System.Collections.Generic;
20 using Tizen.NUI.BaseComponents;
25 /// [Draft] This class implements a grid layout
27 public class GridLayout : LayoutGroup
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;
39 /// [draft] GridLayout Constructor/>
41 /// <returns> New Grid object.</returns>
42 /// <since_tizen> 6 </since_tizen>
45 _locations = new GridLocations();
49 /// [Draft] Get/Set the number of columns in the grid
51 /// <since_tizen> 6 </since_tizen>
66 /// [draft ] Sets the number of columns the GridLayout should have. />
68 /// <param name="columns">The number of columns.</param>
69 internal void SetColumns(int columns)
71 _numberOfRequestedColumns = columns;
72 if( columns != _columns)
74 _columns = Math.Max(1, _columns);
81 /// [draft ] Gets the number of columns in the Grid />
83 /// <returns>The number of columns in the Grid.</returns>
84 internal int GetColumns()
89 void DetermineNumberOfColumns( int availableSpace )
91 if( _numberOfRequestedColumns == AUTO_FIT )
93 if( availableSpace > 0 )
95 // Can only calculate number of columns if a column width has been set
96 _columns = ( _requestedColumnWidth > 0 ) ? ( availableSpace / _requestedColumnWidth ) : 1;
102 /// Measure the layout and its content to determine the measured width and the measured height.<br />
104 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
105 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
106 /// <since_tizen> 6 </since_tizen>
107 protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
109 var gridWidthMode = widthMeasureSpec.Mode;
110 var gridHeightMode = heightMeasureSpec.Mode;
111 int widthSize = (int)widthMeasureSpec.Size.AsRoundedValue();
112 int heightSize = (int)heightMeasureSpec.Size.AsRoundedValue();
114 int availableContentWidth;
115 int availableContentHeight;
117 int desiredChildHeight;
118 int desiredChildWidth;
120 Extents gridLayoutPadding = Padding;
122 var childCount = LayoutChildren.Count;
124 // WIDTH SPECIFICATIONS
127 foreach( LayoutItem childLayout in LayoutChildren )
129 if( childLayout != null )
131 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
135 // Use first child's dimensions for layout measurement
136 View childOwner = LayoutChildren[0].Owner;
138 desiredChildHeight = (int)LayoutChildren[0].MeasuredHeight.Size.AsRoundedValue();
139 desiredChildWidth = (int)LayoutChildren[0].MeasuredWidth.Size.AsRoundedValue();
141 // If child has a margin then add it to desired size
142 Extents childMargin = LayoutChildren[0].Margin;
143 desiredChildHeight += childMargin.Top + childMargin.Bottom;
144 desiredChildWidth += childMargin.Start + childMargin.End;
146 _totalWidth = desiredChildWidth * _columns;
148 // Include padding for max and min checks
149 _totalWidth += gridLayoutPadding.Start + gridLayoutPadding.End;
151 // Ensure width does not exceed specified at most width or less than mininum width
152 _totalWidth = Math.Max( _totalWidth, (int)SuggestedMinimumWidth.AsRoundedValue() );
154 // widthMode EXACTLY so grid must be the given width
155 if( gridWidthMode == MeasureSpecification.ModeType.Exactly || gridWidthMode == MeasureSpecification.ModeType.AtMost )
157 // In the case of AT_MOST, widthSize is the max limit.
158 _totalWidth = Math.Min( _totalWidth, widthSize );
161 availableContentWidth = _totalWidth - gridLayoutPadding.Start - gridLayoutPadding.End;
162 widthSize = _totalWidth;
164 // HEIGHT SPECIFICATIONS
166 // heightMode EXACTLY so grid must be the given height
167 if( gridHeightMode == MeasureSpecification.ModeType.Exactly || gridHeightMode == MeasureSpecification.ModeType.AtMost )
171 _totalHeight = gridLayoutPadding.Top + gridLayoutPadding.Bottom;
173 for( int i = 0; i < childCount; i += _columns )
175 _totalHeight += desiredChildHeight;
178 // Ensure ourHeight does not exceed specified at most height
179 _totalHeight = Math.Min( _totalHeight, heightSize );
180 _totalHeight = Math.Max( _totalHeight, (int)SuggestedMinimumHeight.AsRoundedValue() );
182 heightSize = _totalHeight;
185 // In the case of AT_MOST, availableContentHeight is the max limit.
186 availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom;
190 // Grid expands to fit content
192 // If number of columns AUTO_FIT then set to 1 column.
193 _columns = ( _columns > 0 ) ? _columns : 1;
194 // Calculate numbers of rows, round down result as later check for remainder.
195 _rows = childCount / _columns;
196 // If number of cells not cleanly dividable by columns, add another row to house remainder cells.
197 _rows += ( childCount % _columns > 0 ) ? 1 : 0;
199 heightSize = desiredChildHeight * _rows + gridLayoutPadding.Top + gridLayoutPadding.Bottom;
200 availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom;
203 // If number of columns not defined
204 DetermineNumberOfColumns( availableContentWidth );
206 // Locations define the start, end,top and bottom of each cell.
207 _locations.CalculateLocations(_columns, availableContentWidth, availableContentHeight, childCount);
211 SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK ),
212 ResolveSizeAndState( new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK ) );
216 /// Assign a size and position to each of its children.<br />
218 /// <param name="changed">This is a new size or position for this layout.</param>
219 /// <param name="left">Left position, relative to parent.</param>
220 /// <param name="top"> Top position, relative to parent.</param>
221 /// <param name="right">Right position, relative to parent.</param>
222 /// <param name="bottom">Bottom position, relative to parent.</param>
223 /// <since_tizen> 6 </since_tizen>
224 protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
226 List<GridLocations.Cell> locations = _locations.GetLocations();
228 Extents gridLayoutPadding = Padding;
229 Extents childMargins = new Extents();
231 // Margin for all children dependant on if set on first child
232 if( LayoutChildren.Count > 0 )
234 childMargins = LayoutChildren[0]?.Margin;
238 foreach( LayoutItem childLayout in LayoutChildren )
241 if( childLayout != null )
243 // Get start and end position of child x1,x2
244 int x1 = locations[ index ].Start;
245 int x2 = locations[ index ].End;
247 // Get top and bottom position of child y1,y2
248 int y1 = locations[ index ].Top;
249 int y2 = locations[ index ].Bottom;
251 // Offset children by the grids padding if present
252 x1 += gridLayoutPadding.Start;
253 x2 += gridLayoutPadding.Start;
254 y1 += gridLayoutPadding.Top;
255 y2 += gridLayoutPadding.Top;
257 // Offset children by the margin of the first child ( if required ).
258 x1 += childMargins.Start;
259 x2 -= childMargins.End;
260 y1 += childMargins.Top;
261 y2 -= childMargins.Bottom;
263 childLayout.Layout( new LayoutLength(x1), new LayoutLength(y1),
264 new LayoutLength(x2), new LayoutLength(y2) );