09224ee70a1a61d48adfea6d078415a273c110b4
[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 = std::max( 1, columns );
70     RequestLayout();
71   }
72 }
73
74 int Grid::GetNumberOfColumns() const
75 {
76   return mNumColumns;
77 }
78
79 void Grid::DetermineNumberOfColumns( LayoutLength 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.AsInteger() / 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   LayoutLength availableContentWidth( 0 );
104   LayoutLength 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_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredWidth(" << mTotalWidth << ") \n" );
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_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure availableContentWidth" << availableContentWidth << " mTotalWidth(" << mTotalWidth << ") \n" );
156   // HEIGHT SPECIFICATIONS
157
158   // heightMode EXACTLY so grid must be the given height
159   if( gridHeightMode == MeasureSpec::Mode::EXACTLY || gridHeightMode == MeasureSpec::Mode::AT_MOST )
160   {
161     if( childCount > 0 )
162     {
163       mTotalHeight = gridLayoutPadding.top + gridLayoutPadding.bottom;
164
165       for( auto i = 0u; i < childCount; i += mNumColumns )
166       {
167         mTotalHeight += desiredChildHeight;
168       }
169       DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredHeight(" << mTotalHeight << ") \n" );
170
171       // Ensure ourHeight does not exceed specified atmost height
172       mTotalHeight = std::min( mTotalHeight, heightSize );
173       mTotalHeight = std::max( mTotalHeight, GetSuggestedMinimumHeight() );
174
175       heightSize = mTotalHeight;
176     } // Child exists
177
178     // In the case of AT_MOST, availableContentHeigth is the max limit.
179     availableContentHeight = heightSize - gridLayoutPadding.top - gridLayoutPadding.bottom;
180   }
181   else
182   {
183     // Grid expands to fit content
184
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;
191
192     availableContentHeight = desiredChildHeight * mNumRows;
193   }
194
195   // If number of columns not defined
196   DetermineNumberOfColumns( availableContentWidth );
197
198   // Locations define the start, end,top and bottom of each cell.
199   mLocations->CalculateLocations( mNumColumns, availableContentWidth.AsInteger(), availableContentHeight.AsInteger(), childCount, 0, 0 );
200
201
202   SetMeasuredDimensions( ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ),
203                          ResolveSizeAndState( heightSize, heightMeasureSpec,  MeasuredSize::State::MEASURED_SIZE_OK ) );
204 }
205
206 void Grid::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
207 {
208   auto owner = GetOwner();
209   auto actor = Actor::DownCast(owner);
210   auto count = GetChildCount();
211
212   GridLocations::LocationVector locations =  mLocations->GetLocations();
213
214   Extents gridLayoutPadding = GetPadding();
215   Extents childMargins;
216
217   // Margin for all children dependant on if set on first child
218   if( count )
219   {
220     LayoutItemPtr childLayout = GetChildAt( 0 );
221     if( childLayout )
222     {
223       auto childOwner = childLayout->GetOwner();
224       if( childOwner )
225       {
226         childMargins = childOwner.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
227       }
228     }
229   }
230
231   for( unsigned int i = 0; i < count; i++)
232   {
233     // for each child
234     int childIndex = i;
235     LayoutItemPtr childLayout = GetChildAt( childIndex );
236     if( childLayout != nullptr )
237     {
238       // Get start and end position of child x1,x2
239       auto x1 = locations[ i ].xStart;
240       auto x2 = locations[ i ].xEnd;
241
242       // Get top and bottom position of child y1,y2
243       auto y1 = locations[ i ].yTop;
244       auto y2 = locations[ i ].yBottom;
245
246       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnLayout cellSize(%d,%d) \n", x2 - x1,  y2 - y1);
247
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;
253
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;
259
260       childLayout->Layout( x1, y1, x2, y2 );
261     }
262   }
263 }
264
265 } // namespace Internal
266 } // namespace Toolkit
267 } // namespace Dali