746e8e5509ea85c10c6035efda078e8e07efaa77
[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)\n", actor.GetName().c_str(), &layoutItem, layoutTransitionType );
86   }
87   else
88   {
89     DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout layoutItem[%p] layoutAnimationType(%d)\n", &layoutItem, layoutTransitionType );
90   }
91
92   mLayoutRequested = true;
93   if( layoutTransitionType != -1 )
94   {
95     LayoutTransition layoutTransition = LayoutTransition( layoutItem, layoutTransitionType, gainedChild, lostChild );
96     if( std::find( mLayoutTransitions.begin(), mLayoutTransitions.end(), layoutTransition ) == mLayoutTransitions.end() && layoutItem.GetTransitionData( layoutTransitionType ).Get() )
97     {
98       DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::RequestLayout Add transition layoutTransitionType(%d)\n", layoutTransitionType );
99       mLayoutTransitions.push_back( layoutTransition );
100     }
101   }
102
103   // Go up the tree and mark all parents to relayout
104   LayoutParent* layoutParent = layoutItem.GetParent();
105   if( layoutParent )
106   {
107     LayoutGroup& layoutGroup = static_cast< LayoutGroup& >( *layoutParent );
108     if( ! layoutGroup.IsLayoutRequested() )
109     {
110       layoutGroup.RequestLayout();
111     }
112   }
113 }
114
115 void LayoutController::Process()
116 {
117   // Perform the full process.
118   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutController::Process\n" );
119
120   if( mLayoutRequested )
121   {
122     // If window size has changed, expect stage to have already been updated
123     Stage stage = Stage::GetCurrent();
124     auto stageWidth  = stage.GetSize().width;
125     auto stageHeight = stage.GetSize().height;
126
127     MeasureSpec widthSpec( stageWidth, MeasureSpec::Mode::EXACTLY );
128     MeasureSpec heightSpec( stageHeight, MeasureSpec::Mode::EXACTLY );
129
130     LayoutTransition layoutTransition;
131     LayoutPositionDataArray layoutPositionDataArray;
132     LayoutAnimatorArray layoutAnimatorArray;
133     LayoutDataArray layoutDataArray;
134     LayoutDataArray childrenLayoutDataArray;
135
136     if ( mLayoutTransitions.size() )
137     {
138       layoutTransition = mLayoutTransitions.front();
139       mLayoutTransitions.pop_front();
140       mLayoutRequested = ( mLayoutTransitions.size() != 0 );
141     }
142     else
143     {
144       mLayoutRequested = false;
145     }
146
147     LayoutData layoutData( layoutTransition, layoutPositionDataArray, layoutAnimatorArray, layoutDataArray, childrenLayoutDataArray );
148     LayoutItem::Impl::sLayoutData = &layoutData;
149
150     if( layoutTransition.layoutTransitionType != -1 )
151     {
152       UpdateMeasureHierarchyForAnimation( layoutData );
153     }
154
155     // Test how to perform a measure on each control.
156     MeasureHierarchy( stage.GetRootLayer(), widthSpec, heightSpec );
157
158     LAYOUT_DEBUG_MEASURE_STATES( stage.GetRootLayer() );
159
160     if( layoutTransition.layoutTransitionType != -1 )
161     {
162       RestoreActorsSpecs();
163     }
164
165     layoutAnimatorArray.clear();
166     layoutDataArray.clear();
167     childrenLayoutDataArray.clear();
168
169     PerformLayout( stage.GetRootLayer(), 0, 0, stageWidth, stageHeight );
170
171     PerformLayoutPositioning( layoutPositionDataArray, false );
172
173     PerformLayoutAnimation( layoutTransition, layoutPositionDataArray, layoutDataArray, layoutAnimatorArray );
174     LayoutItem::Impl::sLayoutData = nullptr;
175
176     LAYOUT_DEBUG_AFTER_LAYOUT( stage.GetRootLayer() );
177   }
178 }
179
180 void LayoutController::MeasureHierarchy( Actor root, MeasureSpec widthSpec, MeasureSpec heightSpec )
181 {
182   // Does this actor have a layout?
183   // Yes - measure the layout. It will call this method again for each of it's children.
184   // No - recurse through actor children.
185   //
186   // If in a leaf actor with no layout, it's natural size is bubbled back up.
187   //
188   // What happens if nothing in the tree has a layout?
189
190   Toolkit::Control control = Toolkit::Control::DownCast( root );
191   if( control )
192   {
193     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring control:%s\n", control.GetName().c_str() );
194     Internal::Control& controlImpl = GetImplementation( control );
195
196     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
197     LayoutItemPtr layout = controlDataImpl.GetLayout();
198
199     if( layout )
200     {
201       layout->Measure( widthSpec, heightSpec );
202     }
203   }
204   else
205   {
206     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring (%u) children\n", root.GetChildCount() );
207     // Depth first descent through actor children
208     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
209     {
210       Actor child = root.GetChildAt( i );
211       MeasureHierarchy( child, widthSpec, heightSpec );
212     }
213   }
214 }
215
216 void LayoutController::UpdateMeasureHierarchyForAnimation( LayoutData& layoutData )
217 {
218   LayoutTransition& layoutTransition = layoutData.layoutTransition;
219   Actor transitionOwner = Actor::DownCast( layoutTransition.layoutItem.Get()->GetOwner() );
220   LayoutTransitionDataPtr layoutTransitionDataPtr = layoutTransition.layoutItem->GetTransitionData( layoutTransition.layoutTransitionType );
221
222   if( !layoutTransitionDataPtr->HasUpdateMeasuredSize() )
223   {
224     return;
225   }
226
227   layoutData.updateMeasuredSize = true;
228   layoutTransitionDataPtr->CollectLayoutDataElements( transitionOwner, layoutData );
229
230   UpdateMeasureHierarchyForAnimation( transitionOwner, layoutData );
231
232   for( auto layoutDataElement : layoutData.layoutDataArray )
233   {
234     if( !layoutDataElement.updateMeasuredSize )
235     {
236       continue;
237     }
238
239     Actor actor = Actor::DownCast( layoutDataElement.handle );
240     LayoutDataAnimator animator = layoutData.layoutAnimatorArray[ layoutDataElement.animatorIndex ];
241     float width = actor.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
242     float height = actor.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
243
244     if( layoutDataElement.AdjustMeasuredSize( width, height, animator.animatorType ) )
245     {
246       mActorSizeSpecs.push_back( ActorSizeSpec( actor ) );
247       actor.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, static_cast<int>( width ) );
248       actor.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, static_cast<int>( height ) );
249     }
250   }
251
252   layoutData.updateMeasuredSize = false;
253 }
254
255 void LayoutController::UpdateMeasureHierarchyForAnimation( Actor root, LayoutData& layoutData )
256 {
257   Toolkit::Control control = Toolkit::Control::DownCast( root );
258   if( control )
259   {
260     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::UpdateMeasureHierarchyForAnimation control:%s\n", control.GetName().c_str() );
261     Internal::Control& controlImpl = GetImplementation( control );
262     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
263     LayoutItemPtr layout = controlDataImpl.GetLayout();
264
265     if( layout )
266     {
267       auto layoutGroup = Toolkit::LayoutGroup::DownCast( layout.Get() );
268       if( layoutGroup )
269       {
270         unsigned int childCount = layoutGroup.GetChildCount();
271         for( unsigned int i=0; i<childCount; ++i )
272         {
273           auto childLayout = layoutGroup.GetChildAt( i );
274           if( childLayout )
275           {
276             auto childControl = Toolkit::Control::DownCast( childLayout.GetOwner() );
277             LayoutTransitionData::CollectChildrenLayoutDataElements( childControl, layoutData );
278           }
279         }
280       }
281     }
282   }
283   else
284   {
285     // Depth first descent through actor children
286     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
287     {
288       UpdateMeasureHierarchyForAnimation( root.GetChildAt( i ), layoutData );
289     }
290   }
291 }
292
293 void LayoutController::RestoreActorsSpecs()
294 {
295   for( auto& actorSizeSpec : mActorSizeSpecs )
296   {
297     Actor actor = actorSizeSpec.actor;
298     actor.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, actorSizeSpec.widthSpec );
299     actor.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, actorSizeSpec.heightSpec );
300   }
301 }
302
303 void LayoutController::PerformLayout( Actor root, int left, int top, int right, int bottom )
304 {
305   Toolkit::Control control = Toolkit::Control::DownCast( root );
306   if( control )
307   {
308     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout on control[%s]\n", control.GetName().c_str() );
309     Internal::Control& controlImpl = GetImplementation( control );
310     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
311     LayoutItemPtr layout = controlDataImpl.GetLayout();
312
313     if( layout )
314     {
315       layout->Layout( left, top, right, bottom );
316     }
317   }
318   else
319   {
320     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout (%u) children\n", root.GetChildCount() );
321     // Depth first descent through actor children
322     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
323     {
324       Actor child = root.GetChildAt( i );
325       PerformLayout( child, left, top, right, bottom );
326     }
327   }
328 }
329
330 void LayoutController::PerformLayoutPositioning( LayoutPositionDataArray& layoutPositionDataArray, bool all ) const
331 {
332   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutPositioning %d\n", (int)all );
333
334   for( auto layoutPositionData : layoutPositionDataArray )
335   {
336     Actor actor = Actor::DownCast( layoutPositionData.handle );
337     if( actor && ( !layoutPositionData.animated || all ) )
338     {
339       if ( !layoutPositionData.animated )
340       {
341         actor.SetPosition( layoutPositionData.left, layoutPositionData.top );
342         actor.SetSize( layoutPositionData.right - layoutPositionData.left, layoutPositionData.bottom - layoutPositionData.top );
343       }
344       else
345       {
346         actor.SetPosition( actor.GetCurrentPosition() );
347         actor.SetSize( actor.GetCurrentSize() );
348       }
349     }
350   }
351 }
352
353 void LayoutController::PerformLayoutAnimation( LayoutTransition& layoutTransition, LayoutPositionDataArray& layoutPositionDataArray, LayoutDataArray& layoutDataArray, LayoutAnimatorArray& layoutAnimatorArray )
354 {
355   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutAnimation\n" );
356   Animation animation = Animation::New( 0 );
357   bool isAnimatorAdded = false;
358
359   if( layoutAnimatorArray.size() == 0 )
360   {
361     layoutAnimatorArray.push_back( LayoutDataAnimator() );
362   }
363
364   for( auto layoutDataElement : layoutDataArray )
365   {
366     if( layoutDataElement.animatorIndex >= 0 )
367     {
368       Actor actor = Actor::DownCast( layoutDataElement.handle );
369       if ( actor )
370       {
371         LayoutDataAnimator animator = layoutAnimatorArray[ layoutDataElement.animatorIndex ];
372         TimePeriod timePeriod = TimePeriod( 0, animation.GetDuration() );
373         if( animator.timePeriod.durationSeconds >= 0 )
374         {
375           timePeriod = animator.timePeriod;
376         }
377
378         Property::Value value = layoutDataElement.targetValue;
379         // Capture calculated position and size values after layout if target values are not set.
380         // Other values are set to current actor ones.
381         if( value.GetType() == Property::NONE )
382         {
383           if( layoutDataElement.positionDataIndex < 0)
384           {
385             auto result = std::find_if( layoutPositionDataArray.begin(), layoutPositionDataArray.end(), [&actor](const LayoutPositionData& iter)
386                           { return iter.handle == actor; } );
387             if( result == layoutPositionDataArray.end() )
388             {
389               continue;
390             }
391             layoutDataElement.positionDataIndex = std::distance( layoutPositionDataArray.begin(), result );
392           }
393
394           LayoutPositionData& positionData = layoutPositionDataArray[ layoutDataElement.positionDataIndex ];
395           // with updated measured size scale animation the measured size includes scale, so we need to fit in the centre of the measured rectangle
396           // the real size child so that the all scale related animations placed correctly
397           if( positionData.updateWithCurrentSize )
398           {
399             Vector3 size = actor.GetCurrentSize();
400             float dX = ( ( positionData.right - positionData.left ) - size.width ) / 2;
401             float dY = ( ( positionData.bottom - positionData.top ) - size.height ) / 2;
402             positionData.left += dX;
403             positionData.top += dY;
404             positionData.right -= dX;
405             positionData.bottom -= dY;
406             positionData.updateWithCurrentSize = false;
407           }
408
409           switch ( layoutDataElement.propertyIndex )
410           {
411           case Actor::Property::POSITION:
412             value = Vector3( positionData.left, positionData.top, 0.0f );
413             break;
414           case Actor::Property::POSITION_X:
415             value = positionData.left;
416             break;
417           case Actor::Property::POSITION_Y:
418             value = positionData.top;
419             break;
420           case Actor::Property::SIZE:
421             value = Vector3( positionData.right - positionData.left, positionData.bottom - positionData.top, 0.0f );
422             break;
423           case Actor::Property::SIZE_WIDTH:
424             value = positionData.right - positionData.left;
425             break;
426           case Actor::Property::SIZE_HEIGHT:
427             value = positionData.bottom - positionData.top;
428             break;
429           default:
430             value = actor.GetProperty( layoutDataElement.propertyIndex );
431           }
432         }
433
434         // Failed to get target value, just move to the next one
435         if( value.GetType() == Property::NONE )
436         {
437           continue;
438         }
439
440         // Set initial value
441         Property::Value initialValue = layoutDataElement.initialValue;
442         if( initialValue.GetType() != Property::NONE )
443         {
444           actor.SetProperty( layoutDataElement.propertyIndex, initialValue );
445         }
446
447         // Create an animator for the property
448         switch( animator.animatorType )
449         {
450           case Toolkit::LayoutTransitionData::Animator::ANIMATE_TO:
451           {
452             animation.AnimateTo( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
453             break;
454           }
455           case Toolkit::LayoutTransitionData::Animator::ANIMATE_BY:
456           {
457             animation.AnimateBy( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
458             break;
459           }
460           case Toolkit::LayoutTransitionData::Animator::ANIMATE_BETWEEN:
461           {
462             animation.AnimateBetween( Property( actor, layoutDataElement.propertyIndex ), animator.keyFrames, animator.alphaFunction, animator.interpolation );
463             break;
464           }
465           case Toolkit::LayoutTransitionData::Animator::ANIMATE_PATH:
466           {
467             animation.Animate( actor, animator.path, animator.forward, animator.alphaFunction, timePeriod );
468             break;
469           }
470         }
471         isAnimatorAdded = true;
472       }
473     }
474   }
475
476   if( isAnimatorAdded )
477   {
478     if( mAnimation.GetState() == Animation::PLAYING )
479     {
480       mAnimation.SetCurrentProgress( 1.0f );
481     }
482     mAnimation = animation;
483     mAnimationFinishedFunctors.push_back( AnimationFinishedFunctor( *this, layoutTransition, layoutPositionDataArray ) );
484     mAnimation.FinishedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mAnimationFinishedFunctors.back() );
485     mAnimation.Play();
486   }
487 }
488
489 } // namespace Internal
490 } // namespace Toolkit
491 } // namespace Dali