Merge "TextSelectionPopup to show Paste in data in Clipboard" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / builder-animations.cpp
1 /*
2  * Copyright (c) 2014 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
18 // EXTERNAL INCLUDES
19 #include <dali/public-api/actors/layer.h>
20 #include <dali/public-api/actors/renderable-actor.h>
21 #include <dali/integration-api/debug.h>
22
23 // INTERNAL INCLUDES
24 #include <dali-toolkit/internal/builder/builder-impl.h>
25 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
26 #include <dali-toolkit/internal/builder/replacement.h>
27
28 namespace // unnamed namespace
29 {
30
31 using namespace Dali;
32
33 TimePeriod GetTimePeriod( const TreeNode& child, const Toolkit::Internal::Replacement& constant )
34 {
35   OptionalFloat delay      = constant.IsFloat( IsChild(child, "delay" ) );
36   OptionalFloat duration   = constant.IsFloat( IsChild(child, "duration" ) );
37   DALI_ASSERT_ALWAYS( duration && "Time period must have at least a duration" );
38
39   if( delay )
40   {
41     return TimePeriod( *delay, *duration );
42   }
43   else
44   {
45     return TimePeriod( *duration );
46   }
47 }
48
49 Property::Value GetPropertyValue( const Property::Type& propType, const TreeNode& child )
50 {
51   switch ( propType )
52   {
53     case Property::BOOLEAN:
54     {
55       return Property::Value( GetBoolean( child ) );
56     }
57
58     case Property::FLOAT:
59     {
60       return Property::Value( GetFloat( child ) );
61     }
62
63     case Property::VECTOR2:
64     {
65       return Property::Value( GetVector2( child ) );
66     }
67
68     case Property::VECTOR3:
69     {
70       return Property::Value( GetVector3( child ) );
71     }
72
73     case Property::VECTOR4:
74     {
75       return Property::Value( GetVector4( child ) );
76     }
77
78     case Property::ROTATION:
79     {
80       if( 4 == child.Size() )
81       {
82         Vector4 v(GetVector4(child));
83         // angle, axis as per spec
84         return Property::Value( Quaternion(Radian(Degree(v[3])),
85                                            Vector3(v[0],v[1],v[2])) );
86       }
87       else
88       {
89         // degrees as per spec
90         Vector3 rotation = GetVector3( child );
91         return Property::Value( Quaternion(Radian(Degree(rotation.x)),
92                                            Radian(Degree(rotation.y)),
93                                            Radian(Degree(rotation.z))) );
94       }
95     }
96
97     case Property::NONE: // fall
98     default:
99     {
100       DALI_ASSERT_ALWAYS( !"Property type incorrect" );
101     }
102   }
103 }
104
105 AlphaFunction GetAlphaFunction( const std::string& alphaFunction )
106 {
107   typedef std::map< const std::string, Dali::AlphaFunction > AlphaFunctionLut;
108   static AlphaFunctionLut alphaFunctionLut;
109
110   if( 0 == alphaFunctionLut.size() )
111   {
112     // coding convention is uppercase enums
113     alphaFunctionLut["DEFAULT"]                    = AlphaFunction(AlphaFunction::DEFAULT);
114     alphaFunctionLut["LINEAR"]                     = AlphaFunction(AlphaFunction::LINEAR);
115     alphaFunctionLut["REVERSE"]                    = AlphaFunction(AlphaFunction::REVERSE);
116     alphaFunctionLut["EASE_IN_SQUARE"]             = AlphaFunction(AlphaFunction::EASE_IN_SQUARE);
117     alphaFunctionLut["EASE_OUT_SQUARE"]            = AlphaFunction(AlphaFunction::EASE_OUT_SQUARE);
118     alphaFunctionLut["EASE_IN"]                    = AlphaFunction(AlphaFunction::EASE_IN);
119     alphaFunctionLut["EASE_OUT"]                   = AlphaFunction(AlphaFunction::EASE_OUT);
120     alphaFunctionLut["EASE_IN_OUT"]                = AlphaFunction(AlphaFunction::EASE_IN_OUT);
121     alphaFunctionLut["EASE_IN_SINE"]               = AlphaFunction(AlphaFunction::EASE_IN_SINE);
122     alphaFunctionLut["EASE_OUT_SINE"]              = AlphaFunction(AlphaFunction::EASE_OUT_SINE);
123     alphaFunctionLut["EASE_IN_OUT_SINE"]           = AlphaFunction(AlphaFunction::EASE_IN_OUT_SINE);
124     alphaFunctionLut["BOUNCE"]                     = AlphaFunction(AlphaFunction::BOUNCE);
125     alphaFunctionLut["SIN"]                        = AlphaFunction(AlphaFunction::SIN);
126     alphaFunctionLut["EASE_OUT_BACK"]              = AlphaFunction(AlphaFunction::EASE_OUT_BACK);
127   }
128
129   const AlphaFunctionLut::const_iterator iter( alphaFunctionLut.find( alphaFunction ) );
130
131   if( iter != alphaFunctionLut.end() )
132   {
133     return iter->second;
134   }
135   else
136   {
137     DALI_ASSERT_ALWAYS( iter != alphaFunctionLut.end() && "Unknown Alpha Constant" );
138     return Dali::AlphaFunction::DEFAULT;
139   }
140 }
141
142 } // unnamed namespace
143
144
145 namespace Dali
146 {
147
148 namespace Toolkit
149 {
150
151 namespace Internal
152 {
153
154 Animation CreateAnimation( const TreeNode& child, const Replacement& constant, Dali::Actor searchRoot, Builder* const builder )
155 {
156   float durationSum = 0.f;
157
158   Dali::Actor searchActor = searchRoot ? searchRoot : Dali::Stage::GetCurrent().GetRootLayer();
159
160   Animation animation( Animation::New( 0.f ) );
161
162   // duration needs to be set before AnimateTo calls for correct operation when AnimateTo has no "time-period".
163   OptionalFloat duration = constant.IsFloat( IsChild(child, "duration" ) );
164
165   if( duration )
166   {
167     animation.SetDuration( *duration );
168   }
169
170   if( OptionalBoolean looping = constant.IsBoolean(  IsChild(child, "loop" ) ) )
171   {
172     animation.SetLooping( *looping );
173   }
174
175   if( OptionalString endAction = constant.IsString(  IsChild(child, "end-action" ) ) )
176   {
177     if("BAKE" == *endAction)
178     {
179       animation.SetEndAction( Animation::Bake );
180     }
181     else if("DISCARD" == *endAction)
182     {
183       animation.SetEndAction( Animation::Discard );
184     }
185     else if("BAKE_FINAL" == *endAction)
186     {
187       animation.SetEndAction( Animation::BakeFinal );
188     }
189   }
190
191   if( OptionalString endAction = constant.IsString(  IsChild(child, "disconnect-action" ) ) )
192   {
193     if("BAKE" == *endAction)
194     {
195       animation.SetDisconnectAction( Animation::Bake );
196     }
197     else if("DISCARD" == *endAction)
198     {
199       animation.SetDisconnectAction( Animation::Discard );
200     }
201     else if("BAKE_FINAL" == *endAction)
202     {
203       animation.SetDisconnectAction( Animation::BakeFinal );
204     }
205   }
206
207   OptionalChild propertiesNode = IsChild(child, "properties" );
208   if(propertiesNode)
209   {
210     const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
211     for( TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter )
212     {
213       const TreeNode::KeyNodePair& pKeyChild = *iter;
214
215       OptionalString actorName( constant.IsString( IsChild(pKeyChild.second, "actor" ) ) );
216       OptionalString property(  constant.IsString( IsChild(pKeyChild.second, "property" ) ) );
217       DALI_ASSERT_ALWAYS( actorName && "Animation must specify actor name" );
218
219       Handle targetHandle = searchActor.FindChildByName( *actorName );
220       DALI_ASSERT_ALWAYS( targetHandle && "Actor must exist for property" );
221
222       Property::Value propValue;
223       Property::Index propIndex = Property::INVALID_INDEX;
224       if( property )
225       {
226         propIndex = targetHandle.GetPropertyIndex( *property );
227
228         // if the property is not found from the (actor) handle, try to downcast it to renderable actor
229         // to allow animating shader uniforms
230         if( propIndex == Property::INVALID_INDEX )
231         {
232           RenderableActor renderable = RenderableActor::DownCast( targetHandle );
233           if( renderable )
234           {
235             // A limitation here is that its possible that between creation of animation
236             // and running it the ShaderEffect of the actor has been changed.
237             // However this is a unlikely use case especially when using scripts.
238             if( ShaderEffect effect = renderable.GetShaderEffect() )
239             {
240               propIndex = effect.GetPropertyIndex( *property );
241               if(propIndex != Property::INVALID_INDEX)
242               {
243                 targetHandle = effect;
244               }
245               else
246               {
247                 DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
248                 continue;
249               }
250             }
251           }
252           else
253           {
254             DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
255             continue;
256           }
257         }
258
259         if( propIndex == Property::INVALID_INDEX)
260         {
261           DALI_ASSERT_ALWAYS( propIndex != Property::INVALID_INDEX && "Animation must specify a valid property" );
262           continue;
263         }
264       }
265
266       // these are the defaults
267       AlphaFunction alphaFunction( AlphaFunction::DEFAULT );
268       TimePeriod timePeriod( 0.f );
269
270       OptionalChild timeChild = IsChild( pKeyChild.second, "time-period" );
271
272       if( timeChild )
273       {
274         timePeriod = GetTimePeriod( *timeChild, constant );
275       }
276
277       durationSum = std::max( durationSum, timePeriod.delaySeconds + timePeriod.durationSeconds );
278
279       if( OptionalString alphaChild = constant.IsString( IsChild(pKeyChild.second, "alpha-function" ) ) )
280       {
281         alphaFunction = GetAlphaFunction( *alphaChild );
282       }
283
284       if( OptionalChild keyFrameChild = IsChild(pKeyChild.second, "key-frames") )
285       {
286         DALI_ASSERT_ALWAYS( property  && "Animation must specify a property name" );
287         Property prop = Property( targetHandle, propIndex );
288
289         KeyFrames keyframes = KeyFrames::New();
290
291         const TreeNode::ConstIterator endIter = (*keyFrameChild).CEnd();
292         for( TreeNode::ConstIterator iter = (*keyFrameChild).CBegin(); endIter != iter; ++iter )
293         {
294           const TreeNode::KeyNodePair& kfKeyChild = *iter;
295
296           OptionalFloat kfProgress = constant.IsFloat( IsChild(kfKeyChild.second, "progress" ) );
297           DALI_ASSERT_ALWAYS( kfProgress && "Key frame entry must have 'progress'" );
298
299           OptionalChild kfValue = IsChild( kfKeyChild.second, "value" );
300           DALI_ASSERT_ALWAYS( kfValue && "Key frame entry must have 'value'" );
301
302           try
303           {
304             propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *kfValue );
305           }
306           catch(...)
307           {
308             DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n",
309                               (*property).c_str(),
310                               PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)) );
311
312             throw;
313           }
314
315           AlphaFunction kfAlphaFunction( AlphaFunction::DEFAULT );
316           if( OptionalString alphaFuncStr = constant.IsString( IsChild(pKeyChild.second, "alpha-function") ) )
317           {
318             kfAlphaFunction = GetAlphaFunction( *alphaFuncStr );
319           }
320
321           keyframes.Add( *kfProgress, propValue, kfAlphaFunction );
322         }
323
324         if( timeChild )
325         {
326           animation.AnimateBetween( prop, keyframes, alphaFunction, timePeriod );
327         }
328         else
329         {
330           animation.AnimateBetween( prop, keyframes, alphaFunction );
331         }
332       }
333       else if( OptionalString pathChild = IsString(pKeyChild.second, "path") )
334       {
335         //Get path
336         Path path = builder->GetPath(*pathChild);
337         if( path )
338         {
339           //Get forward vector if specified
340           Vector3 forward( 0.0f, 0.0f, 0.0f );
341           OptionalVector3 forwardProperty = constant.IsVector3( IsChild(pKeyChild.second, "forward" ) );
342           if( forwardProperty )
343           {
344             forward = *forwardProperty;
345           }
346
347           Actor actor = Actor::DownCast( targetHandle );
348           if( actor )
349           {
350             if( timeChild )
351             {
352               animation.Animate( actor, path, forward, alphaFunction, timePeriod );
353             }
354             else
355             {
356               animation.Animate( actor, path, forward, alphaFunction );
357             }
358
359           }
360         }
361         else
362         {
363           //Path not found
364           DALI_SCRIPT_WARNING( "Cannot find animation path '%s'\n", (*pathChild).c_str() );
365         }
366       }
367       else
368       {
369         DALI_ASSERT_ALWAYS( property  && "Animation must specify a property name" );
370
371         Property prop = Property( targetHandle, propIndex );
372         try
373         {
374           propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *IsChild(pKeyChild.second, "value") );
375         }
376         catch(...)
377         {
378           DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n", (*property).c_str(),
379                             PropertyTypes::GetName( prop.object.GetPropertyType(prop.propertyIndex) ) );
380
381           throw;
382         }
383
384         if( OptionalBoolean relative = constant.IsBoolean( IsChild(pKeyChild.second, "relative") ) )
385         {
386           if( timeChild )
387           {
388             animation.AnimateBy( prop, propValue, alphaFunction, timePeriod );
389           }
390           else
391           {
392             animation.AnimateBy( prop, propValue, alphaFunction );
393           }
394         }
395         else
396         {
397           if( timeChild )
398           {
399             animation.AnimateTo( prop, propValue, alphaFunction, timePeriod );
400           }
401           else
402           {
403             animation.AnimateTo( prop, propValue, alphaFunction );
404           }
405         }
406       }
407     }
408   }
409
410   if( !duration )
411   {
412     animation.SetDuration( durationSum );
413   }
414
415   return animation;
416 }
417
418 Animation CreateAnimation( const TreeNode& child, Builder* const builder )
419 {
420   Replacement replacement;
421   return CreateAnimation( child, replacement, Stage::GetCurrent().GetRootLayer(), builder );
422 }
423
424 } // namespace Internal
425
426 } // namespace Toolkit
427
428 } // namespace Dali
429