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