From affeee96ffdf1bd3a8754fedfac4695657d81679 Mon Sep 17 00:00:00 2001 From: Youngsun Suh Date: Tue, 15 Oct 2024 16:51:00 +0900 Subject: [PATCH] [Tizen] Re-enable a11y refactor + includeHidden w/ crash fix Revert "Revert "[Tizen] Add IncludeHidden interface"" This reverts commit b6b30e6edbb0cc78090d1cae94deaae7928bc51b. Prevent crash when a11y bridge is disabled Change-Id: Iba2bd83d2623ead9e907cd1a6d0ffe4f9e4548db --- .../adaptor-framework/accessibility-bridge.h | 52 +++++++----- dali/devel-api/adaptor-framework/accessibility.cpp | 78 ++++++++++-------- dali/devel-api/adaptor-framework/accessibility.h | 10 --- .../adaptor-framework/actor-accessible.cpp | 17 +++- .../devel-api/adaptor-framework/actor-accessible.h | 8 +- dali/devel-api/atspi-interfaces/accessible.h | 22 ++--- dali/devel-api/atspi-interfaces/application.h | 16 +++- dali/internal/accessibility/bridge/accessible.cpp | 6 +- .../accessibility/bridge/bridge-application.cpp | 18 ++++- .../accessibility/bridge/bridge-application.h | 17 +++- dali/internal/accessibility/bridge/bridge-base.cpp | 37 ++++++--- dali/internal/accessibility/bridge/bridge-base.h | 24 +++++- dali/internal/accessibility/bridge/bridge-impl.cpp | 94 +++++++++++++--------- .../accessibility/bridge/bridge-object.cpp | 39 ++++----- dali/internal/accessibility/bridge/bridge-object.h | 6 +- .../accessibility/bridge/dummy/dummy-atspi.h | 36 ++++++--- dali/internal/adaptor/common/adaptor-impl.cpp | 19 ----- dali/internal/adaptor/common/adaptor-impl.h | 7 -- dali/internal/adaptor/common/application-impl.cpp | 1 - .../tizen-wayland/widget-controller-tizen.cpp | 15 ++-- dali/internal/window-system/common/window-impl.cpp | 57 ++++++++----- 21 files changed, 357 insertions(+), 222 deletions(-) diff --git a/dali/devel-api/adaptor-framework/accessibility-bridge.h b/dali/devel-api/adaptor-framework/accessibility-bridge.h index ac827cb..8184223 100644 --- a/dali/devel-api/adaptor-framework/accessibility-bridge.h +++ b/dali/devel-api/adaptor-framework/accessibility-bridge.h @@ -62,6 +62,31 @@ struct DALI_ADAPTOR_API Bridge virtual ~Bridge() = default; /** + * @brief Adds the accessible object associated with given actorId to the brige. + * + * @param[in] actorId The actorId assosiated with the accessible + * @param[in] accessible The accessible object + * + * @return true if given accessible is added to the bridge + */ + virtual bool AddAccessible(uint32_t actorId, std::shared_ptr accessible) = 0; + + /** + * @brief Removed the accessible object associated with given actorId from the brige. + */ + virtual void RemoveAccessible(uint32_t actorId) = 0; + + /** + * @brief Gets the accessible object associated with given actor from the brige. + */ + virtual std::shared_ptr GetAccessible(Actor actor) const = 0; + + /** + * @brief Returns true if GetChildren should include hidden objects; false otherwise. + */ + virtual bool ShouldIncludeHidden() const = 0; + + /** * @brief Gets bus name which bridge is initialized on. * * @return The bus name @@ -98,7 +123,7 @@ struct DALI_ADAPTOR_API Bridge * * @param[in] object The accessible object */ - virtual void RegisterDefaultLabel(Accessible* object) = 0; + virtual void RegisterDefaultLabel(std::shared_ptr object) = 0; /** * @brief Removes object from the stack of "default label" sourcing objects. @@ -107,7 +132,7 @@ struct DALI_ADAPTOR_API Bridge * * @param[in] object The accessible object */ - virtual void UnregisterDefaultLabel(Accessible* object) = 0; + virtual void UnregisterDefaultLabel(std::shared_ptr object) = 0; /** * @brief Gets the top-most object from the stack of "default label" sourcing objects. @@ -125,7 +150,7 @@ struct DALI_ADAPTOR_API Bridge * Following strings are valid values for "default_label" attribute: "enabled", "disabled". * Any other value will be interpreted as "enabled". */ - virtual Accessible* GetDefaultLabel(Accessible* root) const = 0; + virtual Accessible* GetDefaultLabel(Accessible* root) = 0; /** * @brief Sets name of current application which will be visible on accessibility bus. @@ -292,7 +317,7 @@ struct DALI_ADAPTOR_API Bridge * @param[in] newValue Whether the state value is changed to new value or not. * @param[in] reserved Reserved. (Currently, this argument is not implemented in dali) **/ - virtual void EmitStateChanged(Accessible* obj, State state, int newValue, int reserved = 0) = 0; + virtual void EmitStateChanged(std::shared_ptr obj, State state, int newValue, int reserved = 0) = 0; /** * @brief Emits window event on at-spi bus. @@ -309,7 +334,7 @@ struct DALI_ADAPTOR_API Bridge * @param[in] obj The accessible object * @param[in] event Property changed event **/ - virtual void Emit(Accessible* obj, ObjectPropertyChangeEvent event) = 0; + virtual void Emit(std::shared_ptr obj, ObjectPropertyChangeEvent event) = 0; /** * @brief Emits bounds-changed event on at-spi bus. @@ -317,22 +342,7 @@ struct DALI_ADAPTOR_API Bridge * @param[in] obj The accessible object * @param[in] rect The rectangle for changed bounds **/ - virtual void EmitBoundsChanged(Accessible* obj, Rect<> rect) = 0; - - /** - * @brief Emits key event on at-spi bus. - * - * Screen-reader might receive this event and reply, that given keycode is consumed. In that case - * further processing of the keycode should be ignored. - * - * @param[in] type Key event type - * @param[in] keyCode Key code - * @param[in] keyName Key name - * @param[in] timeStamp Time stamp - * @param[in] isText Whether it's text or not - * @return Whether this event is consumed or not - **/ - virtual Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) = 0; + virtual void EmitBoundsChanged(std::shared_ptr obj, Rect<> rect) = 0; /** * @brief Reads given text by screen reader diff --git a/dali/devel-api/adaptor-framework/accessibility.cpp b/dali/devel-api/adaptor-framework/accessibility.cpp index 3a6475f..b570840 100644 --- a/dali/devel-api/adaptor-framework/accessibility.cpp +++ b/dali/devel-api/adaptor-framework/accessibility.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -421,10 +420,8 @@ namespace class AdaptorAccessible : public ActorAccessible { private: - std::unique_ptr mRenderNotification = nullptr; - -protected: - bool mRoot = false; + std::unique_ptr mRenderNotification{nullptr}; + bool mRoot{false}; public: AdaptorAccessible(Dali::Actor actor, bool isRoot) @@ -595,54 +592,71 @@ public: }; // AdaptorAccessible -using AdaptorAccessiblesType = std::map>; - -// Save RefObject from an Actor in Accessible::Get() -AdaptorAccessiblesType gAdaptorAccessibles; +using ConvertingResult = std::pair, bool>; -std::function convertingFunctor = [](Dali::Actor) -> Accessible* { - return nullptr; +std::function convertingFunctor = [](Dali::Actor) -> ConvertingResult { + return {nullptr, true}; }; -ObjectRegistry objectRegistry; } // namespace -void Accessible::SetObjectRegistry(ObjectRegistry registry) -{ - objectRegistry = registry; - objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) { - gAdaptorAccessibles.erase(obj); - }); -} - -void Accessible::RegisterExternalAccessibleGetter(std::function functor) +void Accessible::RegisterExternalAccessibleGetter(std::function functor) { convertingFunctor = functor; } -Accessible* Accessible::Get(Dali::Actor actor) +std::shared_ptr Accessible::GetOwningPtr(Dali::Actor actor) { if(!actor) { return nullptr; } - auto accessible = convertingFunctor(actor); - if(!accessible) + auto bridge = Bridge::GetCurrentBridge(); + + // Try finding exsiting accessible object. + auto accessible = bridge->GetAccessible(actor); + if(accessible) { - auto pair = gAdaptorAccessibles.emplace(&actor.GetBaseObject(), nullptr); - if(pair.second) + return accessible; + } + + // No acessible object created, let's create one. + auto result = convertingFunctor(actor); + accessible = result.first; + const bool creationEnabled = result.second; + if(!accessible && creationEnabled) + { + bool isRoot = false; + Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor); + if(scene) { - bool isRoot = false; - Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor); - if(scene) + isRoot = (actor == scene.GetRootLayer()); + } + accessible = std::make_shared(actor, isRoot); + } + + if(accessible) + { + uint32_t actorId = actor.GetProperty(Dali::Actor::Property::ID); + if(bridge->AddAccessible(actorId, accessible)) + { + if(auto actorAccesible = std::dynamic_pointer_cast(accessible)) { - isRoot = (actor == scene.GetRootLayer()); + actorAccesible->StartObservingDestruction(); } - pair.first->second.reset(new AdaptorAccessible(actor, isRoot)); } - accessible = pair.first->second.get(); + else + { + return nullptr; + } } return accessible; } + +Accessible* Accessible::Get(Dali::Actor actor) +{ + auto accessible = Accessible::GetOwningPtr(actor); + return accessible ? accessible.get() : nullptr; +} \ No newline at end of file diff --git a/dali/devel-api/adaptor-framework/accessibility.h b/dali/devel-api/adaptor-framework/accessibility.h index 6a091da..8f7e654 100644 --- a/dali/devel-api/adaptor-framework/accessibility.h +++ b/dali/devel-api/adaptor-framework/accessibility.h @@ -592,16 +592,6 @@ private: }; /** - * @brief Enumeration describing type of key event - * @see Adaptor::AccessibilityObserver::OnAccessibleKeyEvent - */ -enum class KeyEventType -{ - KEY_PRESSED, - KEY_RELEASED, -}; - -/** * @brief Enumeration with human readable values describing state of event * @see Dali::Accessibility::Bridge::Emit */ diff --git a/dali/devel-api/adaptor-framework/actor-accessible.cpp b/dali/devel-api/adaptor-framework/actor-accessible.cpp index c392b9e..0efad7a 100644 --- a/dali/devel-api/adaptor-framework/actor-accessible.cpp +++ b/dali/devel-api/adaptor-framework/actor-accessible.cpp @@ -28,8 +28,10 @@ namespace Dali::Accessibility { ActorAccessible::ActorAccessible(Actor actor) -: mSelf(actor), - mChildrenDirty{true} // to trigger the initial UpdateChildren() +: Dali::BaseObjectObserver(actor), + mSelf(actor), + mChildrenDirty{true}, // to trigger the initial UpdateChildren() + mActorId{static_cast(actor.GetProperty(Dali::Actor::Property::ID))} { // Select the right overload manually because Connect(this, &OnChildrenChanged) is ambiguous. void (ActorAccessible::*handler)(Dali::Actor) = &ActorAccessible::OnChildrenChanged; @@ -39,6 +41,11 @@ ActorAccessible::ActorAccessible(Actor actor) Dali::DevelActor::ChildOrderChangedSignal(actor).Connect(this, handler); } +void ActorAccessible::ObjectDestroyed() +{ + Bridge::GetCurrentBridge()->RemoveAccessible(mActorId); +} + std::string ActorAccessible::GetName() const { return Self().GetProperty(Dali::Actor::Property::NAME); @@ -214,10 +221,12 @@ void ActorAccessible::UpdateChildren() mChildren.clear(); DoGetChildren(mChildren); + const bool shouldIncludeHidden = Bridge::GetCurrentBridge()->ShouldIncludeHidden(); + // Erase-remove idiom // TODO (C++20): Replace with std::erase_if - auto it = std::remove_if(mChildren.begin(), mChildren.end(), [](const Accessible* child) { - return !child || child->IsHidden(); + auto it = std::remove_if(mChildren.begin(), mChildren.end(), [shouldIncludeHidden](const Accessible* child) { + return !child || (!shouldIncludeHidden && child->IsHidden()); }); mChildren.erase(it, mChildren.end()); mChildren.shrink_to_fit(); diff --git a/dali/devel-api/adaptor-framework/actor-accessible.h b/dali/devel-api/adaptor-framework/actor-accessible.h index 9682c19..ba74c1e 100644 --- a/dali/devel-api/adaptor-framework/actor-accessible.h +++ b/dali/devel-api/adaptor-framework/actor-accessible.h @@ -18,6 +18,7 @@ */ // EXTERNAL INCLUDES +#include #include #include #include @@ -32,13 +33,17 @@ namespace Dali::Accessibility class DALI_ADAPTOR_API ActorAccessible : public virtual Accessible, public virtual Collection, public virtual Component, - public Dali::ConnectionTracker + public Dali::ConnectionTracker, + public Dali::BaseObjectObserver { public: ActorAccessible() = delete; ActorAccessible(Actor actor); + // BaseObjectObserver::ObjectDestroyed + void ObjectDestroyed() override; + /** * @copydoc Dali::Accessibility::Accessible::GetName() */ @@ -166,6 +171,7 @@ private: Dali::WeakHandle mSelf; std::vector mChildren; bool mChildrenDirty; + const uint32_t mActorId; }; } // namespace Dali::Accessibility diff --git a/dali/devel-api/atspi-interfaces/accessible.h b/dali/devel-api/atspi-interfaces/accessible.h index aa60a1e..7804164 100644 --- a/dali/devel-api/atspi-interfaces/accessible.h +++ b/dali/devel-api/atspi-interfaces/accessible.h @@ -35,7 +35,7 @@ namespace Dali::Accessibility /** * @brief Basic interface implemented by all accessibility objects. */ -class DALI_ADAPTOR_API Accessible +class DALI_ADAPTOR_API Accessible : public std::enable_shared_from_this { public: virtual ~Accessible() noexcept; @@ -524,28 +524,30 @@ public: static void SetCurrentlyHighlightedActor(Dali::Actor actor); /** - * @brief Sets ObjectRegistry. - * - * @param[in] registry ObjectRegistry instance - */ - static void SetObjectRegistry(ObjectRegistry registry); - - /** * @brief The method registers functor resposible for converting Actor into Accessible. * @param functor The returning Accessible handle from Actor object */ - static void RegisterExternalAccessibleGetter(std::function functor); + static void RegisterExternalAccessibleGetter(std::function, bool>(Dali::Actor)> functor); /** * @brief Acquires Accessible object from Actor object. * * @param[in] actor Actor object * - * @return The handle to Accessible object + * @return The raw pointer to Accessible object */ static Accessible* Get(Dali::Actor actor); /** + * @brief Acquires Accessible object from Actor object. + * + * @param[in] actor Actor object + * + * @return The owning pointer to Accessible object + */ + static std::shared_ptr GetOwningPtr(Dali::Actor actor); + + /** * @brief Obtains the DBus interface name for the specified AT-SPI interface. * * @param interface AT-SPI interface identifier (e.g. AtspiInterface::ACCESSIBLE) diff --git a/dali/devel-api/atspi-interfaces/application.h b/dali/devel-api/atspi-interfaces/application.h index 44b26ba..b3cba19 100644 --- a/dali/devel-api/atspi-interfaces/application.h +++ b/dali/devel-api/atspi-interfaces/application.h @@ -2,7 +2,7 @@ #define DALI_ADAPTOR_ATSPI_APPLICATION_H /* - * 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. @@ -50,6 +50,20 @@ public: virtual std::string GetVersion() const = 0; /** + * @brief Gets include_hidden flag currently set on the application. + * + * @return true is include_hidden is set; false otherwise. + */ + virtual bool GetIncludeHidden() const = 0; + + /** + * @brief Sets include_hidden flag to the application. + * + * @return true is include_hidden flag is updated; false otherwise. + */ + virtual bool SetIncludeHidden(bool includeHidden) = 0; + + /** * @brief Downcasts an Accessible to an Application. * * @param obj The Accessible diff --git a/dali/internal/accessibility/bridge/accessible.cpp b/dali/internal/accessibility/bridge/accessible.cpp index 5340073..ed8fc1e 100644 --- a/dali/internal/accessibility/bridge/accessible.cpp +++ b/dali/internal/accessibility/bridge/accessible.cpp @@ -113,7 +113,7 @@ void Accessible::EmitStateChanged(State state, int newValue, int reserved) if(shouldEmit) { - bridgeData->mBridge->EmitStateChanged(this, state, newValue, reserved); + bridgeData->mBridge->EmitStateChanged(shared_from_this(), state, newValue, reserved); } } } @@ -189,7 +189,7 @@ void Accessible::Emit(ObjectPropertyChangeEvent event) { if(auto bridgeData = GetBridgeData()) { - bridgeData->mBridge->Emit(this, event); + bridgeData->mBridge->Emit(shared_from_this(), event); } } @@ -197,7 +197,7 @@ void Accessible::EmitBoundsChanged(Rect<> rect) { if(auto bridgeData = GetBridgeData()) { - bridgeData->mBridge->EmitBoundsChanged(this, rect); + bridgeData->mBridge->EmitBoundsChanged(shared_from_this(), rect); } } diff --git a/dali/internal/accessibility/bridge/bridge-application.cpp b/dali/internal/accessibility/bridge/bridge-application.cpp index 9806bad..c080236 100644 --- a/dali/internal/accessibility/bridge/bridge-application.cpp +++ b/dali/internal/accessibility/bridge/bridge-application.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. @@ -28,6 +28,8 @@ void BridgeApplication::RegisterInterfaces() DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)}; AddGetPropertyToInterface(desc, "ToolkitName", &BridgeApplication::GetToolkitName); AddGetPropertyToInterface(desc, "Version", &BridgeApplication::GetVersion); + AddFunctionToInterface(desc, "GetIncludeHidden", &BridgeApplication::GetIncludeHidden); + AddFunctionToInterface(desc, "SetIncludeHidden", &BridgeApplication::SetIncludeHidden); mDbusServer.addInterface("/", desc, true); } @@ -45,3 +47,17 @@ std::string BridgeApplication::GetVersion() { return FindSelf()->GetVersion(); } + +DBus::ValueOrError BridgeApplication::GetIncludeHidden() +{ + return FindSelf()->GetIncludeHidden(); +} + +DBus::ValueOrError BridgeApplication::SetIncludeHidden(bool includeHidden) +{ + if(FindSelf()->SetIncludeHidden(includeHidden)) + { + NotifyIncludeHiddenChanged(); + } + return {}; +} diff --git a/dali/internal/accessibility/bridge/bridge-application.h b/dali/internal/accessibility/bridge/bridge-application.h index f4cefc4..299e772 100644 --- a/dali/internal/accessibility/bridge/bridge-application.h +++ b/dali/internal/accessibility/bridge/bridge-application.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_ACCESSIBILITY_BRIDGE_APPLICATION_H /* - * 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. @@ -44,6 +44,9 @@ protected: */ Dali::Accessibility::Application* FindSelf() const; +private: + virtual void NotifyIncludeHiddenChanged(){}; + public: /** * @brief Gets name of graphic user interface framework used by an application. @@ -58,6 +61,18 @@ public: * @return String with version */ std::string GetVersion(); + + /** + * @brief Gets include_hidden flag currently set on the application. + * + * @return true is include_hidden is set; false otherwise. + */ + DBus::ValueOrError GetIncludeHidden(); + + /** + * @brief Sets include_hidden flag to the application. + */ + DBus::ValueOrError SetIncludeHidden(bool includeHidden); }; #endif // DALI_INTERNAL_ACCESSIBILITY_BRIDGE_APPLICATION_H diff --git a/dali/internal/accessibility/bridge/bridge-base.cpp b/dali/internal/accessibility/bridge/bridge-base.cpp index 7b24a7f..3bd27ff 100644 --- a/dali/internal/accessibility/bridge/bridge-base.cpp +++ b/dali/internal/accessibility/bridge/bridge-base.cpp @@ -233,24 +233,25 @@ void BridgeBase::CompressDefaultLabels() { // Remove entries for objects which no longer exist mDefaultLabels.remove_if([](const DefaultLabelType& label) { - return !label.first.GetBaseHandle(); // Check window's weak handle - // TODO: Once Accessible becomes a handle type, check its weak handle here as well + // Check 1) window's weak handle; 2) accessible's ref object + return !label.first.GetBaseHandle() || label.second.expired(); }); } -void BridgeBase::RegisterDefaultLabel(Accessible* object) +void BridgeBase::RegisterDefaultLabel(std::shared_ptr object) { CompressDefaultLabels(); - Dali::WeakHandle window = GetWindow(object); + Dali::WeakHandle window = GetWindow(object.get()); if(!window.GetBaseHandle()) // true also if `object` is null { DALI_LOG_ERROR("Cannot register default label: object does not belong to any window"); return; } - auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [object](const DefaultLabelType& label) { - return object == label.second; + auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [&object](const DefaultLabelType& label) { + auto labelPtr = label.second.lock(); + return labelPtr && object == labelPtr; }); if(it == mDefaultLabels.end()) @@ -269,17 +270,20 @@ void BridgeBase::RegisterDefaultLabel(Accessible* object) } } -void BridgeBase::UnregisterDefaultLabel(Accessible* object) +void BridgeBase::UnregisterDefaultLabel(std::shared_ptr object) { CompressDefaultLabels(); - mDefaultLabels.remove_if([object](const DefaultLabelType& label) { - return object == label.second; + mDefaultLabels.remove_if([&object](const DefaultLabelType& label) { + auto labelPtr = label.second.lock(); + return labelPtr && object == labelPtr; }); } -Accessible* BridgeBase::GetDefaultLabel(Accessible* root) const +Accessible* BridgeBase::GetDefaultLabel(Accessible* root) { + CompressDefaultLabels(); + Dali::WeakHandle window = GetWindow(root); if(!window.GetBaseHandle()) { @@ -290,7 +294,16 @@ Accessible* BridgeBase::GetDefaultLabel(Accessible* root) const return window == label.first; }); - return (it == mDefaultLabels.rend()) ? root : it->second; + Accessible* rawPtr = root; + if(it != mDefaultLabels.rend()) + { + if(auto labelPtr = it->second.lock()) + { + rawPtr = labelPtr.get(); + } + } + + return rawPtr; } std::string BridgeBase::StripPrefix(const std::string& path) @@ -314,7 +327,7 @@ Accessible* BridgeBase::Find(const std::string& path) const } auto it = mData->mKnownObjects.find(static_cast(accessible)); - if(it == mData->mKnownObjects.end() || (*it)->IsHidden()) + if(it == mData->mKnownObjects.end() || (!mApplication.mShouldIncludeHidden && (*it)->IsHidden())) { throw std::domain_error{"unknown object '" + path + "'"}; } diff --git a/dali/internal/accessibility/bridge/bridge-base.h b/dali/internal/accessibility/bridge/bridge-base.h index eec96fe..c7efdeb 100644 --- a/dali/internal/accessibility/bridge/bridge-base.h +++ b/dali/internal/accessibility/bridge/bridge-base.h @@ -50,6 +50,7 @@ public: std::string mName; std::string mToolkitName{"dali"}; bool mIsEmbedded{false}; + bool mShouldIncludeHidden{false}; std::string GetName() const override { @@ -182,6 +183,21 @@ public: return std::to_string(Dali::ADAPTOR_MAJOR_VERSION) + "." + std::to_string(Dali::ADAPTOR_MINOR_VERSION); } + bool GetIncludeHidden() const override + { + return mShouldIncludeHidden; + } + + bool SetIncludeHidden(bool includeHidden) override + { + if(mShouldIncludeHidden != includeHidden) + { + mShouldIncludeHidden = includeHidden; + return true; + } + return false; + } + // Socket Dali::Accessibility::Address Embed(Dali::Accessibility::Address plug) override @@ -359,17 +375,17 @@ public: /** * @copydoc Dali::Accessibility::Bridge::RegisterDefaultLabel() */ - void RegisterDefaultLabel(Dali::Accessibility::Accessible* object) override; + void RegisterDefaultLabel(std::shared_ptr object) override; /** * @copydoc Dali::Accessibility::Bridge::UnregisterDefaultLabel() */ - void UnregisterDefaultLabel(Dali::Accessibility::Accessible* object) override; + void UnregisterDefaultLabel(std::shared_ptr object) override; /** * @copydoc Dali::Accessibility::Bridge::GetDefaultLabel() */ - Dali::Accessibility::Accessible* GetDefaultLabel(Dali::Accessibility::Accessible* root) const override; + Dali::Accessibility::Accessible* GetDefaultLabel(Dali::Accessibility::Accessible* root) override; /** * @copydoc Dali::Accessibility::Bridge::GetApplication() @@ -613,7 +629,7 @@ public: protected: // We use a weak handle in order not to keep a window alive forever if someone forgets to UnregisterDefaultLabel() - using DefaultLabelType = std::pair, Dali::Accessibility::Accessible*>; + using DefaultLabelType = std::pair, std::weak_ptr>; using DefaultLabelsType = std::list; mutable ApplicationAccessible mApplication; diff --git a/dali/internal/accessibility/bridge/bridge-impl.cpp b/dali/internal/accessibility/bridge/bridge-impl.cpp index 6cafd4a..6d0b630 100644 --- a/dali/internal/accessibility/bridge/bridge-impl.cpp +++ b/dali/internal/accessibility/bridge/bridge-impl.cpp @@ -25,6 +25,7 @@ #include // INTERNAL INCLUDES +#include #include #include #include @@ -76,21 +77,22 @@ class BridgeImpl : public virtual BridgeBase, public BridgeTable, public BridgeTableCell { - DBus::DBusClient mAccessibilityStatusClient; - DBus::DBusClient mRegistryClient; - DBus::DBusClient mDirectReadingClient; - bool mIsScreenReaderEnabled = false; - bool mIsEnabled = false; - bool mIsApplicationRunning = false; - std::map> mDirectReadingCallbacks; - Dali::Actor mHighlightedActor; - std::function mHighlightClearAction; - Dali::CallbackBase* mIdleCallback = NULL; - Dali::Timer mInitializeTimer; - Dali::Timer mReadIsEnabledTimer; - Dali::Timer mReadScreenReaderEnabledTimer; - Dali::Timer mForceUpTimer; - std::string mPreferredBusName; + DBus::DBusClient mAccessibilityStatusClient{}; + DBus::DBusClient mRegistryClient{}; + DBus::DBusClient mDirectReadingClient{}; + bool mIsScreenReaderEnabled{false}; + bool mIsEnabled{false}; + bool mIsApplicationRunning{false}; + std::unordered_map> mDirectReadingCallbacks{}; + Dali::Actor mHighlightedActor; + std::function mHighlightClearAction{nullptr}; + Dali::CallbackBase* mIdleCallback{}; + Dali::Timer mInitializeTimer; + Dali::Timer mReadIsEnabledTimer; + Dali::Timer mReadScreenReaderEnabledTimer; + Dali::Timer mForceUpTimer; + std::string mPreferredBusName; + std::map> mAccessibles; // Actor.ID to Accessible map public: BridgeImpl() @@ -98,36 +100,54 @@ public: } /** - * @copydoc Dali::Accessibility::Bridge::Emit() + * @copydoc Dali::Accessibility::Bridge::AddAccessible() */ - Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override + bool AddAccessible(uint32_t actorId, std::shared_ptr accessible) override { - if(!IsUp()) - { - return Consumed::NO; - } + mAccessibles[actorId] = std::move(accessible); + return true; + } + + /** + * @copydoc Dali::Accessibility::Bridge::RemoveAccessible() + */ + void RemoveAccessible(uint32_t actorId) override + { + mAccessibles.erase(actorId); + } - unsigned int keyType = 0; + /** + * @copydoc Dali::Accessibility::Bridge::GetAccessible() + */ + std::shared_ptr GetAccessible(Dali::Actor actor) const override + { + uint32_t actorId = actor.GetProperty(Dali::Actor::Property::ID); + auto iter = mAccessibles.find(actorId); + return iter != mAccessibles.end() ? iter->second : nullptr; + } - switch(type) + /** + * @copydoc Dali::Accessibility::Bridge::ShouldIncludeHidden() + */ + bool ShouldIncludeHidden() const override + { + return mApplication.mShouldIncludeHidden; + } + + void NotifyIncludeHiddenChanged() override + { + for(const auto& iter : mAccessibles) { - case KeyEventType::KEY_PRESSED: + const auto& accessible = iter.second; + if(accessible->IsHidden()) { - keyType = 0; - break; - } - case KeyEventType::KEY_RELEASED: - { - keyType = 1; - break; - } - default: - { - return Consumed::NO; + auto* parent = dynamic_cast(accessible->GetParent()); + if(parent) + { + parent->OnChildrenChanged(); + } } } - - return Consumed::NO; } /** diff --git a/dali/internal/accessibility/bridge/bridge-object.cpp b/dali/internal/accessibility/bridge/bridge-object.cpp index 2f00293..fa51796 100644 --- a/dali/internal/accessibility/bridge/bridge-object.cpp +++ b/dali/internal/accessibility/bridge/bridge-object.cpp @@ -66,7 +66,7 @@ void BridgeObject::EmitActiveDescendantChanged(Accessible* obj, Accessible* chil {"", "root"}); } -void BridgeObject::Emit(Accessible* obj, ObjectPropertyChangeEvent event) +void BridgeObject::Emit(std::shared_ptr obj, ObjectPropertyChangeEvent event) { static const std::map eventMap{ {ObjectPropertyChangeEvent::NAME, "accessible-name"}, @@ -86,7 +86,7 @@ void BridgeObject::Emit(Accessible* obj, ObjectPropertyChangeEvent event) if(eventName != eventMap.end()) { mDbusServer.emit2, Address>( - GetAccessiblePath(obj), + GetAccessiblePath(obj.get()), Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT), "PropertyChange", std::string{eventName->second}, @@ -143,7 +143,7 @@ void BridgeObject::Emit(Accessible* obj, WindowEvent event, unsigned int detail) } } -void BridgeObject::EmitStateChanged(Accessible* obj, State state, int newValue, int reserved) +void BridgeObject::EmitStateChanged(std::shared_ptr obj, State state, int newValue, int reserved) { static const std::map stateMap{ {State::INVALID, "invalid"}, @@ -204,7 +204,7 @@ void BridgeObject::EmitStateChanged(Accessible* obj, State state, int newValue, if(stateName != stateMap.end()) { mDbusServer.emit2, Address>( - GetAccessiblePath(obj), + GetAccessiblePath(obj.get()), Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT), "StateChanged", std::string{stateName->second}, @@ -215,26 +215,29 @@ void BridgeObject::EmitStateChanged(Accessible* obj, State state, int newValue, } } -void BridgeObject::EmitBoundsChanged(Accessible* obj, Dali::Rect<> rect) +void BridgeObject::EmitBoundsChanged(std::shared_ptr obj, Dali::Rect<> rect) { if(!IsUp() || !IsBoundsChangedEventAllowed || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::BOUNDS_CHANGED]) { return; } - DBus::EldbusVariant > tmp{ - std::tuple{rect.x, rect.y, rect.width, rect.height}}; - - AddCoalescableMessage(CoalescableMessages::BOUNDS_CHANGED, obj, 1.0f, [=]() { - mDbusServer.emit2 >, Address>( - GetAccessiblePath(obj), - Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT), - "BoundsChanged", - "", - 0, - 0, - tmp, - {"", "root"}); + AddCoalescableMessage(CoalescableMessages::BOUNDS_CHANGED, obj.get(), 1.0f, [=, weakObj = std::weak_ptr(obj), rect = std::move(rect)]() { + if(auto accessible = weakObj.lock()) + { + DBus::EldbusVariant > tmp{ + std::tuple{rect.x, rect.y, rect.width, rect.height}}; + + mDbusServer.emit2 >, Address>( + GetAccessiblePath(accessible.get()), + Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT), + "BoundsChanged", + "", + 0, + 0, + tmp, + {"", "root"}); + } }); } diff --git a/dali/internal/accessibility/bridge/bridge-object.h b/dali/internal/accessibility/bridge/bridge-object.h index 2bfd1fb..8b9d850 100644 --- a/dali/internal/accessibility/bridge/bridge-object.h +++ b/dali/internal/accessibility/bridge/bridge-object.h @@ -62,7 +62,7 @@ protected: /** * @copydoc Dali::Accessibility::Bridge::EmitStateChanged() */ - void EmitStateChanged(Dali::Accessibility::Accessible* obj, Dali::Accessibility::State state, int newValue, int reserved) override; + void EmitStateChanged(std::shared_ptr obj, Dali::Accessibility::State state, int newValue, int reserved) override; /** * @copydoc Dali::Accessibility::Bridge::Emit() @@ -72,12 +72,12 @@ protected: /** * @copydoc Dali::Accessibility::Bridge::Emit() */ - void Emit(Dali::Accessibility::Accessible* obj, Dali::Accessibility::ObjectPropertyChangeEvent event) override; + void Emit(std::shared_ptr obj, Dali::Accessibility::ObjectPropertyChangeEvent event) override; /** * @copydoc Dali::Accessibility::Bridge::EmitBoundsChanged() */ - void EmitBoundsChanged(Dali::Accessibility::Accessible* obj, Dali::Rect<> rect) override; + void EmitBoundsChanged(std::shared_ptr obj, Dali::Rect<> rect) override; /** * @copydoc Dali::Accessibility::Bridge::EmitMovedOutOfScreen() diff --git a/dali/internal/accessibility/bridge/dummy/dummy-atspi.h b/dali/internal/accessibility/bridge/dummy/dummy-atspi.h index 4cb4f94..1414c38 100644 --- a/dali/internal/accessibility/bridge/dummy/dummy-atspi.h +++ b/dali/internal/accessibility/bridge/dummy/dummy-atspi.h @@ -46,15 +46,15 @@ struct DummyBridge : Dali::Accessibility::Bridge { } - void RegisterDefaultLabel(Accessibility::Accessible* object) override + void RegisterDefaultLabel(std::shared_ptr object) override { } - void UnregisterDefaultLabel(Accessibility::Accessible* object) override + void UnregisterDefaultLabel(std::shared_ptr object) override { } - Dali::Accessibility::Accessible* GetDefaultLabel(Dali::Accessibility::Accessible* root) const override + Dali::Accessibility::Accessible* GetDefaultLabel(Dali::Accessibility::Accessible* root) override { return nullptr; } @@ -142,7 +142,7 @@ struct DummyBridge : Dali::Accessibility::Bridge { } - void EmitStateChanged(Accessibility::Accessible* obj, Accessibility::State state, int newValue, int reserved) override + void EmitStateChanged(std::shared_ptr obj, Accessibility::State state, int newValue, int reserved) override { } @@ -150,19 +150,14 @@ struct DummyBridge : Dali::Accessibility::Bridge { } - void Emit(Accessibility::Accessible* obj, Accessibility::ObjectPropertyChangeEvent event) override + void Emit(std::shared_ptr obj, Accessibility::ObjectPropertyChangeEvent event) override { } - void EmitBoundsChanged(Accessibility::Accessible* obj, Rect<> rect) override + void EmitBoundsChanged(std::shared_ptr obj, Rect<> rect) override { } - Accessibility::Consumed Emit(Accessibility::KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override - { - return Accessibility::Consumed::YES; - } - void Say(const std::string& text, bool discardable, std::function callback) override { } @@ -213,6 +208,25 @@ struct DummyBridge : Dali::Accessibility::Bridge void SetPreferredBusName(std::string_view preferredBusName) override { } + + bool AddAccessible(uint32_t actorId, std::shared_ptr accessible) override + { + return false; + } + + void RemoveAccessible(uint32_t actorId) override + { + } + + std::shared_ptr GetAccessible(Actor actor) const override + { + return nullptr; + }; + + bool ShouldIncludeHidden() const override + { + return false; + } }; } // namespace Dali::Accessibility diff --git a/dali/internal/adaptor/common/adaptor-impl.cpp b/dali/internal/adaptor/common/adaptor-impl.cpp index a85d772..56ce105 100644 --- a/dali/internal/adaptor/common/adaptor-impl.cpp +++ b/dali/internal/adaptor/common/adaptor-impl.cpp @@ -314,24 +314,6 @@ void Adaptor::Initialize(GraphicsFactory& graphicsFactory) mConfigurationManager = Utils::MakeUnique(systemCachePath, mGraphics.get(), mThreadController); } -void Adaptor::AccessibilityObserver::OnAccessibleKeyEvent(const Dali::KeyEvent& event) -{ - Accessibility::KeyEventType type; - if(event.GetState() == Dali::KeyEvent::DOWN) - { - type = Accessibility::KeyEventType::KEY_PRESSED; - } - else if(event.GetState() == Dali::KeyEvent::UP) - { - type = Accessibility::KeyEventType::KEY_RELEASED; - } - else - { - return; - } - Dali::Accessibility::Bridge::GetCurrentBridge()->Emit(type, event.GetKeyCode(), event.GetKeyName(), event.GetTime(), !event.GetKeyString().empty()); -} - Adaptor::~Adaptor() { Accessibility::Bridge::GetCurrentBridge()->Terminate(); @@ -392,7 +374,6 @@ void Adaptor::Start() auto bridge = Accessibility::Bridge::GetCurrentBridge(); bridge->SetApplicationName(appName); bridge->Initialize(); - Dali::Stage::GetCurrent().KeyEventSignal().Connect(&mAccessibilityObserver, &AccessibilityObserver::OnAccessibleKeyEvent); Dali::Internal::Adaptor::SceneHolder* defaultWindow = mWindows.front(); diff --git a/dali/internal/adaptor/common/adaptor-impl.h b/dali/internal/adaptor/common/adaptor-impl.h index d0f9ed2..1a2ab40 100644 --- a/dali/internal/adaptor/common/adaptor-impl.h +++ b/dali/internal/adaptor/common/adaptor-impl.h @@ -700,13 +700,6 @@ private: // Data std::unique_ptr mAddOnManager; ///< Pointer to the addon manager - class AccessibilityObserver : public ConnectionTracker - { - public: - void OnAccessibleKeyEvent(const Dali::KeyEvent& event); - }; - AccessibilityObserver mAccessibilityObserver; - public: inline static Adaptor& GetImplementation(Dali::Adaptor& adaptor) { diff --git a/dali/internal/adaptor/common/application-impl.cpp b/dali/internal/adaptor/common/application-impl.cpp index fe9dd7f..540cf60 100644 --- a/dali/internal/adaptor/common/application-impl.cpp +++ b/dali/internal/adaptor/common/application-impl.cpp @@ -285,7 +285,6 @@ void Application::OnInit() DALI_TRACE_BEGIN(gTraceFilter, "DALI_APP_ADAPTOR_START"); mAdaptor->Start(); DALI_TRACE_END(gTraceFilter, "DALI_APP_ADAPTOR_START"); - Accessibility::Accessible::SetObjectRegistry(mAdaptor->GetObjectRegistry()); if(!mStylesheet.empty()) { diff --git a/dali/internal/system/tizen-wayland/widget-controller-tizen.cpp b/dali/internal/system/tizen-wayland/widget-controller-tizen.cpp index 40d5891..cf641f7 100644 --- a/dali/internal/system/tizen-wayland/widget-controller-tizen.cpp +++ b/dali/internal/system/tizen-wayland/widget-controller-tizen.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. @@ -19,8 +19,8 @@ #include // EXTERNAL INCLUDES -#include #include +#include #include #include @@ -71,7 +71,7 @@ void WidgetImplTizen::SetUsingKeyEvent(bool flag) void WidgetImplTizen::SetInformation(Dali::Window window, const std::string& widgetId) { - mWindow = window; + mWindow = window; mWidgetId = widgetId; auto bridge = Accessibility::Bridge::GetCurrentBridge(); @@ -83,9 +83,12 @@ void WidgetImplTizen::SetInformation(Dali::Window window, const std::string& wid bridge->SetPreferredBusName(preferredBusName); // Widget should not send window events (which could narrow down the navigation context) - auto& suppressedEvents = Accessibility::Accessible::Get(window.GetRootLayer())->GetSuppressedEvents(); - suppressedEvents[Accessibility::AtspiEvent::STATE_CHANGED] = true; - suppressedEvents[Accessibility::AtspiEvent::WINDOW_CHANGED] = true; + if(auto accessible = Accessibility::Accessible::Get(window.GetRootLayer())) + { + auto& suppressedEvents = accessible->GetSuppressedEvents(); + suppressedEvents[Accessibility::AtspiEvent::STATE_CHANGED] = true; + suppressedEvents[Accessibility::AtspiEvent::WINDOW_CHANGED] = true; + } } Dali::Window WidgetImplTizen::GetWindow() const diff --git a/dali/internal/window-system/common/window-impl.cpp b/dali/internal/window-system/common/window-impl.cpp index 3bc1044..ba4862b 100644 --- a/dali/internal/window-system/common/window-impl.cpp +++ b/dali/internal/window-system/common/window-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 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. @@ -113,12 +113,14 @@ Window::~Window() { if(mScene) { - auto bridge = Accessibility::Bridge::GetCurrentBridge(); - auto rootLayer = mScene.GetRootLayer(); - auto accessible = Accessibility::Accessible::Get(rootLayer); - bridge->RemoveTopLevelWindow(accessible); - // Related to multi-window case. This is called for default window and non-default window, but it is effective for non-default window. - bridge->Emit(accessible, Accessibility::WindowEvent::DESTROY); + auto bridge = Accessibility::Bridge::GetCurrentBridge(); + auto rootLayer = mScene.GetRootLayer(); + if(auto accessible = Accessibility::Accessible::Get(rootLayer)) + { + bridge->RemoveTopLevelWindow(accessible); + // Related to multi-window case. This is called for default window and non-default window, but it is effective for non-default window. + bridge->Emit(accessible, Accessibility::WindowEvent::DESTROY); + } } if(DALI_LIKELY(mAdaptor)) @@ -715,7 +717,10 @@ void Window::SetSize(Dali::Window::WindowSize size) mSurface->SetFullSwapNextFrame(); - Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(oldRect.x, oldRect.y, size.GetWidth(), size.GetHeight())); + if(auto accessible = Accessibility::Accessible::Get(mScene.GetRootLayer())) + { + accessible->EmitBoundsChanged(Dali::Rect<>(oldRect.x, oldRect.y, size.GetWidth(), size.GetHeight())); + } } Dali::Window::WindowSize Window::GetSize() const @@ -742,7 +747,10 @@ void Window::SetPosition(Dali::Window::WindowPosition position) mSurface->SetFullSwapNextFrame(); - Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(position.GetX(), position.GetY(), oldRect.width, oldRect.height)); + if(auto accessible = Accessibility::Accessible::Get(mScene.GetRootLayer())) + { + accessible->EmitBoundsChanged(Dali::Rect<>(position.GetX(), position.GetY(), oldRect.width, oldRect.height)); + } } Dali::Window::WindowPosition Window::GetPosition() const @@ -810,7 +818,10 @@ void Window::SetPositionSize(PositionSize positionSize) mSurface->SetFullSwapNextFrame(); - Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height)); + if(auto accessible = Accessibility::Accessible::Get(mScene.GetRootLayer())) + { + accessible->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height)); + } } void Window::SetLayout(unsigned int numCols, unsigned int numRows, unsigned int column, unsigned int row, unsigned int colSpan, unsigned int rowSpan) @@ -1011,7 +1022,10 @@ void Window::OnUpdatePositionSize(Dali::PositionSize& positionSize) if(DALI_LIKELY(mScene)) { - Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height)); + if(auto accessible = Accessibility::Accessible::Get(mScene.GetRootLayer())) + { + accessible->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height)); + } } } @@ -1108,11 +1122,12 @@ void Window::OnInsetsChanged(WindowInsetsPartType partType, WindowInsetsPartStat void Window::OnAccessibilityEnabled() { - auto bridge = Accessibility::Bridge::GetCurrentBridge(); - auto rootLayer = mScene.GetRootLayer(); - auto accessible = Accessibility::Accessible::Get(rootLayer); - bridge->AddTopLevelWindow(accessible); - + auto bridge = Accessibility::Bridge::GetCurrentBridge(); + auto rootLayer = mScene.GetRootLayer(); + if(auto accessible = Accessibility::Accessible::Get(rootLayer)) + { + bridge->AddTopLevelWindow(accessible); + } DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), Accessibility is enabled\n", this, mNativeWindowId); Dali::Window handle(this); @@ -1141,10 +1156,12 @@ void Window::OnAccessibilityEnabled() void Window::OnAccessibilityDisabled() { - auto bridge = Accessibility::Bridge::GetCurrentBridge(); - auto rootLayer = mScene.GetRootLayer(); - auto accessible = Accessibility::Accessible::Get(rootLayer); - bridge->RemoveTopLevelWindow(accessible); + auto bridge = Accessibility::Bridge::GetCurrentBridge(); + auto rootLayer = mScene.GetRootLayer(); + if(auto accessible = Accessibility::Accessible::Get(rootLayer)) + { + bridge->RemoveTopLevelWindow(accessible); + } DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), Accessibility is disabled\n", this, mNativeWindowId); } -- 2.7.4