[AT-SPI] Add Table and TableCell interfaces
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / adaptor-framework / accessibility.cpp
index cea55f3..bba8f1d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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/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.h>
+#include <dali/devel-api/atspi-interfaces/table-cell.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;
@@ -185,6 +197,102 @@ std::string Accessible::GetRoleName() const
   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{};
@@ -234,6 +342,9 @@ namespace
 {
 class AdaptorAccessible : public ActorAccessible
 {
+private:
+  std::unique_ptr<TriggerEventInterface> mRenderNotification = nullptr;
+
 protected:
   bool mRoot = false;
 
@@ -251,12 +362,69 @@ public:
 
   bool GrabHighlight() override
   {
-    return false;
+    if(!IsUp())
+    {
+      return false;
+    }
+
+    // Only window accessible is able to grab and clear highlight
+    if(!mRoot)
+    {
+      return false;
+    }
+
+    auto self                = Self();
+    auto oldHighlightedActor = GetCurrentlyHighlightedActor();
+    if(self == oldHighlightedActor)
+    {
+      return true;
+    }
+
+    // Clear the old highlight.
+    if(oldHighlightedActor)
+    {
+      auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
+      if(oldHighlightedObject)
+      {
+        oldHighlightedObject->ClearHighlight();
+      }
+    }
+
+    SetCurrentlyHighlightedActor(self);
+
+    auto                             window     = Dali::DevelWindow::Get(self);
+    Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+    windowImpl.EmitAccessibilityHighlightSignal(true);
+
+    return true;
   }
 
   bool ClearHighlight() override
   {
-    return false;
+    if(!IsUp())
+    {
+      return false;
+    }
+
+    // Only window accessible is able to grab and clear highlight
+    if(!mRoot)
+    {
+      return false;
+    }
+
+    auto self = Self();
+    if(self != GetCurrentlyHighlightedActor())
+    {
+      return false;
+    }
+
+    SetCurrentlyHighlightedActor({});
+
+    auto                             window     = Dali::DevelWindow::Get(self);
+    Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+    windowImpl.EmitAccessibilityHighlightSignal(false);
+
+    return true;
   }
 
   Role GetRole() const override
@@ -288,11 +456,20 @@ public:
 
   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
@@ -304,12 +481,48 @@ public:
   {
     return {};
   }
+
+  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()
+  {
+    Accessibility::Bridge::GetCurrentBridge()->EmitPostRender(this);
+  }
 }; // AdaptorAccessible
 
 using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
 
 // Save RefObject from an Actor in Accessible::Get()
-AdaptorAccessiblesType gAdaptorAccessibles;
+AdaptorAccessiblesType& GetAdaptorAccessibles()
+{
+  static AdaptorAccessiblesType gAdaptorAccessibles;
+  return gAdaptorAccessibles;
+}
 
 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
   return nullptr;
@@ -322,7 +535,7 @@ void Accessible::SetObjectRegistry(ObjectRegistry registry)
 {
   objectRegistry = registry;
   objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
-    gAdaptorAccessibles.erase(obj);
+    GetAdaptorAccessibles().erase(obj);
   });
 }
 
@@ -331,7 +544,7 @@ void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali
   convertingFunctor = functor;
 }
 
-Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
+Accessible* Accessible::Get(Dali::Actor actor)
 {
   if(!actor)
   {
@@ -341,9 +554,15 @@ Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
   auto accessible = convertingFunctor(actor);
   if(!accessible)
   {
-    auto pair = gAdaptorAccessibles.emplace(&actor.GetBaseObject(), nullptr);
+    auto pair = GetAdaptorAccessibles().emplace(&actor.GetBaseObject(), nullptr);
     if(pair.second)
     {
+      bool                     isRoot = false;
+      Dali::Integration::Scene scene  = Dali::Integration::Scene::Get(actor);
+      if(scene)
+      {
+        isRoot = (actor == scene.GetRootLayer());
+      }
       pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
     }
     accessible = pair.first->second.get();