fdba7e63c8c950b2199df641954302f4d4a41f0f
[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 #include <dali/devel-api/object/handle-devel.h>
25
26 //INTERNAL INCLUDES
27 #include <dali-toolkit/devel-api/layouting/layout-item.h>
28 #include <dali-toolkit/public-api/controls/control-impl.h>
29 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
30
31 namespace
32 {
33 #if defined(DEBUG_ENABLED)
34 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
35 #endif
36 }
37
38 namespace Dali
39 {
40 namespace Toolkit
41 {
42 namespace Internal
43 {
44
45 const int HORIZONTAL_ALIGNMENT_MASK  = ( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL | Dali::Toolkit::LinearLayout::Alignment::END );
46 const int VERTICAL_ALIGNMENT_MASK = ( Dali::Toolkit::LinearLayout::Alignment::TOP | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL | Dali::Toolkit::LinearLayout::Alignment::BOTTOM );
47
48 LinearLayoutPtr LinearLayout::New()
49 {
50   LinearLayoutPtr layout( new LinearLayout() );
51   return layout;
52 }
53
54 LinearLayout::LinearLayout()
55 : LayoutGroup(),
56   mCellPadding( 0, 0 ),
57   mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
58   mAlignment( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL ),
59   mTotalLength( 0 )
60 {
61 }
62
63 LinearLayout::~LinearLayout()
64 {
65 }
66
67 void LinearLayout::DoRegisterChildProperties( const std::string& containerType )
68 {
69   auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
70   if( typeInfo )
71   {
72     Property::IndexContainer indices;
73     typeInfo.GetChildPropertyIndices( indices );
74
75     if( std::find( indices.Begin(), indices.End(), Toolkit::LinearLayout::ChildProperty::WEIGHT ) ==
76         indices.End() )
77     {
78       ChildPropertyRegistration( typeInfo.GetName(), "weight", Toolkit::LinearLayout::ChildProperty::WEIGHT, Property::FLOAT );
79     }
80   }
81 }
82
83 void LinearLayout::OnChildAdd( LayoutItem& child )
84 {
85   auto owner = child.GetOwner();
86   if( !DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LinearLayout::ChildProperty::WEIGHT ) )
87   {
88     owner.SetProperty( Toolkit::LinearLayout::ChildProperty::WEIGHT, 0.0f );
89   }
90 }
91
92 void LinearLayout::SetCellPadding( LayoutSize size )
93 {
94   if ( mCellPadding != size )
95   {
96     mCellPadding = size;
97     RequestLayout();
98   }
99 }
100
101 LayoutSize LinearLayout::GetCellPadding() const
102 {
103   return mCellPadding;
104 }
105
106 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
107 {
108   if ( mOrientation != orientation )
109   {
110     mOrientation = orientation;
111     RequestLayout();
112   }
113 }
114
115 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation() const
116 {
117   return mOrientation;
118 }
119
120 void LinearLayout::SetAlignment( unsigned int alignment )
121 {
122   if ( mAlignment != alignment )
123   {
124     mAlignment = alignment;
125     RequestLayout();
126   }
127 }
128
129 unsigned int LinearLayout::GetAlignment() const
130 {
131   return mAlignment;
132 }
133
134 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
135 {
136 #if defined(DEBUG_ENABLED)
137   auto actor = Actor::DownCast(GetOwner());
138
139   std::ostringstream oss;
140   oss << "LinearLayout::OnMeasure  ";
141   if( actor )
142   {
143     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
144   }
145   oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
146   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
147 #endif
148
149   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
150   {
151     MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
152   }
153   else
154   {
155     MeasureVertical( widthMeasureSpec, heightMeasureSpec );
156   }
157 }
158
159 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
160 {
161 #if defined(DEBUG_ENABLED)
162   auto actor = Actor::DownCast(GetOwner());
163
164   std::ostringstream oss;
165   oss << "LinearLayout::OnLayout  ";
166   if( actor )
167   {
168     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
169   }
170   oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
171   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
172 #endif
173
174   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
175   {
176     LayoutHorizontal( left, top, right, bottom );
177   }
178   else
179   {
180     LayoutVertical( left, top, right, bottom );
181   }
182 }
183
184 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
185 {
186   auto widthMode = widthMeasureSpec.GetMode();
187   auto heightMode = heightMeasureSpec.GetMode();
188   bool isExactly = ( widthMode == MeasureSpec::Mode::EXACTLY );
189   bool matchHeight = false;
190   bool allFillParent = true;
191   LayoutLength maxHeight = 0;
192   LayoutLength alternativeMaxHeight = 0;
193   LayoutLength weightedMaxHeight = 0;
194   float totalWeight = 0;
195   int usedExcessSpace = 0;
196   struct
197   {
198     MeasuredSize::State widthState;
199     MeasuredSize::State heightState;
200   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
201
202   // Reset total length
203   mTotalLength = 0;
204
205   // measure children, and determine if further resolution is required
206
207   // 1st phase:
208   // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs
209   // to accumulate total used space in mTotalLength based on measured sizes and margins.
210   // Weighted children are not measured at this phase.
211   // Available space for weighted children will be calculated in the phase 2 based on mTotalLength value.
212   for( unsigned int i = 0; i < GetChildCount(); ++i )
213   {
214     auto childLayout = GetChildAt( i );
215     if( childLayout )
216     {
217       auto childOwner = childLayout->GetOwner();
218       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
219       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
220       auto childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
221       auto childMargin = childLayout->GetMargin();
222
223       totalWeight += childWeight;
224
225       const bool useExcessSpace = desiredWidth == 0 && childWeight > 0;
226       if( isExactly && useExcessSpace )
227       {
228         mTotalLength += childMargin.start + childMargin.end;
229       }
230       else
231       {
232         LayoutLength childWidth = 0;
233         if( useExcessSpace )
234         {
235           // The widthMode is either UNSPECIFIED or AT_MOST, and
236           // this child is only laid out using excess space. Measure
237           // using WRAP_CONTENT so that we can find out the view's
238           // optimal width.
239           auto padding = GetPadding();
240           const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( widthMeasureSpec, padding.start + padding.end, Toolkit::ChildLayoutData::WRAP_CONTENT );
241           const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( heightMeasureSpec, padding.top + padding.bottom, desiredHeight );
242           childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
243           childWidth = childLayout->GetMeasuredWidth();
244           usedExcessSpace += childWidth;
245         }
246         else
247         {
248           MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
249           childWidth = childLayout->GetMeasuredWidth();
250         }
251
252         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
253         auto length = childWidth + LayoutLength::IntType( childMargin.start + childMargin.end );
254         auto cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
255         if( isExactly )
256         {
257           mTotalLength += length;
258         }
259         else
260         {
261           auto totalLength = mTotalLength;
262           mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
263         }
264       }
265
266       bool matchHeightLocally = false;
267       if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
268       {
269         // Will have to re-measure at least this child when we know exact height.
270         matchHeight = true;
271         matchHeightLocally = true;
272       }
273
274       auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
275       auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
276
277       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
278       {
279         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
280       }
281       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
282       {
283         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
284       }
285
286       maxHeight = std::max( maxHeight, childHeight );
287       allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
288       if( childWeight > 0 )
289       {
290         /*
291          * Heights of weighted Views are bogus if we end up
292          * remeasuring, so keep them separate.
293          */
294         weightedMaxHeight = std::max( weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight );
295       }
296       else
297       {
298         alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
299       }
300     }
301   }
302
303   Extents padding = GetPadding();
304   mTotalLength += padding.start + padding.end;
305   auto widthSize = mTotalLength;
306   widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
307   MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
308   widthSize = widthSizeAndState.GetSize();
309
310   // Expand children with weight to take up available space
311   // 2nd phase:
312   // We cycle through weighted children now (children with weight > 0).
313   // The children are measured with exact size equal to their share of the available space based on their weights.
314   // mTotalLength is updated to include weighted children measured sizes.
315   int remainingExcess = widthSize - mTotalLength + usedExcessSpace;
316   if( remainingExcess != 0 && totalWeight > 0 )
317   {
318     float remainingWeightSum = totalWeight;
319     maxHeight = 0;
320     mTotalLength = 0;
321
322     for( unsigned int i = 0; i < GetChildCount(); ++i )
323     {
324       auto childLayout = GetChildAt( i );
325       auto childOwner = childLayout->GetOwner();
326       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
327       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
328       auto childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
329       auto childMargin = childLayout->GetMargin();
330
331       LayoutLength childWidth = 0;
332       if( childWeight > 0 )
333       {
334         int share = static_cast<int>( childWeight * remainingExcess / remainingWeightSum );
335         remainingExcess -= share;
336         remainingWeightSum -= childWeight;
337
338         // Always lay out weighted elements with intrinsic size regardless of the parent spec.
339         // for consistency between specs.
340         if( desiredWidth == 0 )
341         {
342           // This child needs to be laid out from scratch using
343           // only its share of excess space.
344           childWidth = share;
345         }
346         else
347         {
348           // This child had some intrinsic width to which we
349           // need to add its share of excess space.
350           childWidth = childLayout->GetMeasuredWidth() + share;
351         }
352
353         const MeasureSpec childWidthMeasureSpec = MeasureSpec( childWidth, MeasureSpec::Mode::EXACTLY );
354         const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( heightMeasureSpec, padding.top + padding.bottom, desiredHeight );
355         childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
356
357         // Child may now not fit in horizontal dimension.
358         if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
359         {
360           childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
361         }
362       }
363
364       auto length = childLayout->GetMeasuredWidth() + LayoutLength::IntType( childMargin.start + childMargin.end );
365       auto cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
366       if( isExactly )
367       {
368         mTotalLength += length;
369       }
370       else
371       {
372         auto totalLength = mTotalLength;
373         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
374       }
375
376       bool matchHeightLocally = heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT;
377       auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
378       auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
379
380       maxHeight = std::max( maxHeight, childHeight );
381       alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
382       allFillParent = (allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT);
383
384       mTotalLength += padding.start + padding.end;
385     }
386   }
387   else
388   {
389     alternativeMaxHeight = std::max( alternativeMaxHeight, weightedMaxHeight );
390   }
391
392   if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
393   {
394     maxHeight = alternativeMaxHeight;
395   }
396   maxHeight += padding.top + padding.bottom;
397   maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
398
399   widthSizeAndState.SetState( childState.widthState );
400
401   SetMeasuredDimensions( widthSizeAndState,
402                          ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
403
404   if( matchHeight )
405   {
406     ForceUniformHeight( GetChildCount(), widthMeasureSpec );
407   }
408 }
409
410 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
411 {
412   // Pretend that the linear layout has an exact size. This is the measured height of
413   // ourselves. The measured height should be the max height of the children, changed
414   // to accommodate the heightMeasureSpec from the parent
415   auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
416   for (int i = 0; i < count; ++i)
417   {
418     LayoutItemPtr childLayout = GetChildAt(i);
419     if( childLayout != nullptr )
420     {
421       auto childOwner = childLayout->GetOwner();
422       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
423       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
424
425       if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
426       {
427         // Temporarily force children to reuse their old measured width
428         int oldWidth = desiredWidth;
429         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
430
431         // Remeasure with new dimensions
432         MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
433
434         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
435       }
436     }
437   }
438 }
439
440 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
441 {
442   auto owner = GetOwner();
443   auto actor = Actor::DownCast(owner);
444   bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
445
446   Extents padding = GetPadding();
447
448   LayoutLength childTop( padding.top );
449   LayoutLength childLeft( padding.start );
450
451   // Where bottom of child should go
452   auto height = bottom - top;
453
454   // Space available for child
455   auto childSpace = height - padding.top - padding.bottom;
456
457   auto count = GetChildCount();
458
459   switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
460   {
461     case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
462     default:
463     {
464       // mTotalLength contains the padding already
465       // In case of RTL map BEGIN alignment to the right edge
466       if ( isLayoutRtl ) {
467         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
468       }
469       else {
470         childLeft = LayoutLength( padding.start );
471       }
472       break;
473     }
474     case Dali::Toolkit::LinearLayout::Alignment::END:
475     {
476       // mTotalLength contains the padding already
477       // In case of RTL map END alignment to the left edge
478       if ( isLayoutRtl ) {
479         childLeft = LayoutLength( padding.start );
480       }
481       else {
482         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
483       }
484       break;
485     }
486     case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
487     {
488       // mTotalLength contains the padding already
489       childLeft = LayoutLength( padding.start ) + ( right - left - mTotalLength ) / 2;
490       break;
491     }
492   }
493
494   int start = 0;
495   int dir = 1;
496
497   // In case of RTL, start drawing from the last child.
498   if( isLayoutRtl ) {
499     start = count - 1;
500     dir = -1;
501   }
502
503   for( unsigned int i = 0; i < count; i++)
504   {
505     int childIndex = start + dir * i;
506     LayoutItemPtr childLayout = GetChildAt( childIndex );
507     if( childLayout != nullptr )
508     {
509       auto childWidth = childLayout->GetMeasuredWidth();
510       auto childHeight = childLayout->GetMeasuredHeight();
511       auto childMargin = childLayout->GetMargin();
512
513       switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
514       {
515         case Dali::Toolkit::LinearLayout::Alignment::TOP:
516         {
517           childTop = LayoutLength( padding.top ) + childMargin.top;
518           break;
519         }
520         case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
521         {
522           childTop = height - padding.bottom - childHeight - childMargin.bottom;
523           break;
524         }
525         case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
526         default:
527         {
528           childTop = LayoutLength( padding.top ) + ( ( childSpace - childHeight ) / 2 ) + childMargin.top - childMargin.bottom;
529           break;
530         }
531       }
532       childLeft += childMargin.start;
533       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
534       childLeft += childWidth + childMargin.end + mCellPadding.width;
535     }
536   }
537 }
538
539 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
540 {
541   auto widthMode = widthMeasureSpec.GetMode();
542   auto heightMode = heightMeasureSpec.GetMode();
543   bool isExactly = ( heightMode == MeasureSpec::Mode::EXACTLY );
544
545   bool matchWidth = false;
546   bool allFillParent = true;
547   LayoutLength maxWidth = 0;
548   LayoutLength alternativeMaxWidth = 0;
549   LayoutLength weightedMaxWidth = 0;
550   float totalWeight = 0;
551   int usedExcessSpace = 0;
552   struct
553   {
554     MeasuredSize::State widthState;
555     MeasuredSize::State heightState;
556   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
557
558   // Reset total length
559   mTotalLength = 0;
560
561   // measure children, and determine if further resolution is required
562
563   // 1st phase:
564   // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs
565   // to accumulate total used space in mTotalLength based on measured sizes and margins.
566   // Weighted children are not measured at this phase.
567   // Available space for weighted children will be calculated in the phase 2 based on mTotalLength value.
568   for( unsigned int i = 0; i < GetChildCount(); ++i )
569   {
570     auto childLayout = GetChildAt( i );
571     if( childLayout )
572     {
573       auto childOwner = childLayout->GetOwner();
574       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
575       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
576       auto childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
577       auto childMargin = childLayout->GetMargin();
578
579       totalWeight += childWeight;
580
581       bool useExcessSpace = desiredHeight == 0 && childWeight > 0;
582
583       if( isExactly && useExcessSpace )
584       {
585         LayoutLength totalLength = mTotalLength;
586         mTotalLength = std::max( totalLength, totalLength + childMargin.top + childMargin.bottom );
587       }
588       else
589       {
590         LayoutLength childHeight = 0;
591         if( useExcessSpace )
592         {
593           // The heightMode is either UNSPECIFIED or AT_MOST, and
594           // this child is only laid out using excess space. Measure
595           // using WRAP_CONTENT so that we can find out the view's
596           // optimal height. We'll restore the original height of 0
597           // after measurement.
598           auto padding = GetPadding();
599           const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( widthMeasureSpec, padding.start + padding.end, desiredWidth );
600           const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( heightMeasureSpec, padding.top + padding.bottom, Toolkit::ChildLayoutData::WRAP_CONTENT );
601           childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
602           childHeight = childLayout->GetMeasuredHeight();
603           usedExcessSpace += childHeight;
604         }
605         else
606         {
607           MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
608           childHeight = childLayout->GetMeasuredHeight();
609         }
610
611         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::MeasureVertical childHeight(%d)\n", MeasureSpec::IntType( childHeight ) );
612
613         auto length = childHeight + LayoutLength::IntType( childMargin.top + childMargin.bottom );
614         auto cellPadding = i < GetChildCount() - 1 ? mCellPadding.height : 0;
615         auto totalLength = mTotalLength;
616         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
617       }
618
619       bool matchWidthLocally = false;
620       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
621       {
622         // Will have to re-measure at least this child when we know exact height.
623         matchWidth = true;
624         matchWidthLocally = true;
625       }
626
627       auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
628       auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
629
630       // was combineMeasuredStates()
631       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
632       {
633         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
634       }
635       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
636       {
637         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
638       }
639
640       maxWidth = std::max( maxWidth, childWidth );
641       allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
642       if( childWeight > 0 )
643       {
644         /*
645          * Widths of weighted Views are bogus if we end up
646          * remeasuring, so keep them separate.
647          */
648         weightedMaxWidth = std::max( weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth );
649       }
650       else
651       {
652         alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
653       }
654     }
655   }
656
657   Extents padding = GetPadding();
658   mTotalLength += padding.top + padding.bottom;
659   auto heightSize = mTotalLength;
660   heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
661   MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
662   heightSize = heightSizeAndState.GetSize();
663
664   // Either expand children with weight to take up available space or
665   // shrink them if they extend beyond our current bounds. If we skipped
666   // measurement on any children, we need to measure them now.
667
668   // 2nd phase:
669   // We cycle through weighted children now (children with weight > 0).
670   // The children are measured with exact size equal to their share of the available space based on their weights.
671   // mTotalLength is updated to include weighted children measured sizes.
672   int remainingExcess = heightSize - mTotalLength + usedExcessSpace;
673   if( remainingExcess != 0 && totalWeight > 0.0f )
674   {
675     float remainingWeightSum = totalWeight;
676
677     mTotalLength = 0;
678
679     for( unsigned int i = 0; i < GetChildCount(); ++i )
680     {
681       auto childLayout = GetChildAt( i );
682       auto childOwner = childLayout->GetOwner();
683       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
684       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
685       auto childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
686       auto childMargin = childLayout->GetMargin();
687
688       LayoutLength childHeight = 0;
689       if( childWeight > 0 )
690       {
691         int share = static_cast<int>( childWeight * remainingExcess / remainingWeightSum );
692         remainingExcess -= share;
693         remainingWeightSum -= childWeight;
694
695         // Always lay out weighted elements with intrinsic size regardless of the parent spec
696         // for consistency between specs.
697         if( desiredHeight == 0 )
698         {
699           // This child needs to be laid out from scratch using
700           // only its share of excess space.
701           childHeight = share;
702         }
703         else
704         {
705           // This child had some intrinsic width to which we
706           // need to add its share of excess space.
707           childHeight = childLayout->GetMeasuredHeight() + share;
708         }
709
710         const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( widthMeasureSpec, padding.start + padding.end, desiredWidth );
711         const MeasureSpec childHeightMeasureSpec = MeasureSpec( childHeight, MeasureSpec::Mode::EXACTLY );
712         childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
713
714         // Child may now not fit in vertical dimension.
715         if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
716         {
717           childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
718         }
719       }
720
721       bool matchWidthLocally = false;
722       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
723       {
724         // Will have to re-measure at least this child when we know exact height.
725         matchWidth = true;
726         matchWidthLocally = true;
727       }
728
729       auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
730       auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
731       maxWidth = std::max( maxWidth, childWidth );
732       allFillParent = allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT;
733       alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
734
735       childHeight = childLayout->GetMeasuredHeight();
736       auto length = childHeight + LayoutLength::IntType( childMargin.top + childMargin.bottom );
737       auto cellPadding = i < GetChildCount() - 1 ? mCellPadding.height : 0;
738       auto totalLength = mTotalLength;
739       mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
740     }
741
742     // Add in our padding
743     mTotalLength += padding.top + padding.bottom;
744   }
745   else
746   {
747     alternativeMaxWidth = std::max( alternativeMaxWidth, weightedMaxWidth );
748   }
749
750   if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
751   {
752     maxWidth = alternativeMaxWidth;
753   }
754   maxWidth += padding.start + padding.end;
755   maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
756
757   heightSizeAndState.SetState( childState.heightState );
758
759   SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
760                          heightSizeAndState );
761
762   if( matchWidth )
763   {
764     ForceUniformWidth( GetChildCount(), heightMeasureSpec );
765   }
766 }
767
768 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
769 {
770   // Pretend that the linear layout has an exact size.
771   auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
772   for (int i = 0; i < count; ++i)
773   {
774     LayoutItemPtr childLayout = GetChildAt(i);
775     if( childLayout != nullptr )
776     {
777       auto childOwner = childLayout->GetOwner();
778       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
779       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
780
781       if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
782       {
783         // Temporarily force children to reuse their old measured height
784         int oldHeight = desiredHeight;
785         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
786
787         // Remeasure with new dimensions
788         MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
789
790         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
791       }
792     }
793   }
794 }
795
796 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
797 {
798   Extents padding = GetPadding();
799
800   LayoutLength childTop( padding.top );
801   LayoutLength childLeft( padding.start );
802
803   // Where end of child should go
804   auto width = right - left;
805
806   // Space available for child
807   auto childSpace = width - padding.start - padding.end;
808   auto count = GetChildCount();
809
810   switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
811   {
812     case Dali::Toolkit::LinearLayout::Alignment::TOP:
813     {
814       // mTotalLength contains the padding already
815       childTop = LayoutLength( padding.top );
816       break;
817     }
818     case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
819     {
820       // mTotalLength contains the padding already
821       childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
822       break;
823     }
824     case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
825     default:
826     {
827       // mTotalLength contains the padding already
828       childTop = LayoutLength( padding.top ) + ( bottom - top - mTotalLength ) / 2;
829       break;
830     }
831   }
832
833   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
834   {
835     LayoutItemPtr childLayout = GetChildAt( childIndex );
836     if( childLayout != nullptr )
837     {
838       auto childWidth = childLayout->GetMeasuredWidth();
839       auto childHeight = childLayout->GetMeasuredHeight();
840       auto childMargin = childLayout->GetMargin();
841
842       childTop += childMargin.top;
843       switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
844       {
845         case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
846         default:
847         {
848           childLeft = LayoutLength( padding.start ) + childMargin.start;
849           break;
850         }
851         case Dali::Toolkit::LinearLayout::Alignment::END:
852         {
853           childLeft = width - padding.end - childWidth - childMargin.end;
854           break;
855         }
856         case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
857         {
858           childLeft = LayoutLength( padding.start ) + ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
859           break;
860         }
861       }
862       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
863       childTop += childHeight + childMargin.bottom + mCellPadding.height;
864     }
865   }
866 }
867
868 } // namespace Internal
869 } // namespace Toolkit
870 } // namespace Dali