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 LinearLayoutPtr LinearLayout::New()
46 LinearLayoutPtr layout( new LinearLayout() );
50 LinearLayout::LinearLayout()
53 mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
58 LinearLayout::~LinearLayout()
62 void LinearLayout::SetCellPadding( LayoutSize size )
64 if ( mCellPadding != size )
71 LayoutSize LinearLayout::GetCellPadding()
76 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
78 if ( mOrientation != orientation )
80 mOrientation = orientation;
85 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation()
90 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
92 #if defined(DEBUG_ENABLED)
93 auto actor = Actor::DownCast(GetOwner());
95 std::ostringstream oss;
96 oss << "LinearLayout::OnMeasure ";
99 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
101 oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
102 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
105 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
107 MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
111 MeasureVertical( widthMeasureSpec, heightMeasureSpec );
115 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
117 #if defined(DEBUG_ENABLED)
118 auto actor = Actor::DownCast(GetOwner());
120 std::ostringstream oss;
121 oss << "LinearLayout::OnLayout ";
124 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
126 oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
127 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
130 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
132 LayoutHorizontal( left, top, right, bottom );
136 LayoutVertical( left, top, right, bottom );
140 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
142 auto widthMode = widthMeasureSpec.GetMode();
143 auto heightMode = heightMeasureSpec.GetMode();
144 bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
145 bool matchHeight = false;
146 bool allFillParent = true;
147 LayoutLength maxHeight = 0;
148 LayoutLength alternativeMaxHeight = 0;
151 MeasuredSize::State widthState;
152 MeasuredSize::State heightState;
153 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
155 // Reset total length
158 // measure children, and determine if further resolution is required
159 for( unsigned int i=0; i<GetChildCount(); ++i )
161 auto childLayout = GetChildAt( i );
164 auto childOwner = childLayout->GetOwner();
165 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
167 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
168 auto childWidth = childLayout->GetMeasuredWidth();
169 auto childMargin = childLayout->GetMargin();
171 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
173 auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
175 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
179 mTotalLength += length;
183 auto totalLength = mTotalLength;
184 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
187 bool matchHeightLocally = false;
188 if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
190 // Will have to re-measure at least this child when we know exact height.
192 matchHeightLocally = true;
195 auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
196 auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
198 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
200 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
202 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
204 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
207 maxHeight = std::max( maxHeight, childHeight );
208 allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
209 alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
213 Extents padding = GetPadding();
214 mTotalLength += padding.start + padding.end;
215 auto widthSize = mTotalLength;
216 widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
217 MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
218 widthSize = widthSizeAndState.GetSize();
220 if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
222 maxHeight = alternativeMaxHeight;
224 maxHeight += padding.top + padding.bottom;
225 maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
227 widthSizeAndState.SetState( childState.widthState );
229 SetMeasuredDimensions( widthSizeAndState,
230 ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
234 ForceUniformHeight( GetChildCount(), widthMeasureSpec );
238 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
240 // Pretend that the linear layout has an exact size. This is the measured height of
241 // ourselves. The measured height should be the max height of the children, changed
242 // to accommodate the heightMeasureSpec from the parent
243 auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
244 for (int i = 0; i < count; ++i)
246 LayoutItemPtr childLayout = GetChildAt(i);
247 if( childLayout != nullptr )
249 auto childOwner = childLayout->GetOwner();
250 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
251 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
253 if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
255 // Temporarily force children to reuse their old measured width
256 int oldWidth = desiredWidth;
257 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
259 // Remeasure with new dimensions
260 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
262 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
268 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
270 auto owner = GetOwner();
271 auto actor = Actor::DownCast(owner);
272 bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
274 Extents padding = GetPadding();
276 LayoutLength childTop( 0 );
277 LayoutLength childLeft( padding.start );
279 // Where bottom of child should go
280 auto height = bottom - top;
282 // Space available for child
283 auto childSpace = height - padding.top - padding.bottom;
285 auto count = GetChildCount();
290 // In case of RTL, start drawing from the last child and apply right alignment.
291 // @TODO Should we have also support Actor HorizontalAlignment|VerticalAlignment in general for LinearLayout?
295 childLeft = padding.start + right - left - mTotalLength;
298 for( unsigned int i = 0; i < count; i++)
300 int childIndex = start + dir * i;
301 LayoutItemPtr childLayout = GetChildAt( childIndex );
302 if( childLayout != nullptr )
304 auto childWidth = childLayout->GetMeasuredWidth();
305 auto childHeight = childLayout->GetMeasuredHeight();
306 auto childMargin = childLayout->GetMargin();
308 childTop = LayoutLength(padding.top) + ((childSpace - childHeight) / 2) + childMargin.top - childMargin.bottom;
310 childLeft += childMargin.start;
311 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
312 childLeft += childWidth + childMargin.end + mCellPadding.width;
317 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
319 auto widthMode = widthMeasureSpec.GetMode();
321 bool matchWidth = false;
322 bool allFillParent = true;
323 LayoutLength maxWidth = 0;
324 LayoutLength alternativeMaxWidth = 0;
328 MeasuredSize::State widthState;
329 MeasuredSize::State heightState;
330 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
332 // Reset total length
335 // measure children, and determine if further resolution is required
336 for( unsigned int i=0; i<GetChildCount(); ++i )
338 auto childLayout = GetChildAt( i );
341 auto childOwner = childLayout->GetOwner();
342 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
344 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
345 auto childHeight = childLayout->GetMeasuredHeight();
346 auto childMargin = childLayout->GetMargin();
347 auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
349 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
350 auto totalLength = mTotalLength;
351 mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
353 bool matchWidthLocally = false;
354 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
356 // Will have to re-measure at least this child when we know exact height.
358 matchWidthLocally = true;
361 auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
362 auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
364 // was combineMeasuredStates()
365 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
367 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
369 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
371 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
374 maxWidth = std::max( maxWidth, childWidth );
375 allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
376 alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
379 Extents padding = GetPadding();
380 mTotalLength += padding.top + padding.bottom;
381 auto heightSize = mTotalLength;
382 heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
383 MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
384 heightSize = heightSizeAndState.GetSize();
386 if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
388 maxWidth = alternativeMaxWidth;
390 maxWidth += padding.start + padding.end;
391 maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
393 heightSizeAndState.SetState( childState.heightState );
395 SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
396 heightSizeAndState );
400 ForceUniformWidth( GetChildCount(), heightMeasureSpec );
404 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
406 // Pretend that the linear layout has an exact size.
407 auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
408 for (int i = 0; i < count; ++i)
410 LayoutItemPtr childLayout = GetChildAt(i);
411 if( childLayout != nullptr )
413 auto childOwner = childLayout->GetOwner();
414 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
415 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
417 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
419 // Temporarily force children to reuse their old measured height
420 int oldHeight = desiredHeight;
421 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
423 // Remeasure with new dimensions
424 MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
426 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
432 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
434 Extents padding = GetPadding();
436 LayoutLength childTop( 0 );
437 LayoutLength childLeft( padding.start );
439 // Where bottom of child should go
440 auto width = right - left;
442 // Space available for child
443 auto childSpace = width - padding.start - padding.end;
444 auto count = GetChildCount();
446 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
448 LayoutItemPtr childLayout = GetChildAt( childIndex );
449 if( childLayout != nullptr )
451 auto childWidth = childLayout->GetMeasuredWidth();
452 auto childHeight = childLayout->GetMeasuredHeight();
453 auto childMargin = childLayout->GetMargin();
455 childTop += childMargin.top;
456 childLeft = ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
458 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
459 childTop += childHeight + childMargin.bottom + mCellPadding.height;
464 } // namespace Internal
465 } // namespace Toolkit