Add post processor
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / builder-signals.cpp
1 /*
2  * Copyright (c) 2021 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/devel-api/common/stage.h>
20 #include <dali/public-api/actors/layer.h>
21 #include <dali/public-api/common/vector-wrapper.h>
22 #include <dali/public-api/object/property-notification.h>
23 #include <dali/public-api/object/type-info.h>
24
25 #include <dali/integration-api/debug.h>
26 #include <limits>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
30 #include <dali-toolkit/internal/builder/builder-impl.h>
31
32 namespace Dali
33 {
34 namespace Toolkit
35 {
36 namespace Internal
37 {
38 extern Animation CreateAnimation(const TreeNode& child, Dali::Toolkit::Internal::Builder* const builder);
39 extern void      DeterminePropertyFromNode(const TreeNode& node, Property::Value& value);
40 } // namespace Internal
41 } // namespace Toolkit
42 } // namespace Dali
43
44 namespace
45 {
46 using namespace Dali;
47
48 //
49 // Signal Actions
50 //
51
52 // Action on child actor. The child is found by name
53 struct ChildActorAction
54 {
55   std::string   actorName;
56   std::string   actionName;
57   std::string   childName;
58   Property::Map parameters;
59
60   void operator()(void)
61   {
62     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
63
64     if(actor)
65     {
66       Actor child_actor = actor.FindChildByName(childName);
67
68       if(child_actor)
69       {
70         child_actor.DoAction(actionName, parameters);
71       }
72       else
73       {
74         DALI_SCRIPT_WARNING("Could not find child by name '%s'\n", childName.c_str());
75       }
76     }
77   };
78 };
79
80 // Action to set a property
81 struct PropertySetAction
82 {
83   std::string     actorName;
84   std::string     propertyName;
85   Property::Value value;
86
87   void operator()(void)
88   {
89     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
90
91     if(actor)
92     {
93       Property::Index idx = actor.GetPropertyIndex(propertyName);
94
95       if(idx != Property::INVALID_INDEX)
96       {
97         if(actor.GetPropertyType(idx) != value.GetType())
98         {
99           DALI_SCRIPT_WARNING("Set property action has different type for property '%s'\n", propertyName.c_str());
100         }
101         else
102         {
103           actor.SetProperty(idx, value);
104         }
105       }
106       else
107       {
108         DALI_SCRIPT_WARNING("Set property action cannot find property '%s'\n", propertyName.c_str());
109       }
110     }
111   };
112 };
113
114 // Generic action on a handle (Animation & Actor)
115 struct GenericAction
116 {
117   std::string   actorName;
118   std::string   actionName;
119   Property::Map parameters;
120
121   void operator()(void)
122   {
123     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
124     if(actor)
125     {
126       actor.DoAction(actionName, parameters);
127     }
128   };
129 };
130
131 struct QuitAction
132 {
133   Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
134
135   void operator()(void)
136   {
137     builder->EmitQuitSignal();
138   }
139 };
140
141 // Delay an animation play; ie wait as its not on stage yet
142 struct DelayedAnimationPlay
143 {
144   OptionalChild                                        animNode;
145   Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
146
147   void operator()(void)
148   {
149     Animation anim = Toolkit::Internal::CreateAnimation(*animNode, builder.Get());
150     if(anim)
151     {
152       anim.Play();
153     }
154   };
155 };
156
157 // Delay a pathConstrainer apply
158 struct DelayedConstrainerApply
159 {
160   std::string constrainerName;
161
162   std::vector<std::string> targetActorNames;
163   std::vector<std::string> sourceActorNames;
164   std::vector<std::string> targetPropertyNames;
165   std::vector<std::string> sourcePropertyNames;
166   std::vector<Vector2>     ranges;
167   std::vector<Vector2>     wrapRanges;
168
169   Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
170
171   /*
172    * Helper function to get the parameters to apply each constraint
173    * @param[in] i i-essim element
174    * @param[out] tagetActor Target actor for the constraint
175    * @param[out] tagetPropertyIndex Target property index for the constraint
176    * @param[out] sourceActor Source actor for the constraint
177    * @param[out] sourcePropertyIndex Source property index for the constraint
178    */
179   bool GetApplyParameters(size_t           i,
180                           Actor&           targetActor,
181                           Property::Index& targetPropertyIndex,
182                           Actor&           sourceActor,
183                           Property::Index& sourcePropertyIndex)
184   {
185     targetActor         = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
186     targetPropertyIndex = Property::INVALID_INDEX;
187     if(targetActor)
188     {
189       targetPropertyIndex = targetActor.GetPropertyIndex(targetPropertyNames[i]);
190       if(targetPropertyIndex == Property::INVALID_INDEX)
191       {
192         DALI_SCRIPT_WARNING("Property '%s' not founded in actor '%s'\n", targetPropertyNames[i].c_str(), targetActorNames[i].c_str());
193         return false;
194       }
195     }
196     else
197     {
198       DALI_SCRIPT_WARNING("Actor '%s' not founded\n", targetActorNames[i].c_str());
199       return false;
200     }
201
202     sourceActor         = Stage::GetCurrent().GetRootLayer().FindChildByName(sourceActorNames[i]);
203     sourcePropertyIndex = Property::INVALID_INDEX;
204     if(sourceActor)
205     {
206       sourcePropertyIndex = sourceActor.GetPropertyIndex(sourcePropertyNames[i]);
207       if(sourcePropertyIndex == Property::INVALID_INDEX)
208       {
209         DALI_SCRIPT_WARNING("Property '%s' not founded in actor '%s'\n", sourcePropertyNames[i].c_str(), sourceActorNames[i].c_str());
210         return false;
211       }
212     }
213     else
214     {
215       DALI_SCRIPT_WARNING("Actor '%s' not founded\n", targetActorNames[i].c_str());
216       return false;
217     }
218     return true;
219   }
220
221   void operator()(void)
222   {
223     Actor           sourceActor, targetActor;
224     Property::Index targetPropertyIndex(Property::INVALID_INDEX);
225     Property::Index sourcePropertyIndex(Property::INVALID_INDEX);
226     size_t          actorCount(targetActorNames.size());
227     if(builder.Get()->IsPathConstrainer(constrainerName))
228     {
229       PathConstrainer constrainer = builder.Get()->GetPathConstrainer(constrainerName);
230       if(constrainer)
231       {
232         for(size_t i(0); i < actorCount; ++i)
233         {
234           if(GetApplyParameters(i, targetActor, targetPropertyIndex, sourceActor, sourcePropertyIndex))
235           {
236             constrainer.Apply(Property(targetActor, targetPropertyIndex),
237                               Property(sourceActor, sourcePropertyIndex),
238                               ranges[i],
239                               wrapRanges[i]);
240           }
241         }
242       }
243       else
244       {
245         DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
246       }
247     }
248     else if(builder.Get()->IsLinearConstrainer(constrainerName))
249     {
250       Dali::LinearConstrainer constrainer(builder.Get()->GetLinearConstrainer(constrainerName));
251       if(constrainer)
252       {
253         for(size_t i(0); i < actorCount; ++i)
254         {
255           if(GetApplyParameters(i, targetActor, targetPropertyIndex, sourceActor, sourcePropertyIndex))
256           {
257             constrainer.Apply(Property(targetActor, targetPropertyIndex),
258                               Property(sourceActor, sourcePropertyIndex),
259                               ranges[i],
260                               wrapRanges[i]);
261           }
262         }
263       }
264       else
265       {
266         DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
267       }
268     }
269     else
270     {
271       DALI_SCRIPT_WARNING("Constrainer %s is not of a valid type\n", constrainerName.c_str());
272     }
273   }
274 };
275
276 // Delay a pathConstrainer remove
277 struct DelayedConstrainerRemove
278 {
279   std::string                                          constrainerName;
280   std::vector<std::string>                             targetActorNames;
281   Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
282
283   void operator()(void)
284   {
285     size_t actorCount(targetActorNames.size());
286     if(builder.Get()->IsPathConstrainer(constrainerName))
287     {
288       PathConstrainer constrainer = builder.Get()->GetPathConstrainer(constrainerName);
289       if(constrainer)
290       {
291         for(size_t i(0); i < actorCount; ++i)
292         {
293           Actor targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
294           if(targetActor)
295           {
296             constrainer.Remove(targetActor);
297           }
298         }
299       }
300       else
301       {
302         DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
303       }
304     }
305     else if(builder.Get()->IsLinearConstrainer(constrainerName))
306     {
307       LinearConstrainer constrainer = builder.Get()->GetLinearConstrainer(constrainerName);
308       if(constrainer)
309       {
310         for(size_t i(0); i < actorCount; ++i)
311         {
312           Actor targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
313           if(targetActor)
314           {
315             constrainer.Remove(targetActor);
316           }
317         }
318       }
319       else
320       {
321         DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
322       }
323     }
324     else
325     {
326       DALI_SCRIPT_WARNING("Constrainer %s is not of a valid type\n", constrainerName.c_str());
327     }
328   }
329 };
330
331 /*
332  * Gets Property::Value from child
333  */
334 Property::Value GetPropertyValue(const TreeNode& child)
335 {
336   size_t nChildren = child.Size();
337
338   Property::Value ret;
339
340   if(0 == nChildren)
341   {
342     // cast away unused return for static analyzers
343     static_cast<void>(Dali::Toolkit::Internal::DeterminePropertyFromNode(child, ret));
344   }
345   else if(1 == nChildren)
346   {
347     // {"property": {"quaternion":[1,2,3,4]} }
348     // {"property": {"angle":22, "axis": [1,2,3]} }
349
350     OptionalChild quaternion = IsChild(&child, "quaternion");
351     OptionalChild axis       = IsChild(&child, "axis");
352     OptionalChild angle      = IsChild(&child, "angle");
353
354     if(quaternion)
355     {
356       ret = Property::Value(Quaternion(GetVector4(*quaternion)));
357     }
358     else if(axis && angle)
359     {
360       ret = Property::Value(AngleAxis(Degree(GetFloat(*angle)), GetVector3(*axis)));
361     }
362   }
363   else if(2 == nChildren)
364   {
365     // {"property": [1,2]}
366     ret = Property::Value(GetVector2(child));
367   }
368   else if(3 == nChildren)
369   {
370     // {"property": [1,2,3]}
371     ret = Property::Value(GetVector3(child));
372   }
373   else if(4 == nChildren)
374   {
375     // {"property": [1,2,3,4]}
376     ret = Property::Value(GetVector4(child));
377   }
378
379   return ret;
380 }
381
382 /*
383  * Gets Parmeter list from child
384  * params is be cleared before insertion
385  */
386 void GetParameters(const TreeNode& child, Property::Map& params)
387 {
388   if(OptionalChild c = IsChild(child, "parameters"))
389   {
390     const TreeNode& node = *c;
391
392     params.Clear();
393
394     for(TreeNode::ConstIterator iter(node.CBegin()); iter != node.CEnd(); ++iter)
395     {
396       params[(*iter).first] = GetPropertyValue((*iter).second);
397     }
398   }
399 }
400
401 // Shim for the property notifcation signal
402 template<typename T>
403 struct PropertyNotifcationSignalShim
404 {
405   T mFunctor;
406
407   PropertyNotifcationSignalShim(T& functor)
408   : mFunctor(functor)
409   {
410   }
411
412   void operator()(PropertyNotification& /* source */)
413   {
414     mFunctor();
415   }
416 };
417
418 // Specializations for the different signal connection calls between actor & PropertyNotification
419 template<typename T>
420 struct SignalConnector
421 {
422 };
423
424 // Actor specialization
425 template<>
426 struct SignalConnector<Actor>
427 {
428   Actor&             mActor;
429   ConnectionTracker* mTracker;
430   const std::string& mName;
431
432   SignalConnector<Actor>(ConnectionTracker* tracker, Actor& actor, const std::string& name)
433   : mActor(actor),
434     mTracker(tracker),
435     mName(name)
436   {
437   }
438
439   template<typename T>
440   void Connect(T& functor)
441   {
442     mActor.ConnectSignal(mTracker, mName, functor);
443   }
444 };
445
446 // PropertyNotification specialization
447 template<>
448 struct SignalConnector<PropertyNotification>
449 {
450   PropertyNotification& mNotification;
451   ConnectionTracker*    mTracker;
452
453   SignalConnector<PropertyNotification>(ConnectionTracker* tracker, PropertyNotification& notification)
454   : mNotification(notification),
455     mTracker(tracker)
456   {
457   }
458
459   template<typename T>
460   void Connect(T& functor)
461   {
462     mNotification.NotifySignal().Connect(mTracker, PropertyNotifcationSignalShim<T>(functor));
463   }
464 };
465
466 /**
467  * Set an action functor on a signal
468  */
469 template<typename T>
470 void SetActionOnSignal(const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder, SignalConnector<T>& connector)
471 {
472   OptionalString childActorName(IsString(IsChild(&child, "childActor")));
473   OptionalString actorName(IsString(IsChild(&child, "actor")));
474   OptionalString propertyName(IsString(IsChild(&child, "property")));
475   OptionalChild  valueChild(IsChild(&child, "value"));
476
477   OptionalString actionName = IsString(IsChild(&child, "action"));
478   DALI_ASSERT_ALWAYS(actionName && "Signal must have an action");
479
480   if(childActorName)
481   {
482     ChildActorAction action;
483     action.actorName  = *actorName;
484     action.childName  = *childActorName;
485     action.actionName = *actionName;
486     GetParameters(child, action.parameters);
487     connector.Connect(action);
488   }
489   else if(actorName)
490   {
491     if(propertyName && valueChild && ("set" == *actionName))
492     {
493       PropertySetAction action;
494       action.actorName    = *actorName;
495       action.propertyName = *propertyName;
496       // actor may not exist yet so we can't check the property type
497       Dali::Toolkit::Internal::DeterminePropertyFromNode(*valueChild, action.value);
498       connector.Connect(action);
499     }
500     else
501     {
502       GenericAction action;
503       action.actorName  = *actorName;
504       action.actionName = *actionName;
505       GetParameters(child, action.parameters);
506       connector.Connect(action);
507     }
508   }
509   else if("quit" == *actionName)
510   {
511     QuitAction action;
512     action.builder = builder;
513     connector.Connect(action);
514   }
515   else if("play" == *actionName)
516   {
517     OptionalChild  animations    = IsChild(root, "animations");
518     OptionalString animationName = IsString(IsChild(child, "animation"));
519     if(animations && animationName)
520     {
521       if(OptionalChild animNode = IsChild(*animations, *animationName))
522       {
523         DelayedAnimationPlay action;
524         action.animNode = animNode;
525         action.builder  = builder;
526         // @todo; put constants into the map
527         connector.Connect(action);
528       }
529       else
530       {
531         DALI_SCRIPT_WARNING("Cannot find animation '%s'\n", (*animationName).c_str());
532       }
533     }
534     else
535     {
536       DALI_SCRIPT_WARNING("Cannot find animations section\n");
537     }
538   }
539   else if("applyConstraint" == *actionName)
540   {
541     OptionalString constrainerName = IsString(IsChild(child, "constrainer"));
542     if(!constrainerName)
543     {
544       DALI_SCRIPT_WARNING("Need to specify a constrainer\n");
545     }
546     else
547     {
548       DelayedConstrainerApply action;
549       action.constrainerName       = *constrainerName;
550       action.builder               = builder;
551       OptionalChild propertiesNode = IsChild(child, "properties");
552       if(propertiesNode)
553       {
554         const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
555         for(TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter)
556         {
557           const TreeNode::KeyNodePair& pKeyChild = *iter;
558           OptionalString               sourceActorName(IsString(IsChild(pKeyChild.second, "source")));
559           if(!sourceActorName)
560           {
561             DALI_SCRIPT_WARNING("Need to specify source actor to apply the constraint\n");
562             continue;
563           }
564           OptionalString sourcePropertyName(IsString(IsChild(pKeyChild.second, "sourceProperty")));
565           if(!sourcePropertyName)
566           {
567             DALI_SCRIPT_WARNING("Need to specify source property to apply the constraint\n");
568             continue;
569           }
570
571           OptionalString targetActorName(IsString(IsChild(pKeyChild.second, "target")));
572           if(!targetActorName)
573           {
574             DALI_SCRIPT_WARNING("Need to specify target actor to apply the constraint\n");
575             continue;
576           }
577
578           OptionalString targetPropertyName(IsString(IsChild(pKeyChild.second, "targetProperty")));
579           if(!targetPropertyName)
580           {
581             DALI_SCRIPT_WARNING("Need to specify target property name to apply the constraint\n");
582             continue;
583           }
584
585           OptionalVector2 range(IsVector2(IsChild(pKeyChild.second, "range")));
586           if(!range)
587           {
588             DALI_SCRIPT_WARNING("Constrainer range not specified\n");
589             continue;
590           }
591
592           Vector2         wrap(-std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
593           OptionalVector2 wrapRange(IsVector2(IsChild(pKeyChild.second, "wrap")));
594           if(wrapRange)
595           {
596             wrap = *wrapRange;
597           }
598
599           action.sourceActorNames.push_back(*sourceActorName);
600           action.sourcePropertyNames.push_back(*sourcePropertyName);
601           action.targetActorNames.push_back(*targetActorName);
602           action.targetPropertyNames.push_back(*targetPropertyName);
603           action.ranges.push_back(*range);
604           action.wrapRanges.push_back(wrap);
605         }
606         connector.Connect(action);
607       }
608     }
609   }
610   else if("removeConstraints" == *actionName)
611   {
612     OptionalString constrainerName = IsString(IsChild(child, "constrainer"));
613     if(!constrainerName)
614     {
615       DALI_SCRIPT_WARNING("Need to specify a constrainer\n");
616     }
617     else
618     {
619       DelayedConstrainerRemove action;
620       action.constrainerName       = *constrainerName;
621       action.builder               = builder;
622       OptionalChild propertiesNode = IsChild(child, "properties");
623       if(propertiesNode)
624       {
625         const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
626         for(TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter)
627         {
628           const TreeNode::KeyNodePair& pKeyChild = *iter;
629           OptionalString               targetActorName(IsString(IsChild(pKeyChild.second, "target")));
630           if(targetActorName)
631           {
632             action.targetActorNames.push_back(*targetActorName);
633           }
634           else
635           {
636             DALI_SCRIPT_WARNING("Need to specify target actor to remove the constraint\n");
637             continue;
638           }
639         }
640       }
641       connector.Connect(action);
642     }
643   }
644   else
645   {
646     // no named actor; presume self
647     GenericAction action;
648     action.actorName  = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
649     action.actionName = *actionName;
650     GetParameters(child, action.parameters);
651     connector.Connect(action);
652   }
653 }
654
655 /**
656  * Get a notification condition argument0 as 'arg0' 'value' or 'min'
657  */
658 float GetConditionArg0(const TreeNode& child)
659 {
660   OptionalFloat f = IsFloat(IsChild(child, "arg0"));
661   // allowing some human preferable alternatives
662   if(!f)
663   {
664     f = IsFloat(IsChild(child, "value"));
665   }
666   if(!f)
667   {
668     f = IsFloat(IsChild(child, "min"));
669   }
670
671   DALI_ASSERT_ALWAYS(f && "Notification condition for arg0 not specified");
672
673   return *f;
674 }
675
676 /**
677  * Get a notification condition argument1 as 'arg1' or 'max'
678  */
679 float GetConditionArg1(const TreeNode& child)
680 {
681   OptionalFloat f = IsFloat(IsChild(child, "arg1"));
682   // allowing some human preferable alternatives
683   if(!f)
684   {
685     f = IsFloat(IsChild(child, "max"));
686   }
687
688   DALI_ASSERT_ALWAYS(f && "Notification condition for arg1 not specified");
689
690   return *f;
691 }
692
693 }; // namespace
694
695 namespace Dali
696 {
697 namespace Toolkit
698 {
699 namespace Internal
700 {
701 Actor SetupSignalAction(const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder);
702 Actor SetupPropertyNotification(const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder);
703
704 /**
705  * Setup signals and actions on an actor
706  */
707 Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder)
708 {
709   DALI_ASSERT_ALWAYS(actor);
710
711   if(OptionalChild signalsChild = IsChild(child, "signals"))
712   {
713     const TreeNode&     signalsNode = *signalsChild;
714     const TreeConstIter endIter     = signalsNode.CEnd();
715     for(TreeConstIter iter = signalsNode.CBegin(); endIter != iter; ++iter)
716     {
717       const TreeNode::KeyNodePair& key_child = *iter;
718
719       DALI_SCRIPT_INFO("  Creating Signal for: %s\n", actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str());
720
721       OptionalString name(IsString(IsChild(key_child.second, "name")));
722       DALI_ASSERT_ALWAYS(name && "Signal must have a name");
723
724       SignalConnector<Actor> connector(tracker, actor, *name);
725       SetActionOnSignal(root, key_child.second, actor, builder, connector);
726     }
727   }
728
729   return actor;
730 }
731
732 /**
733  * Setup Property notifications for an actor
734  */
735 Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder)
736 {
737   DALI_ASSERT_ALWAYS(actor);
738
739   if(OptionalChild notificationsChild = IsChild(child, "notifications"))
740   {
741     const TreeNode&               notificationsNode = *notificationsChild;
742     const TreeNode::ConstIterator endIter           = notificationsNode.CEnd();
743     for(TreeNode::ConstIterator iter = notificationsNode.CBegin(); endIter != iter; ++iter)
744     {
745       const TreeNode::KeyNodePair& key_child = *iter;
746
747       OptionalString prop(IsString(IsChild(key_child.second, "property")));
748       DALI_ASSERT_ALWAYS(prop && "Notification signal must specify a property");
749
750       Property::Index prop_index = actor.GetPropertyIndex(*prop);
751       DALI_ASSERT_ALWAYS(prop_index != Property::INVALID_INDEX && "Notification signal specifies an unknown property");
752
753       OptionalString cond(IsString(IsChild(key_child.second, "condition")));
754       DALI_ASSERT_ALWAYS(cond && "Notification signal must specify a condition");
755
756       if("False" == *cond)
757       {
758         PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
759                                                                           LessThanCondition(1.f));
760
761         SignalConnector<PropertyNotification> connector(tracker, notification);
762         SetActionOnSignal(root, key_child.second, actor, builder, connector);
763       }
764       else if("LessThan" == *cond)
765       {
766         PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
767                                                                           LessThanCondition(GetConditionArg0(key_child.second)));
768
769         SignalConnector<PropertyNotification> connector(tracker, notification);
770         SetActionOnSignal(root, key_child.second, actor, builder, connector);
771       }
772       else if("GreaterThan" == *cond)
773       {
774         PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
775                                                                           GreaterThanCondition(GetConditionArg0(key_child.second)));
776
777         SignalConnector<PropertyNotification> connector(tracker, notification);
778         SetActionOnSignal(root, key_child.second, actor, builder, connector);
779       }
780       else if("Inside" == *cond)
781       {
782         PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
783                                                                           InsideCondition(GetConditionArg0(key_child.second),
784                                                                                           GetConditionArg1(key_child.second)));
785
786         SignalConnector<PropertyNotification> connector(tracker, notification);
787         SetActionOnSignal(root, key_child.second, actor, builder, connector);
788       }
789       else if("Outside" == *cond)
790       {
791         PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
792                                                                           OutsideCondition(GetConditionArg0(key_child.second),
793                                                                                            GetConditionArg1(key_child.second)));
794
795         SignalConnector<PropertyNotification> connector(tracker, notification);
796         SetActionOnSignal(root, key_child.second, actor, builder, connector);
797       }
798       else
799       {
800         DALI_ASSERT_ALWAYS(!"Unknown condition");
801       }
802     }
803   } // if notifications
804
805   return actor;
806
807 } // AddPropertyNotification
808
809 } // namespace Internal
810 } // namespace Toolkit
811 } // namespace Dali