Fix grid layout defaults
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / grid-impl.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 //CLASS HEADER
18 #include <dali-toolkit/internal/layouting/grid-impl.h>
19
20 //EXTERNAL HEADERS
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/common/extents.h>
23 #include <dali/public-api/actors/actor.h>
24
25 //INTERNAL HEADERS
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>
31
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" );
34 #endif
35
36 namespace Dali
37 {
38 namespace Toolkit
39 {
40 namespace Internal
41 {
42
43 GridPtr Grid::New()
44 {
45   GridPtr layout( new Grid() );
46   return layout;
47 }
48
49 Grid::Grid()
50 : LayoutGroup(),
51   mTotalHeight( 0 ),
52   mTotalWidth( 0 ),
53   mNumColumns( 1 ),
54   mNumRows( 1 ),
55   mRequestedColumnWidth( 0 ),
56   mRequestedNumColumns( AUTO_FIT )
57 {
58   mLocations = GridLocations::New();
59 }
60
61 Grid::~Grid(){}
62
63 void Grid::SetNumberOfColumns( int columns )
64 {
65   mRequestedNumColumns = columns;
66   // Store value and Relayout if changed.
67   if( columns != mNumColumns )
68   {
69     mNumColumns = columns;
70     RequestLayout();
71   }
72 }
73
74 int Grid::GetNumberOfColumns() const
75 {
76   return mNumColumns;
77 }
78
79 void Grid::DetermineNumberOfColumns( int availableSpace )
80 {
81   if( mRequestedNumColumns == AUTO_FIT )
82   {
83     if( availableSpace > 0 )
84     {
85       // Can only calculate number of columns if a column width has been set
86       mNumColumns = ( mRequestedColumnWidth > 0 ) ? ( availableSpace / mRequestedColumnWidth ) : 1;
87     }
88   }
89 }
90
91 void Grid::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
92 {
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 );
97
98   auto gridWidthMode = widthMeasureSpec.GetMode();
99   auto gridHeightMode = heightMeasureSpec.GetMode();
100   LayoutLength widthSize = widthMeasureSpec.GetSize();
101   LayoutLength heightSize = heightMeasureSpec.GetSize();
102
103   int availableContentWidth(0);
104   int availableContentHeight(0);
105
106   LayoutLength desiredChildHeight( 0 );
107   LayoutLength desiredChildWidth( 0 );
108
109   Extents gridLayoutPadding = GetPadding();
110
111   auto childCount = GetChildCount();
112
113   // WIDTH SPECIFICATIONS
114
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" );
117
118   if( childCount > 0 )
119   {
120     auto childLayoutItem = GetChildAt( 0 );
121     if( childLayoutItem )
122     {
123       auto childOwner = childLayoutItem->GetOwner();
124
125       MeasureChild( childLayoutItem, widthMeasureSpec, heightMeasureSpec );
126       desiredChildHeight = childLayoutItem->GetMeasuredHeight();
127       desiredChildWidth = childLayoutItem->GetMeasuredWidth();
128
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;
133
134       mTotalWidth = desiredChildWidth * mNumColumns;
135       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredWidth(%d) \n", mTotalWidth.mValue );
136     } // Child is LayoutItem
137   } // Child exists
138
139   // Include padding for max and min checks
140   mTotalWidth += gridLayoutPadding.start + gridLayoutPadding.end;
141
142   // Ensure width does not exceed specified atmost width or less than mininum width
143   mTotalWidth = std::max( mTotalWidth, GetSuggestedMinimumWidth() );
144
145   // widthMode EXACTLY so grid must be the given width
146   if( gridWidthMode == MeasureSpec::Mode::EXACTLY || gridWidthMode == MeasureSpec::Mode::AT_MOST )
147   {
148     // In the case of AT_MOST, widthSize is the max limit.
149       mTotalWidth = std::min( mTotalWidth, widthSize );
150   }
151
152   availableContentWidth = mTotalWidth - gridLayoutPadding.start - gridLayoutPadding.end;
153   widthSize = mTotalWidth;
154
155   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure availableContentWidth(%d) mTotalWidth(%d) \n",
156                  availableContentWidth,
157                  mTotalWidth.mValue );
158   // HEIGHT SPECIFICATIONS
159
160   // heightMode EXACTLY so grid must be the given height
161   if( gridHeightMode == MeasureSpec::Mode::EXACTLY || gridHeightMode == MeasureSpec::Mode::AT_MOST )
162   {
163     if( childCount > 0 )
164     {
165       mTotalHeight = gridLayoutPadding.top + gridLayoutPadding.bottom;
166
167       for( auto i = 0u; i < childCount; i += mNumColumns )
168       {
169         mTotalHeight += desiredChildHeight;
170       }
171       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredHeight(%d) \n",
172                       mTotalHeight.mValue );
173
174       // Ensure ourHeight does not exceed specified atmost height
175       mTotalHeight = std::min( mTotalHeight, heightSize );
176       mTotalHeight = std::max( mTotalHeight, GetSuggestedMinimumHeight() );
177
178       heightSize = mTotalHeight;
179     } // Child exists
180
181     // In the case of AT_MOST, availableContentHeigth is the max limit.
182     availableContentHeight = heightSize - gridLayoutPadding.top - gridLayoutPadding.bottom;
183   }
184   else
185   {
186     // Grid expands to fit content
187
188     // If number of columns AUTO_FIT then set to 1 column.
189
190     // Calculate numbers of rows, round down result as later check for remainder.
191     mNumRows = childCount / ( ( mNumColumns ) ? mNumColumns : 1 );
192     // If number of cells not cleanly dividable by colums, add another row to house remainder cells.
193     mNumRows += ( childCount %  ( ( mNumColumns ) ? mNumColumns : 1 ) ) ? 1 : 0;
194
195     availableContentHeight = desiredChildHeight * mNumRows;
196   }
197
198   // If number of columns not defined
199   DetermineNumberOfColumns( availableContentWidth );
200
201   // Locations define the start, end,top and bottom of each cell.
202   mLocations->CalculateLocations( mNumColumns, availableContentWidth, availableContentHeight, childCount, 0, 0 );
203
204
205   SetMeasuredDimensions( ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ),
206                          ResolveSizeAndState( heightSize, heightMeasureSpec,  MeasuredSize::State::MEASURED_SIZE_OK ) );
207 }
208
209 void Grid::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
210 {
211   auto owner = GetOwner();
212   auto actor = Actor::DownCast(owner);
213   auto count = GetChildCount();
214
215   GridLocations::LocationVector locations =  mLocations->GetLocations();
216
217   Extents gridLayoutPadding = GetPadding();
218   Extents childMargins;
219
220   // Margin for all children dependant on if set on first child
221   if( count )
222   {
223     LayoutItemPtr childLayout = GetChildAt( 0 );
224     if( childLayout )
225     {
226       auto childOwner = childLayout->GetOwner();
227       if( childOwner )
228       {
229         childMargins = childOwner.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
230       }
231     }
232   }
233
234   for( unsigned int i = 0; i < count; i++)
235   {
236     // for each child
237     int childIndex = i;
238     LayoutItemPtr childLayout = GetChildAt( childIndex );
239     if( childLayout != nullptr )
240     {
241       // Get start and end position of child x1,x2
242       auto x1 = locations[ i ].xStart;
243       auto x2 = locations[ i ].xEnd;
244
245       // Get top and bottom position of child y1,y2
246       auto y1 = locations[ i ].yTop;
247       auto y2 = locations[ i ].yBottom;
248
249       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnLayout cellSize(%d,%d) \n", x2 - x1,  y2 - y1);
250
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;
256
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;
262
263       childLayout->Layout( x1, y1, x2, y2 );
264     }
265   }
266 }
267
268 } // namespace Internal
269 } // namespace Toolkit
270 } // namespace Dali