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/public-api/common/stage.h>
18 #include <dali/public-api/actors/layer.h>
19 #include <dali-toolkit/internal/layouting/layout-controller-impl.h>
20 #include <dali-toolkit/internal/layouting/layout-item-data-impl.h>
21 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
22 #include <dali-toolkit/public-api/controls/control.h>
23 #include <dali-toolkit/public-api/controls/control-impl.h>
24 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
25 #include <dali-toolkit/internal/layouting/layout-controller-debug.h>
26 #include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
33 #if defined(DEBUG_ENABLED)
34 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
46 LayoutController::LayoutController()
47 : mLayoutRequested( false ),
48 mFocusChangedFunctor( *this ),
53 LayoutController::~LayoutController()
57 void LayoutController::Initialize()
59 mAnimation = Animation::New( 0.0f );
61 Dali::Toolkit::KeyInputFocusManager manager = Dali::Toolkit::KeyInputFocusManager::Get();
62 manager.KeyInputFocusChangedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mFocusChangedFunctor );
65 void LayoutController::FocusChangedFunctor::operator() ( Dali::Toolkit::Control gainingControl, Dali::Toolkit::Control lostControl )
67 Toolkit::LayoutItem layoutItem = Toolkit::DevelControl::GetLayout( gainingControl );
70 Toolkit::Internal::LayoutItem& layoutItemImpl = GetImplementation( layoutItem );
71 LayoutParent* layoutParent = layoutItemImpl.GetParent();
74 LayoutGroup* layoutGroup = static_cast< LayoutGroup* >( layoutParent );
75 layoutController.RequestLayout( dynamic_cast< Toolkit::Internal::LayoutItem& >( *layoutGroup ), Dali::Toolkit::LayoutTransitionData::ON_CHILD_FOCUS, gainingControl, lostControl );
80 void LayoutController::RequestLayout( LayoutItem& layoutItem, int layoutTransitionType, Actor gainedChild, Actor lostChild )
82 auto actor = Actor::DownCast( layoutItem.GetOwner() );
85 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout owner[%s] layoutItem[%p] layoutTransitionType(%d) gainedChild[%s] lostChild[%s]\n",
86 actor.GetName().c_str(), &layoutItem, layoutTransitionType,
87 gainedChild ? gainedChild.GetName().c_str() : "",
88 lostChild ? lostChild.GetName().c_str() : "");
92 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout layoutItem[%p] layoutAnimationType(%d)\n", &layoutItem, layoutTransitionType );
95 mLayoutRequested = true;
96 if( layoutTransitionType != -1 )
98 LayoutTransition layoutTransition = LayoutTransition( layoutItem, layoutTransitionType, gainedChild, lostChild );
99 if( std::find( mLayoutTransitions.begin(), mLayoutTransitions.end(), layoutTransition ) == mLayoutTransitions.end() && layoutItem.GetTransitionData( layoutTransitionType ).Get() )
101 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout Add transition layoutTransitionType(%d)\n", layoutTransitionType );
102 mLayoutTransitions.push_back( layoutTransition );
106 // Go up the tree and mark all parents to relayout
107 LayoutParent* layoutParent = layoutItem.GetParent();
110 LayoutGroup& layoutGroup = static_cast< LayoutGroup& >( *layoutParent );
111 if( ! layoutGroup.IsLayoutRequested() )
113 layoutGroup.RequestLayout();
118 void LayoutController::Process()
120 // Perform the full process.
121 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::Process\n" );
123 if( mLayoutRequested )
125 // If window size has changed, expect stage to have already been updated
126 Stage stage = Stage::GetCurrent();
127 auto stageWidth = stage.GetSize().width;
128 auto stageHeight = stage.GetSize().height;
130 MeasureSpec widthSpec( stageWidth, MeasureSpec::Mode::EXACTLY );
131 MeasureSpec heightSpec( stageHeight, MeasureSpec::Mode::EXACTLY );
133 LayoutTransition layoutTransition;
134 LayoutPositionDataArray layoutPositionDataArray;
135 LayoutAnimatorArray layoutAnimatorArray;
136 LayoutDataArray layoutDataArray;
137 LayoutDataArray childrenLayoutDataArray;
139 if ( mLayoutTransitions.size() )
141 layoutTransition = mLayoutTransitions.front();
142 mLayoutTransitions.pop_front();
143 mLayoutRequested = ( mLayoutTransitions.size() != 0 );
147 mLayoutRequested = false;
150 LayoutData layoutData( layoutTransition, layoutPositionDataArray, layoutAnimatorArray, layoutDataArray, childrenLayoutDataArray );
151 LayoutItem::Impl::sLayoutData = &layoutData;
153 if( layoutTransition.layoutTransitionType != -1 )
155 UpdateMeasureHierarchyForAnimation( layoutData );
158 // Test how to perform a measure on each control.
159 MeasureHierarchy( stage.GetRootLayer(), widthSpec, heightSpec );
161 LAYOUT_DEBUG_MEASURE_STATES( stage.GetRootLayer() );
163 if( layoutTransition.layoutTransitionType != -1 )
165 RestoreActorsSpecs();
168 layoutAnimatorArray.clear();
169 layoutDataArray.clear();
170 childrenLayoutDataArray.clear();
172 PerformLayout( stage.GetRootLayer(), 0, 0, stageWidth, stageHeight );
174 PerformLayoutPositioning( layoutPositionDataArray, false );
176 PerformLayoutAnimation( layoutTransition, layoutPositionDataArray, layoutDataArray, layoutAnimatorArray );
177 LayoutItem::Impl::sLayoutData = nullptr;
179 LAYOUT_DEBUG_AFTER_LAYOUT( stage.GetRootLayer() );
183 void LayoutController::MeasureHierarchy( Actor root, MeasureSpec widthSpec, MeasureSpec heightSpec )
185 // Does this actor have a layout?
186 // Yes - measure the layout. It will call this method again for each of it's children.
187 // No - recurse through actor children.
189 // If in a leaf actor with no layout, it's natural size is bubbled back up.
191 // What happens if nothing in the tree has a layout?
193 Toolkit::Control control = Toolkit::Control::DownCast( root );
196 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring control:%s\n", control.GetName().c_str() );
197 Internal::Control& controlImpl = GetImplementation( control );
199 Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
200 LayoutItemPtr layout = controlDataImpl.GetLayout();
204 layout->Measure( widthSpec, heightSpec );
209 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring (%u) children\n", root.GetChildCount() );
210 // Depth first descent through actor children
211 for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
213 Actor child = root.GetChildAt( i );
214 MeasureHierarchy( child, widthSpec, heightSpec );
219 void LayoutController::UpdateMeasureHierarchyForAnimation( LayoutData& layoutData )
221 LayoutTransition& layoutTransition = layoutData.layoutTransition;
222 Actor transitionOwner = Actor::DownCast( layoutTransition.layoutItem.Get()->GetOwner() );
223 LayoutTransitionDataPtr layoutTransitionDataPtr = layoutTransition.layoutItem->GetTransitionData( layoutTransition.layoutTransitionType );
225 if( !layoutTransitionDataPtr->HasUpdateMeasuredSize() )
230 layoutData.updateMeasuredSize = true;
231 layoutTransitionDataPtr->CollectLayoutDataElements( transitionOwner, layoutData );
233 UpdateMeasureHierarchyForAnimation( transitionOwner, layoutData );
235 for( auto layoutDataElement : layoutData.layoutDataArray )
237 if( !layoutDataElement.updateMeasuredSize )
242 Actor actor = layoutDataElement.handle.GetHandle();
245 LayoutDataAnimator animator = layoutData.layoutAnimatorArray[ layoutDataElement.animatorIndex ];
246 float width = actor.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
247 float height = actor.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
249 if( layoutDataElement.AdjustMeasuredSize( width, height, animator.animatorType ) )
251 mActorSizeSpecs.push_back( ActorSizeSpec( actor ) );
252 actor.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, static_cast<int>( width ) );
253 actor.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, static_cast<int>( height ) );
258 layoutData.updateMeasuredSize = false;
261 void LayoutController::UpdateMeasureHierarchyForAnimation( Actor root, LayoutData& layoutData )
263 Toolkit::Control control = Toolkit::Control::DownCast( root );
266 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::UpdateMeasureHierarchyForAnimation control:%s\n", control.GetName().c_str() );
267 Internal::Control& controlImpl = GetImplementation( control );
268 Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
269 LayoutItemPtr layout = controlDataImpl.GetLayout();
273 auto layoutGroup = Toolkit::LayoutGroup::DownCast( layout.Get() );
276 unsigned int childCount = layoutGroup.GetChildCount();
277 for( unsigned int i=0; i<childCount; ++i )
279 auto childLayout = layoutGroup.GetChildAt( i );
282 auto childControl = Toolkit::Control::DownCast( childLayout.GetOwner() );
283 LayoutTransitionData::CollectChildrenLayoutDataElements( childControl, layoutData );
291 // Depth first descent through actor children
292 for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
294 UpdateMeasureHierarchyForAnimation( root.GetChildAt( i ), layoutData );
299 void LayoutController::RestoreActorsSpecs()
301 for( auto& actorSizeSpec : mActorSizeSpecs )
303 Actor actor = actorSizeSpec.actor.GetHandle();
306 actor.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, actorSizeSpec.widthSpec );
307 actor.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, actorSizeSpec.heightSpec );
312 void LayoutController::PerformLayout( Actor root, int left, int top, int right, int bottom )
314 Toolkit::Control control = Toolkit::Control::DownCast( root );
317 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout on control[%s]\n", control.GetName().c_str() );
318 Internal::Control& controlImpl = GetImplementation( control );
319 Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
320 LayoutItemPtr layout = controlDataImpl.GetLayout();
324 layout->Layout( left, top, right, bottom );
329 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout (%u) children\n", root.GetChildCount() );
330 // Depth first descent through actor children
331 for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
333 Actor child = root.GetChildAt( i );
334 PerformLayout( child, left, top, right, bottom );
339 void LayoutController::PerformLayoutPositioning( LayoutPositionDataArray& layoutPositionDataArray, bool all ) const
341 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutPositioning %d\n", (int)all );
343 for( auto layoutPositionData : layoutPositionDataArray )
345 Actor actor = layoutPositionData.handle.GetHandle();
346 if( actor && ( !layoutPositionData.animated || all ) )
348 if( !layoutPositionData.animated )
350 actor.SetPosition( layoutPositionData.left, layoutPositionData.top );
351 actor.SetSize( layoutPositionData.right - layoutPositionData.left, layoutPositionData.bottom - layoutPositionData.top );
355 actor.SetPosition( actor.GetCurrentPosition() );
356 actor.SetSize( actor.GetCurrentSize() );
362 void LayoutController::PerformLayoutAnimation( LayoutTransition& layoutTransition, LayoutPositionDataArray& layoutPositionDataArray, LayoutDataArray& layoutDataArray, LayoutAnimatorArray& layoutAnimatorArray )
364 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutAnimation\n" );
365 Animation animation = Animation::New( 0 );
366 bool isAnimatorAdded = false;
368 if( layoutAnimatorArray.size() == 0 )
370 layoutAnimatorArray.push_back( LayoutDataAnimator() );
373 for( auto layoutDataElement : layoutDataArray )
375 if( layoutDataElement.animatorIndex >= 0 )
377 Actor actor = layoutDataElement.handle.GetHandle();
380 LayoutDataAnimator animator = layoutAnimatorArray[ layoutDataElement.animatorIndex ];
381 TimePeriod timePeriod = TimePeriod( 0, animation.GetDuration() );
382 if( animator.timePeriod.durationSeconds >= 0 )
384 timePeriod = animator.timePeriod;
387 Property::Value value = layoutDataElement.targetValue;
388 // Capture calculated position and size values after layout if target values are not set.
389 // Other values are set to current actor ones.
390 if( value.GetType() == Property::NONE )
392 if( layoutDataElement.positionDataIndex < 0)
394 auto result = std::find_if( layoutPositionDataArray.begin(), layoutPositionDataArray.end(), [&actor](const LayoutPositionData& iter)
395 { return iter.handle == actor; } );
396 if( result == layoutPositionDataArray.end() )
400 layoutDataElement.positionDataIndex = std::distance( layoutPositionDataArray.begin(), result );
403 LayoutPositionData& positionData = layoutPositionDataArray[ layoutDataElement.positionDataIndex ];
404 // with updated measured size scale animation the measured size includes scale, so we need to fit in the centre of the measured rectangle
405 // the real size child so that the all scale related animations placed correctly
406 if( positionData.updateWithCurrentSize )
408 Vector3 size = actor.GetCurrentSize();
409 float dX = ( ( positionData.right - positionData.left ) - size.width ) / 2;
410 float dY = ( ( positionData.bottom - positionData.top ) - size.height ) / 2;
411 positionData.left += dX;
412 positionData.top += dY;
413 positionData.right -= dX;
414 positionData.bottom -= dY;
415 positionData.updateWithCurrentSize = false;
418 switch ( layoutDataElement.propertyIndex )
420 case Actor::Property::POSITION:
421 value = Vector3( positionData.left, positionData.top, 0.0f );
423 case Actor::Property::POSITION_X:
424 value = positionData.left;
426 case Actor::Property::POSITION_Y:
427 value = positionData.top;
429 case Actor::Property::SIZE:
430 value = Vector3( positionData.right - positionData.left, positionData.bottom - positionData.top, 0.0f );
432 case Actor::Property::SIZE_WIDTH:
433 value = positionData.right - positionData.left;
435 case Actor::Property::SIZE_HEIGHT:
436 value = positionData.bottom - positionData.top;
439 value = actor.GetProperty( layoutDataElement.propertyIndex );
443 // Failed to get target value, just move to the next one
444 if( value.GetType() == Property::NONE )
450 Property::Value initialValue = layoutDataElement.initialValue;
451 if( initialValue.GetType() != Property::NONE )
453 actor.SetProperty( layoutDataElement.propertyIndex, initialValue );
456 // Create an animator for the property
457 switch( animator.animatorType )
459 case Toolkit::LayoutTransitionData::Animator::ANIMATE_TO:
461 animation.AnimateTo( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
464 case Toolkit::LayoutTransitionData::Animator::ANIMATE_BY:
466 animation.AnimateBy( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
469 case Toolkit::LayoutTransitionData::Animator::ANIMATE_BETWEEN:
471 animation.AnimateBetween( Property( actor, layoutDataElement.propertyIndex ), animator.keyFrames, animator.alphaFunction, animator.interpolation );
474 case Toolkit::LayoutTransitionData::Animator::ANIMATE_PATH:
476 animation.Animate( actor, animator.path, animator.forward, animator.alphaFunction, timePeriod );
480 isAnimatorAdded = true;
485 if( isAnimatorAdded )
487 if( mAnimation.GetState() == Animation::PLAYING )
489 mAnimation.SetCurrentProgress( 1.0f );
491 mAnimation = animation;
492 mAnimationFinishedFunctors.push_back( AnimationFinishedFunctor( *this, layoutTransition, layoutPositionDataArray ) );
493 mAnimation.FinishedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mAnimationFinishedFunctors.back() );
498 } // namespace Internal
499 } // namespace Toolkit