Add focus transition and other fixes.
[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     // Test how to perform a measure on each control.
131     MeasureHierarchy( stage.GetRootLayer(), widthSpec, heightSpec );
132
133     LAYOUT_DEBUG_MEASURE_STATES( stage.GetRootLayer() );
134
135     LayoutTransition layoutTransition;
136     LayoutPositionDataArray layoutPositionDataArray;
137     LayoutDataArray layoutDataArray;
138     LayoutAnimatorArray layoutAnimatorArray;
139     layoutAnimatorArray.push_back( LayoutDataAnimator() );
140     PropertyAnimatorArray childrenPropertiesAnimators;
141
142     if ( mLayoutTransitions.size() )
143     {
144       layoutTransition = mLayoutTransitions.front();
145       mLayoutTransitions.pop_front();
146       mLayoutRequested = ( mLayoutTransitions.size() != 0 );
147     }
148     else
149     {
150       mLayoutRequested = false;
151     }
152
153     LayoutData layoutData( layoutTransition, layoutPositionDataArray, layoutDataArray, layoutAnimatorArray, childrenPropertiesAnimators );
154     LayoutItem::Impl::sLayoutData = &layoutData;
155     PerformLayout( stage.GetRootLayer(), 0, 0, stageWidth, stageHeight );
156
157     PerformLayoutPositioning( layoutPositionDataArray, false );
158
159     PerformLayoutAnimation( layoutTransition, layoutPositionDataArray, layoutDataArray, layoutAnimatorArray );
160     LayoutItem::Impl::sLayoutData = nullptr;
161
162     LAYOUT_DEBUG_AFTER_LAYOUT( stage.GetRootLayer() );
163   }
164 }
165
166 void LayoutController::MeasureHierarchy( Actor root, MeasureSpec widthSpec, MeasureSpec heightSpec )
167 {
168   // Does this actor have a layout?
169   // Yes - measure the layout. It will call this method again for each of it's children.
170   // No - recurse through actor children.
171   //
172   // If in a leaf actor with no layout, it's natural size is bubbled back up.
173   //
174   // What happens if nothing in the tree has a layout?
175
176   Toolkit::Control control = Toolkit::Control::DownCast( root );
177   if( control )
178   {
179     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring control:%s\n", control.GetName().c_str() );
180     Internal::Control& controlImpl = GetImplementation( control );
181
182     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
183     LayoutItemPtr layout = controlDataImpl.GetLayout();
184
185     if( layout )
186     {
187       layout->Measure( widthSpec, heightSpec );
188     }
189   }
190   else
191   {
192     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::Measuring (%u) children\n", root.GetChildCount() );
193     // Depth first descent through actor children
194     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
195     {
196       Actor child = root.GetChildAt( i );
197       MeasureHierarchy( child, widthSpec, heightSpec );
198     }
199   }
200 }
201
202 void LayoutController::PerformLayout( Actor root, int left, int top, int right, int bottom )
203 {
204   Toolkit::Control control = Toolkit::Control::DownCast( root );
205   if( control )
206   {
207     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout on control[%s]\n", control.GetName().c_str() );
208     Internal::Control& controlImpl = GetImplementation( control );
209     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( controlImpl );
210     LayoutItemPtr layout = controlDataImpl.GetLayout();
211
212     if( layout )
213     {
214       layout->Layout( left, top, right, bottom );
215     }
216   }
217   else
218   {
219     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayout (%u) children\n", root.GetChildCount() );
220     // Depth first descent through actor children
221     for( unsigned int i = 0, count = root.GetChildCount(); i < count; ++i )
222     {
223       Actor child = root.GetChildAt( i );
224       PerformLayout( child, left, top, right, bottom );
225     }
226   }
227 }
228
229 void LayoutController::PerformLayoutPositioning( LayoutPositionDataArray& layoutPositionDataArray, bool all ) const
230 {
231   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutPositioning %d\n", (int)all );
232
233   for( auto layoutPositionData : layoutPositionDataArray )
234   {
235     Actor actor = Actor::DownCast( layoutPositionData.handle );
236     if( actor && ( !layoutPositionData.animated || all ) )
237     {
238       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutPositioning %s\n", actor.GetName().c_str() );
239       if ( !layoutPositionData.animated )
240       {
241         actor.SetPosition( layoutPositionData.left, layoutPositionData.top );
242         actor.SetSize( layoutPositionData.right - layoutPositionData.left, layoutPositionData.bottom - layoutPositionData.top );
243       }
244       else
245       {
246         actor.SetPosition( actor.GetCurrentPosition() );
247         actor.SetSize( actor.GetCurrentSize() );
248       }
249     }
250   }
251 }
252
253 void LayoutController::PerformLayoutAnimation( LayoutTransition& layoutTransition, LayoutPositionDataArray& layoutPositionDataArray, LayoutDataArray& layoutDataArray, LayoutAnimatorArray& layoutAnimatorArray )
254 {
255   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutController::PerformLayoutAnimation\n" );
256   Animation animation = Animation::New( 0 );
257   bool isAnimatorAdded = false;
258
259   for( auto layoutDataElement : layoutDataArray )
260   {
261     if ( layoutDataElement.animatorIndex >= 0 )
262     {
263       Actor actor = Actor::DownCast( layoutDataElement.handle );
264       if ( actor )
265       {
266         LayoutDataAnimator animator = layoutAnimatorArray[ layoutDataElement.animatorIndex ];
267         TimePeriod timePeriod = TimePeriod( 0, animation.GetDuration() );
268         if (animator.timePeriod.durationSeconds >= 0)
269         {
270           timePeriod = animator.timePeriod;
271         }
272
273         Property::Value value = layoutDataElement.targetValue;
274         // Capture calculated position and size values after layout if target values are not set.
275         // Other values are set to current actor ones.
276         if( value.GetType() == Property::NONE )
277         {
278           if ( layoutDataElement.positionDataIndex < 0)
279           {
280             auto result = std::find_if( layoutPositionDataArray.begin(), layoutPositionDataArray.end(), [&actor](const LayoutPositionData& iter)
281                           { return iter.handle == actor; } );
282             if( result == layoutPositionDataArray.end() )
283             {
284               continue;
285             }
286             layoutDataElement.positionDataIndex = std::distance(layoutPositionDataArray.begin(), result);
287           }
288
289           LayoutPositionData& positionData = layoutPositionDataArray[ layoutDataElement.positionDataIndex ];
290
291           switch ( layoutDataElement.propertyIndex )
292           {
293           case Actor::Property::POSITION:
294             value = Vector3( positionData.left, positionData.top, 0.0f );
295             break;
296           case Actor::Property::POSITION_X:
297             value = positionData.left;
298             break;
299           case Actor::Property::POSITION_Y:
300             value = positionData.top;
301             break;
302           case Actor::Property::SIZE:
303             value = Vector3( positionData.right - positionData.left, positionData.bottom - positionData.top, 0.0f );
304             break;
305           case Actor::Property::SIZE_WIDTH:
306             value = positionData.right - positionData.left;
307             break;
308           case Actor::Property::SIZE_HEIGHT:
309             value = positionData.bottom - positionData.top;
310             break;
311           default:
312             value = actor.GetProperty( layoutDataElement.propertyIndex );
313           }
314         }
315
316         // Failed to get target value, just move the next one
317         if( value.GetType() == Property::NONE )
318         {
319           continue;
320         }
321
322         // Set initial value
323         Property::Value initialValue = layoutDataElement.initialValue;
324         if( initialValue.GetType() != Property::NONE )
325         {
326           actor.SetProperty( layoutDataElement.propertyIndex, initialValue );
327         }
328
329         // Create an animator for the property
330         switch (animator.animatorType)
331         {
332         case LayoutDataAnimator::AnimatorType::ANIMATE_TO:
333         {
334           animation.AnimateTo( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
335           break;
336         }
337         case LayoutDataAnimator::AnimatorType::ANIMATE_BY:
338         {
339           animation.AnimateBy( Property( actor, layoutDataElement.propertyIndex ), value, animator.alphaFunction, timePeriod );
340           break;
341         }
342         case LayoutDataAnimator::AnimatorType::ANIMATE_BETWEEN:
343         {
344           animation.AnimateBetween( Property( actor, layoutDataElement.propertyIndex ), animator.keyFrames, animator.alphaFunction, animator.interpolation );
345           break;
346         }
347         case LayoutDataAnimator::AnimatorType::ANIMATE_PATH:
348           animation.Animate( actor, animator.path, animator.forward, animator.alphaFunction, timePeriod );
349           break;
350         }
351       }
352       isAnimatorAdded = true;
353     }
354   }
355
356   if( isAnimatorAdded )
357   {
358     if( mAnimation.GetState() == Animation::PLAYING )
359     {
360       mAnimation.SetCurrentProgress( 1.0f );
361     }
362     mAnimation = animation;
363     mAnimationFinishedFunctors.push_back( AnimationFinishedFunctor( *this, layoutTransition, layoutPositionDataArray ) );
364     mAnimation.FinishedSignal().Connect( mSlotDelegate.GetConnectionTracker(), mAnimationFinishedFunctors.back() );
365     mAnimation.Play();
366   }
367 }
368
369 } // namespace Internal
370 } // namespace Toolkit
371 } // namespace Dali