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 LayoutLength 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 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();
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 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;
248 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
249 childWidth = childLayout->GetMeasuredWidth();
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;
257 mTotalLength += length;
261 LayoutLength 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 LayoutLength marginHeight = childMargin.top + childMargin.bottom;
275 LayoutLength 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 LayoutLength 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 LayoutLength 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 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();
331 LayoutLength childWidth = 0;
332 if( childWeight > 0 )
334 LayoutLength share = ( 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 LayoutLength length = childLayout->GetMeasuredWidth() + LayoutLength( childMargin.start + childMargin.end );
365 LayoutLength cellPadding = i < GetChildCount() - 1 ? mCellPadding.width : 0;
368 mTotalLength += length;
372 LayoutLength totalLength = mTotalLength;
373 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
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;
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 MeasureSpec uniformMeasureSpec( 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 LayoutLength desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
423 LayoutLength 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 LayoutLength oldWidth = desiredWidth;
429 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().AsInteger() );
431 // Remeasure with new dimensions
432 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0 );
434 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth.AsInteger() );
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 LayoutLength height = bottom - top;
454 // Space available for child
455 LayoutLength childSpace = height - padding.top - 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
468 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
472 childLeft = LayoutLength( padding.start );
476 case Dali::Toolkit::LinearLayout::Alignment::END:
478 // mTotalLength contains the padding already
479 // In case of RTL map END alignment to the left edge
482 childLeft = LayoutLength( padding.start );
486 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
490 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
492 // mTotalLength contains the padding already
493 childLeft = padding.start + ( right - left - mTotalLength ) / 2.0f;
501 // In case of RTL, start drawing from the last child.
508 for( unsigned int i = 0; i < count; i++)
510 int childIndex = start + dir * i;
511 LayoutItemPtr childLayout = GetChildAt( childIndex );
512 if( childLayout != nullptr )
514 LayoutLength childWidth = childLayout->GetMeasuredWidth();
515 LayoutLength childHeight = childLayout->GetMeasuredHeight();
516 Extents childMargin = childLayout->GetMargin();
518 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
520 case Dali::Toolkit::LinearLayout::Alignment::TOP:
522 childTop = LayoutLength( padding.top ) + childMargin.top;
525 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
527 childTop = height - padding.bottom - childHeight - childMargin.bottom;
530 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL: // FALLTHROUGH
533 childTop = padding.top + ( ( childSpace - childHeight ) / 2.0f ) + childMargin.top - childMargin.bottom;
537 childLeft += childMargin.start;
538 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
539 childLeft += childWidth + childMargin.end + mCellPadding.width;
544 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
546 auto widthMode = widthMeasureSpec.GetMode();
547 auto heightMode = heightMeasureSpec.GetMode();
548 bool isExactly = ( heightMode == MeasureSpec::Mode::EXACTLY );
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;
559 MeasuredSize::State widthState;
560 MeasuredSize::State heightState;
561 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
563 // Reset total length
566 // measure children, and determine if further resolution is required
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 )
575 auto childLayout = GetChildAt( i );
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();
584 totalWeight += childWeight;
586 bool useExcessSpace = desiredHeight == 0 && childWeight > 0;
588 if( isExactly && useExcessSpace )
590 LayoutLength totalLength = mTotalLength;
591 mTotalLength = std::max( totalLength, totalLength + childMargin.top + childMargin.bottom );
595 LayoutLength childHeight = 0;
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;
612 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
613 childHeight = childLayout->GetMeasuredHeight();
616 DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LinearLayout::MeasureVertical childHeight(" << childHeight << ")\n" );
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 );
624 bool matchWidthLocally = false;
625 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
627 // Will have to re-measure at least this child when we know exact height.
629 matchWidthLocally = true;
632 LayoutLength marginWidth = childMargin.start + childMargin.end;
633 LayoutLength childWidth = childLayout->GetMeasuredWidth() + marginWidth;
635 // was combineMeasuredStates()
636 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
638 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
640 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
642 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
645 maxWidth = std::max( maxWidth, childWidth );
646 allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
647 if( childWeight > 0 )
650 * Widths of weighted Views are bogus if we end up
651 * remeasuring, so keep them separate.
653 weightedMaxWidth = std::max( weightedMaxWidth, matchWidthLocally ? marginWidth : childWidth );
657 alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
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();
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.
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 )
680 float remainingWeightSum = totalWeight;
684 for( unsigned int i = 0; i < GetChildCount(); ++i )
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();
693 LayoutLength childHeight = 0;
694 if( childWeight > 0 )
696 LayoutLength share = ( childWeight * remainingExcess ) / remainingWeightSum;
697 remainingExcess -= share;
698 remainingWeightSum -= childWeight;
700 // Always lay out weighted elements with intrinsic size regardless of the parent spec
701 // for consistency between specs.
702 if( desiredHeight == 0 )
704 // This child needs to be laid out from scratch using
705 // only its share of excess space.
710 // This child had some intrinsic width to which we
711 // need to add its share of excess space.
712 childHeight = childLayout->GetMeasuredHeight() + share;
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 );
719 // Child may now not fit in vertical dimension.
720 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
722 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
726 bool matchWidthLocally = false;
727 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
729 // Will have to re-measure at least this child when we know exact height.
731 matchWidthLocally = true;
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 );
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 );
747 // Add in our padding
748 mTotalLength += padding.top + padding.bottom;
752 alternativeMaxWidth = std::max( alternativeMaxWidth, weightedMaxWidth );
755 if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
757 maxWidth = alternativeMaxWidth;
759 maxWidth += padding.start + padding.end;
760 maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
762 heightSizeAndState.SetState( childState.heightState );
764 SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
765 heightSizeAndState );
769 ForceUniformWidth( GetChildCount(), heightMeasureSpec );
773 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
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)
779 LayoutItemPtr childLayout = GetChildAt(i);
780 if( childLayout != nullptr )
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 );
786 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
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() );
792 // Remeasure with new dimensions
793 MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
795 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight.AsInteger() );
801 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
803 Extents padding = GetPadding();
805 LayoutLength childTop( padding.top );
806 LayoutLength childLeft( padding.start );
808 // Where end of child should go
809 LayoutLength width = right - left;
811 // Space available for child
812 LayoutLength childSpace = width - padding.start - padding.end;
813 auto count = GetChildCount();
815 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
817 case Dali::Toolkit::LinearLayout::Alignment::TOP:
819 // mTotalLength contains the padding already
820 childTop = LayoutLength( padding.top );
823 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
825 // mTotalLength contains the padding already
826 childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
829 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
832 // mTotalLength contains the padding already
833 childTop = padding.top + ( bottom - top - mTotalLength ) / 2.0f;
838 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
840 LayoutItemPtr childLayout = GetChildAt( childIndex );
841 if( childLayout != nullptr )
843 LayoutLength childWidth = childLayout->GetMeasuredWidth();
844 LayoutLength childHeight = childLayout->GetMeasuredHeight();
845 Extents childMargin = childLayout->GetMargin();
847 childTop += childMargin.top;
848 switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
850 case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
853 childLeft = LayoutLength( padding.start ) + childMargin.start;
856 case Dali::Toolkit::LinearLayout::Alignment::END:
858 childLeft = width - padding.end - childWidth - childMargin.end;
861 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
863 childLeft = padding.start + ( childSpace - childWidth ) / 2.0f + childMargin.start - childMargin.end;
867 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
868 childTop += childHeight + childMargin.bottom + mCellPadding.height;
873 } // namespace Internal
874 } // namespace Toolkit