Merge "Fix resource leaks in layouting." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / layout-controller-impl.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
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>
27
28 using namespace Dali;
29
30 namespace
31 {
32
33 #if defined(DEBUG_ENABLED)
34 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
35 #endif
36
37 }
38
39 namespace Dali
40 {
41 namespace Toolkit
42 {
43 namespace Internal
44 {
45
46 LayoutController::LayoutController()
47 : mLayoutRequested( false ),
48   mFocusChangedFunctor( *this ),
49   mSlotDelegate( this )
50 {
51 }
52
53 LayoutController::~LayoutController()
54 {
55 }
56
57 void LayoutController::Initialize()
58 {
59   mAnimation = Animation::New( 0.0f );
60
61   Dali::Toolkit::KeyInputFocusManager manager = Dali::Toolkit::KeyInputFocusManager::Get();
62   manager.KeyInputFocusChangedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mFocusChangedFunctor );
63 }
64
65 void LayoutController::FocusChangedFunctor::operator() ( Dali::Toolkit::Control gainingControl, Dali::Toolkit::Control lostControl )
66 {
67   Toolkit::LayoutItem layoutItem = Toolkit::DevelControl::GetLayout( gainingControl );
68   if( layoutItem )
69   {
70     Toolkit::Internal::LayoutItem& layoutItemImpl = GetImplementation( layoutItem );
71     LayoutParent* layoutParent = layoutItemImpl.GetParent();
72     if( layoutParent )
73     {
74       LayoutGroup* layoutGroup = static_cast< LayoutGroup* >( layoutParent );
75       layoutController.RequestLayout( dynamic_cast< Toolkit::Internal::LayoutItem& >( *layoutGroup ), Dali::Toolkit::LayoutTransitionData::ON_CHILD_FOCUS, gainingControl, lostControl );
76     }
77   }
78 }
79
80 void LayoutController::RequestLayout( LayoutItem& layoutItem, int layoutTransitionType, Actor gainedChild, Actor lostChild )
81 {
82   auto actor = Actor::DownCast( layoutItem.GetOwner() );
83   if ( actor )
84   {
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() : "");
89   }
90   else
91   {
92     DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout layoutItem[%p] layoutAnimationType(%d)\n", &layoutItem, layoutTransitionType );
93   }
94
95   mLayoutRequested = true;
96   if( layoutTransitionType != -1 )
97   {
98     LayoutTransition layoutTransition = LayoutTransition( layoutItem, layoutTransitionType, gainedChild, lostChild );
99     if( std::find( mLayoutTransitions.begin(), mLayoutTransitions.end(), layoutTransition ) == mLayoutTransitions.end() && layoutItem.GetTransitionData( layoutTransitionType ).Get() )
100     {
101       DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout Add transition layoutTransitionType(%d)\n", layoutTransitionType );
102       mLayoutTransitions.push_back( layoutTransition );
103     }
104   }
105
106   // Go up the tree and mark all parents to relayout
107   LayoutParent* layoutParent = layoutItem.GetParent();
108   if( layoutParent )
109   {
110     LayoutGroup& layoutGroup = static_cast< LayoutGroup& >( *layoutParent );
111     if( ! layoutGroup.IsLayoutRequested() )
112     {
113       layoutGroup.RequestLayout();
114     }
115   }
116 }
117
118 void LayoutController::Process()
119 {
120   // Perform the full process.
121   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::Process\n" );
122
123   if( mLayoutRequested )
124   {
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;
129
130     MeasureSpec widthSpec( stageWidth, MeasureSpec::Mode::EXACTLY );
131     MeasureSpec heightSpec( stageHeight, MeasureSpec::Mode::EXACTLY );
132
133     LayoutTransition layoutTransition;
134     LayoutPositionDataArray layoutPositionDataArray;
135     LayoutAnimatorArray layoutAnimatorArray;
136     LayoutDataArray layoutDataArray;
137     LayoutDataArray childrenLayoutDataArray;
138
139     if ( mLayoutTransitions.size() )
140     {
141       layoutTransition = mLayoutTransitions.front();
142       mLayoutTransitions.pop_front();
143       mLayoutRequested = ( mLayoutTransitions.size() != 0 );
144     }
145     else
146     {
147       mLayoutRequested = false;
148     }
149
150     LayoutData layoutData( layoutTransition, layoutPositionDataArray, layoutAnimatorArray, layoutDataArray, childrenLayoutDataArray );
151     LayoutItem::Impl::sLayoutData = &layoutData;
152
153     if( layoutTransition.layoutTransitionType != -1 )
154     {
155       UpdateMeasureHierarchyForAnimation( layoutData );
156     }
157
158     // Test how to perform a measure on each control.
159     MeasureHierarchy( stage.GetRootLayer(), widthSpec, heightSpec );
160
161     LAYOUT_DEBUG_MEASURE_STATES( stage.GetRootLayer() );
162
163     if( layoutTransition.layoutTransitionType != -1 )
164     {
165       RestoreActorsSpecs();
166     }
167
168     layoutAnimatorArray.clear();
169     layoutDataArray.clear();
170     childrenLayoutDataArray.clear();
171
172     PerformLayout( stage.GetRootLayer(), 0, 0, stageWidth, stageHeight );
173
174     PerformLayoutPositioning( layoutPositionDataArray, false );
175
176     PerformLayoutAnimation( layoutTransition, layoutPositionDataArray, layoutDataArray, layoutAnimatorArray );
177     LayoutItem::Impl::sLayoutData = nullptr;
178
179     LAYOUT_DEBUG_AFTER_LAYOUT( stage.GetRootLayer() );
180   }
181 }
182
183 void LayoutController::MeasureHierarchy( Actor root, MeasureSpec widthSpec, MeasureSpec heightSpec )
184 {
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.
188   //
189   // If in a leaf actor with no layout, it's natural size is bubbled back up.
190   //
191   // What happens if nothing in the tree has a layout?
192
193   Toolkit::Control control = Toolkit::Control::DownCast( root );
194   if( control )
195   {
196     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring control:%s\n", control.GetName().c_str() );
197     Internal::Control& controlImpl = GetImplementation( control );
198
199     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
200     LayoutItemPtr layout = controlDataImpl.GetLayout();
201
202     if( layout )
203     {
204       layout->Measure( widthSpec, heightSpec );
205     }
206   }
207   else
208   {
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 )
212     {
213       Actor child = root.GetChildAt( i );
214       MeasureHierarchy( child, widthSpec, heightSpec );
215     }
216   }
217 }
218
219 void LayoutController::UpdateMeasureHierarchyForAnimation( LayoutData& layoutData )
220 {
221   LayoutTransition& layoutTransition = layoutData.layoutTransition;
222   Actor transitionOwner = Actor::DownCast( layoutTransition.layoutItem.Get()->GetOwner() );
223   LayoutTransitionDataPtr layoutTransitionDataPtr = layoutTransition.layoutItem->GetTransitionData( layoutTransition.layoutTransitionType );
224
225   if( !layoutTransitionDataPtr->HasUpdateMeasuredSize() )
226   {
227     return;
228   }
229
230   layoutData.updateMeasuredSize = true;
231   layoutTransitionDataPtr->CollectLayoutDataElements( transitionOwner, layoutData );
232
233   UpdateMeasureHierarchyForAnimation( transitionOwner, layoutData );
234
235   for( auto layoutDataElement : layoutData.layoutDataArray )
236   {
237     if( !layoutDataElement.updateMeasuredSize )
238     {
239       continue;
240     }
241
242     Actor actor = layoutDataElement.handle.GetHandle();
243     if( actor )
244     {
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 );
248
249       if( layoutDataElement.AdjustMeasuredSize( width, height, animator.animatorType ) )
250       {
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 ) );
254       }
255     }
256   }
257
258   layoutData.updateMeasuredSize = false;
259 }
260
261 void LayoutController::UpdateMeasureHierarchyForAnimation( Actor root, LayoutData& layoutData )
262 {
263   Toolkit::Control control = Toolkit::Control::DownCast( root );
264   if( control )
265   {
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();
270
271     if( layout )
272     {
273       auto layoutGroup = Toolkit::LayoutGroup::DownCast( layout.Get() );
274       if( layoutGroup )
275       {
276         unsigned int childCount = layoutGroup.GetChildCount();
277         for( unsigned int i=0; i<childCount; ++i )
278         {
279           auto childLayout = layoutGroup.GetChildAt( i );
280           if( childLayout )
281           {
282             auto childControl = Toolkit::Control::DownCast( childLayout.GetOwner() );
283             LayoutTransitionData::CollectChildrenLayoutDataElements( childControl, layoutData );
284           }
285         }
286       }
287     }
288   }
289   else
290   {
291     // Depth first descent through actor children
292     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
293     {
294       UpdateMeasureHierarchyForAnimation( root.GetChildAt( i ), layoutData );
295     }
296   }
297 }
298
299 void LayoutController::RestoreActorsSpecs()
300 {
301   for( auto& actorSizeSpec : mActorSizeSpecs )
302   {
303     Actor actor = actorSizeSpec.actor.GetHandle();
304     if( actor )
305     {
306       actor.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, actorSizeSpec.widthSpec );
307       actor.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, actorSizeSpec.heightSpec );
308     }
309   }
310 }
311
312 void LayoutController::PerformLayout( Actor root, int left, int top, int right, int bottom )
313 {
314   Toolkit::Control control = Toolkit::Control::DownCast( root );
315   if( control )
316   {
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();
321
322     if( layout )
323     {
324       layout->Layout( left, top, right, bottom );
325     }
326   }
327   else
328   {
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 )
332     {
333       Actor child = root.GetChildAt( i );
334       PerformLayout( child, left, top, right, bottom );
335     }
336   }
337 }
338
339 void LayoutController::PerformLayoutPositioning( LayoutPositionDataArray& layoutPositionDataArray, bool all ) const
340 {
341   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutPositioning %d\n", (int)all );
342
343   for( auto layoutPositionData : layoutPositionDataArray )
344   {
345     Actor actor = layoutPositionData.handle.GetHandle();
346     if( actor && ( !layoutPositionData.animated || all ) )
347     {
348       if( !layoutPositionData.animated )
349       {
350         actor.SetPosition( layoutPositionData.left, layoutPositionData.top );
351         actor.SetSize( layoutPositionData.right - layoutPositionData.left, layoutPositionData.bottom - layoutPositionData.top );
352       }
353       else
354       {
355         actor.SetPosition( actor.GetCurrentPosition() );
356         actor.SetSize( actor.GetCurrentSize() );
357       }
358     }
359   }
360 }
361
362 void LayoutController::PerformLayoutAnimation( LayoutTransition& layoutTransition, LayoutPositionDataArray& layoutPositionDataArray, LayoutDataArray& layoutDataArray, LayoutAnimatorArray& layoutAnimatorArray )
363 {
364   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutAnimation\n" );
365   Animation animation = Animation::New( 0 );
366   bool isAnimatorAdded = false;
367
368   if( layoutAnimatorArray.size() == 0 )
369   {
370     layoutAnimatorArray.push_back( LayoutDataAnimator() );
371   }
372
373   for( auto layoutDataElement : layoutDataArray )
374   {
375     if( layoutDataElement.animatorIndex >= 0 )
376     {
377       Actor actor = layoutDataElement.handle.GetHandle();
378       if ( actor )
379       {
380         LayoutDataAnimator animator = layoutAnimatorArray[ layoutDataElement.animatorIndex ];
381         TimePeriod timePeriod = TimePeriod( 0, animation.GetDuration() );
382         if( animator.timePeriod.durationSeconds >= 0 )
383         {
384           timePeriod = animator.timePeriod;
385         }
386
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 )
391         {
392           if( layoutDataElement.positionDataIndex < 0)
393           {
394             auto result = std::find_if( layoutPositionDataArray.begin(), layoutPositionDataArray.end(), [&actor](const LayoutPositionData& iter)
395                           { return iter.handle == actor; } );
396             if( result == layoutPositionDataArray.end() )
397             {
398               continue;
399             }
400             layoutDataElement.positionDataIndex = std::distance( layoutPositionDataArray.begin(), result );
401           }
402
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 )
407           {
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;
416           }
417
418           switch ( layoutDataElement.propertyIndex )
419           {
420           case Actor::Property::POSITION:
421             value = Vector3( positionData.left, positionData.top, 0.0f );
422             break;
423           case Actor::Property::POSITION_X:
424             value = positionData.left;
425             break;
426           case Actor::Property::POSITION_Y:
427             value = positionData.top;
428             break;
429           case Actor::Property::SIZE:
430             value = Vector3( positionData.right - positionData.left, positionData.bottom - positionData.top, 0.0f );
431             break;
432           case Actor::Property::SIZE_WIDTH:
433             value = positionData.right - positionData.left;
434             break;
435           case Actor::Property::SIZE_HEIGHT:
436             value = positionData.bottom - positionData.top;
437             break;
438           default:
439             value = actor.GetProperty( layoutDataElement.propertyIndex );
440           }
441         }
442
443         // Failed to get target value, just move to the next one
444         if( value.GetType() == Property::NONE )
445         {
446           continue;
447         }
448
449         // Set initial value
450         Property::Value initialValue = layoutDataElement.initialValue;
451         if( initialValue.GetType() != Property::NONE )
452         {
453           actor.SetProperty( layoutDataElement.propertyIndex, initialValue );
454         }
455
456         // Create an animator for the property
457         switch( animator.animatorType )
458         {
459           case Toolkit::LayoutTransitionData::Animator::ANIMATE_TO:
460           {
461             animation.AnimateTo( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
462             break;
463           }
464           case Toolkit::LayoutTransitionData::Animator::ANIMATE_BY:
465           {
466             animation.AnimateBy( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
467             break;
468           }
469           case Toolkit::LayoutTransitionData::Animator::ANIMATE_BETWEEN:
470           {
471             animation.AnimateBetween( Property( actor, layoutDataElement.propertyIndex ), animator.keyFrames, animator.alphaFunction, animator.interpolation );
472             break;
473           }
474           case Toolkit::LayoutTransitionData::Animator::ANIMATE_PATH:
475           {
476             animation.Animate( actor, animator.path, animator.forward, animator.alphaFunction, timePeriod );
477             break;
478           }
479         }
480         isAnimatorAdded = true;
481       }
482     }
483   }
484
485   if( isAnimatorAdded )
486   {
487     if( mAnimation.GetState() == Animation::PLAYING )
488     {
489       mAnimation.SetCurrentProgress( 1.0f );
490     }
491     mAnimation = animation;
492     mAnimationFinishedFunctors.push_back( AnimationFinishedFunctor( *this, layoutTransition, layoutPositionDataArray ) );
493     mAnimation.FinishedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mAnimationFinishedFunctors.back() );
494     mAnimation.Play();
495   }
496 }
497
498 } // namespace Internal
499 } // namespace Toolkit
500 } // namespace Dali