Merge "Use new animatable registered properties in ScrollView" into tizen
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / builder-signals.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 <boost/function.hpp>
20 #include <dali/public-api/actors/layer.h>
21 #include <dali/public-api/object/type-info.h>
22 #include <dali/public-api/object/property-notification.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/builder/builder-impl.h>
27 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
28
29 namespace Dali
30 {
31 namespace Toolkit
32 {
33 namespace Internal
34 {
35 extern Animation CreateAnimation( const TreeNode& child, Dali::Toolkit::Internal::Builder* const builder  );
36 extern bool SetPropertyFromNode( const TreeNode& node, Property::Value& value );
37 }
38 }
39 }
40
41 namespace
42 {
43 using namespace Dali;
44
45 //
46 // Signal Actions
47 //
48
49 // Action on child actor. The child is found by name
50 struct ChildActorAction
51 {
52   std::string actorName;
53   std::string actionName;
54   std::string childName;
55   PropertyValueContainer parameters;
56
57   void operator()(void)
58   {
59     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
60
61     if(actor)
62     {
63       Actor child_actor = actor.FindChildByName(childName);
64
65       if(child_actor)
66       {
67         child_actor.DoAction(actionName, parameters);
68       }
69       else
70       {
71         DALI_SCRIPT_WARNING("Could not find child by name '%s'\n", childName.c_str());
72       }
73     }
74   };
75 };
76
77 // Action to set a property
78 struct PropertySetAction
79 {
80   std::string actorName;
81   std::string propertyName;
82   Property::Value value;
83
84   void operator()(void)
85   {
86     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
87
88     if(actor)
89     {
90       Property::Index idx = actor.GetPropertyIndex(propertyName);
91
92       if( idx != Property::INVALID_INDEX )
93       {
94         if( actor.GetPropertyType(idx) != value.GetType() )
95         {
96           DALI_SCRIPT_WARNING("Set property action has different type for property '%s'\n", propertyName.c_str());
97         }
98         else
99         {
100           actor.SetProperty( idx, value );
101         }
102       }
103       else
104       {
105         DALI_SCRIPT_WARNING("Set property action cannot find property '%s'\n", propertyName.c_str());
106       }
107     }
108   };
109 };
110
111 // Generic action on a handle (Animation & Actor)
112 struct GenericAction
113 {
114   std::string actorName;
115   std::string actionName;
116   PropertyValueContainer parameters;
117
118   void operator()(void)
119   {
120     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
121     if(actor)
122     {
123       actor.DoAction(actionName, parameters);
124     }
125
126   };
127 };
128
129 // Delay an animation play; ie wait as its not on stage yet
130 struct DelayedAnimationPlay
131 {
132   OptionalChild                                         animNode;
133   Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder>  builder;
134
135   void operator()(void)
136   {
137     Animation anim = Toolkit::Internal::CreateAnimation(*animNode, builder.Get() );
138     if(anim)
139     {
140       anim.Play();
141     }
142   };
143 };
144
145 /*
146  * Gets Property::Value from child
147  */
148 Property::Value GetPropertyValue(const TreeNode &child)
149 {
150   size_t nChildren = child.Size();
151
152   Property::Value ret;
153
154   if(0 == nChildren)
155   {
156     // cast away unused return for static analyzers
157     static_cast<void>( Dali::Toolkit::Internal::SetPropertyFromNode( child, ret ) );
158   }
159   else if(1 == nChildren)
160   {
161     // {"property": {"quaternion":[1,2,3,4]} }
162     // {"property": {"angle":22, "axis": [1,2,3]} }
163
164     OptionalChild quaternion  = IsChild(&child, "quaternion");
165     OptionalChild axis        = IsChild(&child, "axis");
166     OptionalChild angle       = IsChild(&child, "angle");
167
168     if(quaternion)
169     {
170       ret = Property::Value(Quaternion(GetVector4(*quaternion)));
171     }
172     else if(axis && angle)
173     {
174       ret = Property::Value(AngleAxis(Degree(GetFloat(*angle)), GetVector3(*axis)));
175     }
176   }
177   else if(2 == nChildren)
178   {
179     // {"property": [1,2]}
180     ret = Property::Value(GetVector2(child));
181   }
182   else if(3 == nChildren)
183   {
184     // {"property": [1,2,3]}
185     ret = Property::Value(GetVector3(child));
186   }
187   else if(4 == nChildren)
188   {
189     // {"property": [1,2,3,4]}
190     ret = Property::Value(GetVector4(child));
191   }
192
193   return ret;
194 }
195
196
197 /*
198  * Gets Parmeter list from child
199  * params is be cleared before insertion
200  */
201 void GetParameters(const TreeNode& child, PropertyValueContainer& params)
202 {
203   if( OptionalChild c = IsChild(child, "parameters") )
204   {
205     const TreeNode& node = *c;
206
207     if(0 == node.Size())
208     {
209       GetPropertyValue(node);
210     }
211     else
212     {
213       params.clear();
214       params.reserve(node.Size());
215
216       for(TreeNode::ConstIterator iter(node.CBegin()); iter != node.CEnd(); ++iter)
217       {
218         params.push_back( GetPropertyValue( (*iter).second ) );
219       }
220     }
221   }
222 }
223
224 void DoNothing(void) {};
225
226 /**
227  * Get an action as boost function callback
228  */
229 boost::function<void (void)> GetAction(const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction, Dali::Toolkit::Internal::Builder* const builder)
230 {
231   OptionalString childActorName(IsString( IsChild(&child, "child-actor")) );
232   OptionalString actorName(IsString( IsChild(&child, "actor")) );
233   OptionalString propertyName(IsString( IsChild(&child, "property")) );
234   OptionalChild  valueChild( IsChild(&child, "value") );
235
236   OptionalString actionName = IsString( IsChild(&child, "action") );
237   DALI_ASSERT_ALWAYS(actionName && "Signal must have an action");
238
239   boost::function<void(void)> callback = DoNothing;
240
241   if(childActorName)
242   {
243     ChildActorAction action;
244     action.actorName       = *actorName;
245     action.childName       = *childActorName;
246     action.actionName      = *actionName;
247     GetParameters(child, action.parameters);
248     callback = action;
249   }
250   else if(actorName)
251   {
252     if(propertyName && valueChild && ("set" == *actionName) )
253     {
254       PropertySetAction action;
255       action.actorName       = *actorName;
256       action.propertyName    = *propertyName;
257       // actor may not exist yet so we can't check the property type
258       if( !Dali::Toolkit::Internal::SetPropertyFromNode( *valueChild, action.value ) )
259       {
260         DALI_SCRIPT_WARNING("Cannot set property for set property action\n");
261       }
262       callback = action;
263     }
264     else
265     {
266       GenericAction action;
267       action.actorName       = *actorName;
268       action.actionName      = *actionName;
269       GetParameters(child, action.parameters);
270       callback = action;
271     }
272   }
273   else if("quit" == *actionName)
274   {
275     callback = quitAction;
276   }
277   else if("play" == *actionName)
278   {
279     OptionalChild animations     = IsChild( root, "animations" );
280     OptionalString animationName = IsString( IsChild(child, "animation") );
281     if( animations && animationName )
282     {
283       if( OptionalChild animNode = IsChild(*animations, *animationName) )
284       {
285         DelayedAnimationPlay action;
286         action.animNode = animNode;
287         action.builder = builder;
288         // @todo; put constants into the map
289         callback = action;
290       }
291       else
292       {
293         DALI_SCRIPT_WARNING("Cannot find animation '%s'\n", (*animationName).c_str());
294       }
295     }
296     else
297     {
298       DALI_SCRIPT_WARNING("Cannot find animations section\n");
299     }
300   }
301   else
302   {
303     // no named actor; presume self
304     GenericAction action;
305     action.actorName       = actor.GetName();
306     action.actionName      = *actionName;
307     GetParameters(child, action.parameters);
308     callback = action;
309   }
310
311   return callback;
312 }
313
314
315 /**
316  * Get a notification condition argument0 as 'arg0' 'value' or 'min'
317  */
318 float GetConditionArg0(const TreeNode &child)
319 {
320   OptionalFloat f = IsFloat( IsChild(child, "arg0") );
321   // allowing some human preferable alternatives
322   if(!f)
323   {
324     f = IsFloat( IsChild(child, "value") );
325   }
326   if(!f)
327   {
328     f = IsFloat( IsChild(child, "min") );
329   }
330
331   DALI_ASSERT_ALWAYS(f && "Notification condition for arg0 not specified");
332
333   return *f;
334 }
335
336 /**
337  * Get a notification condition argument1 as 'arg1' or 'max'
338  */
339 float GetConditionArg1(const TreeNode &child)
340 {
341   OptionalFloat f = IsFloat( IsChild(child, "arg1") );
342   // allowing some human preferable alternatives
343   if(!f)
344   {
345     f = IsFloat( IsChild(child, "max") );
346   }
347
348   DALI_ASSERT_ALWAYS(f && "Notification condition for arg1 not specified");
349
350   return *f;
351 }
352
353
354
355 }; // anon namespace
356
357 namespace Dali
358 {
359 namespace Toolkit
360 {
361 namespace Internal
362 {
363
364 Actor SetupSignalAction(const TreeNode &child, Actor actor, Dali::Toolkit::Internal::Builder* const builder );
365 Actor SetupPropertyNotification(const TreeNode &child, Actor actor, Dali::Toolkit::Internal::Builder* const builder );
366
367 /**
368  * Setup signals and actions on an actor
369  */
370 Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction, Dali::Toolkit::Internal::Builder* const builder )
371 {
372   DALI_ASSERT_ALWAYS(actor);
373
374   if(OptionalChild signalsChild = IsChild(child, "signals"))
375   {
376     const TreeNode& signalsNode = *signalsChild;
377     const TreeConstIter endIter = signalsNode.CEnd();
378     for( TreeConstIter iter = signalsNode.CBegin(); endIter != iter; ++iter )
379     {
380       const TreeNode::KeyNodePair& key_child = *iter;
381
382       DALI_SCRIPT_INFO("  Creating Signal for: %s\n", actor.GetName().c_str());
383
384       OptionalString name( IsString( IsChild( key_child.second, "name")) );
385       DALI_ASSERT_ALWAYS(name && "Signal must have a name");
386
387       boost::function<void (void)> callback = GetAction(root, key_child.second, actor, quitAction, builder );
388
389       actor.ConnectSignal(tracker, *name, callback);
390     }
391   }
392
393   return actor;
394 }
395
396 /**
397  * Setup Property notifications for an actor
398  */
399 Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction, Dali::Toolkit::Internal::Builder* const builder )
400 {
401   DALI_ASSERT_ALWAYS(actor);
402
403   if(OptionalChild notificationsChild = IsChild(child,"notifications"))
404   {
405     const TreeNode& notificationsNode = *notificationsChild;
406     const TreeNode::ConstIterator endIter = notificationsNode.CEnd();
407     for( TreeNode::ConstIterator iter = notificationsNode.CBegin(); endIter != iter; ++iter )
408     {
409       const TreeNode::KeyNodePair& key_child = *iter;
410
411       // Actor actions reference by pointer because of circular reference actor->signal
412       // So this callback should only go onto the actor maintained list.
413       boost::function<void (void)> callback = GetAction(root, key_child.second, actor, quitAction, builder );
414
415       OptionalString prop(IsString( IsChild(key_child.second, "property")) );
416       DALI_ASSERT_ALWAYS(prop && "Notification signal must specify a property");
417
418       Property::Index prop_index = actor.GetPropertyIndex(*prop);
419       DALI_ASSERT_ALWAYS(prop_index != Property::INVALID_INDEX && "Notification signal specifies an unknown property");
420
421       OptionalString cond(IsString( IsChild(key_child.second, "condition")));
422       DALI_ASSERT_ALWAYS(cond && "Notification signal must specify a condition");
423
424       if("False" == *cond)
425       {
426         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
427                                                                            LessThanCondition(1.f) );
428         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
429       }
430       else if("LessThan" == *cond)
431       {
432         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
433                                                                            LessThanCondition(GetConditionArg0(key_child.second)) );
434         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
435       }
436       else if("GreaterThan" == *cond)
437       {
438         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
439                                                                            GreaterThanCondition(GetConditionArg0(key_child.second)) );
440         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
441       }
442       else if("Inside" == *cond)
443       {
444         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
445                                                                            InsideCondition(GetConditionArg0(key_child.second),
446                                                                            GetConditionArg1(key_child.second)) );
447         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
448       }
449       else if("Outside" == *cond)
450       {
451         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
452                                                                            OutsideCondition(GetConditionArg0(key_child.second),
453                                                                            GetConditionArg1(key_child.second)) );
454         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
455       }
456       else
457       {
458         DALI_ASSERT_ALWAYS(!"Unknown condition");
459       }
460     }
461   } // if notifications
462
463   return actor;
464
465 } // AddPropertyNotification
466
467
468 } // namespace Internal
469 } // namespace Toolkit
470 } // namespace Dali