[dali_2.3.29] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-base.cpp
index 9f88d3c..ba4e154 100644 (file)
@@ -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.
@@ -25,6 +25,7 @@
 #include <memory>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/public-api/adaptor-framework/timer.h>
 
 using namespace Dali::Accessibility;
@@ -38,57 +39,67 @@ BridgeBase::BridgeBase()
 BridgeBase::~BridgeBase()
 {
   mApplication.mChildren.clear();
-  mApplication.mWindows.clear();
 }
 
-void BridgeBase::AddFilteredEvent(FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
+void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
 {
   if(delay < 0)
   {
     delay = 0;
   }
+  auto countdownBase = static_cast<unsigned int>(delay * 10);
 
-  auto it = mFilteredEvents.insert({{kind, obj}, {static_cast<unsigned int>(delay * 10), {}}});
+  auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
   if(it.second)
   {
     functor();
   }
   else
   {
-    it.first->second.second = std::move(functor);
+    std::get<1>(it.first->second) = countdownBase;
+    std::get<2>(it.first->second) = std::move(functor);
   }
 
   if(!tickTimer)
   {
     tickTimer = Dali::Timer::New(100);
-    tickTimer.TickSignal().Connect(this, &BridgeBase::TickFilteredEvents);
+    tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
+  }
+
+  if(!tickTimer.IsRunning())
+  {
+    tickTimer.Start();
   }
 }
 
-bool BridgeBase::TickFilteredEvents()
+bool BridgeBase::TickCoalescableMessages()
 {
-  for(auto it = mFilteredEvents.begin(); it != mFilteredEvents.end();)
+  for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
   {
-    if(it->second.first)
+    auto& countdown     = std::get<0>(it->second);
+    auto  countdownBase = std::get<1>(it->second);
+    auto& functor       = std::get<2>(it->second);
+    if(countdown)
     {
-      --it->second.first;
+      --countdown;
     }
     else
     {
-      if(it->second.second)
+      if(functor)
       {
-        it->second.second();
-        it->second.second = {};
+        functor();
+        functor   = {};
+        countdown = countdownBase;
       }
       else
       {
-        it = mFilteredEvents.erase(it);
+        it = mCoalescableMessages.erase(it);
         continue;
       }
     }
     ++it;
   }
-  return !mFilteredEvents.empty();
+  return !mCoalescableMessages.empty();
 }
 
 void BridgeBase::UpdateRegisteredEvents()
@@ -135,17 +146,17 @@ BridgeBase::ForceUpResult BridgeBase::ForceUp()
   mDbusServer     = {mConnectionPtr};
 
   {
-    DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCache};
+    DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
     AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
     mDbusServer.addInterface(AtspiDbusPathCache, desc);
   }
   {
-    DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceApplication};
+    DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
     AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
     mDbusServer.addInterface(AtspiPath, desc);
   }
 
-  mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, AtspiDbusInterfaceRegistry, mConnectionPtr};
+  mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
 
   UpdateRegisteredEvents();
 
@@ -163,6 +174,8 @@ BridgeBase::ForceUpResult BridgeBase::ForceUp()
 void BridgeBase::ForceDown()
 {
   Bridge::ForceDown();
+  tickTimer.Reset();
+  mCoalescableMessages.clear();
   mRegistry      = {};
   mDbusServer    = {};
   mConnectionPtr = {};
@@ -186,31 +199,6 @@ Accessible* BridgeBase::FindByPath(const std::string& name) const
   }
 }
 
-void BridgeBase::OnWindowVisibilityChanged(Dali::Window window, bool visible)
-{
-  if(visible)
-  {
-    // TODO : Should we check 'out of screen' here? -> Then, we need an actor of this change.
-    Dali::Accessibility::Bridge::GetCurrentBridge()->WindowShown(window); // Called when Window is shown.
-  }
-  else
-  {
-    Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(window); // Called when Window is hidden and iconified.
-  }
-}
-
-void BridgeBase::OnWindowFocusChanged(Dali::Window window, bool focusIn)
-{
-  if(focusIn)
-  {
-    Dali::Accessibility::Bridge::GetCurrentBridge()->WindowFocused(window); // Called when Window is focused.
-  }
-  else
-  {
-    Dali::Accessibility::Bridge::GetCurrentBridge()->WindowUnfocused(window); // Called when Window is out of focus.
-  }
-}
-
 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
 {
   if(windowAccessible->GetInternalActor() == nullptr)
@@ -228,34 +216,10 @@ void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
   // Adds Window to a list of Windows.
   mApplication.mChildren.push_back(windowAccessible);
   SetIsOnRootLevel(windowAccessible);
-
-  RegisterDefaultLabel(windowAccessible);
-
-  Dali::Window window = Dali::DevelWindow::Get(windowAccessible->GetInternalActor());
-  if(window)
-  {
-    mApplication.mWindows.push_back(window);
-    Dali::DevelWindow::VisibilityChangedSignal(window).Connect(this, &BridgeBase::OnWindowVisibilityChanged);
-    window.FocusChangeSignal().Connect(this, &BridgeBase::OnWindowFocusChanged);
-  }
 }
 
 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
 {
-  for(auto i = 0u; i < mApplication.mWindows.size(); ++i)
-  {
-    if(windowAccessible->GetInternalActor() == mApplication.mWindows[i].GetRootLayer())
-    {
-      Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(mApplication.mWindows[i]);
-      Dali::DevelWindow::VisibilityChangedSignal(mApplication.mWindows[i]).Disconnect(this, &BridgeBase::OnWindowVisibilityChanged);
-      mApplication.mWindows[i].FocusChangeSignal().Disconnect(this, &BridgeBase::OnWindowFocusChanged);
-      mApplication.mWindows.erase(mApplication.mWindows.begin() + i);
-      break;
-    }
-  }
-
-  UnregisterDefaultLabel(windowAccessible);
-
   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
   {
     if(mApplication.mChildren[i] == windowAccessible)
@@ -266,21 +230,81 @@ void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
   }
 }
 
-void BridgeBase::RegisterDefaultLabel(Accessible* object)
+void BridgeBase::CompressDefaultLabels()
+{
+  // Remove entries for objects which no longer exist
+  mDefaultLabels.remove_if([](const DefaultLabelType& label) {
+    // Check 1) window's weak handle; 2) accessible's ref object
+    return !label.first.GetBaseHandle() || label.second.expired();
+  });
+}
+
+void BridgeBase::RegisterDefaultLabel(std::shared_ptr<Accessible> object)
 {
-  if(std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object) == mDefaultLabels.end())
+  CompressDefaultLabels();
+
+  Dali::WeakHandle<Dali::Window> window = GetWindow(object.get());
+  if(!window.GetBaseHandle()) // true also if `object` is null
   {
-    mDefaultLabels.push_back(object);
+    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) {
+    auto labelPtr = label.second.lock();
+    return labelPtr && object == labelPtr;
+  });
+
+  if(it == mDefaultLabels.end())
+  {
+    mDefaultLabels.push_back({window, object});
+  }
+  else if(it->first != window)
+  {
+    // TODO: Tentative implementation. It is yet to be specified what should happen
+    // when the same object is re-registered as a default label for another window.
+    *it = {window, object};
+  }
+  else // it->first == window && it->second == object
+  {
+    // Nothing to do
+  }
+}
+
+void BridgeBase::UnregisterDefaultLabel(std::shared_ptr<Accessible> object)
+{
+  CompressDefaultLabels();
+
+  mDefaultLabels.remove_if([&object](const DefaultLabelType& label) {
+    auto labelPtr = label.second.lock();
+    return labelPtr && object == labelPtr;
+  });
 }
 
-void BridgeBase::UnregisterDefaultLabel(Accessible* object)
+Accessible* BridgeBase::GetDefaultLabel(Accessible* root)
 {
-  auto it = std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object);
-  if(it != mDefaultLabels.end())
+  CompressDefaultLabels();
+
+  Dali::WeakHandle<Dali::Window> window = GetWindow(root);
+  if(!window.GetBaseHandle())
+  {
+    return root;
+  }
+
+  auto it = std::find_if(mDefaultLabels.rbegin(), mDefaultLabels.rend(), [&window](const DefaultLabelType& label) {
+    return window == label.first;
+  });
+
+  Accessible* rawPtr = root;
+  if(it != mDefaultLabels.rend())
   {
-    mDefaultLabels.erase(it);
+    if(auto labelPtr = it->second.lock())
+    {
+      rawPtr = labelPtr.get();
+    }
   }
+
+  return rawPtr;
 }
 
 std::string BridgeBase::StripPrefix(const std::string& path)
@@ -304,7 +328,7 @@ Accessible* BridgeBase::Find(const std::string& path) const
   }
 
   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
-  if(it == mData->mKnownObjects.end())
+  if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
   {
     throw std::domain_error{"unknown object '" + path + "'"};
   }
@@ -318,7 +342,7 @@ Accessible* BridgeBase::Find(const Address& ptr) const
   return Find(ptr.GetPath());
 }
 
-Accessible* BridgeBase::FindSelf() const
+Accessible* BridgeBase::FindCurrentObject() const
 {
   auto path = DBus::DBusServer::getCurrentObjectPath();
   auto size = strlen(AtspiPath);
@@ -386,9 +410,23 @@ auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
     root->GetAddress(),
     parent ? parent->GetAddress() : Address{},
     children,
-    item->GetInterfaces(),
+    item->GetInterfacesAsStrings(),
     item->GetName(),
     item->GetRole(),
     item->GetDescription(),
     item->GetStates().GetRawData());
 }
+
+Dali::WeakHandle<Dali::Window> BridgeBase::GetWindow(Dali::Accessibility::Accessible* accessible)
+{
+  Dali::WeakHandle<Dali::Window> windowHandle;
+  Dali::Actor                    actor = accessible ? accessible->GetInternalActor() : Dali::Actor();
+
+  if(actor)
+  {
+    Dali::Window window = Dali::DevelWindow::Get(actor);
+    windowHandle        = {window};
+  }
+
+  return windowHandle;
+}