Merge "Exporting one constructor used by csharpbinder and making an API private....
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / linear-layout-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 "linear-layout-impl.h"
19
20 //PUBLIC INCLUDES
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 INCLUDES
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
30 namespace
31 {
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
34 #endif
35 }
36
37 namespace Dali
38 {
39 namespace Toolkit
40 {
41 namespace Internal
42 {
43
44 LinearLayoutPtr LinearLayout::New()
45 {
46   LinearLayoutPtr layout( new LinearLayout() );
47   return layout;
48 }
49
50 LinearLayout::LinearLayout()
51 : LayoutGroup(),
52   mCellPadding( 0, 0 ),
53   mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
54   mTotalLength( 0 )
55 {
56 }
57
58 LinearLayout::~LinearLayout()
59 {
60 }
61
62 void LinearLayout::SetCellPadding( LayoutSize size )
63 {
64   mCellPadding = size;
65 }
66
67 LayoutSize LinearLayout::GetCellPadding()
68 {
69   return mCellPadding;
70 }
71
72 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
73 {
74   mOrientation = orientation;
75 }
76
77 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation()
78 {
79   return mOrientation;
80 }
81
82 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
83 {
84 #if defined(DEBUG_ENABLED)
85   auto actor = Actor::DownCast(GetOwner());
86
87   std::ostringstream oss;
88   oss << "LinearLayout::OnMeasure  ";
89   if( actor )
90   {
91     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
92   }
93   oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
94   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
95 #endif
96
97   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
98   {
99     MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
100   }
101   else
102   {
103     MeasureVertical( widthMeasureSpec, heightMeasureSpec );
104   }
105 }
106
107 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
108 {
109 #if defined(DEBUG_ENABLED)
110   auto actor = Actor::DownCast(GetOwner());
111
112   std::ostringstream oss;
113   oss << "LinearLayout::OnLayout  ";
114   if( actor )
115   {
116     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
117   }
118   oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
119   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
120 #endif
121
122   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
123   {
124     LayoutHorizontal( left, top, right, bottom );
125   }
126   else
127   {
128     LayoutVertical( left, top, right, bottom );
129   }
130 }
131
132 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
133 {
134   auto widthMode = widthMeasureSpec.GetMode();
135   auto heightMode = heightMeasureSpec.GetMode();
136   bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
137   bool matchHeight = false;
138   bool allFillParent = true;
139   LayoutLength maxHeight = 0;
140   LayoutLength alternativeMaxHeight = 0;
141   struct
142   {
143     MeasuredSize::State widthState;
144     MeasuredSize::State heightState;
145   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
146
147   // Reset total length
148   mTotalLength = 0;
149
150   // measure children, and determine if further resolution is required
151   for( unsigned int i=0; i<GetChildCount(); ++i )
152   {
153     auto childLayout = GetChildAt( i );
154     if( childLayout )
155     {
156       auto childOwner = childLayout->GetOwner();
157       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
158
159       MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
160       auto childWidth = childLayout->GetMeasuredWidth();
161       auto childMargin = childLayout->GetMargin();
162
163       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
164
165       auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
166
167       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
168
169       if( isExactly )
170       {
171         mTotalLength += length;
172       }
173       else
174       {
175         auto totalLength = mTotalLength;
176         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
177       }
178
179       bool matchHeightLocally = false;
180       if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
181       {
182         // Will have to re-measure at least this child when we know exact height.
183         matchHeight = true;
184         matchHeightLocally = true;
185       }
186
187       auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
188       auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
189
190       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
191       {
192         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
193       }
194       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
195       {
196         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
197       }
198
199       maxHeight = std::max( maxHeight, childHeight );
200       allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
201       alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
202     }
203   }
204
205   Extents padding = GetPadding();
206   mTotalLength += padding.start + padding.end;
207   auto widthSize = mTotalLength;
208   widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
209   MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
210   widthSize = widthSizeAndState.GetSize();
211
212   if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
213   {
214     maxHeight = alternativeMaxHeight;
215   }
216   maxHeight += padding.top + padding.bottom;
217   maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
218
219   widthSizeAndState.SetState( childState.widthState );
220
221   SetMeasuredDimensions( widthSizeAndState,
222                          ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
223
224   if( matchHeight )
225   {
226     ForceUniformHeight( GetChildCount(), widthMeasureSpec );
227   }
228 }
229
230 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
231 {
232   // Pretend that the linear layout has an exact size. This is the measured height of
233   // ourselves. The measured height should be the max height of the children, changed
234   // to accommodate the heightMeasureSpec from the parent
235   auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
236   for (int i = 0; i < count; ++i)
237   {
238     LayoutItemPtr childLayout = GetChildAt(i);
239     if( childLayout != nullptr )
240     {
241       auto childOwner = childLayout->GetOwner();
242       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
243       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
244
245       if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
246       {
247         // Temporarily force children to reuse their old measured width
248         int oldWidth = desiredWidth;
249         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
250
251         // Remeasure with new dimensions
252         MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
253
254         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
255       }
256     }
257   }
258 }
259
260 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
261 {
262   auto owner = GetOwner();
263   auto actor = Actor::DownCast(owner);
264   bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
265
266   Extents padding = GetPadding();
267
268   LayoutLength childTop( 0 );
269   LayoutLength childLeft( padding.start );
270
271   // Where bottom of child should go
272   auto height = bottom - top;
273
274   // Space available for child
275   auto childSpace = height - padding.top - padding.bottom;
276
277   auto count = GetChildCount();
278
279   int start = 0;
280   int dir = 1;
281
282   // In case of RTL, start drawing from the last child.
283   // @todo re-work to draw the first child from the right edge, and move leftwards.
284   // (Should have an alignment also)
285   if( isLayoutRtl ) {
286     start = count - 1;
287     dir = -1;
288   }
289
290   for( unsigned int i = 0; i < count; i++)
291   {
292     int childIndex = start + dir * i;
293     LayoutItemPtr childLayout = GetChildAt( childIndex );
294     if( childLayout != nullptr )
295     {
296       auto childWidth = childLayout->GetMeasuredWidth();
297       auto childHeight = childLayout->GetMeasuredHeight();
298       auto childMargin = childLayout->GetMargin();
299
300       childTop = LayoutLength(padding.top) + ((childSpace - childHeight) / 2) + childMargin.top - childMargin.bottom;
301
302       childLeft += childMargin.start;
303       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
304       childLeft += childWidth + childMargin.end + mCellPadding.width;
305     }
306   }
307 }
308
309 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
310 {
311   auto widthMode = widthMeasureSpec.GetMode();
312
313   bool matchWidth = false;
314   bool allFillParent = true;
315   LayoutLength maxWidth = 0;
316   LayoutLength alternativeMaxWidth = 0;
317
318   struct
319   {
320     MeasuredSize::State widthState;
321     MeasuredSize::State heightState;
322   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
323
324   // Reset total length
325   mTotalLength = 0;
326
327   // measure children, and determine if further resolution is required
328   for( unsigned int i=0; i<GetChildCount(); ++i )
329   {
330     auto childLayout = GetChildAt( i );
331     if( childLayout )
332     {
333       auto childOwner = childLayout->GetOwner();
334       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
335
336       MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
337       auto childHeight = childLayout->GetMeasuredHeight();
338       auto childMargin = childLayout->GetMargin();
339       auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
340
341       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
342       auto totalLength = mTotalLength;
343       mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
344
345       bool matchWidthLocally = false;
346       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
347       {
348         // Will have to re-measure at least this child when we know exact height.
349         matchWidth = true;
350         matchWidthLocally = true;
351       }
352
353       auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
354       auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
355
356       // was combineMeasuredStates()
357       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
358       {
359         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
360       }
361       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
362       {
363         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
364       }
365
366       maxWidth = std::max( maxWidth, childWidth );
367       allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
368       alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
369     }
370   }
371   Extents padding = GetPadding();
372   mTotalLength += padding.top + padding.bottom;
373   auto heightSize = mTotalLength;
374   heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
375   MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
376   heightSize = heightSizeAndState.GetSize();
377
378   if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
379   {
380     maxWidth = alternativeMaxWidth;
381   }
382   maxWidth += padding.start + padding.end;
383   maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
384
385   heightSizeAndState.SetState( childState.heightState );
386
387   SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
388                          heightSizeAndState );
389
390   if( matchWidth )
391   {
392     ForceUniformWidth( GetChildCount(), heightMeasureSpec );
393   }
394 }
395
396 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
397 {
398   // Pretend that the linear layout has an exact size.
399   auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
400   for (int i = 0; i < count; ++i)
401   {
402     LayoutItemPtr childLayout = GetChildAt(i);
403     if( childLayout != nullptr )
404     {
405       auto childOwner = childLayout->GetOwner();
406       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
407       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
408
409       if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
410       {
411         // Temporarily force children to reuse their old measured height
412         int oldHeight = desiredHeight;
413         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
414
415         // Remeasure with new dimensions
416         MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
417
418         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
419       }
420     }
421   }
422 }
423
424 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
425 {
426   Extents padding = GetPadding();
427
428   LayoutLength childTop( 0 );
429   LayoutLength childLeft( padding.start );
430
431   // Where bottom of child should go
432   auto width = right - left;
433
434   // Space available for child
435   auto childSpace = width - padding.start - padding.end;
436   auto count = GetChildCount();
437
438   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
439   {
440     LayoutItemPtr childLayout = GetChildAt( childIndex );
441     if( childLayout != nullptr )
442     {
443       auto childWidth = childLayout->GetMeasuredWidth();
444       auto childHeight = childLayout->GetMeasuredHeight();
445       auto childMargin = childLayout->GetMargin();
446
447       childTop += childMargin.top;
448       childLeft = ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
449
450       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
451       childTop += childHeight + childMargin.bottom + mCellPadding.height;
452     }
453   }
454 }
455
456 } // namespace Internal
457 } // namespace Toolkit
458 } // namespace Dali