/*
- * Copyright 2020 Samsung Electronics Co., Ltd
+ * Copyright 2022 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.
#include <dali/public-api/actors/actor.h>
#include <dali/public-api/actors/layer.h>
#include <dali/public-api/object/base-object.h>
-#include <dali/public-api/object/object-registry.h>
#include <dali/public-api/object/type-info.h>
#include <dali/public-api/object/type-registry-helper.h>
-#include <dali/public-api/object/weak-handle.h>
#include <string_view>
#include <unordered_map>
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/accessibility-bridge.h>
+#include <dali/devel-api/adaptor-framework/actor-accessible.h>
#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/action.h>
+#include <dali/devel-api/atspi-interfaces/application.h>
#include <dali/devel-api/atspi-interfaces/collection.h>
#include <dali/devel-api/atspi-interfaces/component.h>
+#include <dali/devel-api/atspi-interfaces/editable-text.h>
+#include <dali/devel-api/atspi-interfaces/hyperlink.h>
+#include <dali/devel-api/atspi-interfaces/hypertext.h>
+#include <dali/devel-api/atspi-interfaces/selection.h>
+#include <dali/devel-api/atspi-interfaces/socket.h>
+#include <dali/devel-api/atspi-interfaces/table-cell.h>
+#include <dali/devel-api/atspi-interfaces/table.h>
+#include <dali/devel-api/atspi-interfaces/text.h>
+#include <dali/devel-api/atspi-interfaces/value.h>
#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/internal/window-system/common/window-impl.h>
#include <dali/public-api/dali-adaptor-common.h>
using namespace Dali::Accessibility;
return std::string{it->second};
}
+AtspiInterfaces Accessible::GetInterfaces() const
+{
+ if(!mInterfaces)
+ {
+ mInterfaces = DoGetInterfaces();
+ DALI_ASSERT_DEBUG(mInterfaces); // There has to be at least AtspiInterface::ACCESSIBLE
+ }
+
+ return mInterfaces;
+}
+
+std::vector<std::string> Accessible::GetInterfacesAsStrings() const
+{
+ std::vector<std::string> ret;
+ AtspiInterfaces interfaces = GetInterfaces();
+
+ for(std::size_t i = 0u; i < static_cast<std::size_t>(AtspiInterface::MAX_COUNT); ++i)
+ {
+ auto interface = static_cast<AtspiInterface>(i);
+
+ if(interfaces[interface])
+ {
+ auto name = GetInterfaceName(interface);
+
+ DALI_ASSERT_DEBUG(!name.empty());
+ ret.emplace_back(std::move(name));
+ }
+ }
+
+ return ret;
+}
+
+AtspiInterfaces Accessible::DoGetInterfaces() const
+{
+ AtspiInterfaces interfaces;
+
+ interfaces[AtspiInterface::ACCESSIBLE] = true;
+ interfaces[AtspiInterface::ACTION] = dynamic_cast<const Action*>(this);
+ interfaces[AtspiInterface::APPLICATION] = dynamic_cast<const Application*>(this);
+ interfaces[AtspiInterface::COLLECTION] = dynamic_cast<const Collection*>(this);
+ interfaces[AtspiInterface::COMPONENT] = dynamic_cast<const Component*>(this);
+ interfaces[AtspiInterface::EDITABLE_TEXT] = dynamic_cast<const EditableText*>(this);
+ interfaces[AtspiInterface::HYPERLINK] = dynamic_cast<const Hyperlink*>(this);
+ interfaces[AtspiInterface::HYPERTEXT] = dynamic_cast<const Hypertext*>(this);
+ interfaces[AtspiInterface::SELECTION] = dynamic_cast<const Selection*>(this);
+ interfaces[AtspiInterface::SOCKET] = dynamic_cast<const Socket*>(this);
+ interfaces[AtspiInterface::TABLE] = dynamic_cast<const Table*>(this);
+ interfaces[AtspiInterface::TABLE_CELL] = dynamic_cast<const TableCell*>(this);
+ interfaces[AtspiInterface::TEXT] = dynamic_cast<const Text*>(this);
+ interfaces[AtspiInterface::VALUE] = dynamic_cast<const Value*>(this);
+
+ return interfaces;
+}
+
+std::string Accessible::GetInterfaceName(AtspiInterface interface)
+{
+ static const std::unordered_map<AtspiInterface, std::string_view> interfaceMap{
+ {AtspiInterface::ACCESSIBLE, "org.a11y.atspi.Accessible"},
+ {AtspiInterface::ACTION, "org.a11y.atspi.Action"},
+ {AtspiInterface::APPLICATION, "org.a11y.atspi.Application"},
+ {AtspiInterface::CACHE, "org.a11y.atspi.Cache"},
+ {AtspiInterface::COLLECTION, "org.a11y.atspi.Collection"},
+ {AtspiInterface::COMPONENT, "org.a11y.atspi.Component"},
+ {AtspiInterface::DEVICE_EVENT_CONTROLLER, "org.a11y.atspi.DeviceEventController"},
+ {AtspiInterface::DEVICE_EVENT_LISTENER, "org.a11y.atspi.DeviceEventListener"},
+ {AtspiInterface::DOCUMENT, "org.a11y.atspi.Document"},
+ {AtspiInterface::EDITABLE_TEXT, "org.a11y.atspi.EditableText"},
+ {AtspiInterface::EVENT_DOCUMENT, "org.a11y.atspi.Event.Document"},
+ {AtspiInterface::EVENT_FOCUS, "org.a11y.atspi.Event.Focus"},
+ {AtspiInterface::EVENT_KEYBOARD, "org.a11y.atspi.Event.Keyboard"},
+ {AtspiInterface::EVENT_MOUSE, "org.a11y.atspi.Event.Mouse"},
+ {AtspiInterface::EVENT_OBJECT, "org.a11y.atspi.Event.Object"},
+ {AtspiInterface::EVENT_TERMINAL, "org.a11y.atspi.Event.Terminal"},
+ {AtspiInterface::EVENT_WINDOW, "org.a11y.atspi.Event.Window"},
+ {AtspiInterface::HYPERLINK, "org.a11y.atspi.Hyperlink"},
+ {AtspiInterface::HYPERTEXT, "org.a11y.atspi.Hypertext"},
+ {AtspiInterface::IMAGE, "org.a11y.atspi.Image"},
+ {AtspiInterface::REGISTRY, "org.a11y.atspi.Registry"},
+ {AtspiInterface::SELECTION, "org.a11y.atspi.Selection"},
+ {AtspiInterface::SOCKET, "org.a11y.atspi.Socket"},
+ {AtspiInterface::TABLE, "org.a11y.atspi.Table"},
+ {AtspiInterface::TABLE_CELL, "org.a11y.atspi.TableCell"},
+ {AtspiInterface::TEXT, "org.a11y.atspi.Text"},
+ {AtspiInterface::VALUE, "org.a11y.atspi.Value"},
+ };
+
+ auto it = interfaceMap.find(interface);
+
+ if(it == interfaceMap.end())
+ {
+ return {};
+ }
+
+ return std::string{it->second};
+}
+
Dali::Actor Accessible::GetCurrentlyHighlightedActor()
{
return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
}
}
+bool Accessible::IsHighlighted() const
+{
+ Dali::Actor self = GetInternalActor();
+
+ return self && self == GetCurrentlyHighlightedActor();
+}
+
Dali::Actor Accessible::GetHighlightActor()
{
return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
namespace
{
-class AdaptorAccessible : public virtual Accessible, public virtual Collection, public virtual Component
+class AdaptorAccessible : public ActorAccessible
{
-protected:
- Dali::WeakHandle<Dali::Actor> mSelf;
- bool mRoot = false;
-
- Dali::Actor Self() const
- {
- auto handle = mSelf.GetHandle();
-
- // AdaptorAccessible is deleted on ObjectDestroyedSignal
- // for the respective actor (see `nonControlAccessibles`).
- DALI_ASSERT_ALWAYS(handle);
-
- return handle;
- }
+private:
+ std::unique_ptr<TriggerEventInterface> mRenderNotification{nullptr};
+ bool mRoot{false};
public:
AdaptorAccessible(Dali::Actor actor, bool isRoot)
- : mSelf(actor),
+ : ActorAccessible(actor),
mRoot(isRoot)
{
}
- Dali::Rect<> GetExtents(Dali::Accessibility::CoordinateType type) const override
- {
- Dali::Actor actor = Self();
- Vector2 screenPosition = actor.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
- Vector3 size = actor.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * actor.GetCurrentProperty<Vector3>(Actor::Property::WORLD_SCALE);
- bool positionUsesAnchorPoint = actor.GetProperty(Actor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
- Vector3 anchorPointOffSet = size * (positionUsesAnchorPoint ? actor.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
- Vector2 position = Vector2(screenPosition.x - anchorPointOffSet.x, screenPosition.y - anchorPointOffSet.y);
-
- if(type == Dali::Accessibility::CoordinateType::WINDOW)
- {
- return {position.x, position.y, size.x, size.y};
- }
- else // Dali::Accessibility::CoordinateType::SCREEN
- {
- auto window = Dali::DevelWindow::Get(actor);
- auto windowPosition = window.GetPosition();
- return {position.x + windowPosition.GetX(), position.y + windowPosition.GetY(), size.x, size.y};
- }
- }
-
- Dali::Accessibility::ComponentLayer GetLayer() const override
- {
- return Dali::Accessibility::ComponentLayer::WINDOW;
- }
-
- int16_t GetMdiZOrder() const override
- {
- return 0;
- }
-
- double GetAlpha() const override
- {
- return 0;
- }
-
bool GrabFocus() override
{
return false;
bool GrabHighlight() override
{
- return false;
- }
-
- bool ClearHighlight() override
- {
- return false;
- }
-
- bool IsScrollable() const override
- {
- return false;
- }
+ if(!IsUp())
+ {
+ return false;
+ }
- std::string GetName() const override
- {
- return Self().GetProperty<std::string>(Dali::Actor::Property::NAME);
- }
+ // Only window accessible is able to grab and clear highlight
+ if(!mRoot)
+ {
+ return false;
+ }
- std::string GetDescription() const override
- {
- return "";
- }
+ auto self = Self();
+ auto oldHighlightedActor = GetCurrentlyHighlightedActor();
+ if(self == oldHighlightedActor)
+ {
+ return true;
+ }
- Accessible* GetParent() override
- {
- if(IsOnRootLevel())
+ // Clear the old highlight.
+ if(oldHighlightedActor)
{
- auto data = GetBridgeData();
- return data->mBridge->GetApplication();
+ auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
+ if(oldHighlightedObject)
+ {
+ oldHighlightedObject->ClearHighlight();
+ }
}
- return Get(Self().GetParent());
- }
- size_t GetChildCount() const override
- {
- return static_cast<size_t>(Self().GetChildCount());
+ SetCurrentlyHighlightedActor(self);
+
+ auto window = Dali::DevelWindow::Get(self);
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ windowImpl.EmitAccessibilityHighlightSignal(true);
+
+ return true;
}
- Accessible* GetChildAtIndex(size_t index) override
+ bool ClearHighlight() override
{
- auto numberOfChildren = static_cast<size_t>(Self().GetChildCount());
- if(index >= numberOfChildren)
+ if(!IsUp())
{
- throw std::domain_error{"invalid index " + std::to_string(index) + " for object with " + std::to_string(numberOfChildren) + " children"};
+ return false;
}
- return Get(Self().GetChildAt(static_cast<unsigned int>(index)));
- }
- size_t GetIndexInParent() override
- {
- auto parent = Self().GetParent();
- if(!parent)
+ // Only window accessible is able to grab and clear highlight
+ if(!mRoot)
{
- return 0;
+ return false;
}
- auto size = static_cast<size_t>(parent.GetChildCount());
- for(auto i = 0u; i < size; ++i)
+
+ if(!IsHighlighted())
{
- if(parent.GetChildAt(i) == Self())
- {
- return i;
- }
+ return false;
}
- throw std::domain_error{"actor is not a child of it's parent"};
+
+ SetCurrentlyHighlightedActor({});
+
+ auto window = Dali::DevelWindow::Get(Self());
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ windowImpl.EmitAccessibilityHighlightSignal(false);
+
+ return true;
}
Role GetRole() const override
state[State::VISIBLE] = true;
state[State::ACTIVE] = visible;
}
- else
+ else if(GetParent())
{
auto parentState = GetParent()->GetStates();
state[State::SHOWING] = parentState[State::SHOWING];
state[State::VISIBLE] = parentState[State::VISIBLE];
}
+ else
+ {
+ state[State::SHOWING] = false;
+ state[State::VISIBLE] = false;
+ }
return state;
}
Attributes GetAttributes() const override
{
+ Attributes attributes;
+
+ if(mRoot)
+ {
+ Dali::Window window = Dali::DevelWindow::Get(Self());
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ attributes["resID"] = windowImpl.GetNativeResourceId();
+ }
+
Dali::TypeInfo type;
Self().GetTypeInfo(type);
- return {
- {"class", type.GetName()},
- };
+ attributes["class"] = type.GetName();
+
+ return attributes;
}
bool DoGesture(const GestureInfo& gestureInfo) override
return {};
}
- Dali::Actor GetInternalActor() override
+ void SetListenPostRender(bool enabled) override
+ {
+ if(!mRoot)
+ {
+ return;
+ }
+
+ auto window = Dali::DevelWindow::Get(Self());
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+
+ if(!mRenderNotification)
+ {
+ mRenderNotification = std::unique_ptr<TriggerEventInterface>(
+ TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &AdaptorAccessible::OnPostRender),
+ TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
+ }
+
+ if(enabled)
+ {
+ windowImpl.SetRenderNotification(mRenderNotification.get());
+ }
+ else
+ {
+ windowImpl.SetRenderNotification(nullptr);
+ }
+ }
+
+ void OnPostRender()
{
- return mSelf.GetHandle();
+ Accessibility::Bridge::GetCurrentBridge()->EmitPostRender(shared_from_this());
}
}; // AdaptorAccessible
-using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
-
-// Save RefObject from an Actor in Accessible::Get()
-AdaptorAccessiblesType gAdaptorAccessibles;
+using ConvertingResult = std::pair<std::shared_ptr<Accessible>, bool>;
-std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
- return nullptr;
+std::function<ConvertingResult(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> ConvertingResult {
+ return {nullptr, true};
};
-ObjectRegistry objectRegistry;
} // namespace
-void Accessible::SetObjectRegistry(ObjectRegistry registry)
-{
- objectRegistry = registry;
-}
-
-void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali::Actor)> functor)
+void Accessible::RegisterExternalAccessibleGetter(std::function<ConvertingResult(Dali::Actor)> functor)
{
convertingFunctor = functor;
}
-Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
+std::shared_ptr<Accessible> 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)
+ {
+ 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)
{
- if(gAdaptorAccessibles.empty() && objectRegistry)
+ bool isRoot = false;
+ Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor);
+ if(scene)
{
- objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
- gAdaptorAccessibles.erase(obj);
- });
+ isRoot = (actor == scene.GetRootLayer());
}
- auto pair = gAdaptorAccessibles.emplace(&actor.GetBaseObject(), nullptr);
- if(pair.second)
+ accessible = std::make_shared<AdaptorAccessible>(actor, isRoot);
+ }
+
+ if(accessible)
+ {
+ uint32_t actorId = actor.GetProperty<int>(Dali::Actor::Property::ID);
+ bridge->AddAccessible(actorId, accessible);
+ if(auto actorAccesible = std::dynamic_pointer_cast<ActorAccessible>(accessible))
{
- pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
+ actorAccesible->StartObservingDestruction();
}
- accessible = pair.first->second.get();
}
return accessible;
}
+
+Accessible* Accessible::Get(Dali::Actor actor)
+{
+ auto accessible = Accessible::GetOwningPtr(actor);
+ return accessible ? accessible.get() : nullptr;
+}
\ No newline at end of file