[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / controls / control-accessible.cpp
index 7010a6c..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,43 +69,50 @@ static Dali::Actor CreateHighlightIndicatorActor()
 
   return actor;
 }
-} // unnamed namespace
 
-ControlAccessible::ControlAccessible(Dali::Actor self, Dali::Accessibility::Role role, bool modal)
-: mSelf(self),
-  mIsModal(modal)
+std::string FetchImageSrcFromMap(const Dali::Property::Map& imageMap)
 {
-  auto control = Dali::Toolkit::Control::DownCast(Self());
-
-  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
-  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
-  if(controlImpl.mAccessibilityRole == Dali::Accessibility::Role::UNKNOWN)
+  auto urlVal = imageMap.Find(Toolkit::ImageVisual::Property::URL);
+  if(urlVal)
   {
-    controlImpl.mAccessibilityRole = role;
-  }
-
-  Self().PropertySetSignal().Connect(&controlImpl, [this, &controlImpl](Dali::Handle& handle, Dali::Property::Index index, Dali::Property::Value value) {
-    if(this->Self() != Dali::Accessibility::Accessible::GetCurrentlyHighlightedActor())
+    if(urlVal->GetType() == Dali::Property::STRING)
     {
-      return;
+      return urlVal->Get<std::string>();
     }
-
-    if(index == DevelControl::Property::ACCESSIBILITY_NAME || (index == GetNamePropertyIndex() && !controlImpl.mAccessibilityNameSet))
+    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.mAccessibilityDescriptionSet))
-    {
-      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
@@ -109,13 +121,13 @@ 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())
   {
     controlImpl.mAccessibilityGetNameSignal.Emit(name);
   }
-  else if(controlImpl.mAccessibilityNameSet)
+  else if(!controlImpl.mAccessibilityName.empty())
   {
     name = controlImpl.mAccessibilityName;
   }
@@ -128,7 +140,7 @@ std::string ControlAccessible::GetName() const
     name = Self().GetProperty<std::string>(Actor::Property::NAME);
   }
 
-  if(controlImpl.mAccessibilityTranslationDomainSet)
+  if(!controlImpl.mAccessibilityTranslationDomain.empty())
   {
     return GetLocaleText(name, controlImpl.mAccessibilityTranslationDomain.c_str());
   }
@@ -147,13 +159,13 @@ 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())
   {
     controlImpl.mAccessibilityGetDescriptionSignal.Emit(description);
   }
-  else if(controlImpl.mAccessibilityDescriptionSet)
+  else if(!controlImpl.mAccessibilityDescription.empty())
   {
     description = controlImpl.mAccessibilityDescription;
   }
@@ -161,7 +173,8 @@ std::string ControlAccessible::GetDescription() const
   {
     description = GetDescriptionRaw();
   }
-  if(controlImpl.mAccessibilityTranslationDomainSet)
+
+  if(!controlImpl.mAccessibilityTranslationDomain.empty())
   {
     return GetLocaleText(description, controlImpl.mAccessibilityTranslationDomain.c_str());
   }
@@ -174,40 +187,6 @@ std::string ControlAccessible::GetDescriptionRaw() const
   return {};
 }
 
-Dali::Accessibility::Accessible* ControlAccessible::GetParent()
-{
-  return Dali::Accessibility::Accessible::Get(Self().GetParent());
-}
-
-size_t ControlAccessible::GetChildCount() const
-{
-  return Self().GetChildCount();
-}
-
-Dali::Accessibility::Accessible* ControlAccessible::GetChildAtIndex(size_t index)
-{
-  return Dali::Accessibility::Accessible::Get(Self().GetChildAt(static_cast<unsigned int>(index)));
-}
-
-size_t ControlAccessible::GetIndexInParent()
-{
-  auto self = Self();
-  auto parent = self.GetParent();
-  DALI_ASSERT_ALWAYS(parent && "can't call GetIndexInParent on object without parent");
-
-  auto count = parent.GetChildCount();
-  for(auto i = 0u; i < count; ++i)
-  {
-    auto child = parent.GetChildAt(i);
-    if(child == self)
-    {
-      return i;
-    }
-  }
-  DALI_ASSERT_ALWAYS(false && "object isn't child of it's parent");
-  return static_cast<size_t>(-1);
-}
-
 Dali::Accessibility::Role ControlAccessible::GetRole() const
 {
   return Self().GetProperty<Dali::Accessibility::Role>(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE);
@@ -221,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;
   }
@@ -233,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());
   }
 
@@ -255,32 +227,22 @@ bool ControlAccessible::IsShowing()
 
 Dali::Accessibility::States ControlAccessible::CalculateStates()
 {
-  Dali::Actor self = Self();
-  Dali::Accessibility::States state;
-  state[Dali::Accessibility::State::FOCUSABLE] = self.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE);
-  state[Dali::Accessibility::State::FOCUSED]   = Toolkit::KeyboardFocusManager::Get().GetCurrentFocusActor() == self;
+  using Dali::Accessibility::State;
 
-  if(self.GetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE).GetType() == Dali::Property::NONE)
-  {
-    state[Dali::Accessibility::State::HIGHLIGHTABLE] = false;
-  }
-  else
-  {
-    state[Dali::Accessibility::State::HIGHLIGHTABLE] = self.GetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE).Get<bool>();
-  }
+  Dali::Actor                 self = Self();
+  Dali::Accessibility::States states;
 
-  state[Dali::Accessibility::State::HIGHLIGHTED] = GetCurrentlyHighlightedActor() == self;
-  state[Dali::Accessibility::State::ENABLED]     = true;
-  state[Dali::Accessibility::State::SENSITIVE]   = true;
-  state[Dali::Accessibility::State::VISIBLE]     = self.GetProperty<bool>(Actor::Property::VISIBLE);
+  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]   = IsHighlighted();
+  states[State::ENABLED]       = 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>();
 
-  if(mIsModal)
-  {
-    state[Dali::Accessibility::State::MODAL] = true;
-  }
-  state[Dali::Accessibility::State::SHOWING] = IsShowing();
-  state[Dali::Accessibility::State::DEFUNCT] = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
-  return state;
+  return states;
 }
 
 Dali::Accessibility::States ControlAccessible::GetStates()
@@ -290,67 +252,68 @@ 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));
     }
   }
 
-  return attributeMap;
-}
-
-Dali::Accessibility::ComponentLayer ControlAccessible::GetLayer() const
-{
-  return Dali::Accessibility::ComponentLayer::WINDOW;
-}
-
-Dali::Rect<> ControlAccessible::GetExtents(Dali::Accessibility::CoordinateType type) const
-{
-  Dali::Actor self = Self();
-
-  Vector2 screenPosition = self.GetProperty(Dali::DevelActor::Property::SCREEN_POSITION).Get<Vector2>();
-  auto size = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * self.GetCurrentProperty<Vector3>(Actor::Property::WORLD_SCALE);
-  bool positionUsesAnchorPoint = self.GetProperty(Dali::DevelActor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
-  Vector3 anchorPointOffSet = size * (positionUsesAnchorPoint ? self.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
-  Vector2 position = Vector2((screenPosition.x - anchorPointOffSet.x), (screenPosition.y - anchorPointOffSet.y));
+  auto automationId = control.GetProperty<std::string>(DevelControl::Property::AUTOMATION_ID);
+  if(!automationId.empty())
+  {
+    result.emplace(automationIdKey, std::move(automationId));
+  }
 
-  if(type == Dali::Accessibility::CoordinateType::WINDOW)
+  if(auto imageView = Toolkit::ImageView::DownCast(Self()))
   {
-    return {position.x, position.y, size.x, size.y};
+    auto imageSrc = FetchImageSrc(imageView);
+    if(!imageSrc.empty())
+    {
+      result.emplace(ATTR_IMG_SRC_KEY, std::move(imageSrc));
+    }
   }
-  else // Dali::Accessibility::CoordinateType::SCREEN
+
+  // Add "class" if not present already
+  if(result.find(classKey) == result.end())
   {
-    auto window = Dali::DevelWindow::Get(self);
-    auto windowPosition = window.GetPosition();
-    return {position.x + windowPosition.GetX(), position.y + windowPosition.GetY(), size.x, size.y};
+    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);
+    }
   }
-}
 
-int16_t ControlAccessible::GetMdiZOrder() const
-{
-  return 0;
+  return result;
 }
 
-double ControlAccessible::GetAlpha() const
+bool ControlAccessible::IsHidden() const
 {
-  return 0;
+  auto control = Dali::Toolkit::Control::DownCast(Self());
+
+  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
+  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
+
+  return controlImpl.mAccessibilityHidden;
 }
 
 bool ControlAccessible::GrabFocus()
@@ -360,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());
     }
@@ -390,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())
   {
@@ -408,10 +387,10 @@ bool ControlAccessible::GrabHighlight()
   // Clear the old highlight.
   if(oldHighlightedActor)
   {
-    auto oldHighlightObject = dynamic_cast<Dali::Accessibility::Component*>(Internal::Control::Impl::GetAccessibilityObject(oldHighlightedActor));
-    if(oldHighlightObject)
+    auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
+    if(oldHighlightedObject)
     {
-      oldHighlightObject->ClearHighlight();
+      oldHighlightedObject->ClearHighlight();
     }
   }
 
@@ -439,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);
@@ -532,29 +511,7 @@ std::vector<Dali::Accessibility::Relation> ControlAccessible::GetRelationSet()
 {
   auto control = Dali::Toolkit::Control::DownCast(Self());
 
-  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
-  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
-
-  std::vector<Dali::Accessibility::Relation> ret;
-
-  for(auto& relation : controlImpl.mAccessibilityRelations)
-  {
-    auto& targets = relation.second;
-
-    ret.emplace_back(Accessibility::Relation{relation.first, {}});
-
-    // Map every Accessible* to its Address
-    std::transform(targets.begin(), targets.end(), std::back_inserter(ret.back().targets), [](auto* x) {
-      return x->GetAddress();
-    });
-  }
-
-  return ret;
-}
-
-Dali::Actor ControlAccessible::GetInternalActor()
-{
-  return Dali::Actor{};
+  return DevelControl::GetAccessibilityRelations(control);
 }
 
 bool ControlAccessible::ScrollToChild(Actor child)