2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
25 #include <dali/integration-api/debug.h>
29 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
30 #include <dali-toolkit/internal/builder/builder-impl.h>
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
52 // Action on child actor. The child is found by name
53 struct ChildActorAction
55 std::string actorName;
56 std::string actionName;
57 std::string childName;
58 Property::Map parameters;
62 Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
66 Actor child_actor = actor.FindChildByName(childName);
70 child_actor.DoAction(actionName, parameters);
74 DALI_SCRIPT_WARNING("Could not find child by name '%s'\n", childName.c_str());
80 // Action to set a property
81 struct PropertySetAction
83 std::string actorName;
84 std::string propertyName;
85 Property::Value value;
89 Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
93 Property::Index idx = actor.GetPropertyIndex(propertyName);
95 if(idx != Property::INVALID_INDEX)
97 if(actor.GetPropertyType(idx) != value.GetType())
99 DALI_SCRIPT_WARNING("Set property action has different type for property '%s'\n", propertyName.c_str());
103 actor.SetProperty(idx, value);
108 DALI_SCRIPT_WARNING("Set property action cannot find property '%s'\n", propertyName.c_str());
114 // Generic action on a handle (Animation & Actor)
117 std::string actorName;
118 std::string actionName;
119 Property::Map parameters;
121 void operator()(void)
123 Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
126 actor.DoAction(actionName, parameters);
133 Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
135 void operator()(void)
137 builder->EmitQuitSignal();
141 // Delay an animation play; ie wait as its not on stage yet
142 struct DelayedAnimationPlay
144 OptionalChild animNode;
145 Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
147 void operator()(void)
149 Animation anim = Toolkit::Internal::CreateAnimation(*animNode, builder.Get());
157 // Delay a pathConstrainer apply
158 struct DelayedConstrainerApply
160 std::string constrainerName;
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;
169 Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
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
179 bool GetApplyParameters(size_t i,
181 Property::Index& targetPropertyIndex,
183 Property::Index& sourcePropertyIndex)
185 targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
186 targetPropertyIndex = Property::INVALID_INDEX;
189 targetPropertyIndex = targetActor.GetPropertyIndex(targetPropertyNames[i]);
190 if(targetPropertyIndex == Property::INVALID_INDEX)
192 DALI_SCRIPT_WARNING("Property '%s' not founded in actor '%s'\n", targetPropertyNames[i].c_str(), targetActorNames[i].c_str());
198 DALI_SCRIPT_WARNING("Actor '%s' not founded\n", targetActorNames[i].c_str());
202 sourceActor = Stage::GetCurrent().GetRootLayer().FindChildByName(sourceActorNames[i]);
203 sourcePropertyIndex = Property::INVALID_INDEX;
206 sourcePropertyIndex = sourceActor.GetPropertyIndex(sourcePropertyNames[i]);
207 if(sourcePropertyIndex == Property::INVALID_INDEX)
209 DALI_SCRIPT_WARNING("Property '%s' not founded in actor '%s'\n", sourcePropertyNames[i].c_str(), sourceActorNames[i].c_str());
215 DALI_SCRIPT_WARNING("Actor '%s' not founded\n", targetActorNames[i].c_str());
221 void operator()(void)
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))
229 PathConstrainer constrainer = builder.Get()->GetPathConstrainer(constrainerName);
232 for(size_t i(0); i < actorCount; ++i)
234 if(GetApplyParameters(i, targetActor, targetPropertyIndex, sourceActor, sourcePropertyIndex))
236 constrainer.Apply(Property(targetActor, targetPropertyIndex),
237 Property(sourceActor, sourcePropertyIndex),
245 DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
248 else if(builder.Get()->IsLinearConstrainer(constrainerName))
250 Dali::LinearConstrainer constrainer(builder.Get()->GetLinearConstrainer(constrainerName));
253 for(size_t i(0); i < actorCount; ++i)
255 if(GetApplyParameters(i, targetActor, targetPropertyIndex, sourceActor, sourcePropertyIndex))
257 constrainer.Apply(Property(targetActor, targetPropertyIndex),
258 Property(sourceActor, sourcePropertyIndex),
266 DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
271 DALI_SCRIPT_WARNING("Constrainer %s is not of a valid type\n", constrainerName.c_str());
276 // Delay a pathConstrainer remove
277 struct DelayedConstrainerRemove
279 std::string constrainerName;
280 std::vector<std::string> targetActorNames;
281 Dali::IntrusivePtr<Dali::Toolkit::Internal::Builder> builder;
283 void operator()(void)
285 size_t actorCount(targetActorNames.size());
286 if(builder.Get()->IsPathConstrainer(constrainerName))
288 PathConstrainer constrainer = builder.Get()->GetPathConstrainer(constrainerName);
291 for(size_t i(0); i < actorCount; ++i)
293 Actor targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
296 constrainer.Remove(targetActor);
302 DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
305 else if(builder.Get()->IsLinearConstrainer(constrainerName))
307 LinearConstrainer constrainer = builder.Get()->GetLinearConstrainer(constrainerName);
310 for(size_t i(0); i < actorCount; ++i)
312 Actor targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName(targetActorNames[i]);
315 constrainer.Remove(targetActor);
321 DALI_SCRIPT_WARNING("Constrainer %s not found\n", constrainerName.c_str());
326 DALI_SCRIPT_WARNING("Constrainer %s is not of a valid type\n", constrainerName.c_str());
332 * Gets Property::Value from child
334 Property::Value GetPropertyValue(const TreeNode& child)
336 size_t nChildren = child.Size();
342 // cast away unused return for static analyzers
343 static_cast<void>(Dali::Toolkit::Internal::DeterminePropertyFromNode(child, ret));
345 else if(1 == nChildren)
347 // {"property": {"quaternion":[1,2,3,4]} }
348 // {"property": {"angle":22, "axis": [1,2,3]} }
350 OptionalChild quaternion = IsChild(&child, "quaternion");
351 OptionalChild axis = IsChild(&child, "axis");
352 OptionalChild angle = IsChild(&child, "angle");
356 ret = Property::Value(Quaternion(GetVector4(*quaternion)));
358 else if(axis && angle)
360 ret = Property::Value(AngleAxis(Degree(GetFloat(*angle)), GetVector3(*axis)));
363 else if(2 == nChildren)
365 // {"property": [1,2]}
366 ret = Property::Value(GetVector2(child));
368 else if(3 == nChildren)
370 // {"property": [1,2,3]}
371 ret = Property::Value(GetVector3(child));
373 else if(4 == nChildren)
375 // {"property": [1,2,3,4]}
376 ret = Property::Value(GetVector4(child));
383 * Gets Parmeter list from child
384 * params is be cleared before insertion
386 void GetParameters(const TreeNode& child, Property::Map& params)
388 if(OptionalChild c = IsChild(child, "parameters"))
390 const TreeNode& node = *c;
394 for(TreeNode::ConstIterator iter(node.CBegin()); iter != node.CEnd(); ++iter)
396 params[(*iter).first] = GetPropertyValue((*iter).second);
401 // Shim for the property notifcation signal
403 struct PropertyNotifcationSignalShim
407 PropertyNotifcationSignalShim(T& functor)
412 void operator()(PropertyNotification& /* source */)
418 // Specializations for the different signal connection calls between actor & PropertyNotification
420 struct SignalConnector
424 // Actor specialization
426 struct SignalConnector<Actor>
429 ConnectionTracker* mTracker;
430 const std::string& mName;
432 SignalConnector(ConnectionTracker* tracker, Actor& actor, const std::string& name)
440 void Connect(T& functor)
442 mActor.ConnectSignal(mTracker, mName, functor);
446 // PropertyNotification specialization
448 struct SignalConnector<PropertyNotification>
450 PropertyNotification& mNotification;
451 ConnectionTracker* mTracker;
453 SignalConnector(ConnectionTracker* tracker, PropertyNotification& notification)
454 : mNotification(notification),
460 void Connect(T& functor)
462 mNotification.NotifySignal().Connect(mTracker, PropertyNotifcationSignalShim<T>(functor));
467 * Set an action functor on a signal
470 void SetActionOnSignal(const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder, SignalConnector<T>& connector)
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"));
477 OptionalString actionName = IsString(IsChild(&child, "action"));
478 DALI_ASSERT_ALWAYS(actionName && "Signal must have an action");
482 ChildActorAction action;
483 action.actorName = *actorName;
484 action.childName = *childActorName;
485 action.actionName = *actionName;
486 GetParameters(child, action.parameters);
487 connector.Connect(action);
491 if(propertyName && valueChild && ("set" == *actionName))
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);
502 GenericAction action;
503 action.actorName = *actorName;
504 action.actionName = *actionName;
505 GetParameters(child, action.parameters);
506 connector.Connect(action);
509 else if("quit" == *actionName)
512 action.builder = builder;
513 connector.Connect(action);
515 else if("play" == *actionName)
517 OptionalChild animations = IsChild(root, "animations");
518 OptionalString animationName = IsString(IsChild(child, "animation"));
519 if(animations && animationName)
521 if(OptionalChild animNode = IsChild(*animations, *animationName))
523 DelayedAnimationPlay action;
524 action.animNode = animNode;
525 action.builder = builder;
526 // @todo; put constants into the map
527 connector.Connect(action);
531 DALI_SCRIPT_WARNING("Cannot find animation '%s'\n", (*animationName).c_str());
536 DALI_SCRIPT_WARNING("Cannot find animations section\n");
539 else if("applyConstraint" == *actionName)
541 OptionalString constrainerName = IsString(IsChild(child, "constrainer"));
544 DALI_SCRIPT_WARNING("Need to specify a constrainer\n");
548 DelayedConstrainerApply action;
549 action.constrainerName = *constrainerName;
550 action.builder = builder;
551 OptionalChild propertiesNode = IsChild(child, "properties");
554 const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
555 for(TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter)
557 const TreeNode::KeyNodePair& pKeyChild = *iter;
558 OptionalString sourceActorName(IsString(IsChild(pKeyChild.second, "source")));
561 DALI_SCRIPT_WARNING("Need to specify source actor to apply the constraint\n");
564 OptionalString sourcePropertyName(IsString(IsChild(pKeyChild.second, "sourceProperty")));
565 if(!sourcePropertyName)
567 DALI_SCRIPT_WARNING("Need to specify source property to apply the constraint\n");
571 OptionalString targetActorName(IsString(IsChild(pKeyChild.second, "target")));
574 DALI_SCRIPT_WARNING("Need to specify target actor to apply the constraint\n");
578 OptionalString targetPropertyName(IsString(IsChild(pKeyChild.second, "targetProperty")));
579 if(!targetPropertyName)
581 DALI_SCRIPT_WARNING("Need to specify target property name to apply the constraint\n");
585 OptionalVector2 range(IsVector2(IsChild(pKeyChild.second, "range")));
588 DALI_SCRIPT_WARNING("Constrainer range not specified\n");
592 Vector2 wrap(-std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
593 OptionalVector2 wrapRange(IsVector2(IsChild(pKeyChild.second, "wrap")));
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);
606 connector.Connect(action);
610 else if("removeConstraints" == *actionName)
612 OptionalString constrainerName = IsString(IsChild(child, "constrainer"));
615 DALI_SCRIPT_WARNING("Need to specify a constrainer\n");
619 DelayedConstrainerRemove action;
620 action.constrainerName = *constrainerName;
621 action.builder = builder;
622 OptionalChild propertiesNode = IsChild(child, "properties");
625 const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
626 for(TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter)
628 const TreeNode::KeyNodePair& pKeyChild = *iter;
629 OptionalString targetActorName(IsString(IsChild(pKeyChild.second, "target")));
632 action.targetActorNames.push_back(*targetActorName);
636 DALI_SCRIPT_WARNING("Need to specify target actor to remove the constraint\n");
641 connector.Connect(action);
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);
656 * Get a notification condition argument0 as 'arg0' 'value' or 'min'
658 float GetConditionArg0(const TreeNode& child)
660 OptionalFloat f = IsFloat(IsChild(child, "arg0"));
661 // allowing some human preferable alternatives
664 f = IsFloat(IsChild(child, "value"));
668 f = IsFloat(IsChild(child, "min"));
671 DALI_ASSERT_ALWAYS(f && "Notification condition for arg0 not specified");
677 * Get a notification condition argument1 as 'arg1' or 'max'
679 float GetConditionArg1(const TreeNode& child)
681 OptionalFloat f = IsFloat(IsChild(child, "arg1"));
682 // allowing some human preferable alternatives
685 f = IsFloat(IsChild(child, "max"));
688 DALI_ASSERT_ALWAYS(f && "Notification condition for arg1 not specified");
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);
705 * Setup signals and actions on an actor
707 Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder)
709 DALI_ASSERT_ALWAYS(actor);
711 if(OptionalChild signalsChild = IsChild(child, "signals"))
713 const TreeNode& signalsNode = *signalsChild;
714 const TreeConstIter endIter = signalsNode.CEnd();
715 for(TreeConstIter iter = signalsNode.CBegin(); endIter != iter; ++iter)
717 const TreeNode::KeyNodePair& key_child = *iter;
719 DALI_SCRIPT_INFO(" Creating Signal for: %s\n", actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str());
721 OptionalString name(IsString(IsChild(key_child.second, "name")));
722 DALI_ASSERT_ALWAYS(name && "Signal must have a name");
724 SignalConnector<Actor> connector(tracker, actor, *name);
725 SetActionOnSignal(root, key_child.second, actor, builder, connector);
733 * Setup Property notifications for an actor
735 Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode& root, const TreeNode& child, Actor actor, Dali::Toolkit::Internal::Builder* const builder)
737 DALI_ASSERT_ALWAYS(actor);
739 if(OptionalChild notificationsChild = IsChild(child, "notifications"))
741 const TreeNode& notificationsNode = *notificationsChild;
742 const TreeNode::ConstIterator endIter = notificationsNode.CEnd();
743 for(TreeNode::ConstIterator iter = notificationsNode.CBegin(); endIter != iter; ++iter)
745 const TreeNode::KeyNodePair& key_child = *iter;
747 OptionalString prop(IsString(IsChild(key_child.second, "property")));
748 DALI_ASSERT_ALWAYS(prop && "Notification signal must specify a property");
750 Property::Index prop_index = actor.GetPropertyIndex(*prop);
751 DALI_ASSERT_ALWAYS(prop_index != Property::INVALID_INDEX && "Notification signal specifies an unknown property");
753 OptionalString cond(IsString(IsChild(key_child.second, "condition")));
754 DALI_ASSERT_ALWAYS(cond && "Notification signal must specify a condition");
758 PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
759 LessThanCondition(1.f));
761 SignalConnector<PropertyNotification> connector(tracker, notification);
762 SetActionOnSignal(root, key_child.second, actor, builder, connector);
764 else if("LessThan" == *cond)
766 PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
767 LessThanCondition(GetConditionArg0(key_child.second)));
769 SignalConnector<PropertyNotification> connector(tracker, notification);
770 SetActionOnSignal(root, key_child.second, actor, builder, connector);
772 else if("GreaterThan" == *cond)
774 PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
775 GreaterThanCondition(GetConditionArg0(key_child.second)));
777 SignalConnector<PropertyNotification> connector(tracker, notification);
778 SetActionOnSignal(root, key_child.second, actor, builder, connector);
780 else if("Inside" == *cond)
782 PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
783 InsideCondition(GetConditionArg0(key_child.second),
784 GetConditionArg1(key_child.second)));
786 SignalConnector<PropertyNotification> connector(tracker, notification);
787 SetActionOnSignal(root, key_child.second, actor, builder, connector);
789 else if("Outside" == *cond)
791 PropertyNotification notification = actor.AddPropertyNotification(actor.GetPropertyIndex(*prop),
792 OutsideCondition(GetConditionArg0(key_child.second),
793 GetConditionArg1(key_child.second)));
795 SignalConnector<PropertyNotification> connector(tracker, notification);
796 SetActionOnSignal(root, key_child.second, actor, builder, connector);
800 DALI_ASSERT_ALWAYS(!"Unknown condition");
803 } // if notifications
807 } // AddPropertyNotification
809 } // namespace Internal
810 } // namespace Toolkit