X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fcontrol%2Fcontrol-data-impl.cpp;h=e67be8ed674f35c6dfc38405ffef6fc94e673e08;hp=b49f9d0c5e29ea5c7105a9d66865726bffe323bd;hb=HEAD;hpb=ca37fd65672a1f1628d0d09b8c39cb52f2acf66c diff --git a/dali-toolkit/internal/controls/control/control-data-impl.cpp b/dali-toolkit/internal/controls/control/control-data-impl.cpp index b49f9d0..5488957 100644 --- a/dali-toolkit/internal/controls/control/control-data-impl.cpp +++ b/dali-toolkit/internal/controls/control/control-data-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,17 +30,18 @@ #include #include #include +#include #include #include #include #include // INTERNAL INCLUDES -#include #include #include #include #include +#include #include #include #include @@ -51,12 +52,12 @@ namespace { -const std::string READING_INFO_TYPE_NAME = "name"; -const std::string READING_INFO_TYPE_ROLE = "role"; -const std::string READING_INFO_TYPE_DESCRIPTION = "description"; -const std::string READING_INFO_TYPE_STATE = "state"; -const std::string READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type"; -const std::string READING_INFO_TYPE_SEPARATOR = "|"; +const char* READING_INFO_TYPE_NAME = "name"; +const char* READING_INFO_TYPE_ROLE = "role"; +const char* READING_INFO_TYPE_DESCRIPTION = "description"; +const char* READING_INFO_TYPE_STATE = "state"; +const char* READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type"; +const char* READING_INFO_TYPE_SEPARATOR = "|"; } // namespace namespace Dali @@ -128,6 +129,38 @@ bool FindVisual(std::string visualName, const RegisteredVisualContainer& visuals return false; } +/** + * Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter + */ +bool FindVisual(const Toolkit::Visual::Base findVisual, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter) +{ + for(iter = visuals.Begin(); iter != visuals.End(); iter++) + { + Toolkit::Visual::Base visual = (*iter)->visual; + if(visual && visual == findVisual) + { + return true; + } + } + return false; +} + +/** + * Finds internal visual in given array, returning true if found along with the iterator for that visual as a out parameter + */ +bool FindVisual(const Visual::Base& findInternalVisual, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter) +{ + for(iter = visuals.Begin(); iter != visuals.End(); iter++) + { + Visual::Base& visual = Toolkit::GetImplementation((*iter)->visual); + if((&visual == &findInternalVisual)) + { + return true; + } + } + return false; +} + void FindChangableVisuals(Dictionary& stateVisualsToAdd, Dictionary& stateVisualsToChange, DictionaryKeys& stateVisualsToRemove) @@ -200,6 +233,23 @@ void MoveVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVisual } /** + * Discard visual from source to visual factory. + */ +void DiscardVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVisualContainer& source) +{ + Toolkit::Visual::Base visual = (*sourceIter)->visual; + if(visual) + { + if(Stage::IsInstalled()) + { + Toolkit::VisualFactory::Get().DiscardVisual(visual); + } + } + + source.Erase(sourceIter); +} + +/** * Performs actions as requested using the action name. * @param[in] object The object on which to perform the action. * @param[in] actionName The action to perform. @@ -448,7 +498,7 @@ static bool IsShowingGeometryOnScreen(Dali::Rect<> rect) Dali::Accessibility::Accessible* ExternalAccessibleGetter(Dali::Actor actor) { auto control = Toolkit::Control::DownCast(actor); - if (!control) + if(!control) { return nullptr; } @@ -485,6 +535,7 @@ const PropertyRegistration Control::Impl::PROPERTY_22(typeRegistration, "dispatc const PropertyRegistration Control::Impl::PROPERTY_23(typeRegistration, "accessibilityHidden", Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, Property::BOOLEAN, &Control::Impl::SetProperty, &Control::Impl::GetProperty); const PropertyRegistration Control::Impl::PROPERTY_24(typeRegistration, "clockwiseFocusableActorId", Toolkit::DevelControl::Property::CLOCKWISE_FOCUSABLE_ACTOR_ID, Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty); const PropertyRegistration Control::Impl::PROPERTY_25(typeRegistration, "counterClockwiseFocusableActorId", Toolkit::DevelControl::Property::COUNTER_CLOCKWISE_FOCUSABLE_ACTOR_ID, Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty); +const PropertyRegistration Control::Impl::PROPERTY_26(typeRegistration, "automationId", Toolkit::DevelControl::Property::AUTOMATION_ID, Property::STRING, &Control::Impl::SetProperty, &Control::Impl::GetProperty); // clang-format on @@ -522,7 +573,7 @@ Control::Impl::Impl(Control& controlImpl) mIsKeyboardNavigationSupported(false), mIsKeyboardFocusGroup(false), mIsEmittingResourceReadySignal(false), - mNeedToEmitResourceReady(false), + mIdleCallbackRegistered(false), mDispatchKeyEvents(true) { Dali::Accessibility::Accessible::RegisterExternalAccessibleGetter(&ExternalAccessibleGetter); @@ -552,68 +603,75 @@ Control::Impl::~Impl() Control::Impl& Control::Impl::Get(Internal::Control& internalControl) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + return *internalControl.mImpl; } const Control::Impl& Control::Impl::Get(const Internal::Control& internalControl) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + return *internalControl.mImpl; } void Control::Impl::CheckHighlightedObjectGeometry() { - auto accessible = GetAccessibleObject(); - auto lastPosition = accessible->GetLastPosition(); - auto accessibleRect = accessible->GetExtents(Dali::Accessibility::CoordinateType::WINDOW); - auto rect = GetShowingGeometry(accessibleRect, accessible); - - switch(mAccessibilityLastScreenRelativeMoveType) + auto accessible = GetAccessibleObject(); + if(DALI_LIKELY(accessible)) { - case Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE: - { - if(IsShowingGeometryOnScreen(rect)) - { - mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE; - } - break; - } - case Dali::Accessibility::ScreenRelativeMoveType::INSIDE: + auto lastPosition = accessible->GetLastPosition(); + auto accessibleRect = accessible->GetExtents(Dali::Accessibility::CoordinateType::WINDOW); + auto rect = GetShowingGeometry(accessibleRect, accessible); + + switch(mAccessibilityLastScreenRelativeMoveType) { - if(rect.width < 0 && accessibleRect.x != lastPosition.x) - { - mAccessibilityLastScreenRelativeMoveType = (accessibleRect.x < lastPosition.x) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT; - } - if(rect.height < 0 && accessibleRect.y != lastPosition.y) + case Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE: { - mAccessibilityLastScreenRelativeMoveType = (accessibleRect.y < lastPosition.y) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT; + if(IsShowingGeometryOnScreen(rect)) + { + mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE; + } + break; } - // notify AT-clients on outgoing moves only - if(mAccessibilityLastScreenRelativeMoveType != Dali::Accessibility::ScreenRelativeMoveType::INSIDE) + case Dali::Accessibility::ScreenRelativeMoveType::INSIDE: { - accessible->EmitMovedOutOfScreen(mAccessibilityLastScreenRelativeMoveType); + if(rect.width < 0 && !Dali::Equals(accessibleRect.x, lastPosition.x)) + { + mAccessibilityLastScreenRelativeMoveType = (accessibleRect.x < lastPosition.x) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT; + } + if(rect.height < 0 && !Dali::Equals(accessibleRect.y, lastPosition.y)) + { + mAccessibilityLastScreenRelativeMoveType = (accessibleRect.y < lastPosition.y) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT; + } + // notify AT-clients on outgoing moves only + if(mAccessibilityLastScreenRelativeMoveType != Dali::Accessibility::ScreenRelativeMoveType::INSIDE) + { + accessible->EmitMovedOutOfScreen(mAccessibilityLastScreenRelativeMoveType); + } + break; } - break; - } - case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT: - case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT: - { - if(IsShowingGeometryOnScreen(rect)) + case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT: + case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT: { - mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE; + if(IsShowingGeometryOnScreen(rect)) + { + mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE; + } + else + { + mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE; + } + break; } - else + default: { - mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE; + break; } - break; } - default: - { - break; - } - } - accessible->SetLastPosition(Vector2(accessibleRect.x, accessibleRect.y)); + accessible->SetLastPosition(Vector2(accessibleRect.x, accessibleRect.y)); + } } void Control::Impl::RegisterAccessibilityPositionPropertyNotification() @@ -626,9 +684,9 @@ void Control::Impl::RegisterAccessibilityPositionPropertyNotification() mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE; // recalculate mAccessibilityLastScreenRelativeMoveType accordingly to the initial position CheckHighlightedObjectGeometry(); - mAccessibilityPositionNotification = mControlImpl.Self().AddPropertyNotification(Actor::Property::WORLD_POSITION, StepCondition(1.0f, 1.0f)); + mAccessibilityPositionNotification = mControlImpl.Self().AddPropertyNotification(Actor::Property::WORLD_POSITION, StepCondition(1.0f, 1.0f)); mAccessibilityPositionNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED); - mAccessibilityPositionNotification.NotifySignal().Connect(this, [this](PropertyNotification&){ CheckHighlightedObjectGeometry(); }); + mAccessibilityPositionNotification.NotifySignal().Connect(this, [this](PropertyNotification&) { CheckHighlightedObjectGeometry(); }); mIsAccessibilityPositionPropertyNotificationSet = true; } @@ -638,6 +696,49 @@ void Control::Impl::UnregisterAccessibilityPositionPropertyNotification() mIsAccessibilityPositionPropertyNotificationSet = false; } +void Control::Impl::RegisterAccessibilityPropertySetSignal() +{ + if(mIsAccessibilityPropertySetSignalRegistered) + { + return; + } + mControlImpl.Self().PropertySetSignal().Connect(this, &Control::Impl::OnAccessibilityPropertySet); + mIsAccessibilityPropertySetSignalRegistered = true; +} + +void Control::Impl::UnregisterAccessibilityPropertySetSignal() +{ + if(!mIsAccessibilityPropertySetSignalRegistered) + { + return; + } + mControlImpl.Self().PropertySetSignal().Disconnect(this, &Control::Impl::OnAccessibilityPropertySet); + mIsAccessibilityPropertySetSignalRegistered = false; +} + +void Control::Impl::OnAccessibilityPropertySet(Dali::Handle& handle, Dali::Property::Index index, const Dali::Property::Value& value) +{ + auto* accessible = GetAccessibleObject(); + if(DALI_LIKELY(accessible)) + { + if(mAccessibilityGetNameSignal.Empty()) + { + if(index == DevelControl::Property::ACCESSIBILITY_NAME || (mAccessibilityName.empty() && index == accessible->GetNamePropertyIndex())) + { + accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::NAME); + } + } + + if(mAccessibilityGetDescriptionSignal.Empty()) + { + if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (mAccessibilityDescription.empty() && index == accessible->GetDescriptionPropertyIndex())) + { + accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION); + } + } + } +} + // Gesture Detection Methods void Control::Impl::PinchDetected(Actor actor, const PinchGesture& pinch) { @@ -681,6 +782,8 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& visual, VisualState::Type enabled, DepthIndexValue::Type depthIndexValueSet, int depthIndex) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + DALI_LOG_INFO(gLogFilter, Debug::Concise, "RegisterVisual:%d \n", index); bool visualReplaced(false); @@ -695,6 +798,10 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& requiredDepthIndex = depthIndex; } + // Change the depth index value automatically if the visual has DepthIndex to AUTO_INDEX + // or if RegisterVisual set DepthIndex to AUTO_INDEX. + const bool requiredDepthIndexChanged = (requiredDepthIndex == DepthIndex::AUTO_INDEX); + // Visual replacement, existing visual should only be removed from stage when replacement ready. if(!mVisuals.Empty()) { @@ -736,11 +843,11 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& mVisuals.Erase(registeredVisualsiter); } - // If we've not set the depth-index value and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index - if((depthIndexValueSet == DepthIndexValue::NOT_SET) && - (visual.GetDepthIndex() == 0)) + // If the visual have a depth index as AUTO_INDEX and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index + if(requiredDepthIndexChanged) { requiredDepthIndex = currentDepthIndex; + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Use replaced visual index. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex); } } @@ -763,12 +870,10 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& if(!visualReplaced) // New registration entry { - // If we've not set the depth-index value, we have more than one visual and the visual does not have a depth index, then set it to be the highest - if((depthIndexValueSet == DepthIndexValue::NOT_SET) && - (mVisuals.Size() > 0) && - (visual.GetDepthIndex() == 0)) + // If we have more than one visual and the visual have a depth index as AUTO_INDEX, then set it to be the highest + if((mVisuals.Size() > 0) && requiredDepthIndexChanged) { - int maxDepthIndex = std::numeric_limits::min(); + int maxDepthIndex = static_cast(DepthIndex::CONTENT) - 1; // Start at DepthIndex::CONTENT if maxDepth index belongs to a background or no visuals have been added yet. RegisteredVisualContainer::ConstIterator iter; const RegisteredVisualContainer::ConstIterator endIter = mVisuals.End(); @@ -780,13 +885,20 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& maxDepthIndex = visualDepthIndex; } } - ++maxDepthIndex; // Add one to the current maximum depth index so that our added visual appears on top - requiredDepthIndex = std::max(0, maxDepthIndex); // Start at zero if maxDepth index belongs to a background + requiredDepthIndex = ++maxDepthIndex; // Add one to the current maximum depth index so that our added visual appears on top. + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Use top of all visuals. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex); } } if(visual) { + // If required depth index still DepthIndex::AUTO_INDEX, Make it as DepthIndex::CONTENT now + if(requiredDepthIndex == static_cast(DepthIndex::AUTO_INDEX)) + { + requiredDepthIndex = static_cast(DepthIndex::CONTENT); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Some strange cases. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex); + } + // Set determined depth index visual.SetDepthIndex(requiredDepthIndex); @@ -803,7 +915,7 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& { visualImpl.SetOnScene(self); } - else if(visualImpl.IsResourceReady()) // When not being staged, check if visual already 'ResourceReady' before it was Registered. ( Resource may have been loaded already ) + else if(enabled && visualImpl.IsResourceReady()) // When not being staged, check if visual already 'ResourceReady' before it was Registered. ( Resource may have been loaded already ) { ResourceReady(visualImpl); } @@ -814,6 +926,8 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& void Control::Impl::UnregisterVisual(Property::Index index) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + RegisteredVisualContainer::Iterator iter; if(FindVisual(index, mVisuals, iter)) { @@ -831,8 +945,9 @@ void Control::Impl::UnregisterVisual(Property::Index index) Actor self(mControlImpl.Self()); Toolkit::GetImplementation((*iter)->visual).SetOffScene(self); (*iter)->pending = false; - (*iter)->visual.Reset(); - mRemoveVisuals.Erase(iter); + + // Discard removed visual. It will be destroyed at next Idle time. + DiscardVisual(iter, mRemoveVisuals); } } @@ -892,6 +1007,23 @@ bool Control::Impl::IsVisualEnabled(Property::Index index) const return false; } +void Control::Impl::EnableReadyTransitionOverriden(Toolkit::Visual::Base& visual, bool enable) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "Control::EnableReadyTransitionOverriden(%p, %s)\n", visual, enable ? "T" : "F"); + + RegisteredVisualContainer::Iterator iter; + if(FindVisual(visual, mVisuals, iter)) + { + if((*iter)->overideReadyTransition == enable) + { + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::EnableReadyTransitionOverriden Visual %s(%p) already %s\n", (*iter)->visual.GetName().c_str(), visual, enable ? "enabled" : "disabled"); + return; + } + + (*iter)->overideReadyTransition = enable; + } +} + void Control::Impl::StopObservingVisual(Toolkit::Visual::Base& visual) { Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); @@ -908,6 +1040,17 @@ void Control::Impl::StartObservingVisual(Toolkit::Visual::Base& visual) visualImpl.AddEventObserver(*this); } +void Control::Impl::ResourceReady() +{ + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + + // Emit signal if all enabled visuals registered by the control are ready or there are no visuals. + if(IsResourceReady()) + { + EmitResourceReadySignal(); + } +} + // Called by a Visual when it's resource is ready void Control::Impl::ResourceReady(Visual::Base& object) { @@ -915,41 +1058,38 @@ void Control::Impl::ResourceReady(Visual::Base& object) Actor self = mControlImpl.Self(); + RegisteredVisualContainer::Iterator registeredIter; + // A resource is ready, find resource in the registered visuals container and get its index - for(auto registeredIter = mVisuals.Begin(), end = mVisuals.End(); registeredIter != end; ++registeredIter) + if(!FindVisual(object, mVisuals, registeredIter)) { - Internal::Visual::Base& registeredVisualImpl = Toolkit::GetImplementation((*registeredIter)->visual); + return; + } - if(&object == ®isteredVisualImpl) + RegisteredVisualContainer::Iterator visualToRemoveIter; + // Find visual with the same index in the removal container + // Set if off stage as it's replacement is now ready. + // Remove if from removal list as now removed from stage. + // Set Pending flag on the ready visual to false as now ready. + if(FindVisual((*registeredIter)->index, mRemoveVisuals, visualToRemoveIter)) + { + (*registeredIter)->pending = false; + if(!((*visualToRemoveIter)->overideReadyTransition)) { - RegisteredVisualContainer::Iterator visualToRemoveIter; - // Find visual with the same index in the removal container - // Set if off stage as it's replacement is now ready. - // Remove if from removal list as now removed from stage. - // Set Pending flag on the ready visual to false as now ready. - if(FindVisual((*registeredIter)->index, mRemoveVisuals, visualToRemoveIter)) - { - (*registeredIter)->pending = false; - Toolkit::GetImplementation((*visualToRemoveIter)->visual).SetOffScene(self); - mRemoveVisuals.Erase(visualToRemoveIter); - } - break; + Toolkit::GetImplementation((*visualToRemoveIter)->visual).SetOffScene(self); } + + // Discard removed visual. It will be destroyed at next Idle time. + DiscardVisual(visualToRemoveIter, mRemoveVisuals); } // A visual is ready so control may need relayouting if staged - if(self.GetProperty(Actor::Property::CONNECTED_TO_SCENE)) - { - mControlImpl.RelayoutRequest(); - } + RelayoutRequest(object); - // Emit signal if all enabled visuals registered by the control are ready. - if(IsResourceReady()) + // Called by a Visual when it's resource is ready + if(((*registeredIter)->enabled)) { - // Reset the flag - mNeedToEmitResourceReady = false; - - EmitResourceReadySignal(); + ResourceReady(); } } @@ -967,6 +1107,14 @@ void Control::Impl::NotifyVisualEvent(Visual::Base& object, Property::Index sign } } +void Control::Impl::RelayoutRequest(Visual::Base& object) +{ + if(mControlImpl.Self().GetProperty(Actor::Property::CONNECTED_TO_SCENE)) + { + mControlImpl.RelayoutRequest(); + } +} + bool Control::Impl::IsResourceReady() const { // Iterate through and check all the enabled visuals are ready @@ -1090,6 +1238,15 @@ void Control::Impl::DoAction(Dali::Property::Index visualIndex, Dali::Property:: } } +void Control::Impl::DoActionExtension(Dali::Property::Index visualIndex, Dali::Property::Index actionId, Dali::Any attributes) +{ + RegisteredVisualContainer::Iterator iter; + if(FindVisual(visualIndex, mVisuals, iter)) + { + Toolkit::GetImplementation((*iter)->visual).DoActionExtension(actionId, attributes); + } +} + void Control::Impl::AppendAccessibilityAttribute(const std::string& key, const std::string value) { Property::Value* checkedValue = mAccessibilityAttributes.Find(key); @@ -1105,6 +1262,8 @@ void Control::Impl::AppendAccessibilityAttribute(const std::string& key, const s void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + Toolkit::Control control = Toolkit::Control::DownCast(BaseHandle(object)); if(control) @@ -1363,10 +1522,13 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const controlImpl.mImpl->mAccessibilityHidden = hidden; auto* accessible = controlImpl.GetAccessibleObject(); - auto* parent = dynamic_cast(accessible->GetParent()); - if (parent) + if(DALI_LIKELY(accessible)) { - parent->OnChildrenChanged(); + auto* parent = dynamic_cast(accessible->GetParent()); + if(parent) + { + parent->OnChildrenChanged(); + } } } break; @@ -1389,12 +1551,24 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const } break; } + + case Toolkit::DevelControl::Property::AUTOMATION_ID: + { + std::string automationId; + if(value.Get(automationId)) + { + controlImpl.mImpl->mAutomationId = automationId; + } + break; + } } } } Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index index) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + Property::Value value; Toolkit::Control control = Toolkit::Control::DownCast(BaseHandle(object)); @@ -1561,6 +1735,12 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i value = controlImpl.mImpl->mCounterClockwiseFocusableActorId; break; } + + case Toolkit::DevelControl::Property::AUTOMATION_ID: + { + value = controlImpl.mImpl->mAutomationId; + break; + } } } @@ -1760,6 +1940,8 @@ void Control::Impl::RecreateChangedVisuals(Dictionary& stateVisua void Control::Impl::ReplaceStateVisualsAndProperties(const StylePtr oldState, const StylePtr newState, const std::string& subState) { + DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?"); + // Collect all old visual names DictionaryKeys stateVisualsToRemove; if(oldState) @@ -1897,17 +2079,25 @@ void Control::Impl::OnSceneDisconnection() // Visuals pending replacement can now be taken out of the removal list and set off scene // Iterate through all replacement visuals and add to a move queue then set off scene - for(auto removalIter = mRemoveVisuals.Begin(), end = mRemoveVisuals.End(); removalIter != end; removalIter++) + + if(!mRemoveVisuals.Empty()) { - Toolkit::GetImplementation((*removalIter)->visual).SetOffScene(self); + std::reverse(mRemoveVisuals.Begin(), mRemoveVisuals.End()); + + while(!mRemoveVisuals.Empty()) + { + auto removalIter = mRemoveVisuals.End() - 1u; + Toolkit::GetImplementation((*removalIter)->visual).SetOffScene(self); + + // Discard removed visual. It will be destroyed at next Idle time. + DiscardVisual(removalIter, mRemoveVisuals); + } } for(auto replacedIter = mVisuals.Begin(), end = mVisuals.End(); replacedIter != end; replacedIter++) { (*replacedIter)->pending = false; } - - mRemoveVisuals.Clear(); } void Control::Impl::SetMargin(Extents margin) @@ -1984,7 +2174,7 @@ Dali::Property Control::Impl::GetVisualProperty(Dali::Property::Index index, Dal if(visual) { Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); - return visualImpl.GetPropertyObject(visualPropertyKey); + return visualImpl.GetPropertyObject(std::move(visualPropertyKey)); } Handle handle; @@ -1993,7 +2183,8 @@ Dali::Property Control::Impl::GetVisualProperty(Dali::Property::Index index, Dal void Control::Impl::CreateTransitions(std::vector>& sourceProperties, std::vector>& destinationProperties, - Dali::Toolkit::Control source, Dali::Toolkit::Control destination) + Dali::Toolkit::Control source, + Dali::Toolkit::Control destination) { // Retrieves background properties to be transitioned. Dali::Property::Map backgroundSourcePropertyMap, backgroundDestinationPropertyMap; @@ -2041,53 +2232,61 @@ void Control::Impl::EmitResourceReadySignal() 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 + // get called again recursively. If so, mIdleCallbackRegistered 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) + mIsEmittingResourceReadySignal = false; + } + else + { + if(!mIdleCallbackRegistered) { + mIdleCallbackRegistered = true; + // 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); + if(DALI_UNLIKELY(!Adaptor::Get().AddIdle(mIdleCallback, true))) + { + DALI_LOG_ERROR("Fail to add idle callback for control resource ready. Skip this callback.\n"); + mIdleCallback = nullptr; + mIdleCallbackRegistered = false; + } } } - - mIsEmittingResourceReadySignal = false; - } - else - { - mNeedToEmitResourceReady = true; } } -void Control::Impl::OnIdleCallback() +bool Control::Impl::OnIdleCallback() { - if(mNeedToEmitResourceReady) + // Reset the flag + mIdleCallbackRegistered = false; + + // A visual is ready so control may need relayouting if staged + if(mControlImpl.Self().GetProperty(Actor::Property::CONNECTED_TO_SCENE)) { - // Reset the flag - mNeedToEmitResourceReady = false; + mControlImpl.RelayoutRequest(); + } - // A visual is ready so control may need relayouting if staged - if(mControlImpl.Self().GetProperty(Actor::Property::CONNECTED_TO_SCENE)) - { - mControlImpl.RelayoutRequest(); - } + EmitResourceReadySignal(); - EmitResourceReadySignal(); + if(!mIdleCallbackRegistered) + { + // Set the pointer to null as the callback manager deletes the callback after execute it. + mIdleCallback = nullptr; } - // Set the pointer to null as the callback manager deletes the callback after execute it. - mIdleCallback = nullptr; + // Repeat idle if mIdleCallbackRegistered become true one more time. + return mIdleCallbackRegistered; } Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject() { - if(!mAccessibleObject) + if(mAccessibleCreatable && !mAccessibleObject) { mAccessibleObject.reset(mControlImpl.CreateAccessibleObject()); } @@ -2095,6 +2294,21 @@ Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject() return mAccessibleObject.get(); } +bool Control::Impl::IsAccessibleCreated() const +{ + return !!mAccessibleObject; +} + +void Control::Impl::EnableCreateAccessible(bool enable) +{ + mAccessibleCreatable = enable; +} + +bool Control::Impl::IsCreateAccessibleEnabled() const +{ + return mAccessibleCreatable; +} + } // namespace Internal } // namespace Toolkit