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.
17 #include <dali/integration-api/debug.h>
19 #include <dali/public-api/animation/animation.h>
20 #include <dali/public-api/object/type-registry-helper.h>
21 #include <dali-toolkit/public-api/controls/control.h>
22 #include <dali/devel-api/object/handle-devel.h>
23 #include <dali-toolkit/devel-api/layouting/layout-item-impl.h>
24 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
25 #include <dali-toolkit/internal/layouting/layout-transition-data-impl.h>
26 #include <dali-toolkit/internal/layouting/layout-item-data-impl.h>
28 #include <dali/devel-api/scripting/enum-helper.h>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLayoutFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
37 const char* WIDTH_SPECIFICATION_NAME( "widthSpecification" );
38 const char* HEIGHT_SPECIFICATION_NAME( "heightSpecification" );
49 LayoutItem::LayoutItem()
50 : mImpl( new LayoutItem::Impl() ),
55 LayoutItem::~LayoutItem()
57 // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
58 // where the opaque structure is defined. It cannot use the default method in the header file.
61 LayoutItemPtr LayoutItem::New( Handle& owner )
63 LayoutItemPtr layoutPtr = new LayoutItem();
67 void LayoutItem::Initialize( Handle& owner, const std::string& containerType )
69 mImpl->mOwner = &(owner.GetBaseObject());
70 RegisterChildProperties( containerType );
71 OnInitialize(); // Ensure direct deriving class gets initialized
74 Handle LayoutItem::GetOwner() const
76 return Handle::DownCast(BaseHandle(mImpl->mOwner));
79 void LayoutItem::Unparent()
81 // Enable directly derived types to first remove children
84 // Remove myself from parent
85 LayoutParent* parent = GetParent();
88 parent->Remove( *this );
91 // Remove parent reference
98 LayoutTransitionDataPtr LayoutItem::GetDefaultTransition()
100 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::GetDefaultTransition\n" );
101 if ( !mImpl->mDefaultTransitionData.Get() )
103 auto owner = GetOwner();
104 auto actor = Actor::DownCast( owner );
106 mImpl->mDefaultTransitionData = LayoutTransitionData::New();
109 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::PROPERTY ] = Actor::Property::POSITION;
110 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::TARGET_VALUE ] = Property::Value(); // capture from layout update
111 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::ANIMATOR ] = std::string(); // default animator with default duration
112 // Capture calculated position after layout, apply default linear animation
113 mImpl->mDefaultTransitionData->AddPropertyAnimator( actor, map );
117 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::PROPERTY ] = Actor::Property::SIZE;
118 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::TARGET_VALUE ] = Property::Value(); // capture from layout update
119 map[ Dali::Toolkit::LayoutTransitionData::AnimatorKey::ANIMATOR ] = std::string(); // default animator with default duration
120 // Capture calculated size after layout, apply default linear animation
121 mImpl->mDefaultTransitionData->AddPropertyAnimator( actor, map );
124 return mImpl->mDefaultTransitionData;
127 void LayoutItem::SetAnimateLayout( bool animateLayout )
129 auto owner = GetOwner();
130 auto actor = Actor::DownCast(owner);
132 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetAnimateLayout animateLayout(%s) owner(%s)\n", (animateLayout)?"true":"false",
133 ( ( Actor::DownCast( owner) ) ? Actor::DownCast(owner).GetName().c_str() : "Invalid Actor" ) );
135 mImpl->mAnimated = animateLayout;
137 OnAnimationStateChanged( animateLayout );
140 bool LayoutItem::IsLayoutAnimated() const
142 return mImpl->mAnimated;
145 void LayoutItem::SetTransitionData( int layoutTransitionType, Internal::LayoutTransitionDataPtr layoutTransitionDataPtr )
147 switch ( layoutTransitionType )
149 case Dali::Toolkit::LayoutTransitionData::ON_CHILD_ADD:
150 mImpl->mOnChildAddTransitionData = layoutTransitionDataPtr;
152 case Dali::Toolkit::LayoutTransitionData::ON_CHILD_REMOVE:
153 mImpl->mOnChildRemoveTransitionData = layoutTransitionDataPtr;
155 case Dali::Toolkit::LayoutTransitionData::ON_OWNER_SET:
156 mImpl->mOnOwnerSetTransitionData = layoutTransitionDataPtr;
163 Internal::LayoutTransitionDataPtr LayoutItem::GetTransitionData( int layoutTransitionType ) const
165 switch ( layoutTransitionType )
167 case Dali::Toolkit::LayoutTransitionData::ON_CHILD_ADD:
168 return mImpl->mOnChildAddTransitionData.Get();
169 case Dali::Toolkit::LayoutTransitionData::ON_CHILD_REMOVE:
170 return mImpl->mOnChildRemoveTransitionData.Get();
171 case Dali::Toolkit::LayoutTransitionData::ON_OWNER_SET:
172 return mImpl->mOnOwnerSetTransitionData.Get();
174 return LayoutTransitionDataPtr();
178 void LayoutItem::RegisterChildProperties( const std::string& containerType )
180 // Call on derived types
181 auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
184 Property::IndexContainer indices;
185 typeInfo.GetChildPropertyIndices( indices );
187 if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) ==
190 ChildPropertyRegistration( typeInfo.GetName(), WIDTH_SPECIFICATION_NAME,
191 Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, Property::INTEGER );
193 ChildPropertyRegistration( typeInfo.GetName(), HEIGHT_SPECIFICATION_NAME,
194 Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, Property::INTEGER );
197 OnRegisterChildProperties( containerType );
201 void LayoutItem::OnRegisterChildProperties( const std::string& containerType )
206 void LayoutItem::Measure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
208 DALI_LOG_TRACE_METHOD( gLayoutFilter );
210 const bool forceLayout = mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
212 const bool specChanged =
213 ( widthMeasureSpec != mImpl->mOldWidthMeasureSpec ) ||
214 ( heightMeasureSpec != mImpl->mOldHeightMeasureSpec );
216 const bool isSpecExactly =
217 ( widthMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY ) &&
218 ( heightMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY );
220 const bool matchesSpecSize =
221 ( GetMeasuredWidth() == widthMeasureSpec.GetSize() ) &&
222 ( GetMeasuredHeight() == heightMeasureSpec.GetSize() );
224 const bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize );
226 DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::Measure("<<widthMeasureSpec<<", "<<heightMeasureSpec<<") Owner:"
227 <<Actor::DownCast(GetOwner()).GetName() <<" forceLayout="<<forceLayout
228 <<", specChanged="<<specChanged<<", isSpecExactly="<<isSpecExactly
229 <<", matchesSpecSize="<<matchesSpecSize
230 <<", needsLayout="<<needsLayout <<(forceLayout||needsLayout?" Remeasuring":" NoChange"));
232 if( forceLayout || needsLayout )
234 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET );
236 // measure ourselves, this should set the measured dimension flag back
237 #if defined(DEBUG_ENABLED)
238 std::ostringstream o;
239 o<<widthMeasureSpec<<","<<heightMeasureSpec;
240 DALI_LOG_INFO( gLayoutFilter, Debug::General, "LayoutItem::Measure Calling %s OnMeasure( %s )\n", Actor::DownCast(GetOwner()).GetName().c_str(), o.str().c_str());
242 OnMeasure( widthMeasureSpec, heightMeasureSpec );
243 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT );
245 // flag not set, setMeasuredDimension() was not invoked, we raise an exception to warn the developer
246 DALI_ASSERT_ALWAYS( mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET ) &&
247 "Layout's OnMeasure() Measured dimension flag not set" );
248 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED );
251 mImpl->mOldWidthMeasureSpec = widthMeasureSpec;
252 mImpl->mOldHeightMeasureSpec = heightMeasureSpec;
255 void LayoutItem::Layout( LayoutLength l, LayoutLength t, LayoutLength r, LayoutLength b )
257 DALI_LOG_TRACE_METHOD( gLayoutFilter );
259 if( mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT ) )
261 OnMeasure( mImpl->mOldWidthMeasureSpec, mImpl->mOldHeightMeasureSpec );
262 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT );
265 LayoutData& layoutData = *mImpl->sLayoutData;
266 size_t size = layoutData.childrenPropertyAnimators.size();
268 bool changed = SetFrame( l, t, r, b );
270 if( changed || mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED ) )
273 OnLayout( changed, l, t, r, b );
274 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED );
277 if ( size != layoutData.childrenPropertyAnimators.size() )
279 layoutData.childrenPropertyAnimators.resize( size );
282 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
283 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_IS_LAID_OUT );
286 LayoutLength LayoutItem::GetMinimumWidth() const
288 return mImpl->mMinimumSize.GetWidth();
291 LayoutLength LayoutItem::GetMinimumHeight() const
293 return mImpl->mMinimumSize.GetHeight();
296 void LayoutItem::SetMinimumWidth( LayoutLength minimumWidth )
298 mImpl->mMinimumSize.SetWidth( minimumWidth );
302 void LayoutItem::SetMinimumHeight( LayoutLength minimumHeight )
304 mImpl->mMinimumSize.SetHeight( minimumHeight );
308 Extents LayoutItem::GetPadding() const
310 Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
313 Extents padding = control.GetProperty<Extents>( Toolkit::Control::Property::PADDING );
315 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::Padding for %s : (%d,%d,%d,%d) \n",
316 control.GetName().c_str(),
317 padding.start, padding.end, padding.top, padding.bottom
327 Extents LayoutItem::GetMargin() const
329 Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
332 return control.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
340 LayoutLength LayoutItem::GetDefaultSize( LayoutLength size, MeasureSpec measureSpec )
342 LayoutLength result = size;
343 auto specMode = measureSpec.GetMode();
344 auto specSize = measureSpec.GetSize();
346 DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::GetDefaultSize MeasureSpec("<<measureSpec<< ") size:" << size << "\n" );
350 case MeasureSpec::Mode::UNSPECIFIED:
355 case MeasureSpec::Mode::AT_MOST:
357 // Ensure the default size does not exceed the spec size unless the default size is 0.
358 // Another container could provide a default size of 0.
359 LayoutLength tmp = specSize;
361 // Do not set size to 0, use specSize in this case as could be a legacy container
362 if( size < tmp && size > LayoutLength( 0 ) )
372 case MeasureSpec::Mode::EXACTLY:
378 DALI_LOG_STREAM( gLayoutFilter, Debug::General, "LayoutItem::GetDefaultSize setting default size:" << result << "\n" );
382 void LayoutItem::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
384 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::OnMeasure\n");
386 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
387 SetMeasuredDimensions( GetDefaultSize( GetSuggestedMinimumWidth(), widthMeasureSpec ),
388 GetDefaultSize( GetSuggestedMinimumHeight(), heightMeasureSpec ) );
391 void LayoutItem::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
395 void LayoutItem::SetParent( LayoutParent* parent )
397 mImpl->mLayoutParent = parent;
398 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME );
401 LayoutParent* LayoutItem::GetParent()
403 return mImpl->mLayoutParent;
406 void LayoutItem::RequestLayout()
408 Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
411 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::RequestLayout control(%s)\n",
412 control.GetName().c_str() );
414 // @todo Enforce failure if called in Measure/Layout passes.
415 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
416 Toolkit::LayoutController layoutController = Toolkit::LayoutController::Get();
417 layoutController.RequestLayout( Toolkit::LayoutItem( this ) );
420 void LayoutItem::RequestLayout( Dali::Toolkit::LayoutTransitionData::LayoutTransitionType layoutAnimationType )
422 Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
425 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::RequestLayout control(%s) layoutTranstionType(%d)\n",
426 control.GetName().c_str(), (int)layoutAnimationType );
428 // @todo Enforce failure if called in Measure/Layout passes.
429 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
430 Toolkit::LayoutController layoutController = Toolkit::LayoutController::Get();
431 layoutController.RequestLayout( Toolkit::LayoutItem(this), layoutAnimationType );
434 bool LayoutItem::IsLayoutRequested() const
436 return mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
439 void LayoutItem::SetLayoutRequested()
441 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
444 bool LayoutItem::IsResizePolicyRequired() const
446 return mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
449 void LayoutItem::SetResizePolicyRequired( bool resizePolicyRequired )
451 if( resizePolicyRequired )
453 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
457 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
461 void LayoutItem::SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
464 DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetMeasuredDimensions width(" << measuredWidth.GetSize() << ") height(" << measuredHeight.GetSize() << ") Control:" <<
465 ( ( Actor::DownCast( GetOwner()) ) ? Actor::DownCast(GetOwner()).GetName().c_str() : "Invalid Actor" ) << "\n" );
467 mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET );
468 mImpl->mMeasuredWidth = measuredWidth;
469 mImpl->mMeasuredHeight = measuredHeight;
472 LayoutLength LayoutItem::GetMeasuredWidth() const
474 // Get the size portion of the measured width
475 return mImpl->mMeasuredWidth.GetSize();
478 LayoutLength LayoutItem::GetMeasuredHeight() const
480 return mImpl->mMeasuredHeight.GetSize();
483 MeasuredSize LayoutItem::GetMeasuredWidthAndState() const
485 return mImpl->mMeasuredWidth;
488 MeasuredSize LayoutItem::GetMeasuredHeightAndState() const
490 return mImpl->mMeasuredHeight;
493 LayoutLength LayoutItem::GetSuggestedMinimumWidth() const
495 auto owner = GetOwner();
496 auto actor = Actor::DownCast(owner);
497 auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
499 return std::max( mImpl->mMinimumSize.GetWidth(), LayoutLength( naturalSize.width ) );
502 LayoutLength LayoutItem::GetSuggestedMinimumHeight() const
504 auto owner = GetOwner();
505 auto actor = Actor::DownCast(owner);
506 auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
508 return std::max( mImpl->mMinimumSize.GetHeight(), LayoutLength( naturalSize.height ) );
511 MeasuredSize LayoutItem::ResolveSizeAndState( LayoutLength size, MeasureSpec measureSpec, MeasuredSize::State childMeasuredState )
513 auto specMode = measureSpec.GetMode();
514 LayoutLength specSize = measureSpec.GetSize();
519 case MeasureSpec::Mode::AT_MOST:
523 result = MeasuredSize( specSize, MeasuredSize::MEASURED_SIZE_TOO_SMALL );
527 result.SetSize( size );
532 case MeasureSpec::Mode::EXACTLY:
534 result.SetSize( specSize );
538 case MeasureSpec::Mode::UNSPECIFIED:
541 result.SetSize( size );
546 result.SetState( childMeasuredState );
551 bool LayoutItem::SetFrame( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
553 bool changed = false;
555 DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame enter(" << left << ", " << top << ", " << right << ", " << bottom << ")\n" );
557 if( mImpl->mLeft != left || mImpl->mRight != right || mImpl->mTop != top || mImpl->mBottom != bottom || mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME ) )
560 mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME );
563 LayoutLength oldWidth = mImpl->mRight - mImpl->mLeft;
564 LayoutLength oldHeight = mImpl->mBottom - mImpl->mTop;
565 LayoutLength newWidth = right - left;
566 LayoutLength newHeight = bottom - top;
567 bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
571 mImpl->mRight = right;
572 mImpl->mBottom = bottom;
574 // Reflect up to parent control
575 auto owner = GetOwner();
576 auto actor = Actor::DownCast( owner );
577 LayoutData& layoutData = *mImpl->sLayoutData;
580 if( mImpl->mAnimated && !layoutData.speculativeLayout )
583 LayoutItem* transitionOwner = layoutData.layoutTransition.layoutItem.Get();
584 LayoutTransitionDataPtr layoutTransitionDataPtr = GetTransitionData( layoutData.layoutTransition.layoutTransitionType );
586 // Found transition owner
587 if( transitionOwner == this && layoutTransitionDataPtr.Get() )
589 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame apply transition to (%s), transition type (%d)\n", actor.GetName().c_str(), layoutData.layoutTransition.layoutTransitionType );
590 layoutData.layoutPositionDataArray.push_back( LayoutPositionData( actor, left.AsDecimal(), top.AsDecimal(), right.AsDecimal(), bottom.AsDecimal(), true ) );
591 layoutTransitionDataPtr->ConvertToLayoutDataElements( actor, layoutData );
598 DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame apply default transition to (%s), transition type (%d)\n", actor.GetName().c_str(), layoutData.layoutTransition.layoutTransitionType );
599 layoutData.layoutPositionDataArray.push_back( LayoutPositionData( actor, left.AsDecimal(), top.AsDecimal(), right.AsDecimal(), bottom.AsDecimal(), true ) );
600 GetDefaultTransition()->ConvertToLayoutDataElements( actor, layoutData );
608 layoutData.layoutPositionDataArray.push_back( LayoutPositionData( actor, left.AsDecimal(), top.AsDecimal(), right.AsDecimal(), bottom.AsDecimal(), false ) );
613 // TODO: do we need it
616 SizeChange( LayoutSize( newWidth, newHeight ), LayoutSize( oldWidth, oldHeight ) );
619 DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame exit(" << left << ", " << top << ", " << right << ", " << bottom << ")\n" );
624 void LayoutItem::OnLayoutAnimationFinished( Animation& animation )
626 auto owner = GetOwner();
627 auto actor = Actor::DownCast(owner);
630 actor.SetSize( Vector3( mImpl->mRight.AsInteger() - mImpl->mLeft.AsInteger(), mImpl->mBottom.AsInteger() - mImpl->mTop.AsInteger(), 0.0f ) );
634 void LayoutItem::SizeChange( LayoutSize newSize, LayoutSize oldSize)
636 OnSizeChanged( newSize, oldSize );
640 void LayoutItem::OnSizeChanged( LayoutSize newSize, LayoutSize oldSize )
644 void LayoutItem::OnInitialize()
649 } // namespace Internal
650 } // namespace Toolkit