+void Control::Impl::SetShadow(const Property::Map& map)
+{
+ Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(map);
+ visual.SetName("shadow");
+
+ if(visual)
+ {
+ mControlImpl.mImpl->RegisterVisual(Toolkit::DevelControl::Property::SHADOW, visual, DepthIndex::BACKGROUND_EFFECT);
+
+ mControlImpl.RelayoutRequest();
+ }
+}
+
+void Control::Impl::ClearShadow()
+{
+ mControlImpl.mImpl->UnregisterVisual(Toolkit::DevelControl::Property::SHADOW);
+
+ // Trigger a size negotiation request that may be needed when unregistering a visual.
+ mControlImpl.RelayoutRequest();
+}
+
+Dali::Property Control::Impl::GetVisualProperty(Dali::Property::Index index, Dali::Property::Key visualPropertyKey)
+{
+ Toolkit::Visual::Base visual = GetVisualByIndex(mVisuals, index);
+ if(visual)
+ {
+ Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
+ return visualImpl.GetPropertyObject(visualPropertyKey);
+ }
+
+ Handle handle;
+ return Dali::Property(handle, Property::INVALID_INDEX);
+}
+
+void Control::Impl::EmitResourceReadySignal()
+{
+ if(!mIsEmittingResourceReadySignal)
+ {
+ // Guard against calls to emit the signal during the callback
+ mIsEmittingResourceReadySignal = true;
+
+ // If the signal handler changes visual, it may become ready during this call & therefore this method will
+ // get called again recursively. If so, mNeedToEmitResourceReady is set below, and we act on it after that secondary
+ // invocation has completed by notifying in an Idle callback to prevent further recursion.
+ Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+ mResourceReadySignal.Emit(handle);
+
+ if(mNeedToEmitResourceReady)
+ {
+ // Add idler to emit the signal again
+ if(!mIdleCallback)
+ {
+ // The callback manager takes the ownership of the callback object.
+ mIdleCallback = MakeCallback(this, &Control::Impl::OnIdleCallback);
+ Adaptor::Get().AddIdle(mIdleCallback, false);
+ }
+ }
+
+ mIsEmittingResourceReadySignal = false;
+ }
+ else
+ {
+ mNeedToEmitResourceReady = true;
+ }
+}
+
+void Control::Impl::OnIdleCallback()
+{
+ if(mNeedToEmitResourceReady)
+ {
+ // Reset the flag
+ mNeedToEmitResourceReady = false;
+
+ // A visual is ready so control may need relayouting if staged
+ if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+ {
+ mControlImpl.RelayoutRequest();
+ }
+
+ EmitResourceReadySignal();
+ }
+
+ // Set the pointer to null as the callback manager deletes the callback after execute it.
+ mIdleCallback = nullptr;
+}
+
+Dali::Accessibility::Accessible* Control::Impl::GetAccessibilityObject()
+{
+ if(!accessibilityObject)
+ accessibilityObject = accessibilityConstructor(mControlImpl.Self());
+ return accessibilityObject.get();
+}
+
+Dali::Accessibility::Accessible* Control::Impl::GetAccessibilityObject(Dali::Actor actor)
+{
+ if(actor)
+ {
+ auto q = Dali::Toolkit::Control::DownCast(actor);
+ if(q)
+ {
+ auto q2 = static_cast<Internal::Control*>(&q.GetImplementation());
+ return q2->mImpl->GetAccessibilityObject();
+ }
+ }
+ return nullptr;
+}
+
+void Control::Impl::PositionOrSizeChangedCallback(PropertyNotification& p)
+{
+ auto self = Dali::Actor::DownCast(p.GetTarget());
+ if(Dali::Accessibility::IsUp() && !self.GetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ANIMATED).Get<bool>())
+ {
+ auto extents = DevelActor::CalculateScreenExtents(self);
+ Dali::Accessibility::Accessible::Get(self)->EmitBoundsChanged(extents);
+ }
+}
+
+void Control::Impl::CulledChangedCallback(PropertyNotification& p)
+{
+ if(Dali::Accessibility::IsUp())
+ {
+ auto self = Dali::Actor::DownCast(p.GetTarget());
+ Dali::Accessibility::Accessible::Get(self)->EmitShowing(!self.GetProperty(DevelActor::Property::CULLED).Get<bool>());
+ }
+}
+
+void Control::Impl::AccessibilityRegister()
+{
+ if(!accessibilityNotificationSet)
+ {
+ accessibilityNotificationPosition = mControlImpl.Self().AddPropertyNotification(Actor::Property::POSITION, StepCondition(0.01f));
+ accessibilityNotificationPosition.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
+ accessibilityNotificationPosition.NotifySignal().Connect(&Control::Impl::PositionOrSizeChangedCallback);
+
+ accessibilityNotificationSize = mControlImpl.Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(0.01f));
+ accessibilityNotificationSize.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
+ accessibilityNotificationSize.NotifySignal().Connect(&Control::Impl::PositionOrSizeChangedCallback);
+
+ accessibilityNotificationCulled = mControlImpl.Self().AddPropertyNotification(DevelActor::Property::CULLED, LessThanCondition(0.5f));
+ accessibilityNotificationCulled.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
+ accessibilityNotificationCulled.NotifySignal().Connect(&Control::Impl::CulledChangedCallback);
+
+ accessibilityNotificationSet = true;
+ }
+}
+
+void Control::Impl::AccessibilityDeregister(bool remove)
+{
+ if(accessibilityNotificationSet)
+ {
+ accessibilityNotificationPosition.NotifySignal().Disconnect(&Control::Impl::PositionOrSizeChangedCallback);
+ if(remove)
+ {
+ mControlImpl.Self().RemovePropertyNotification(accessibilityNotificationPosition);
+ }
+ accessibilityNotificationPosition.Reset();
+ accessibilityNotificationPosition = {};
+
+ accessibilityNotificationSize.NotifySignal().Disconnect(&Control::Impl::PositionOrSizeChangedCallback);
+ if(remove)
+ {
+ mControlImpl.Self().RemovePropertyNotification(accessibilityNotificationSize);
+ }
+ accessibilityNotificationSize.Reset();
+ accessibilityNotificationSize = {};
+
+ accessibilityNotificationCulled.NotifySignal().Disconnect(&Control::Impl::CulledChangedCallback);
+ if(remove)
+ {
+ mControlImpl.Self().RemovePropertyNotification(accessibilityNotificationCulled);
+ }
+ accessibilityNotificationCulled.Reset();
+ accessibilityNotificationCulled = {};
+ accessibilityNotificationSet = false;
+ }
+}
+