2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali-toolkit/internal/layouting/grid-impl.h>
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/common/extents.h>
23 #include <dali/public-api/actors/actor.h>
26 #include <dali-toolkit/devel-api/layouting/layout-item.h>
27 #include <dali-toolkit/public-api/controls/control-impl.h>
28 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
29 #include <dali-toolkit/devel-api/layouting/layout-length.h>
30 #include <dali-toolkit/internal/layouting/grid-locations.h>
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" );
45 GridPtr layout( new Grid() );
55 mRequestedColumnWidth( 0 ),
56 mRequestedNumColumns( AUTO_FIT )
58 mLocations = GridLocations::New();
63 void Grid::SetNumberOfColumns( int columns )
65 mRequestedNumColumns = columns;
66 // Store value and Relayout if changed.
67 if( columns != mNumColumns )
69 mNumColumns = std::max( 1, columns );
74 int Grid::GetNumberOfColumns() const
79 void Grid::DetermineNumberOfColumns( LayoutLength availableSpace )
81 if( mRequestedNumColumns == AUTO_FIT )
83 if( availableSpace > 0 )
85 // Can only calculate number of columns if a column width has been set
86 mNumColumns = ( mRequestedColumnWidth > 0 ) ? ( availableSpace.AsInteger() / mRequestedColumnWidth ) : 1;
91 void Grid::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
93 DALI_LOG_STREAM( gLogFilter, Debug::Verbose,
94 "Grid::OnMeasure Actor Id:" << Actor::DownCast(GetOwner()).GetId() <<
95 " Owner:" << Actor::DownCast(GetOwner()).GetName() <<
96 " MeasureSpecs( width:"<<widthMeasureSpec<<", height:"<<heightMeasureSpec );
98 auto gridWidthMode = widthMeasureSpec.GetMode();
99 auto gridHeightMode = heightMeasureSpec.GetMode();
100 LayoutLength widthSize = widthMeasureSpec.GetSize();
101 LayoutLength heightSize = heightMeasureSpec.GetSize();
103 LayoutLength availableContentWidth( 0 );
104 LayoutLength availableContentHeight( 0 );
106 LayoutLength desiredChildHeight( 0 );
107 LayoutLength desiredChildWidth( 0 );
109 Extents gridLayoutPadding = GetPadding();
111 auto childCount = GetChildCount();
113 // WIDTH SPECIFICATIONS
115 // measure first child and use it's dimensions for layout measurement
116 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure MeasureSpec::Mode::EXACTLY or MeasureSpec::Mode::AT_MOST \n" );
120 auto childLayoutItem = GetChildAt( 0 );
121 if( childLayoutItem )
123 auto childOwner = childLayoutItem->GetOwner();
125 MeasureChild( childLayoutItem, widthMeasureSpec, heightMeasureSpec );
126 desiredChildHeight = childLayoutItem->GetMeasuredHeight();
127 desiredChildWidth = childLayoutItem->GetMeasuredWidth();
129 // If child has a margin then add it to desired size
130 Extents childMargin = childOwner.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
131 desiredChildHeight += childMargin.top + childMargin.bottom;
132 desiredChildWidth += childMargin.start + childMargin.end;
134 mTotalWidth = desiredChildWidth * mNumColumns;
135 DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredWidth(" << mTotalWidth << ") \n" );
136 } // Child is LayoutItem
139 // Include padding for max and min checks
140 mTotalWidth += gridLayoutPadding.start + gridLayoutPadding.end;
142 // Ensure width does not exceed specified atmost width or less than mininum width
143 mTotalWidth = std::max( mTotalWidth, GetSuggestedMinimumWidth() );
145 // widthMode EXACTLY so grid must be the given width
146 if( gridWidthMode == MeasureSpec::Mode::EXACTLY || gridWidthMode == MeasureSpec::Mode::AT_MOST )
148 // In the case of AT_MOST, widthSize is the max limit.
149 mTotalWidth = std::min( mTotalWidth, widthSize );
152 availableContentWidth = mTotalWidth - gridLayoutPadding.start - gridLayoutPadding.end;
153 widthSize = mTotalWidth;
155 DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure availableContentWidth" << availableContentWidth << " mTotalWidth(" << mTotalWidth << ") \n" );
156 // HEIGHT SPECIFICATIONS
158 // heightMode EXACTLY so grid must be the given height
159 if( gridHeightMode == MeasureSpec::Mode::EXACTLY || gridHeightMode == MeasureSpec::Mode::AT_MOST )
163 mTotalHeight = gridLayoutPadding.top + gridLayoutPadding.bottom;
165 for( auto i = 0u; i < childCount; i += mNumColumns )
167 mTotalHeight += desiredChildHeight;
169 DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredHeight(" << mTotalHeight << ") \n" );
171 // Ensure ourHeight does not exceed specified atmost height
172 mTotalHeight = std::min( mTotalHeight, heightSize );
173 mTotalHeight = std::max( mTotalHeight, GetSuggestedMinimumHeight() );
175 heightSize = mTotalHeight;
178 // In the case of AT_MOST, availableContentHeigth is the max limit.
179 availableContentHeight = heightSize - gridLayoutPadding.top - gridLayoutPadding.bottom;
183 // Grid expands to fit content
185 // If number of columns AUTO_FIT then set to 1 column.
186 mNumColumns = ( mNumColumns > 0 ) ? mNumColumns : 1;
187 // Calculate numbers of rows, round down result as later check for remainder.
188 mNumRows = childCount / mNumColumns;
189 // If number of cells not cleanly dividable by colums, add another row to house remainder cells.
190 mNumRows += ( childCount % mNumColumns ) ? 1 : 0;
192 availableContentHeight = desiredChildHeight * mNumRows;
195 // If number of columns not defined
196 DetermineNumberOfColumns( availableContentWidth );
198 // Locations define the start, end,top and bottom of each cell.
199 mLocations->CalculateLocations( mNumColumns, availableContentWidth.AsInteger(), availableContentHeight.AsInteger(), childCount );
202 SetMeasuredDimensions( ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ),
203 ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ) );
206 void Grid::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
208 auto owner = GetOwner();
209 auto actor = Actor::DownCast(owner);
210 auto count = GetChildCount();
212 GridLocations::LocationVector locations = mLocations->GetLocations();
214 Extents gridLayoutPadding = GetPadding();
215 Extents childMargins;
217 // Margin for all children dependant on if set on first child
220 LayoutItemPtr childLayout = GetChildAt( 0 );
223 auto childOwner = childLayout->GetOwner();
226 childMargins = childOwner.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
231 for( unsigned int i = 0; i < count; i++)
235 LayoutItemPtr childLayout = GetChildAt( childIndex );
236 if( childLayout != nullptr )
238 // Get start and end position of child x1,x2
239 auto x1 = locations[ i ].xStart;
240 auto x2 = locations[ i ].xEnd;
242 // Get top and bottom position of child y1,y2
243 auto y1 = locations[ i ].yTop;
244 auto y2 = locations[ i ].yBottom;
246 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnLayout cellSize(%d,%d) \n", x2 - x1, y2 - y1);
248 // Offset children by the grids padding if present
249 x1 += gridLayoutPadding.start;
250 x2 += gridLayoutPadding.start;
251 y1 += gridLayoutPadding.top;
252 y2 += gridLayoutPadding.top;
254 // Offset children by the margin of the first child ( if required ).
255 x1 += childMargins.start;
256 x2 -= childMargins.end;
257 y1 += childMargins.top;
258 y2 -= childMargins.bottom;
260 childLayout->Layout( x1, y1, x2, y2 );
265 } // namespace Internal
266 } // namespace Toolkit