#define DALI_INTERNAL_ACCESSIBILITY_BRIDGE_BASE_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.
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/actors/layer.h>
#include <dali/public-api/dali-adaptor-version.h>
+#include <dali/public-api/object/weak-handle.h>
#include <dali/public-api/signals/connection-tracker.h>
-#include <dali/public-api/actors/layer.h>
#include <memory>
+#include <tuple>
// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/proxy-accessible.h>
#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/devel-api/atspi-interfaces/accessible.h>
+#include <dali/devel-api/atspi-interfaces/application.h>
+#include <dali/devel-api/atspi-interfaces/collection.h>
+#include <dali/devel-api/atspi-interfaces/socket.h>
#include <dali/internal/accessibility/bridge/accessibility-common.h>
/**
- * @brief The AppAccessible class is to define Accessibility Application.
+ * @brief The ApplicationAccessible class is to define Accessibility Application.
*/
-class AppAccessible : public virtual Dali::Accessibility::Accessible, public virtual Dali::Accessibility::Collection, public virtual Dali::Accessibility::Application
+class ApplicationAccessible : public virtual Dali::Accessibility::Accessible,
+ public virtual Dali::Accessibility::Application,
+ public virtual Dali::Accessibility::Collection,
+ public virtual Dali::Accessibility::Component,
+ public virtual Dali::Accessibility::Socket
{
public:
- Dali::Accessibility::EmptyAccessibleWithAddress mParent;
- std::vector<Dali::Accessibility::Accessible*> mChildren;
- std::vector<Dali::Window> mWindows;
- std::string mName;
-
- std::string GetName() override
+ Dali::Accessibility::ProxyAccessible mParent;
+ std::vector<Dali::Accessibility::Accessible*> mChildren;
+ std::string mName;
+ std::string mToolkitName{"dali"};
+ bool mIsEmbedded{false};
+ bool mShouldIncludeHidden{false};
+
+ std::string GetName() const override
{
return mName;
}
- std::string GetDescription() override
+ std::string GetDescription() const override
+ {
+ return {};
+ }
+
+ std::string GetValue() const override
{
- return "";
+ return {};
}
Dali::Accessibility::Accessible* GetParent() override
return &mParent;
}
- size_t GetChildCount() override
+ size_t GetChildCount() const override
{
return mChildren.size();
}
+ std::vector<Dali::Accessibility::Accessible*> GetChildren() override
+ {
+ return mChildren;
+ }
+
Dali::Accessibility::Accessible* GetChildAtIndex(size_t index) override
{
auto size = mChildren.size();
size_t GetIndexInParent() override
{
+ if(mIsEmbedded)
+ {
+ return 0u;
+ }
+
throw std::domain_error{"can't call GetIndexInParent on application object"};
}
- Dali::Accessibility::Role GetRole() override
+ Dali::Accessibility::Role GetRole() const override
{
return Dali::Accessibility::Role::APPLICATION;
}
Dali::Accessibility::States GetStates() override
{
- return {};
+ Dali::Accessibility::States result;
+
+ for(auto* child : mChildren)
+ {
+ result = result | child->GetStates();
+ }
+
+ // The Application object should never have the SENSITIVE state
+ result[Dali::Accessibility::State::SENSITIVE] = false;
+
+ return result;
}
- Dali::Accessibility::Attributes GetAttributes() override
+ Dali::Accessibility::Attributes GetAttributes() const override
{
return {};
}
return {};
}
- Dali::Actor GetInternalActor() override
+ Dali::Actor GetInternalActor() const override
{
return Dali::Actor{};
}
- Dali::Accessibility::Address GetAddress() override
+ Dali::Accessibility::Address GetAddress() const override
{
return {"", "root"};
}
- std::string GetToolkitName() override
+ // Application
+
+ std::string GetToolkitName() const override
{
- return {"dali"};
+ return mToolkitName;
}
- std::string GetVersion() override
+ std::string GetVersion() const override
{
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
+ {
+ mIsEmbedded = true;
+ mParent.SetAddress(plug);
+
+ return GetAddress();
+ }
+
+ void Unembed(Dali::Accessibility::Address plug) override
+ {
+ if(mParent.GetAddress() == plug)
+ {
+ mIsEmbedded = false;
+ mParent.SetAddress({});
+ Dali::Accessibility::Bridge::GetCurrentBridge()->SetExtentsOffset(0, 0);
+ }
+ }
+
+ void SetOffset(std::int32_t x, std::int32_t y) override
+ {
+ if(!mIsEmbedded)
+ {
+ return;
+ }
+
+ Dali::Accessibility::Bridge::GetCurrentBridge()->SetExtentsOffset(x, y);
+ }
+
+ // Component
+
+ Dali::Rect<> GetExtents(Dali::Accessibility::CoordinateType type) const override
+ {
+ using limits = std::numeric_limits<float>;
+
+ float minX = limits::max();
+ float minY = limits::max();
+ float maxX = limits::min();
+ float maxY = limits::min();
+
+ for(Dali::Accessibility::Accessible* child : mChildren)
+ {
+ auto* component = Dali::Accessibility::Component::DownCast(child);
+ if(!component)
+ {
+ continue;
+ }
+
+ auto extents = component->GetExtents(type);
+
+ minX = std::min(minX, extents.x);
+ minY = std::min(minY, extents.y);
+ maxX = std::max(maxX, extents.x + extents.width);
+ maxY = std::max(maxY, extents.y + extents.height);
+ }
+
+ return {minX, minY, maxX - minX, maxY - minY};
+ }
+
+ Dali::Accessibility::ComponentLayer GetLayer() const override
+ {
+ return Dali::Accessibility::ComponentLayer::WINDOW;
+ }
+
+ std::int16_t GetMdiZOrder() const override
+ {
+ return 0;
+ }
+
+ bool GrabFocus() override
+ {
+ return false;
+ }
+
+ double GetAlpha() const override
+ {
+ return 0.0;
+ }
+
+ bool GrabHighlight() override
+ {
+ return false;
+ }
+
+ bool ClearHighlight() override
+ {
+ return false;
+ }
+
+ bool IsScrollable() const override
+ {
+ return false;
+ }
};
/**
- * @brief Enumeration for FilteredEvents.
+ * @brief Enumeration for CoalescableMessages.
*/
-enum class FilteredEvents
+enum class CoalescableMessages
{
- BOUNDS_CHANGED ///< Bounds changed
+ BOUNDS_CHANGED, ///< Bounds changed
+ SET_OFFSET, ///< Set offset
+ POST_RENDER, ///< Post render
+ STATE_CHANGED_BEGIN = 500, ///< State changed (begin of reserved range)
+ STATE_CHANGED_END = STATE_CHANGED_BEGIN + 99, ///< State changed (end of reserved range)
+ PROPERTY_CHANGED_BEGIN, ///< Property changed (begin of reserved range)
+ PROPERTY_CHANGED_END = PROPERTY_CHANGED_BEGIN + 99, ///< Property changed (end of reserved range)
};
// Custom specialization of std::hash
namespace std
{
template<>
-struct hash<std::pair<FilteredEvents, Dali::Accessibility::Accessible*>>
+struct hash<std::pair<CoalescableMessages, Dali::Accessibility::Accessible*>>
{
- size_t operator()(std::pair<FilteredEvents, Dali::Accessibility::Accessible*> value) const
+ size_t operator()(std::pair<CoalescableMessages, Dali::Accessibility::Accessible*> value) const
{
return (static_cast<size_t>(value.first) * 131) ^ reinterpret_cast<size_t>(value.second);
}
*/
class BridgeBase : public Dali::Accessibility::Bridge, public Dali::ConnectionTracker
{
- std::unordered_map<std::pair<FilteredEvents, Dali::Accessibility::Accessible*>, std::pair<unsigned int, std::function<void()>>> mFilteredEvents;
+ std::unordered_map<std::pair<CoalescableMessages, Dali::Accessibility::Accessible*>, std::tuple<unsigned int, unsigned int, std::function<void()>>> mCoalescableMessages;
/**
- * @brief Removes all FilteredEvents using Tick signal.
+ * @brief Removes all CoalescableMessages using Tick signal.
*
- * @return False if mFilteredEvents is empty, otherwise true.
+ * @return False if mCoalescableMessages is empty, otherwise true.
*/
- bool TickFilteredEvents();
+ bool TickCoalescableMessages();
public:
/**
- * @brief Adds FilteredEvents, Accessible, and delay time to mFilteredEvents.
+ * @brief Adds CoalescableMessages, Accessible, and delay time to mCoalescableMessages.
*
- * @param[in] kind FilteredEvents enum value
+ * @param[in] kind CoalescableMessages enum value
* @param[in] obj Accessible object
* @param[in] delay The delay time
* @param[in] functor The function to be called // NEED TO UPDATE!
*/
- void AddFilteredEvent(FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor);
+ void AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor);
/**
* @brief Callback when the visibility of the window is changed.
void RemoveTopLevelWindow(Dali::Accessibility::Accessible* windowAccessible) override;
/**
- * @copydoc Dali::Accessibility::Bridge::AddPopup()
+ * @copydoc Dali::Accessibility::Bridge::RegisterDefaultLabel()
*/
- void AddPopup(Dali::Accessibility::Accessible* object) override;
+ void RegisterDefaultLabel(std::shared_ptr<Dali::Accessibility::Accessible> object) override;
/**
- * @copydoc Dali::Accessibility::Bridge::RemovePopup()
+ * @copydoc Dali::Accessibility::Bridge::UnregisterDefaultLabel()
*/
- void RemovePopup(Dali::Accessibility::Accessible* object) override;
+ void UnregisterDefaultLabel(std::shared_ptr<Dali::Accessibility::Accessible> object) override;
+
+ /**
+ * @copydoc Dali::Accessibility::Bridge::GetDefaultLabel()
+ */
+ Dali::Accessibility::Accessible* GetDefaultLabel(Dali::Accessibility::Accessible* root) override;
/**
* @copydoc Dali::Accessibility::Bridge::GetApplication()
/**
* @brief Returns the target object of the currently executed DBus method call.
*
- * And any subclasses redefine `FindSelf` with a different return type as a convenient wrapper around dynamic_cast.
* @return The Accessible object
* @note When a DBus method is called on some object, this target object (`currentObject`) is temporarily saved by the bridge,
* because DBus handles the invocation target separately from the method arguments.
* We then use the saved object inside the 'glue' method (e.g. BridgeValue::GetMinimum)
* to call the equivalent method on the respective C++ object (this could be ScrollBar::AccessibleImpl::GetMinimum in the example given).
*/
- Dali::Accessibility::Accessible* FindSelf() const;
+ Dali::Accessibility::Accessible* FindCurrentObject() const;
+
+ /**
+ * @brief Returns the target object of the currently executed DBus method call.
+ *
+ * This method tries to downcast the return value of FindCurrentObject() to the requested type,
+ * issuing an error reply to the DBus caller if the requested type is not implemented. Whether
+ * a given type is implemented is decided based on the return value of Accessible::GetInterfaces()
+ * for the current object.
+ *
+ * @tparam I The requested AT-SPI interface
+ * @return The Accessible object (cast to a more derived type)
+ *
+ * @see FindCurrentObject()
+ * @see Dali::Accessibility::AtspiInterface
+ * @see Dali::Accessibility::AtspiInterfaceType
+ * @see Dali::Accessibility::Accessible::GetInterfaces()
+ */
+ template<Dali::Accessibility::AtspiInterface I>
+ auto* FindCurrentObjectWithInterface() const
+ {
+ using Type = Dali::Accessibility::AtspiInterfaceType<I>;
+
+ Type* result;
+ auto* currentObject = FindCurrentObject();
+ DALI_ASSERT_DEBUG(currentObject); // FindCurrentObject() throws domain_error
+
+ if(!(result = Dali::Accessibility::Accessible::DownCast<I>(currentObject)))
+ {
+ std::stringstream s;
+
+ s << "Object " << currentObject->GetAddress().ToString();
+ s << " does not implement ";
+ s << Dali::Accessibility::Accessible::GetInterfaceName(I);
+
+ throw std::domain_error{s.str()};
+ }
+
+ return result;
+ }
/**
* @copydoc Dali::Accessibility::Bridge::FindByPath()
mApplication.mName = std::move(name);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::SetToolkitName()
+ */
+ void SetToolkitName(std::string_view toolkitName) override
+ {
+ mApplication.mToolkitName = std::string{toolkitName};
+ }
+
protected:
- mutable AppAccessible mApplication;
- std::vector<Dali::Accessibility::Accessible*> mPopups;
+ // We use a weak handle in order not to keep a window alive forever if someone forgets to UnregisterDefaultLabel()
+ using DefaultLabelType = std::pair<Dali::WeakHandle<Dali::Window>, std::weak_ptr<Dali::Accessibility::Accessible>>;
+ using DefaultLabelsType = std::list<DefaultLabelType>;
-private:
+ mutable ApplicationAccessible mApplication;
+ DefaultLabelsType mDefaultLabels;
+ bool mIsScreenReaderSuppressed = false;
+private:
/**
* @brief Sets an ID.
* @param[in] id An ID (integer value)
*/
CacheElementType CreateCacheElement(Dali::Accessibility::Accessible* item);
+ /**
+ * @brief Removes expired elements from the default label collection.
+ */
+ void CompressDefaultLabels();
+
+ /**
+ * @brief Gets the window to which this accessible belongs (or an empty handle).
+ *
+ * @param accessible The accessible
+ * @return The window
+ */
+ static Dali::WeakHandle<Dali::Window> GetWindow(Dali::Accessibility::Accessible* accessible);
+
protected:
BridgeBase();
virtual ~BridgeBase();