2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "linear-layout-impl.h"
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>
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>
33 #if defined(DEBUG_ENABLED)
34 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
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 );
48 LinearLayoutPtr LinearLayout::New()
50 LinearLayoutPtr layout( new LinearLayout() );
54 LinearLayout::LinearLayout()
57 mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
58 mAlignment( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL ),
63 LinearLayout::~LinearLayout()
67 void LinearLayout::DoRegisterChildProperties( const std::string& containerType )
69 auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
72 Property::IndexContainer indices;
73 typeInfo.GetChildPropertyIndices( indices );
75 if( std::find( indices.Begin(), indices.End(), Toolkit::LinearLayout::ChildProperty::WEIGHT ) ==
78 ChildPropertyRegistration( typeInfo.GetName(), "weight", Toolkit::LinearLayout::ChildProperty::WEIGHT, Property::FLOAT );
83 void LinearLayout::OnChildAdd( LayoutItem& child )
85 auto owner = child.GetOwner();
86 if( !DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LinearLayout::ChildProperty::WEIGHT ) )
88 owner.SetProperty( Toolkit::LinearLayout::ChildProperty::WEIGHT, 0.0f );
92 void LinearLayout::SetCellPadding( LayoutSize size )
94 if ( mCellPadding != size )
101 LayoutSize LinearLayout::GetCellPadding() const
106 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
108 if ( mOrientation != orientation )
110 mOrientation = orientation;
115 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation() const
120 void LinearLayout::SetAlignment( unsigned int alignment )
122 if ( mAlignment != alignment )
124 mAlignment = alignment;
129 unsigned int LinearLayout::GetAlignment() const
134 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
136 #if defined(DEBUG_ENABLED)
137 auto actor = Actor::DownCast(GetOwner());
139 std::ostringstream oss;
140 oss << "LinearLayout::OnMeasure ";
143 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
145 oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
146 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
149 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
151 MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
155 MeasureVertical( widthMeasureSpec, heightMeasureSpec );
159 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
161 #if defined(DEBUG_ENABLED)
162 auto actor = Actor::DownCast(GetOwner());
164 std::ostringstream oss;
165 oss << "LinearLayout::OnLayout ";
168 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
170 oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
171 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
174 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
176 LayoutHorizontal( left, top, right, bottom );
180 LayoutVertical( left, top, right, bottom );
184 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
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;
198 MeasuredSize::State widthState;
199 MeasuredSize::State heightState;
200 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
202 // Reset total length
205 // measure children, and determine if further resolution is required
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 )
214 auto childLayout = GetChildAt( i );
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();
223 totalWeight += childWeight;
225 const bool useExcessSpace = desiredWidth == 0 && childWeight > 0;
226 if( isExactly && useExcessSpace )
228 mTotalLength += childMargin.start + childMargin.end;
232 LayoutLength childWidth = 0;
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
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;
248 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
249 childWidth = childLayout->GetMeasuredWidth();
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;
257 mTotalLength += length;
261 auto totalLength = mTotalLength;
262 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
266 bool matchHeightLocally = false;
267 if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
269 // Will have to re-measure at least this child when we know exact height.
271 matchHeightLocally = true;
274 auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
275 auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
277 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
279 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
281 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
283 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
286 maxHeight = std::max( maxHeight, childHeight );
287 allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
288 if( childWeight > 0 )
291 * Heights of weighted Views are bogus if we end up
292 * remeasuring, so keep them separate.
294 weightedMaxHeight = std::max( weightedMaxHeight, matchHeightLocally ? marginHeight : childHeight );
298 alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
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();
310 // Expand children with weight to take up available space
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 )
318 float remainingWeightSum = totalWeight;
322 for( unsigned int i = 0; i < GetChildCount(); ++i )
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();
331 LayoutLength childWidth = 0;
332 if( childWeight > 0 )
334 int share = static_cast<int>( childWeight * remainingExcess / remainingWeightSum );
335 remainingExcess -= share;
336 remainingWeightSum -= childWeight;
338 // Always lay out weighted elements with intrinsic size regardless of the parent spec.
339 // for consistency between specs.
340 if( desiredWidth == 0 )
342 // This child needs to be laid out from scratch using
343 // only its share of excess space.
348 // This child had some intrinsic width to which we
349 // need to add its share of excess space.
350 childWidth = childLayout->GetMeasuredWidth() + share;
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 );
357 // Child may now not fit in horizontal dimension.
358 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
360 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
364 auto length = childLayout->GetMeasuredWidth() + LayoutLength::IntType( childMargin.start + childMargin.end );
365 auto cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
368 mTotalLength += length;
372 auto totalLength = mTotalLength;
373 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
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;
380 maxHeight = std::max( maxHeight, childHeight );
381 alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
382 allFillParent = (allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT);
384 mTotalLength += padding.start + padding.end;
389 alternativeMaxHeight = std::max( alternativeMaxHeight, weightedMaxHeight );
392 if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
394 maxHeight = alternativeMaxHeight;
396 maxHeight += padding.top + padding.bottom;
397 maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
399 widthSizeAndState.SetState( childState.widthState );
401 SetMeasuredDimensions( widthSizeAndState,
402 ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
406 ForceUniformHeight( GetChildCount(), widthMeasureSpec );
410 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
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)
418 LayoutItemPtr childLayout = GetChildAt(i);
419 if( childLayout != nullptr )
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 );
425 if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
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 );
431 // Remeasure with new dimensions
432 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
434 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
440 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
442 auto owner = GetOwner();
443 auto actor = Actor::DownCast(owner);
444 bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
446 Extents padding = GetPadding();
448 LayoutLength childTop( padding.top );
449 LayoutLength childLeft( padding.start );
451 // Where bottom of child should go
452 auto height = bottom - top;
454 // Space available for child
455 auto childSpace = height - LayoutLength( padding.top ) - LayoutLength( padding.bottom );
457 auto count = GetChildCount();
459 switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
461 case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
464 // mTotalLength contains the padding already
465 // In case of RTL map BEGIN alignment to the right edge
467 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
470 childLeft = LayoutLength( padding.start );
474 case Dali::Toolkit::LinearLayout::Alignment::END:
476 // mTotalLength contains the padding already
477 // In case of RTL map END alignment to the left edge
479 childLeft = LayoutLength( padding.start );
482 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
486 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
488 // mTotalLength contains the padding already
489 childLeft = LayoutLength( padding.start ) + ( right - left - mTotalLength ) / 2;
497 // In case of RTL, start drawing from the last child.
503 for( unsigned int i = 0; i < count; i++)
505 int childIndex = start + dir * i;
506 LayoutItemPtr childLayout = GetChildAt( childIndex );
507 if( childLayout != nullptr )
509 auto childWidth = childLayout->GetMeasuredWidth();
510 auto childHeight = childLayout->GetMeasuredHeight();
511 auto childMargin = childLayout->GetMargin();
513 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
515 case Dali::Toolkit::LinearLayout::Alignment::TOP:
517 childTop = LayoutLength( padding.top ) + LayoutLength( childMargin.top );
520 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
522 childTop = height - LayoutLength( padding.bottom ) - childHeight - LayoutLength( childMargin.bottom );
525 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
528 childTop = LayoutLength( padding.top ) + ( ( childSpace - childHeight ) / 2 ) + LayoutLength( childMargin.top ) - LayoutLength( childMargin.bottom );
532 childLeft += childMargin.start;
533 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
534 childLeft += childWidth + LayoutLength( childMargin.end ) + mCellPadding.width;
539 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
541 auto widthMode = widthMeasureSpec.GetMode();
542 auto heightMode = heightMeasureSpec.GetMode();
543 bool isExactly = ( heightMode == MeasureSpec::Mode::EXACTLY );
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;
554 MeasuredSize::State widthState;
555 MeasuredSize::State heightState;
556 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
558 // Reset total length
561 // measure children, and determine if further resolution is required
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 )
570 auto childLayout = GetChildAt( i );
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();
579 totalWeight += childWeight;
581 bool useExcessSpace = desiredHeight == 0 && childWeight > 0;
583 if( isExactly && useExcessSpace )
585 LayoutLength totalLength = mTotalLength;
586 mTotalLength = std::max( totalLength, totalLength + LayoutLength( childMargin.top ) + LayoutLength( childMargin.bottom ) );
590 LayoutLength childHeight = 0;
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;
607 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
608 childHeight = childLayout->GetMeasuredHeight();
611 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::MeasureVertical childHeight(%d)\n", MeasureSpec::IntType( childHeight ) );
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 );
619 bool matchWidthLocally = false;
620 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
622 // Will have to re-measure at least this child when we know exact height.
624 matchWidthLocally = true;
627 auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
628 auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
630 // was combineMeasuredStates()
631 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
633 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
635 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
637 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
640 maxWidth = std::max( maxWidth, childWidth );
641 allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
642 if( childWeight > 0 )
645 * Widths of weighted Views are bogus if we end up
646 * remeasuring, so keep them separate.
648 weightedMaxWidth = std::max( weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth );
652 alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
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();
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.
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 )
675 float remainingWeightSum = totalWeight;
679 for( unsigned int i = 0; i < GetChildCount(); ++i )
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();
688 LayoutLength childHeight = 0;
689 if( childWeight > 0 )
691 int share = static_cast<int>( childWeight * remainingExcess / remainingWeightSum );
692 remainingExcess -= share;
693 remainingWeightSum -= childWeight;
695 // Always lay out weighted elements with intrinsic size regardless of the parent spec
696 // for consistency between specs.
697 if( desiredHeight == 0 )
699 // This child needs to be laid out from scratch using
700 // only its share of excess space.
705 // This child had some intrinsic width to which we
706 // need to add its share of excess space.
707 childHeight = childLayout->GetMeasuredHeight() + share;
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 );
714 // Child may now not fit in vertical dimension.
715 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
717 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
721 bool matchWidthLocally = false;
722 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
724 // Will have to re-measure at least this child when we know exact height.
726 matchWidthLocally = true;
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 );
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 );
742 // Add in our padding
743 mTotalLength += padding.top + padding.bottom;
747 alternativeMaxWidth = std::max( alternativeMaxWidth, weightedMaxWidth );
750 if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
752 maxWidth = alternativeMaxWidth;
754 maxWidth += padding.start + padding.end;
755 maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
757 heightSizeAndState.SetState( childState.heightState );
759 SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
760 heightSizeAndState );
764 ForceUniformWidth( GetChildCount(), heightMeasureSpec );
768 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
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)
774 LayoutItemPtr childLayout = GetChildAt(i);
775 if( childLayout != nullptr )
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 );
781 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
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 );
787 // Remeasure with new dimensions
788 MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
790 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
796 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
798 Extents padding = GetPadding();
800 LayoutLength childTop( padding.top );
801 LayoutLength childLeft( padding.start );
803 // Where end of child should go
804 auto width = right - left;
806 // Space available for child
807 auto childSpace = width - LayoutLength( padding.start ) - LayoutLength( padding.end );
808 auto count = GetChildCount();
810 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
812 case Dali::Toolkit::LinearLayout::Alignment::TOP:
814 // mTotalLength contains the padding already
815 childTop = LayoutLength( padding.top );
818 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
820 // mTotalLength contains the padding already
821 childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
824 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
827 // mTotalLength contains the padding already
828 childTop = LayoutLength( padding.top ) + ( bottom - top - mTotalLength ) / 2;
833 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
835 LayoutItemPtr childLayout = GetChildAt( childIndex );
836 if( childLayout != nullptr )
838 auto childWidth = childLayout->GetMeasuredWidth();
839 auto childHeight = childLayout->GetMeasuredHeight();
840 auto childMargin = childLayout->GetMargin();
842 childTop += childMargin.top;
843 switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
845 case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
848 childLeft = LayoutLength( padding.start ) + LayoutLength( childMargin.start );
851 case Dali::Toolkit::LinearLayout::Alignment::END:
853 childLeft = width - LayoutLength( padding.end ) - childWidth - LayoutLength( childMargin.end );
856 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
858 childLeft = LayoutLength( padding.start ) + ( childSpace - childWidth ) / 2 + LayoutLength( childMargin.start ) - LayoutLength( childMargin.end );
862 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
863 childTop += childHeight + LayoutLength( childMargin.bottom ) + mCellPadding.height;
868 } // namespace Internal
869 } // namespace Toolkit