/* Copyright (c) 2019 Samsung Electronics Co., Ltd. .* .* Licensed under the Apache License, Version 2.0 (the "License"); .* you may not use this file except in compliance with the License. .* You may obtain a copy of the License at .* .* http://www.apache.org/licenses/LICENSE-2.0 .* .* Unless required by applicable law or agreed to in writing, software .* distributed under the License is distributed on an "AS IS" BASIS, .* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. .* See the License for the specific language governing permissions and .* limitations under the License. .* .*/ using System; using System.ComponentModel; using System.Collections.Generic; using Tizen.NUI.BaseComponents; namespace Tizen.NUI { /// /// [Draft] This class implements a grid layout /// internal class GridLayout : LayoutGroup { const int AUTO_FIT = -1; private int _columns = 1; private int _rows = 1; private int _totalWidth; private int _totalHeight; private int _requestedColumnWidth = 1; private int _numberOfRequestedColumns; private GridLocations _locations; /// /// [draft] GridLayout Constructor/> /// /// New Grid object. public GridLayout() { _locations = new GridLocations(); } // // [Draft] Get/Set the number of columns in the grid // public int Columns { get { return GetColumns(); } set { SetColumns(value); } } /// /// [draft ] Sets the number of columns the GridLayout should have. /> /// /// The number of columns. internal void SetColumns(int columns) { _numberOfRequestedColumns = columns; if( columns != _columns) { _columns = Math.Max(1, _columns); _columns = columns; RequestLayout(); } } /// /// [draft ] Gets the number of columns in the Grid /> /// /// The number of columns in the Grid. internal int GetColumns() { return _columns; } void DetermineNumberOfColumns( int availableSpace ) { if( _numberOfRequestedColumns == AUTO_FIT ) { if( availableSpace > 0 ) { // Can only calculate number of columns if a column width has been set _columns = ( _requestedColumnWidth > 0 ) ? ( availableSpace / _requestedColumnWidth ) : 1; } } } protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec ) { var gridWidthMode = widthMeasureSpec.Mode; var gridHeightMode = heightMeasureSpec.Mode; int widthSize = (int)widthMeasureSpec.Size.AsRoundedValue(); int heightSize = (int)heightMeasureSpec.Size.AsRoundedValue(); int availableContentWidth; int availableContentHeight; int desiredChildHeight; int desiredChildWidth; Extents gridLayoutPadding = Padding; var childCount = _children.Count; // WIDTH SPECIFICATIONS // measure first child and use it's dimensions for layout measurement if (childCount > 0) { LayoutItem childLayoutItem = _children[0]; View childOwner = childLayoutItem.Owner; MeasureChild( childLayoutItem, widthMeasureSpec, heightMeasureSpec ); desiredChildHeight = (int)childLayoutItem.MeasuredHeight.Size.AsRoundedValue(); desiredChildWidth = (int)childLayoutItem.MeasuredWidth.Size.AsRoundedValue(); // If child has a margin then add it to desired size Extents childMargin = childLayoutItem.Margin; desiredChildHeight += childMargin.Top + childMargin.Bottom; desiredChildWidth += childMargin.Start + childMargin.End; _totalWidth = desiredChildWidth * _columns; // Include padding for max and min checks _totalWidth += gridLayoutPadding.Start + gridLayoutPadding.End; // Ensure width does not exceed specified at most width or less than mininum width _totalWidth = Math.Max( _totalWidth, (int)SuggestedMinimumWidth.AsRoundedValue() ); // widthMode EXACTLY so grid must be the given width if( gridWidthMode == MeasureSpecification.ModeType.Exactly || gridWidthMode == MeasureSpecification.ModeType.AtMost ) { // In the case of AT_MOST, widthSize is the max limit. _totalWidth = Math.Min( _totalWidth, widthSize ); } availableContentWidth = _totalWidth - gridLayoutPadding.Start - gridLayoutPadding.End; widthSize = _totalWidth; // HEIGHT SPECIFICATIONS // heightMode EXACTLY so grid must be the given height if( gridHeightMode == MeasureSpecification.ModeType.Exactly || gridHeightMode == MeasureSpecification.ModeType.AtMost ) { if( childCount > 0 ) { _totalHeight = gridLayoutPadding.Top + gridLayoutPadding.Bottom; for( int i = 0; i < childCount; i += _columns ) { _totalHeight += desiredChildHeight; } // Ensure ourHeight does not exceed specified at most height _totalHeight = Math.Min( _totalHeight, heightSize ); _totalHeight = Math.Max( _totalHeight, (int)SuggestedMinimumHeight.AsRoundedValue() ); heightSize = _totalHeight; } // Child exists // In the case of AT_MOST, availableContentHeight is the max limit. availableContentHeight = heightSize - gridLayoutPadding.Top - gridLayoutPadding.Bottom; } else { // Grid expands to fit content // If number of columns AUTO_FIT then set to 1 column. _columns = ( _columns > 0 ) ? _columns : 1; // Calculate numbers of rows, round down result as later check for remainder. _rows = childCount / _columns; // If number of cells not cleanly dividable by columns, add another row to house remainder cells. _rows += ( childCount % _columns > 0 ) ? 1 : 0; availableContentHeight = desiredChildHeight * _rows; } // If number of columns not defined DetermineNumberOfColumns( availableContentWidth ); // Locations define the start, end,top and bottom of each cell. _locations.CalculateLocations(_columns, availableContentWidth, availableContentHeight, childCount); } // Children exists SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(widthSize), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK ), ResolveSizeAndState( new LayoutLength(heightSize), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK ) ); } protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom ) { List locations = _locations.GetLocations(); Extents gridLayoutPadding = Padding; Extents childMargins = new Extents(); // Margin for all children dependant on if set on first child if( _children.Count > 0 ) { childMargins = _children[0]?.Margin; } int index = 0; foreach( LayoutItem childLayout in _children ) { // for each child if( childLayout != null ) { // Get start and end position of child x1,x2 int x1 = locations[ index ].Start; int x2 = locations[ index ].End; // Get top and bottom position of child y1,y2 int y1 = locations[ index ].Top; int y2 = locations[ index ].Bottom; // Offset children by the grids padding if present x1 += gridLayoutPadding.Start; x2 += gridLayoutPadding.Start; y1 += gridLayoutPadding.Top; y2 += gridLayoutPadding.Top; // Offset children by the margin of the first child ( if required ). x1 += childMargins.Start; x2 -= childMargins.End; y1 += childMargins.Top; y2 -= childMargins.Bottom; childLayout.Layout( new LayoutLength(x1), new LayoutLength(y1), new LayoutLength(x2), new LayoutLength(y2) ); index++; } } } } }