[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / controls / control-accessible.cpp
index a24e59d..7e6981b 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.
 
 #include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/public-api/object/property-map.h>
+#include <dali/public-api/object/type-info.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/visuals/image/image-visual.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/control.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
@@ -38,18 +41,20 @@ namespace Dali::Toolkit::DevelControl
 {
 namespace
 {
-static std::string GetLocaleText(std::string string, const char *domain = "dali-toolkit")
+constexpr const char* ATTR_IMG_SRC_KEY = "imgSrc";
+
+std::string GetLocaleText(std::string string, const char* domain = "dali-toolkit")
 {
 #ifdef DGETTEXT_ENABLED
-    /*TODO: currently non-localized string is used as a key for translation lookup. In case the lookup key formatting is forced
+  /*TODO: currently non-localized string is used as a key for translation lookup. In case the lookup key formatting is forced
           consider calling utility function for converting non-localized string into well-formatted key before lookup. */
-    return dgettext(domain, string.c_str());
+  return dgettext(domain, string.c_str());
 #else
-    return string;
+  return string;
 #endif
 }
 
-static Dali::Actor CreateHighlightIndicatorActor()
+Dali::Actor CreateHighlightIndicatorActor()
 {
   std::string focusBorderImagePath(AssetManager::GetDaliImagePath());
   focusBorderImagePath += "/keyboard_focus.9.png";
@@ -64,38 +69,50 @@ static Dali::Actor CreateHighlightIndicatorActor()
 
   return actor;
 }
-} // unnamed namespace
 
-ControlAccessible::ControlAccessible(Dali::Actor self)
-: ActorAccessible(self)
+std::string FetchImageSrcFromMap(const Dali::Property::Map& imageMap)
 {
-  auto control = Toolkit::Control::DownCast(self);
-
-  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
-  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
-
-  self.PropertySetSignal().Connect(&controlImpl, [this, &controlImpl](Dali::Handle& handle, Dali::Property::Index index, Dali::Property::Value value) {
-    if(this->Self() != Dali::Accessibility::Accessible::GetCurrentlyHighlightedActor())
+  auto urlVal = imageMap.Find(Toolkit::ImageVisual::Property::URL);
+  if(urlVal)
+  {
+    if(urlVal->GetType() == Dali::Property::STRING)
     {
-      return;
+      return urlVal->Get<std::string>();
     }
-
-    if(index == DevelControl::Property::ACCESSIBILITY_NAME || (index == GetNamePropertyIndex() && controlImpl.mAccessibilityName.empty()))
+    else if(urlVal->GetType() == Dali::Property::ARRAY)
     {
-      if(controlImpl.mAccessibilityGetNameSignal.Empty())
+      auto urlArray = urlVal->GetArray();
+      if(urlArray && !urlArray->Empty())
       {
-        Emit(Dali::Accessibility::ObjectPropertyChangeEvent::NAME);
+        // Returns first element if url is an array
+        return (*urlArray)[0].Get<std::string>();
       }
     }
+  }
+  return {};
+}
 
-    if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (index == GetDescriptionPropertyIndex() && controlImpl.mAccessibilityDescription.empty()))
-    {
-      if(controlImpl.mAccessibilityGetDescriptionSignal.Empty())
-      {
-        Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION);
-      }
-    }
-  });
+std::string FetchImageSrc(const Toolkit::ImageView& imageView)
+{
+  const auto imageUrl = imageView.GetProperty<std::string>(Toolkit::ImageView::Property::IMAGE);
+  if(!imageUrl.empty())
+  {
+    return imageUrl;
+  }
+
+  const auto imageMap = imageView.GetProperty<Dali::Property::Map>(Toolkit::ImageView::Property::IMAGE);
+  if(!imageMap.Empty())
+  {
+    return FetchImageSrcFromMap(imageMap);
+  }
+  return {};
+}
+
+} // unnamed namespace
+
+ControlAccessible::ControlAccessible(Dali::Actor self)
+: ActorAccessible(self)
+{
 }
 
 std::string ControlAccessible::GetName() const
@@ -104,7 +121,7 @@ std::string ControlAccessible::GetName() const
 
   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
-  std::string name;
+  std::string              name;
 
   if(!controlImpl.mAccessibilityGetNameSignal.Empty())
   {
@@ -142,7 +159,7 @@ std::string ControlAccessible::GetDescription() const
 
   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
-  std::string description;
+  std::string              description;
 
   if(!controlImpl.mAccessibilityGetDescriptionSignal.Empty())
   {
@@ -183,7 +200,7 @@ std::string ControlAccessible::GetLocalizedRoleName() const
 bool ControlAccessible::IsShowing()
 {
   Dali::Actor self = Self();
-  if(!self.GetProperty<bool>(Actor::Property::VISIBLE) || self.GetProperty<Vector4>(Actor::Property::WORLD_COLOR).a == 0 || self.GetProperty<bool>(Dali::DevelActor::Property::CULLED))
+  if(!self.GetProperty<bool>(Actor::Property::VISIBLE) || Dali::EqualsZero(self.GetProperty<Vector4>(Actor::Property::WORLD_COLOR).a) || self.GetProperty<bool>(Dali::DevelActor::Property::CULLED))
   {
     return false;
   }
@@ -195,20 +212,13 @@ bool ControlAccessible::IsShowing()
     return true;
   }
 
-  auto childExtent = child->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
   while(parent)
   {
-    auto control      = Dali::Toolkit::Control::DownCast(parent->Self());
+    auto control = Dali::Toolkit::Control::DownCast(parent->Self());
     if(!control.GetProperty<bool>(Actor::Property::VISIBLE))
     {
       return false;
     }
-    auto clipMode     = control.GetProperty(Actor::Property::CLIPPING_MODE).Get<bool>();
-    auto parentExtent = parent->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
-    if ((clipMode != ClippingMode::DISABLED) && !parentExtent.Intersects(childExtent))
-    {
-      return false;
-    }
     parent = dynamic_cast<Toolkit::DevelControl::ControlAccessible*>(parent->GetParent());
   }
 
@@ -219,15 +229,15 @@ Dali::Accessibility::States ControlAccessible::CalculateStates()
 {
   using Dali::Accessibility::State;
 
-  Dali::Actor self = Self();
+  Dali::Actor                 self = Self();
   Dali::Accessibility::States states;
 
   states[State::FOCUSABLE]     = self.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE);
   states[State::FOCUSED]       = Toolkit::KeyboardFocusManager::Get().GetCurrentFocusActor() == self;
   states[State::HIGHLIGHTABLE] = self.GetProperty<bool>(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE);
-  states[State::HIGHLIGHTED]   = GetCurrentlyHighlightedActor() == self;
+  states[State::HIGHLIGHTED]   = IsHighlighted();
   states[State::ENABLED]       = true;
-  states[State::SENSITIVE]     = true;
+  states[State::SENSITIVE]     = (Dali::DevelActor::IsHittable(self) && Dali::DevelActor::GetTouchRequired(self));
   states[State::VISIBLE]       = self.GetProperty<bool>(Actor::Property::VISIBLE);
   states[State::SHOWING]       = IsShowing();
   states[State::DEFUNCT]       = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
@@ -242,36 +252,58 @@ Dali::Accessibility::States ControlAccessible::GetStates()
 
 Dali::Accessibility::Attributes ControlAccessible::GetAttributes() const
 {
-  std::unordered_map<std::string, std::string> attributeMap;
-  auto control = Dali::Toolkit::Control::DownCast(Self());
-  auto attribute = control.GetProperty(Dali::Toolkit::DevelControl::Property::ACCESSIBILITY_ATTRIBUTES);
-  auto map = attribute.GetMap();
+  static const std::string automationIdKey = "automationId";
+  static const std::string classKey        = "class";
+
+  Accessibility::Attributes result;
+  Toolkit::Control          control        = Toolkit::Control::DownCast(Self());
+  Dali::Property::Value     property       = control.GetProperty(DevelControl::Property::ACCESSIBILITY_ATTRIBUTES);
+  Dali::Property::Map*      attributeMap   = property.GetMap();
+  std::size_t               attributeCount = attributeMap ? attributeMap->Count() : 0U;
 
-  if(map)
+  for(std::size_t i = 0; i < attributeCount; i++)
   {
-    auto mapSize = map->Count();
+    Dali::Property::Key mapKey = attributeMap->GetKeyAt(i);
+    std::string         mapValue;
 
-    for(unsigned int i = 0; i < mapSize; i++)
+    if(mapKey.type == Dali::Property::Key::STRING && attributeMap->GetValue(i).Get(mapValue))
     {
-      auto mapKey = map->GetKeyAt(i);
-      if(mapKey.type == Dali::Property::Key::STRING)
-      {
-        std::string mapValue;
-        if(map->GetValue(i).Get(mapValue))
-        {
-          attributeMap.emplace(std::move(mapKey.stringKey), std::move(mapValue));
-        }
-      }
+      result.emplace(std::move(mapKey.stringKey), std::move(mapValue));
     }
   }
 
-  auto automationId = control.GetProperty<std::string>(Dali::Toolkit::DevelControl::Property::AUTOMATION_ID);
+  auto automationId = control.GetProperty<std::string>(DevelControl::Property::AUTOMATION_ID);
   if(!automationId.empty())
   {
-    attributeMap.emplace("automationId", std::move(automationId));
+    result.emplace(automationIdKey, std::move(automationId));
+  }
+
+  if(auto imageView = Toolkit::ImageView::DownCast(Self()))
+  {
+    auto imageSrc = FetchImageSrc(imageView);
+    if(!imageSrc.empty())
+    {
+      result.emplace(ATTR_IMG_SRC_KEY, std::move(imageSrc));
+    }
+  }
+
+  // Add "class" if not present already
+  if(result.find(classKey) == result.end())
+  {
+    Dali::TypeInfo typeInfo;
+    Self().GetTypeInfo(typeInfo);
+    if(typeInfo)
+    {
+      const std::string& typeName = typeInfo.GetName();
+
+      result.emplace(classKey, typeName);
+
+      // Save the 'typeName' so we don't have to calculate it again
+      DevelControl::AppendAccessibilityAttribute(control, classKey, typeName);
+    }
   }
 
-  return attributeMap;
+  return result;
 }
 
 bool ControlAccessible::IsHidden() const
@@ -291,12 +323,12 @@ bool ControlAccessible::GrabFocus()
 
 void ControlAccessible::ScrollToSelf()
 {
-  auto* child = this;
+  auto* child  = this;
   auto* parent = dynamic_cast<Toolkit::DevelControl::ControlAccessible*>(child->GetParent());
 
-  while (parent)
+  while(parent)
   {
-    if (parent->IsScrollable())
+    if(parent->IsScrollable())
     {
       parent->ScrollToChild(child->Self());
     }
@@ -321,10 +353,26 @@ void ControlAccessible::UnregisterPositionPropertyNotification()
   controlImpl.UnregisterAccessibilityPositionPropertyNotification();
 }
 
+void ControlAccessible::RegisterPropertySetSignal()
+{
+  auto                     control         = Dali::Toolkit::Control::DownCast(Self());
+  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
+  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
+  controlImpl.RegisterAccessibilityPropertySetSignal();
+}
+
+void ControlAccessible::UnregisterPropertySetSignal()
+{
+  auto                     control         = Dali::Toolkit::Control::DownCast(Self());
+  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
+  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
+  controlImpl.UnregisterAccessibilityPropertySetSignal();
+}
+
 bool ControlAccessible::GrabHighlight()
 {
-  Dali::Actor self = Self();
-  auto oldHighlightedActor = GetCurrentlyHighlightedActor();
+  Dali::Actor self                = Self();
+  auto        oldHighlightedActor = GetCurrentlyHighlightedActor();
 
   if(!Dali::Accessibility::IsUp())
   {
@@ -370,23 +418,23 @@ bool ControlAccessible::GrabHighlight()
   SetCurrentlyHighlightedActor(self);
   EmitHighlighted(true);
   RegisterPositionPropertyNotification();
+  RegisterPropertySetSignal();
 
   return true;
 }
 
 bool ControlAccessible::ClearHighlight()
 {
-  Dali::Actor self = Self();
-
   if(!Dali::Accessibility::IsUp())
   {
     return false;
   }
 
-  if(GetCurrentlyHighlightedActor() == self)
+  if(IsHighlighted())
   {
+    UnregisterPropertySetSignal();
     UnregisterPositionPropertyNotification();
-    self.Remove(mCurrentHighlightActor.GetHandle());
+    Self().Remove(mCurrentHighlightActor.GetHandle());
     mCurrentHighlightActor = {};
     SetCurrentlyHighlightedActor({});
     EmitHighlighted(false);