[ATSPI] Fix for SCREEN coordinate type in GetExtents
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / controls / accessible-impl.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "accessible-impl.h"
20
21 // EXTERNAL INCLUDES
22 #ifdef DGETTEXT_ENABLED
23 #include <libintl.h>
24 #endif
25
26 #include <dali/devel-api/actors/actor-devel.h>
27 #include <dali/devel-api/adaptor-framework/window-devel.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
31 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
32 #include <dali-toolkit/public-api/controls/control-impl.h>
33 #include <dali-toolkit/public-api/controls/control.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
36
37 namespace Dali::Toolkit::DevelControl
38 {
39
40 static std::string GetLocaleText(std::string string, const char *domain = "dali-toolkit")
41 {
42 #ifdef DGETTEXT_ENABLED
43     /*TODO: currently non-localized string is used as a key for translation lookup. In case the lookup key formatting is forced
44           consider calling utility function for converting non-localized string into well-formatted key before lookup. */
45     return dgettext(domain, string.c_str());
46 #else
47     return string;
48 #endif
49 }
50
51 AccessibleImpl::AccessibleImpl(Dali::Actor self, Dali::Accessibility::Role role, bool modal)
52 : mSelf(self),
53   mIsModal(modal)
54 {
55   auto control = Dali::Toolkit::Control::DownCast(Self());
56
57   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
58   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
59   if(controlImpl.mAccessibilityRole == Dali::Accessibility::Role::UNKNOWN)
60   {
61     controlImpl.mAccessibilityRole = role;
62   }
63
64   Self().PropertySetSignal().Connect(&controlImpl, [this, &controlImpl](Dali::Handle& handle, Dali::Property::Index index, Dali::Property::Value value) {
65     if(this->Self() != Dali::Accessibility::Accessible::GetCurrentlyHighlightedActor())
66     {
67       return;
68     }
69
70     if(index == DevelControl::Property::ACCESSIBILITY_NAME || (index == GetNamePropertyIndex() && !controlImpl.mAccessibilityNameSet))
71     {
72       if(controlImpl.mAccessibilityGetNameSignal.Empty())
73       {
74         Emit(Dali::Accessibility::ObjectPropertyChangeEvent::NAME);
75       }
76     }
77
78     if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (index == GetDescriptionPropertyIndex() && !controlImpl.mAccessibilityDescriptionSet))
79     {
80       if(controlImpl.mAccessibilityGetDescriptionSignal.Empty())
81       {
82         Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION);
83       }
84     }
85   });
86 }
87
88 std::string AccessibleImpl::GetName()
89 {
90   auto control = Dali::Toolkit::Control::DownCast(Self());
91
92   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
93   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
94   std::string name;
95
96   if(!controlImpl.mAccessibilityGetNameSignal.Empty())
97   {
98     controlImpl.mAccessibilityGetNameSignal.Emit(name);
99   }
100   else if(controlImpl.mAccessibilityNameSet)
101   {
102     name = controlImpl.mAccessibilityName;
103   }
104   else if(auto raw = GetNameRaw(); !raw.empty())
105   {
106     name = raw;
107   }
108   else
109   {
110     name = Self().GetProperty<std::string>(Actor::Property::NAME);
111   }
112
113   if(controlImpl.mAccessibilityTranslationDomainSet)
114   {
115     return GetLocaleText(name, controlImpl.mAccessibilityTranslationDomain.c_str());
116   }
117
118   return GetLocaleText(name);
119 }
120
121 std::string AccessibleImpl::GetNameRaw()
122 {
123   return {};
124 }
125
126 std::string AccessibleImpl::GetDescription()
127 {
128   auto control = Dali::Toolkit::Control::DownCast(Self());
129
130   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
131   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
132   std::string description;
133
134   if(!controlImpl.mAccessibilityGetDescriptionSignal.Empty())
135   {
136     controlImpl.mAccessibilityGetDescriptionSignal.Emit(description);
137   }
138   else if(controlImpl.mAccessibilityDescriptionSet)
139   {
140     description = controlImpl.mAccessibilityDescription;
141   }
142   else
143   {
144     description = GetDescriptionRaw();
145   }
146   if(controlImpl.mAccessibilityTranslationDomainSet)
147   {
148     return GetLocaleText(description, controlImpl.mAccessibilityTranslationDomain.c_str());
149   }
150
151   return GetLocaleText(description);
152 }
153
154 std::string AccessibleImpl::GetDescriptionRaw()
155 {
156   return {};
157 }
158
159 Dali::Accessibility::Accessible* AccessibleImpl::GetParent()
160 {
161   return Dali::Accessibility::Accessible::Get(Self().GetParent());
162 }
163
164 size_t AccessibleImpl::GetChildCount()
165 {
166   return Self().GetChildCount();
167 }
168
169 Dali::Accessibility::Accessible* AccessibleImpl::GetChildAtIndex(size_t index)
170 {
171   return Dali::Accessibility::Accessible::Get(Self().GetChildAt(static_cast<unsigned int>(index)));
172 }
173
174 size_t AccessibleImpl::GetIndexInParent()
175 {
176   auto self = Self();
177   auto parent = self.GetParent();
178   DALI_ASSERT_ALWAYS(parent && "can't call GetIndexInParent on object without parent");
179
180   auto count = parent.GetChildCount();
181   for(auto i = 0u; i < count; ++i)
182   {
183     auto child = parent.GetChildAt(i);
184     if(child == self)
185     {
186       return i;
187     }
188   }
189   DALI_ASSERT_ALWAYS(false && "object isn't child of it's parent");
190   return static_cast<size_t>(-1);
191 }
192
193 Dali::Accessibility::Role AccessibleImpl::GetRole()
194 {
195   return Self().GetProperty<Dali::Accessibility::Role>(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE);
196 }
197
198 std::string AccessibleImpl::GetLocalizedRoleName()
199 {
200   return GetLocaleText(GetRoleName());
201 }
202
203 Dali::Accessibility::States AccessibleImpl::CalculateStates()
204 {
205   Dali::Actor self = Self();
206   Dali::Accessibility::States state;
207   state[Dali::Accessibility::State::FOCUSABLE] = self.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE);
208   state[Dali::Accessibility::State::FOCUSED]   = Toolkit::KeyboardFocusManager::Get().GetCurrentFocusActor() == self;
209
210   if(self.GetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE).GetType() == Dali::Property::NONE)
211   {
212     state[Dali::Accessibility::State::HIGHLIGHTABLE] = false;
213   }
214   else
215   {
216     state[Dali::Accessibility::State::HIGHLIGHTABLE] = self.GetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE).Get<bool>();
217   }
218
219   state[Dali::Accessibility::State::HIGHLIGHTED] = GetCurrentlyHighlightedActor() == self;
220   state[Dali::Accessibility::State::ENABLED]     = true;
221   state[Dali::Accessibility::State::SENSITIVE]   = true;
222   state[Dali::Accessibility::State::VISIBLE]     = true;
223
224   if(mIsModal)
225   {
226     state[Dali::Accessibility::State::MODAL] = true;
227   }
228   state[Dali::Accessibility::State::SHOWING] = !self.GetProperty(Dali::DevelActor::Property::CULLED).Get<bool>() && self.GetCurrentProperty<bool>(Actor::Property::VISIBLE);
229
230   state[Dali::Accessibility::State::DEFUNCT] = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
231   return state;
232 }
233
234 Dali::Accessibility::States AccessibleImpl::GetStates()
235 {
236   return CalculateStates();
237 }
238
239 Dali::Accessibility::Attributes AccessibleImpl::GetAttributes()
240 {
241   std::unordered_map<std::string, std::string> attributeMap;
242   auto control = Dali::Toolkit::Control::DownCast(Self());
243   auto attribute = control.GetProperty(Dali::Toolkit::DevelControl::Property::ACCESSIBILITY_ATTRIBUTES);
244   auto map = attribute.GetMap();
245
246   if(map)
247   {
248     auto mapSize = map->Count();
249
250     for(unsigned int i = 0; i < mapSize; i++)
251     {
252       auto mapKey = map->GetKeyAt(i);
253       if(mapKey.type == Dali::Property::Key::STRING)
254       {
255         std::string mapValue;
256         if(map->GetValue(i).Get(mapValue))
257         {
258           attributeMap.emplace(std::move(mapKey.stringKey), std::move(mapValue));
259         }
260       }
261     }
262   }
263
264   return attributeMap;
265 }
266
267 Dali::Accessibility::ComponentLayer AccessibleImpl::GetLayer()
268 {
269   return Dali::Accessibility::ComponentLayer::WINDOW;
270 }
271
272 Dali::Rect<> AccessibleImpl::GetExtents(Dali::Accessibility::CoordinateType type)
273 {
274   Dali::Actor self = Self();
275
276   Vector2 screenPosition = self.GetProperty(Dali::DevelActor::Property::SCREEN_POSITION).Get<Vector2>();
277   auto size = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * self.GetCurrentProperty<Vector3>(Actor::Property::WORLD_SCALE);
278   bool positionUsesAnchorPoint = self.GetProperty(Dali::DevelActor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
279   Vector3 anchorPointOffSet = size * (positionUsesAnchorPoint ? self.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
280   Vector2 position = Vector2((screenPosition.x - anchorPointOffSet.x), (screenPosition.y - anchorPointOffSet.y));
281
282   if(type == Dali::Accessibility::CoordinateType::WINDOW)
283   {
284     return {position.x, position.y, size.x, size.y};
285   }
286   else // Dali::Accessibility::CoordinateType::SCREEN
287   {
288     auto window = Dali::DevelWindow::Get(self);
289     auto windowPosition = window.GetPosition();
290     return {position.x + windowPosition.GetX(), position.y + windowPosition.GetY(), size.x, size.y};
291   }
292 }
293
294 int16_t AccessibleImpl::GetMdiZOrder()
295 {
296   return 0;
297 }
298 double AccessibleImpl::GetAlpha()
299 {
300   return 0;
301 }
302
303 bool AccessibleImpl::GrabFocus()
304 {
305   return Toolkit::KeyboardFocusManager::Get().SetCurrentFocusActor(Self());
306 }
307
308 static Dali::Actor CreateHighlightIndicatorActor()
309 {
310   std::string focusBorderImagePath(AssetManager::GetDaliImagePath());
311   focusBorderImagePath += "/keyboard_focus.9.png";
312
313   // Create the default if it hasn't been set and one that's shared by all the
314   // keyboard focusable actors
315   auto actor = Toolkit::ImageView::New(focusBorderImagePath);
316   actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
317
318   DevelControl::AppendAccessibilityAttribute(actor, "highlight", std::string());
319   actor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, false);
320
321   return actor;
322 }
323
324 void AccessibleImpl::ScrollToSelf()
325 {
326   auto* child = this;
327   auto* parent = dynamic_cast<Toolkit::DevelControl::AccessibleImpl*>(child->GetParent());
328
329   while (parent)
330   {
331     if (parent->IsScrollable())
332     {
333       parent->ScrollToChild(child->Self());
334     }
335
336     child = parent;
337     parent = dynamic_cast<Toolkit::DevelControl::AccessibleImpl*>(parent->GetParent());
338   }
339 }
340
341 void AccessibleImpl::RegisterPositionPropertyNotification()
342 {
343   auto                     control         = Dali::Toolkit::Control::DownCast(Self());
344   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
345   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
346   controlImpl.RegisterAccessibilityPositionPropertyNotification();
347 }
348
349 void AccessibleImpl::UnregisterPositionPropertyNotification()
350 {
351   auto                     control         = Dali::Toolkit::Control::DownCast(Self());
352   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
353   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
354   controlImpl.UnregisterAccessibilityPositionPropertyNotification();
355 }
356
357 bool AccessibleImpl::GrabHighlight()
358 {
359   Dali::Actor self = Self();
360   auto oldHighlightedActor = GetCurrentlyHighlightedActor();
361
362   if(!Dali::Accessibility::IsUp())
363   {
364     return false;
365   }
366
367   if(self == oldHighlightedActor)
368   {
369     return true;
370   }
371
372   // Clear the old highlight.
373   if(oldHighlightedActor)
374   {
375     auto oldHighlightObject = dynamic_cast<Dali::Accessibility::Component*>(Internal::Control::Impl::GetAccessibilityObject(oldHighlightedActor));
376     if(oldHighlightObject)
377     {
378       oldHighlightObject->ClearHighlight();
379     }
380   }
381
382   auto highlight = GetHighlightActor();
383   if(!highlight)
384   {
385     highlight = CreateHighlightIndicatorActor();
386     SetHighlightActor(highlight);
387   }
388
389   highlight.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
390   highlight.SetProperty(Actor::Property::POSITION_Z, 1.0f);
391   highlight.SetProperty(Actor::Property::POSITION, Vector2(0.0f, 0.0f));
392
393   // Need to set resize policy again, to update SIZE property which is set by
394   // AccessibleImpl_NUI. The highlight could move from AccessibleImpl_NUI to
395   // AccessibleImpl. In this case, highlight has incorrect size.
396   highlight.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
397
398   // Remember the highlight actor, so that when the default is changed with
399   // SetHighlightActor(), the currently displayed highlight can still be cleared.
400   mCurrentHighlightActor = highlight;
401   ScrollToSelf();
402   self.Add(highlight);
403   SetCurrentlyHighlightedActor(self);
404   EmitHighlighted(true);
405   RegisterPositionPropertyNotification();
406
407   return true;
408 }
409
410 bool AccessibleImpl::ClearHighlight()
411 {
412   Dali::Actor self = Self();
413
414   if(!Dali::Accessibility::IsUp())
415   {
416     return false;
417   }
418
419   if(GetCurrentlyHighlightedActor() == self)
420   {
421     UnregisterPositionPropertyNotification();
422     self.Remove(mCurrentHighlightActor.GetHandle());
423     mCurrentHighlightActor = {};
424     SetCurrentlyHighlightedActor({});
425     EmitHighlighted(false);
426     return true;
427   }
428   return false;
429 }
430
431 std::string AccessibleImpl::GetActionName(size_t index)
432 {
433   if(index >= GetActionCount())
434   {
435     return {};
436   }
437
438   Dali::TypeInfo type;
439   Self().GetTypeInfo(type);
440   DALI_ASSERT_ALWAYS(type && "no TypeInfo object");
441   return type.GetActionName(index);
442 }
443
444 std::string AccessibleImpl::GetLocalizedActionName(size_t index)
445 {
446   return GetLocaleText(GetActionName(index));
447 }
448
449 std::string AccessibleImpl::GetActionDescription(size_t index)
450 {
451   return {};
452 }
453
454 size_t AccessibleImpl::GetActionCount()
455 {
456   Dali::TypeInfo type;
457   Self().GetTypeInfo(type);
458   DALI_ASSERT_ALWAYS(type && "no TypeInfo object");
459   return type.GetActionCount();
460 }
461
462 std::string AccessibleImpl::GetActionKeyBinding(size_t index)
463 {
464   return {};
465 }
466
467 bool AccessibleImpl::DoAction(size_t index)
468 {
469   std::string actionName = GetActionName(index);
470   return Self().DoAction(actionName, {});
471 }
472
473 bool AccessibleImpl::DoAction(const std::string& name)
474 {
475   return Self().DoAction(name, {});
476 }
477
478 bool AccessibleImpl::DoGesture(const Dali::Accessibility::GestureInfo& gestureInfo)
479 {
480   auto control = Dali::Toolkit::Control::DownCast(Self());
481
482   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
483   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
484
485   if(!controlImpl.mAccessibilityDoGestureSignal.Empty())
486   {
487     auto ret = std::make_pair(gestureInfo, false);
488     controlImpl.mAccessibilityDoGestureSignal.Emit(ret);
489     return ret.second;
490   }
491
492   return false;
493 }
494
495 std::vector<Dali::Accessibility::Relation> AccessibleImpl::GetRelationSet()
496 {
497   auto control = Dali::Toolkit::Control::DownCast(Self());
498
499   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
500   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
501
502   std::vector<Dali::Accessibility::Relation> ret;
503
504   auto& relation = controlImpl.mAccessibilityRelations;
505   for(auto i = 0u; i < relation.size(); ++i)
506   {
507     if(relation[i].empty()) continue;
508
509     ret.emplace_back(Accessibility::Relation{static_cast<Accessibility::RelationType>(i), relation[i]});
510   }
511
512   return ret;
513 }
514
515 bool AccessibleImpl::ScrollToChild(Actor child)
516 {
517   return false;
518 }
519
520 Dali::Property::Index AccessibleImpl::GetNamePropertyIndex()
521 {
522   return Actor::Property::NAME;
523 }
524
525 Dali::Property::Index AccessibleImpl::GetDescriptionPropertyIndex()
526 {
527   return Dali::Property::INVALID_INDEX;
528 }
529
530 void AccessibleImpl::SetLastPosition(Vector2 position)
531 {
532   mLastPosition = position;
533 }
534
535 Vector2 AccessibleImpl::GetLastPosition() const
536 {
537   return mLastPosition;
538 }
539
540 } // namespace Dali::Toolkit::DevelControl