Control impl layout code for Margin removed
[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   LayoutLength 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       LayoutLength desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
219       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
220       float childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
221       Extents 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           Extents 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_STREAM( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(" << childWidth << ")\n" );
253         LayoutLength length = childWidth + childMargin.start + childMargin.end;
254         LayoutLength cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
255         if( isExactly )
256         {
257           mTotalLength += length;
258         }
259         else
260         {
261           LayoutLength 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       LayoutLength marginHeight = childMargin.top + childMargin.bottom;
275       LayoutLength 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   LayoutLength 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   LayoutLength 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       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
327       LayoutLength desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
328       float childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
329       Extents childMargin = childLayout->GetMargin();
330
331       LayoutLength childWidth = 0;
332       if( childWeight > 0 )
333       {
334         LayoutLength share = ( 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       LayoutLength length = childLayout->GetMeasuredWidth() + LayoutLength( childMargin.start + childMargin.end );
365       LayoutLength cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
366       if( isExactly )
367       {
368         mTotalLength += length;
369       }
370       else
371       {
372         LayoutLength 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       LayoutLength marginHeight = childMargin.top + childMargin.bottom;
378       LayoutLength 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   MeasureSpec uniformMeasureSpec( 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       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
423       LayoutLength 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         LayoutLength oldWidth = desiredWidth;
429         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().AsInteger() );
430
431         // Remeasure with new dimensions
432         MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0 );
433
434         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth.AsInteger() );
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   LayoutLength height = bottom - top;
453
454   // Space available for child
455   LayoutLength 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       {
468         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
469       }
470       else
471       {
472         childLeft = LayoutLength( padding.start );
473       }
474       break;
475     }
476     case Dali::Toolkit::LinearLayout::Alignment::END:
477     {
478       // mTotalLength contains the padding already
479       // In case of RTL map END alignment to the left edge
480       if ( isLayoutRtl )
481       {
482         childLeft = LayoutLength( padding.start );
483       }
484       else
485       {
486         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
487       }
488       break;
489     }
490     case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
491     {
492       // mTotalLength contains the padding already
493       childLeft = padding.start + ( right - left - mTotalLength ) / 2.0f;
494       break;
495     }
496   }
497
498   int start = 0;
499   int dir = 1;
500
501   // In case of RTL, start drawing from the last child.
502   if( isLayoutRtl )
503   {
504     start = count - 1;
505     dir = -1;
506   }
507
508   for( unsigned int i = 0; i < count; i++)
509   {
510     int childIndex = start + dir * i;
511     LayoutItemPtr childLayout = GetChildAt( childIndex );
512     if( childLayout != nullptr )
513     {
514       LayoutLength childWidth = childLayout->GetMeasuredWidth();
515       LayoutLength childHeight = childLayout->GetMeasuredHeight();
516       Extents childMargin = childLayout->GetMargin();
517
518       switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
519       {
520         case Dali::Toolkit::LinearLayout::Alignment::TOP:
521         {
522           childTop = LayoutLength( padding.top ) + childMargin.top;
523           break;
524         }
525         case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
526         {
527           childTop = height - padding.bottom - childHeight - childMargin.bottom;
528           break;
529         }
530         case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL: // FALLTHROUGH
531         default:
532         {
533           childTop = padding.top + ( ( childSpace - childHeight ) / 2.0f ) + childMargin.top - childMargin.bottom;
534           break;
535         }
536       }
537       childLeft += childMargin.start;
538       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
539       childLeft += childWidth + childMargin.end + mCellPadding.width;
540     }
541   }
542 }
543
544 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
545 {
546   auto widthMode = widthMeasureSpec.GetMode();
547   auto heightMode = heightMeasureSpec.GetMode();
548   bool isExactly = ( heightMode == MeasureSpec::Mode::EXACTLY );
549
550   bool matchWidth = false;
551   bool allFillParent = true;
552   LayoutLength maxWidth = 0;
553   LayoutLength alternativeMaxWidth = 0;
554   LayoutLength weightedMaxWidth = 0;
555   float totalWeight = 0;
556   LayoutLength usedExcessSpace = 0;
557   struct
558   {
559     MeasuredSize::State widthState;
560     MeasuredSize::State heightState;
561   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
562
563   // Reset total length
564   mTotalLength = 0;
565
566   // measure children, and determine if further resolution is required
567
568   // 1st phase:
569   // We cycle through all children and measure children with weight 0 (non weighted children) according to their specs
570   // to accumulate total used space in mTotalLength based on measured sizes and margins.
571   // Weighted children are not measured at this phase.
572   // Available space for weighted children will be calculated in the phase 2 based on mTotalLength value.
573   for( unsigned int i = 0; i < GetChildCount(); ++i )
574   {
575     auto childLayout = GetChildAt( i );
576     if( childLayout )
577     {
578       auto childOwner = childLayout->GetOwner();
579       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
580       LayoutLength desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
581       float childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
582       Extents childMargin = childLayout->GetMargin();
583
584       totalWeight += childWeight;
585
586       bool useExcessSpace = desiredHeight == 0 && childWeight > 0;
587
588       if( isExactly && useExcessSpace )
589       {
590         LayoutLength totalLength = mTotalLength;
591         mTotalLength = std::max( totalLength, totalLength + childMargin.top + childMargin.bottom );
592       }
593       else
594       {
595         LayoutLength childHeight = 0;
596         if( useExcessSpace )
597         {
598           // The heightMode is either UNSPECIFIED or AT_MOST, and
599           // this child is only laid out using excess space. Measure
600           // using WRAP_CONTENT so that we can find out the view's
601           // optimal height. We'll restore the original height of 0
602           // after measurement.
603           Extents padding = GetPadding();
604           const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( widthMeasureSpec, padding.start + padding.end, desiredWidth );
605           const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( heightMeasureSpec, padding.top + padding.bottom, Toolkit::ChildLayoutData::WRAP_CONTENT );
606           childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
607           childHeight = childLayout->GetMeasuredHeight();
608           usedExcessSpace += childHeight;
609         }
610         else
611         {
612           MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
613           childHeight = childLayout->GetMeasuredHeight();
614         }
615
616         DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LinearLayout::MeasureVertical childHeight(" << childHeight << ")\n" );
617
618         LayoutLength length = childHeight + childMargin.top + childMargin.bottom;
619         LayoutLength cellPadding = i < GetChildCount() - 1 ? mCellPadding.height : 0;
620         LayoutLength totalLength = mTotalLength;
621         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
622       }
623
624       bool matchWidthLocally = false;
625       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
626       {
627         // Will have to re-measure at least this child when we know exact height.
628         matchWidth = true;
629         matchWidthLocally = true;
630       }
631
632       LayoutLength marginWidth = childMargin.start + childMargin.end;
633       LayoutLength childWidth = childLayout->GetMeasuredWidth() + marginWidth;
634
635       // was combineMeasuredStates()
636       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
637       {
638         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
639       }
640       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
641       {
642         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
643       }
644
645       maxWidth = std::max( maxWidth, childWidth );
646       allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
647       if( childWeight > 0 )
648       {
649         /*
650          * Widths of weighted Views are bogus if we end up
651          * remeasuring, so keep them separate.
652          */
653         weightedMaxWidth = std::max( weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth );
654       }
655       else
656       {
657         alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
658       }
659     }
660   }
661
662   Extents padding = GetPadding();
663   mTotalLength += padding.top + padding.bottom;
664   LayoutLength heightSize = mTotalLength;
665   heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
666   MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
667   heightSize = heightSizeAndState.GetSize();
668
669   // Either expand children with weight to take up available space or
670   // shrink them if they extend beyond our current bounds. If we skipped
671   // measurement on any children, we need to measure them now.
672
673   // 2nd phase:
674   // We cycle through weighted children now (children with weight > 0).
675   // The children are measured with exact size equal to their share of the available space based on their weights.
676   // mTotalLength is updated to include weighted children measured sizes.
677   LayoutLength remainingExcess = heightSize - mTotalLength + usedExcessSpace;
678   if( remainingExcess != 0 && totalWeight > 0.0f )
679   {
680     float remainingWeightSum = totalWeight;
681
682     mTotalLength = 0;
683
684     for( unsigned int i = 0; i < GetChildCount(); ++i )
685     {
686       auto childLayout = GetChildAt( i );
687       auto childOwner = childLayout->GetOwner();
688       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
689       LayoutLength desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
690       float childWeight = childOwner.GetProperty<float>( Toolkit::LinearLayout::ChildProperty::WEIGHT );
691       Extents childMargin = childLayout->GetMargin();
692
693       LayoutLength childHeight = 0;
694       if( childWeight > 0 )
695       {
696         LayoutLength share = ( childWeight * remainingExcess ) / remainingWeightSum;
697         remainingExcess -= share;
698         remainingWeightSum -= childWeight;
699
700         // Always lay out weighted elements with intrinsic size regardless of the parent spec
701         // for consistency between specs.
702         if( desiredHeight == 0 )
703         {
704           // This child needs to be laid out from scratch using
705           // only its share of excess space.
706           childHeight = share;
707         }
708         else
709         {
710           // This child had some intrinsic width to which we
711           // need to add its share of excess space.
712           childHeight = childLayout->GetMeasuredHeight() + share;
713         }
714
715         const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( widthMeasureSpec, padding.start + padding.end, desiredWidth );
716         const MeasureSpec childHeightMeasureSpec = MeasureSpec( childHeight, MeasureSpec::Mode::EXACTLY );
717         childLayout->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
718
719         // Child may now not fit in vertical dimension.
720         if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
721         {
722           childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
723         }
724       }
725
726       bool matchWidthLocally = false;
727       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
728       {
729         // Will have to re-measure at least this child when we know exact height.
730         matchWidth = true;
731         matchWidthLocally = true;
732       }
733
734       LayoutLength marginWidth = childMargin.start + childMargin.end;
735       LayoutLength childWidth = childLayout->GetMeasuredWidth() + marginWidth;
736       maxWidth = std::max( maxWidth, childWidth );
737       allFillParent = allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT;
738       alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
739
740       childHeight = childLayout->GetMeasuredHeight();
741       LayoutLength length = childHeight + childMargin.top + childMargin.bottom;
742       LayoutLength cellPadding = i < GetChildCount() - 1 ? mCellPadding.height : 0;
743       LayoutLength totalLength = mTotalLength;
744       mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
745     }
746
747     // Add in our padding
748     mTotalLength += padding.top + padding.bottom;
749   }
750   else
751   {
752     alternativeMaxWidth = std::max( alternativeMaxWidth, weightedMaxWidth );
753   }
754
755   if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
756   {
757     maxWidth = alternativeMaxWidth;
758   }
759   maxWidth += padding.start + padding.end;
760   maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
761
762   heightSizeAndState.SetState( childState.heightState );
763
764   SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
765                          heightSizeAndState );
766
767   if( matchWidth )
768   {
769     ForceUniformWidth( GetChildCount(), heightMeasureSpec );
770   }
771 }
772
773 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
774 {
775   // Pretend that the linear layout has an exact size.
776   MeasureSpec uniformMeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
777   for (int i = 0; i < count; ++i)
778   {
779     LayoutItemPtr childLayout = GetChildAt(i);
780     if( childLayout != nullptr )
781     {
782       auto childOwner = childLayout->GetOwner();
783       LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
784       LayoutLength desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
785
786       if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
787       {
788         // Temporarily force children to reuse their old measured height
789         LayoutLength oldHeight = desiredHeight;
790         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().AsInteger() );
791
792         // Remeasure with new dimensions
793         MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
794
795         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight.AsInteger() );
796       }
797     }
798   }
799 }
800
801 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
802 {
803   Extents padding = GetPadding();
804
805   LayoutLength childTop( padding.top );
806   LayoutLength childLeft( padding.start );
807
808   // Where end of child should go
809   LayoutLength width = right - left;
810
811   // Space available for child
812   LayoutLength childSpace = width - padding.start - padding.end;
813   auto count = GetChildCount();
814
815   switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
816   {
817     case Dali::Toolkit::LinearLayout::Alignment::TOP:
818     {
819       // mTotalLength contains the padding already
820       childTop = LayoutLength( padding.top );
821       break;
822     }
823     case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
824     {
825       // mTotalLength contains the padding already
826       childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
827       break;
828     }
829     case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
830     default:
831     {
832       // mTotalLength contains the padding already
833       childTop = padding.top + ( bottom - top - mTotalLength ) / 2.0f;
834       break;
835     }
836   }
837
838   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
839   {
840     LayoutItemPtr childLayout = GetChildAt( childIndex );
841     if( childLayout != nullptr )
842     {
843       LayoutLength childWidth = childLayout->GetMeasuredWidth();
844       LayoutLength childHeight = childLayout->GetMeasuredHeight();
845       Extents childMargin = childLayout->GetMargin();
846
847       childTop += childMargin.top;
848       switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
849       {
850         case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
851         default:
852         {
853           childLeft = LayoutLength( padding.start ) + childMargin.start;
854           break;
855         }
856         case Dali::Toolkit::LinearLayout::Alignment::END:
857         {
858           childLeft = width - padding.end - childWidth - childMargin.end;
859           break;
860         }
861         case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
862         {
863           childLeft = padding.start + ( childSpace - childWidth ) / 2.0f + childMargin.start - childMargin.end;
864           break;
865         }
866       }
867       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
868       childTop += childHeight + childMargin.bottom + mCellPadding.height;
869     }
870   }
871 }
872
873 } // namespace Internal
874 } // namespace Toolkit
875 } // namespace Dali