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 )
67 LayoutSize LinearLayout::GetCellPadding()
72 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
74 mOrientation = orientation;
77 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation()
82 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
84 #if defined(DEBUG_ENABLED)
85 auto actor = Actor::DownCast(GetOwner());
87 std::ostringstream oss;
88 oss << "LinearLayout::OnMeasure ";
91 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
93 oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
94 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
97 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
99 MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
103 MeasureVertical( widthMeasureSpec, heightMeasureSpec );
107 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
109 #if defined(DEBUG_ENABLED)
110 auto actor = Actor::DownCast(GetOwner());
112 std::ostringstream oss;
113 oss << "LinearLayout::OnLayout ";
116 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
118 oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
119 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
122 if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
124 LayoutHorizontal( left, top, right, bottom );
128 LayoutVertical( left, top, right, bottom );
132 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
134 auto widthMode = widthMeasureSpec.GetMode();
135 auto heightMode = heightMeasureSpec.GetMode();
136 bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
137 bool matchHeight = false;
138 bool allFillParent = true;
139 LayoutLength maxHeight = 0;
140 LayoutLength alternativeMaxHeight = 0;
143 MeasuredSize::State widthState;
144 MeasuredSize::State heightState;
145 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
147 // Reset total length
150 // measure children, and determine if further resolution is required
151 for( unsigned int i=0; i<GetChildCount(); ++i )
153 auto childLayout = GetChildAt( i );
156 auto childOwner = childLayout->GetOwner();
157 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
159 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
160 auto childWidth = childLayout->GetMeasuredWidth();
161 auto childMargin = childLayout->GetMargin();
163 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
165 auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
167 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
171 mTotalLength += length;
175 auto totalLength = mTotalLength;
176 mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
179 bool matchHeightLocally = false;
180 if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
182 // Will have to re-measure at least this child when we know exact height.
184 matchHeightLocally = true;
187 auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
188 auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
190 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
192 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
194 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
196 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
199 maxHeight = std::max( maxHeight, childHeight );
200 allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
201 alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
205 Extents padding = GetPadding();
206 mTotalLength += padding.start + padding.end;
207 auto widthSize = mTotalLength;
208 widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
209 MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
210 widthSize = widthSizeAndState.GetSize();
212 if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
214 maxHeight = alternativeMaxHeight;
216 maxHeight += padding.top + padding.bottom;
217 maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
219 widthSizeAndState.SetState( childState.widthState );
221 SetMeasuredDimensions( widthSizeAndState,
222 ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
226 ForceUniformHeight( GetChildCount(), widthMeasureSpec );
230 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
232 // Pretend that the linear layout has an exact size. This is the measured height of
233 // ourselves. The measured height should be the max height of the children, changed
234 // to accommodate the heightMeasureSpec from the parent
235 auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
236 for (int i = 0; i < count; ++i)
238 LayoutItemPtr childLayout = GetChildAt(i);
239 if( childLayout != nullptr )
241 auto childOwner = childLayout->GetOwner();
242 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
243 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
245 if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
247 // Temporarily force children to reuse their old measured width
248 int oldWidth = desiredWidth;
249 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
251 // Remeasure with new dimensions
252 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
254 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
260 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
262 auto owner = GetOwner();
263 auto actor = Actor::DownCast(owner);
264 bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
266 Extents padding = GetPadding();
268 LayoutLength childTop( 0 );
269 LayoutLength childLeft( padding.start );
271 // Where bottom of child should go
272 auto height = bottom - top;
274 // Space available for child
275 auto childSpace = height - padding.top - padding.bottom;
277 auto count = GetChildCount();
282 // In case of RTL, start drawing from the last child and apply right alignment.
283 // @TODO Should we have also support Actor HorizontalAlignment|VerticalAlignment in general for LinearLayout?
287 childLeft = padding.start + right - left - mTotalLength;
290 for( unsigned int i = 0; i < count; i++)
292 int childIndex = start + dir * i;
293 LayoutItemPtr childLayout = GetChildAt( childIndex );
294 if( childLayout != nullptr )
296 auto childWidth = childLayout->GetMeasuredWidth();
297 auto childHeight = childLayout->GetMeasuredHeight();
298 auto childMargin = childLayout->GetMargin();
300 childTop = LayoutLength(padding.top) + ((childSpace - childHeight) / 2) + childMargin.top - childMargin.bottom;
302 childLeft += childMargin.start;
303 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
304 childLeft += childWidth + childMargin.end + mCellPadding.width;
309 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
311 auto widthMode = widthMeasureSpec.GetMode();
313 bool matchWidth = false;
314 bool allFillParent = true;
315 LayoutLength maxWidth = 0;
316 LayoutLength alternativeMaxWidth = 0;
320 MeasuredSize::State widthState;
321 MeasuredSize::State heightState;
322 } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
324 // Reset total length
327 // measure children, and determine if further resolution is required
328 for( unsigned int i=0; i<GetChildCount(); ++i )
330 auto childLayout = GetChildAt( i );
333 auto childOwner = childLayout->GetOwner();
334 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
336 MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
337 auto childHeight = childLayout->GetMeasuredHeight();
338 auto childMargin = childLayout->GetMargin();
339 auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
341 auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
342 auto totalLength = mTotalLength;
343 mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
345 bool matchWidthLocally = false;
346 if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
348 // Will have to re-measure at least this child when we know exact height.
350 matchWidthLocally = true;
353 auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
354 auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
356 // was combineMeasuredStates()
357 if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
359 childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
361 if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
363 childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
366 maxWidth = std::max( maxWidth, childWidth );
367 allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
368 alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
371 Extents padding = GetPadding();
372 mTotalLength += padding.top + padding.bottom;
373 auto heightSize = mTotalLength;
374 heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
375 MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
376 heightSize = heightSizeAndState.GetSize();
378 if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
380 maxWidth = alternativeMaxWidth;
382 maxWidth += padding.start + padding.end;
383 maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
385 heightSizeAndState.SetState( childState.heightState );
387 SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
388 heightSizeAndState );
392 ForceUniformWidth( GetChildCount(), heightMeasureSpec );
396 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
398 // Pretend that the linear layout has an exact size.
399 auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
400 for (int i = 0; i < count; ++i)
402 LayoutItemPtr childLayout = GetChildAt(i);
403 if( childLayout != nullptr )
405 auto childOwner = childLayout->GetOwner();
406 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
407 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
409 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
411 // Temporarily force children to reuse their old measured height
412 int oldHeight = desiredHeight;
413 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
415 // Remeasure with new dimensions
416 MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
418 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
424 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
426 Extents padding = GetPadding();
428 LayoutLength childTop( 0 );
429 LayoutLength childLeft( padding.start );
431 // Where bottom of child should go
432 auto width = right - left;
434 // Space available for child
435 auto childSpace = width - padding.start - padding.end;
436 auto count = GetChildCount();
438 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
440 LayoutItemPtr childLayout = GetChildAt( childIndex );
441 if( childLayout != nullptr )
443 auto childWidth = childLayout->GetMeasuredWidth();
444 auto childHeight = childLayout->GetMeasuredHeight();
445 auto childMargin = childLayout->GetMargin();
447 childTop += childMargin.top;
448 childLeft = ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
450 childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
451 childTop += childHeight + childMargin.bottom + mCellPadding.height;
456 } // namespace Internal
457 } // namespace Toolkit