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>
26 #include <dali-toolkit/devel-api/layouting/layout-item.h>
27 #include <dali-toolkit/public-api/controls/control-impl.h>
28 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
44 const int HORIZONTAL_ALIGNMENT_MASK = ( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL | Dali::Toolkit::LinearLayout::Alignment::END );
45 const int VERTICAL_ALIGNMENT_MASK = ( Dali::Toolkit::LinearLayout::Alignment::TOP | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL | Dali::Toolkit::LinearLayout::Alignment::BOTTOM );
47 LinearLayoutPtr LinearLayout::New()
49 LinearLayoutPtr layout( new LinearLayout() );
53 LinearLayout::LinearLayout()
56 mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
57 mAlignment( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL ),
62 LinearLayout::~LinearLayout()
66 void LinearLayout::SetCellPadding( LayoutSize size )
68 if ( mCellPadding != size )
75 LayoutSize LinearLayout::GetCellPadding() const
80 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
82 if ( mOrientation != orientation )
84 mOrientation = orientation;
89 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation() const
94 void LinearLayout::SetAlignment( unsigned int alignment )
96 if ( mAlignment != alignment )
98 mAlignment = alignment;
103 unsigned int LinearLayout::GetAlignment() const
108 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
110 #if defined(DEBUG_ENABLED)
111 auto actor = Actor::DownCast(GetOwner());
113 std::ostringstream oss;
114 oss << "LinearLayout::OnMeasure ";
117 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
119 oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
120 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
123 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
125 MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
129 MeasureVertical( widthMeasureSpec, heightMeasureSpec );
133 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
135 #if defined(DEBUG_ENABLED)
136 auto actor = Actor::DownCast(GetOwner());
138 std::ostringstream oss;
139 oss << "LinearLayout::OnLayout ";
142 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
144 oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
145 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
148 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
150 LayoutHorizontal( left, top, right, bottom );
154 LayoutVertical( left, top, right, bottom );
158 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
160 auto widthMode = widthMeasureSpec.GetMode();
161 auto heightMode = heightMeasureSpec.GetMode();
162 bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
163 bool matchHeight = false;
164 bool allFillParent = true;
165 LayoutLength maxHeight = 0;
166 LayoutLength alternativeMaxHeight = 0;
169 MeasuredSize::State widthState;
170 MeasuredSize::State heightState;
171 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
173 // Reset total length
176 // measure children, and determine if further resolution is required
177 for( unsigned int i=0; i<GetChildCount(); ++i )
179 auto childLayout = GetChildAt( i );
182 auto childOwner = childLayout->GetOwner();
183 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
185 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
186 auto childWidth = childLayout->GetMeasuredWidth();
187 auto childMargin = childLayout->GetMargin();
189 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
191 auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
193 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
197 mTotalLength += length;
201 auto totalLength = mTotalLength;
202 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
205 bool matchHeightLocally = false;
206 if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
208 // Will have to re-measure at least this child when we know exact height.
210 matchHeightLocally = true;
213 auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
214 auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
216 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
218 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
220 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
222 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
225 maxHeight = std::max( maxHeight, childHeight );
226 allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
227 alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
231 Extents padding = GetPadding();
232 mTotalLength += padding.start + padding.end;
233 auto widthSize = mTotalLength;
234 widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
235 MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
236 widthSize = widthSizeAndState.GetSize();
238 if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
240 maxHeight = alternativeMaxHeight;
242 maxHeight += padding.top + padding.bottom;
243 maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
245 widthSizeAndState.SetState( childState.widthState );
247 SetMeasuredDimensions( widthSizeAndState,
248 ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
252 ForceUniformHeight( GetChildCount(), widthMeasureSpec );
256 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
258 // Pretend that the linear layout has an exact size. This is the measured height of
259 // ourselves. The measured height should be the max height of the children, changed
260 // to accommodate the heightMeasureSpec from the parent
261 auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
262 for (int i = 0; i < count; ++i)
264 LayoutItemPtr childLayout = GetChildAt(i);
265 if( childLayout != nullptr )
267 auto childOwner = childLayout->GetOwner();
268 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
269 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
271 if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
273 // Temporarily force children to reuse their old measured width
274 int oldWidth = desiredWidth;
275 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
277 // Remeasure with new dimensions
278 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
280 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
286 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
288 auto owner = GetOwner();
289 auto actor = Actor::DownCast(owner);
290 bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
292 Extents padding = GetPadding();
294 LayoutLength childTop( padding.top );
295 LayoutLength childLeft( padding.start );
297 // Where bottom of child should go
298 auto height = bottom - top;
300 // Space available for child
301 auto childSpace = height - (int)padding.top - (int)padding.bottom;
303 auto count = GetChildCount();
305 switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
307 case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
310 // mTotalLength contains the padding already
311 // In case of RTL map BEGIN alignment to the right edge
313 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
316 childLeft = LayoutLength( padding.start );
320 case Dali::Toolkit::LinearLayout::Alignment::END:
322 // mTotalLength contains the padding already
323 // In case of RTL map END alignment to the left edge
325 childLeft = LayoutLength( padding.start );
328 childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
332 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
334 // mTotalLength contains the padding already
335 childLeft = LayoutLength( padding.start ) + ( right - left - mTotalLength ) / 2;
343 // In case of RTL, start drawing from the last child.
349 for( unsigned int i = 0; i < count; i++)
351 int childIndex = start + dir * i;
352 LayoutItemPtr childLayout = GetChildAt( childIndex );
353 if( childLayout != nullptr )
355 auto childWidth = childLayout->GetMeasuredWidth();
356 auto childHeight = childLayout->GetMeasuredHeight();
357 auto childMargin = childLayout->GetMargin();
359 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
361 case Dali::Toolkit::LinearLayout::Alignment::TOP:
363 childTop = LayoutLength( padding.top ) + (int)childMargin.top;
366 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
368 childTop = height - (int)padding.bottom - childHeight - (int)childMargin.bottom;
371 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
374 childTop = LayoutLength( padding.top ) + (int)( ( childSpace - childHeight ) / 2 ) + (int)childMargin.top - (int)childMargin.bottom;
379 childLeft += childMargin.start;
380 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
381 childLeft += childWidth + (int)childMargin.end + mCellPadding.width;
386 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
388 auto widthMode = widthMeasureSpec.GetMode();
390 bool matchWidth = false;
391 bool allFillParent = true;
392 LayoutLength maxWidth = 0;
393 LayoutLength alternativeMaxWidth = 0;
397 MeasuredSize::State widthState;
398 MeasuredSize::State heightState;
399 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
401 // Reset total length
404 // measure children, and determine if further resolution is required
405 for( unsigned int i=0; i<GetChildCount(); ++i )
407 auto childLayout = GetChildAt( i );
410 auto childOwner = childLayout->GetOwner();
411 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
413 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
414 auto childHeight = childLayout->GetMeasuredHeight();
415 auto childMargin = childLayout->GetMargin();
416 auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
418 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
419 auto totalLength = mTotalLength;
420 mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
422 bool matchWidthLocally = false;
423 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
425 // Will have to re-measure at least this child when we know exact height.
427 matchWidthLocally = true;
430 auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
431 auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
433 // was combineMeasuredStates()
434 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
436 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
438 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
440 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
443 maxWidth = std::max( maxWidth, childWidth );
444 allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
445 alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
448 Extents padding = GetPadding();
449 mTotalLength += padding.top + padding.bottom;
450 auto heightSize = mTotalLength;
451 heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
452 MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
453 heightSize = heightSizeAndState.GetSize();
455 if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
457 maxWidth = alternativeMaxWidth;
459 maxWidth += padding.start + padding.end;
460 maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
462 heightSizeAndState.SetState( childState.heightState );
464 SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
465 heightSizeAndState );
469 ForceUniformWidth( GetChildCount(), heightMeasureSpec );
473 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
475 // Pretend that the linear layout has an exact size.
476 auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
477 for (int i = 0; i < count; ++i)
479 LayoutItemPtr childLayout = GetChildAt(i);
480 if( childLayout != nullptr )
482 auto childOwner = childLayout->GetOwner();
483 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
484 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
486 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
488 // Temporarily force children to reuse their old measured height
489 int oldHeight = desiredHeight;
490 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
492 // Remeasure with new dimensions
493 MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
495 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
501 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
503 Extents padding = GetPadding();
505 LayoutLength childTop( padding.top );
506 LayoutLength childLeft( padding.start );
508 // Where end of child should go
509 auto width = right - left;
511 // Space available for child
512 auto childSpace = width - (int)padding.start - (int)padding.end;
513 auto count = GetChildCount();
515 switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
517 case Dali::Toolkit::LinearLayout::Alignment::TOP:
519 // mTotalLength contains the padding already
520 childTop = LayoutLength( padding.top );
523 case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
525 // mTotalLength contains the padding already
526 childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
529 case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
532 // mTotalLength contains the padding already
533 childTop = LayoutLength( padding.top ) + ( bottom - top - mTotalLength ) / 2;
538 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
540 LayoutItemPtr childLayout = GetChildAt( childIndex );
541 if( childLayout != nullptr )
543 auto childWidth = childLayout->GetMeasuredWidth();
544 auto childHeight = childLayout->GetMeasuredHeight();
545 auto childMargin = childLayout->GetMargin();
547 childTop += childMargin.top;
548 switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
550 case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
553 childLeft = LayoutLength( padding.start ) + childMargin.start;
556 case Dali::Toolkit::LinearLayout::Alignment::END:
558 childLeft = width - padding.end - childWidth - childMargin.end;
561 case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
563 childLeft = LayoutLength( padding.start ) + ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
567 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
568 childTop += childHeight + (int)childMargin.bottom + mCellPadding.height;
573 } // namespace Internal
574 } // namespace Toolkit