[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / adaptor-framework / accessibility.cpp
1 /*
2  * Copyright 2022  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 // EXTERNAL INCLUDES
18 #include <dali/integration-api/debug.h>
19 #include <dali/public-api/actors/actor.h>
20 #include <dali/public-api/actors/layer.h>
21 #include <dali/public-api/object/base-object.h>
22 #include <dali/public-api/object/object-registry.h>
23 #include <dali/public-api/object/type-info.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <string_view>
26 #include <unordered_map>
27
28 // INTERNAL INCLUDES
29 #include <dali/devel-api/adaptor-framework/accessibility-bridge.h>
30 #include <dali/devel-api/adaptor-framework/actor-accessible.h>
31 #include <dali/devel-api/adaptor-framework/proxy-accessible.h>
32 #include <dali/devel-api/adaptor-framework/window-devel.h>
33 #include <dali/devel-api/atspi-interfaces/accessible.h>
34 #include <dali/devel-api/atspi-interfaces/action.h>
35 #include <dali/devel-api/atspi-interfaces/application.h>
36 #include <dali/devel-api/atspi-interfaces/collection.h>
37 #include <dali/devel-api/atspi-interfaces/component.h>
38 #include <dali/devel-api/atspi-interfaces/editable-text.h>
39 #include <dali/devel-api/atspi-interfaces/hyperlink.h>
40 #include <dali/devel-api/atspi-interfaces/hypertext.h>
41 #include <dali/devel-api/atspi-interfaces/selection.h>
42 #include <dali/devel-api/atspi-interfaces/socket.h>
43 #include <dali/devel-api/atspi-interfaces/table.h>
44 #include <dali/devel-api/atspi-interfaces/table-cell.h>
45 #include <dali/devel-api/atspi-interfaces/text.h>
46 #include <dali/devel-api/atspi-interfaces/value.h>
47 #include <dali/internal/adaptor/common/adaptor-impl.h>
48 #include <dali/internal/window-system/common/window-impl.h>
49 #include <dali/public-api/dali-adaptor-common.h>
50
51 using namespace Dali::Accessibility;
52 using namespace Dali;
53
54 const std::string& Dali::Accessibility::Address::GetBus() const
55 {
56   return mBus.empty() && Bridge::GetCurrentBridge() ? Bridge::GetCurrentBridge()->GetBusName() : mBus;
57 }
58
59 std::string Accessible::GetLocalizedRoleName() const
60 {
61   return GetRoleName();
62 }
63
64 std::string Accessible::GetRoleName() const
65 {
66   static const std::unordered_map<Role, std::string_view> roleMap{
67     {Role::INVALID, "invalid"},
68     {Role::ACCELERATOR_LABEL, "accelerator label"},
69     {Role::ALERT, "alert"},
70     {Role::ANIMATION, "animation"},
71     {Role::ARROW, "arrow"},
72     {Role::CALENDAR, "calendar"},
73     {Role::CANVAS, "canvas"},
74     {Role::CHECK_BOX, "check box"},
75     {Role::CHECK_MENU_ITEM, "check menu item"},
76     {Role::COLOR_CHOOSER, "color chooser"},
77     {Role::COLUMN_HEADER, "column header"},
78     {Role::COMBO_BOX, "combo box"},
79     {Role::DATE_EDITOR, "date editor"},
80     {Role::DESKTOP_ICON, "desktop icon"},
81     {Role::DESKTOP_FRAME, "desktop frame"},
82     {Role::DIAL, "dial"},
83     {Role::DIALOG, "dialog"},
84     {Role::DIRECTORY_PANE, "directory pane"},
85     {Role::DRAWING_AREA, "drawing area"},
86     {Role::FILE_CHOOSER, "file chooser"},
87     {Role::FILLER, "filler"},
88     {Role::FOCUS_TRAVERSABLE, "focus traversable"},
89     {Role::FONT_CHOOSER, "font chooser"},
90     {Role::FRAME, "frame"},
91     {Role::GLASS_PANE, "glass pane"},
92     {Role::HTML_CONTAINER, "html container"},
93     {Role::ICON, "icon"},
94     {Role::IMAGE, "image"},
95     {Role::INTERNAL_FRAME, "internal frame"},
96     {Role::LABEL, "label"},
97     {Role::LAYERED_PANE, "layered pane"},
98     {Role::LIST, "list"},
99     {Role::LIST_ITEM, "list item"},
100     {Role::MENU, "menu"},
101     {Role::MENU_BAR, "menu bar"},
102     {Role::MENU_ITEM, "menu item"},
103     {Role::OPTION_PANE, "option pane"},
104     {Role::PAGE_TAB, "page tab"},
105     {Role::PAGE_TAB_LIST, "page tab list"},
106     {Role::PANEL, "panel"},
107     {Role::PASSWORD_TEXT, "password text"},
108     {Role::POPUP_MENU, "popup menu"},
109     {Role::PROGRESS_BAR, "progress bar"},
110     {Role::PUSH_BUTTON, "push button"},
111     {Role::RADIO_BUTTON, "radio button"},
112     {Role::RADIO_MENU_ITEM, "radio menu item"},
113     {Role::ROOT_PANE, "root pane"},
114     {Role::ROW_HEADER, "row header"},
115     {Role::SCROLL_BAR, "scroll bar"},
116     {Role::SCROLL_PANE, "scroll pane"},
117     {Role::SEPARATOR, "separator"},
118     {Role::SLIDER, "slider"},
119     {Role::SPIN_BUTTON, "spin button"},
120     {Role::SPLIT_PANE, "split pane"},
121     {Role::STATUS_BAR, "status bar"},
122     {Role::TABLE, "table"},
123     {Role::TABLE_CELL, "table cell"},
124     {Role::TABLE_COLUMN_HEADER, "table column header"},
125     {Role::TABLE_ROW_HEADER, "table row header"},
126     {Role::TEAROFF_MENU_ITEM, "tearoff menu item"},
127     {Role::TERMINAL, "terminal"},
128     {Role::TEXT, "text"},
129     {Role::TOGGLE_BUTTON, "toggle button"},
130     {Role::TOOL_BAR, "tool bar"},
131     {Role::TOOL_TIP, "tool tip"},
132     {Role::TREE, "tree"},
133     {Role::TREE_TABLE, "tree table"},
134     {Role::UNKNOWN, "unknown"},
135     {Role::VIEWPORT, "viewport"},
136     {Role::WINDOW, "window"},
137     {Role::EXTENDED, "extended"},
138     {Role::HEADER, "header"},
139     {Role::FOOTER, "footer"},
140     {Role::PARAGRAPH, "paragraph"},
141     {Role::RULER, "ruler"},
142     {Role::APPLICATION, "application"},
143     {Role::AUTOCOMPLETE, "autocomplete"},
144     {Role::EDITBAR, "edit bar"},
145     {Role::EMBEDDED, "embedded"},
146     {Role::ENTRY, "entry"},
147     {Role::CHART, "chart"},
148     {Role::CAPTION, "caution"},
149     {Role::DOCUMENT_FRAME, "document frame"},
150     {Role::HEADING, "heading"},
151     {Role::PAGE, "page"},
152     {Role::SECTION, "section"},
153     {Role::REDUNDANT_OBJECT, "redundant object"},
154     {Role::FORM, "form"},
155     {Role::LINK, "link"},
156     {Role::INPUT_METHOD_WINDOW, "input method window"},
157     {Role::TABLE_ROW, "table row"},
158     {Role::TREE_ITEM, "tree item"},
159     {Role::DOCUMENT_SPREADSHEET, "document spreadsheet"},
160     {Role::DOCUMENT_PRESENTATION, "document presentation"},
161     {Role::DOCUMENT_TEXT, "document text"},
162     {Role::DOCUMENT_WEB, "document web"},
163     {Role::DOCUMENT_EMAIL, "document email"},
164     {Role::COMMENT, "comment"},
165     {Role::LIST_BOX, "list box"},
166     {Role::GROUPING, "grouping"},
167     {Role::IMAGE_MAP, "image map"},
168     {Role::NOTIFICATION, "notification"},
169     {Role::INFO_BAR, "info bar"},
170     {Role::LEVEL_BAR, "level bar"},
171     {Role::TITLE_BAR, "title bar"},
172     {Role::BLOCK_QUOTE, "block quote"},
173     {Role::AUDIO, "audio"},
174     {Role::VIDEO, "video"},
175     {Role::DEFINITION, "definition"},
176     {Role::ARTICLE, "article"},
177     {Role::LANDMARK, "landmark"},
178     {Role::LOG, "log"},
179     {Role::MARQUEE, "marquee"},
180     {Role::MATH, "math"},
181     {Role::RATING, "rating"},
182     {Role::TIMER, "timer"},
183     {Role::STATIC, "static"},
184     {Role::MATH_FRACTION, "math fraction"},
185     {Role::MATH_ROOT, "math root"},
186     {Role::SUBSCRIPT, "subscript"},
187     {Role::SUPERSCRIPT, "superscript"},
188   };
189
190   auto it = roleMap.find(GetRole());
191
192   if(it == roleMap.end())
193   {
194     return {};
195   }
196
197   return std::string{it->second};
198 }
199
200 AtspiInterfaces Accessible::GetInterfaces() const
201 {
202   if(!mInterfaces)
203   {
204     mInterfaces = DoGetInterfaces();
205     DALI_ASSERT_DEBUG(mInterfaces); // There has to be at least AtspiInterface::ACCESSIBLE
206   }
207
208   return mInterfaces;
209 }
210
211 std::vector<std::string> Accessible::GetInterfacesAsStrings() const
212 {
213   std::vector<std::string> ret;
214   AtspiInterfaces          interfaces = GetInterfaces();
215
216   for(std::size_t i = 0u; i < static_cast<std::size_t>(AtspiInterface::MAX_COUNT); ++i)
217   {
218     auto interface = static_cast<AtspiInterface>(i);
219
220     if(interfaces[interface])
221     {
222       auto name = GetInterfaceName(interface);
223
224       DALI_ASSERT_DEBUG(!name.empty());
225       ret.emplace_back(std::move(name));
226     }
227   }
228
229   return ret;
230 }
231
232 AtspiInterfaces Accessible::DoGetInterfaces() const
233 {
234   AtspiInterfaces interfaces;
235
236   interfaces[AtspiInterface::ACCESSIBLE]    = true;
237   interfaces[AtspiInterface::ACTION]        = dynamic_cast<const Action*>(this);
238   interfaces[AtspiInterface::APPLICATION]   = dynamic_cast<const Application*>(this);
239   interfaces[AtspiInterface::COLLECTION]    = dynamic_cast<const Collection*>(this);
240   interfaces[AtspiInterface::COMPONENT]     = dynamic_cast<const Component*>(this);
241   interfaces[AtspiInterface::EDITABLE_TEXT] = dynamic_cast<const EditableText*>(this);
242   interfaces[AtspiInterface::HYPERLINK]     = dynamic_cast<const Hyperlink*>(this);
243   interfaces[AtspiInterface::HYPERTEXT]     = dynamic_cast<const Hypertext*>(this);
244   interfaces[AtspiInterface::SELECTION]     = dynamic_cast<const Selection*>(this);
245   interfaces[AtspiInterface::SOCKET]        = dynamic_cast<const Socket*>(this);
246   interfaces[AtspiInterface::TABLE]         = dynamic_cast<const Table*>(this);
247   interfaces[AtspiInterface::TABLE_CELL]    = dynamic_cast<const TableCell*>(this);
248   interfaces[AtspiInterface::TEXT]          = dynamic_cast<const Text*>(this);
249   interfaces[AtspiInterface::VALUE]         = dynamic_cast<const Value*>(this);
250
251   return interfaces;
252 }
253
254 std::string Accessible::GetInterfaceName(AtspiInterface interface)
255 {
256   static const std::unordered_map<AtspiInterface, std::string_view> interfaceMap{
257     {AtspiInterface::ACCESSIBLE, "org.a11y.atspi.Accessible"},
258     {AtspiInterface::ACTION, "org.a11y.atspi.Action"},
259     {AtspiInterface::APPLICATION, "org.a11y.atspi.Application"},
260     {AtspiInterface::CACHE, "org.a11y.atspi.Cache"},
261     {AtspiInterface::COLLECTION, "org.a11y.atspi.Collection"},
262     {AtspiInterface::COMPONENT, "org.a11y.atspi.Component"},
263     {AtspiInterface::DEVICE_EVENT_CONTROLLER, "org.a11y.atspi.DeviceEventController"},
264     {AtspiInterface::DEVICE_EVENT_LISTENER, "org.a11y.atspi.DeviceEventListener"},
265     {AtspiInterface::DOCUMENT, "org.a11y.atspi.Document"},
266     {AtspiInterface::EDITABLE_TEXT, "org.a11y.atspi.EditableText"},
267     {AtspiInterface::EVENT_DOCUMENT, "org.a11y.atspi.Event.Document"},
268     {AtspiInterface::EVENT_FOCUS, "org.a11y.atspi.Event.Focus"},
269     {AtspiInterface::EVENT_KEYBOARD, "org.a11y.atspi.Event.Keyboard"},
270     {AtspiInterface::EVENT_MOUSE, "org.a11y.atspi.Event.Mouse"},
271     {AtspiInterface::EVENT_OBJECT, "org.a11y.atspi.Event.Object"},
272     {AtspiInterface::EVENT_TERMINAL, "org.a11y.atspi.Event.Terminal"},
273     {AtspiInterface::EVENT_WINDOW, "org.a11y.atspi.Event.Window"},
274     {AtspiInterface::HYPERLINK, "org.a11y.atspi.Hyperlink"},
275     {AtspiInterface::HYPERTEXT, "org.a11y.atspi.Hypertext"},
276     {AtspiInterface::IMAGE, "org.a11y.atspi.Image"},
277     {AtspiInterface::REGISTRY, "org.a11y.atspi.Registry"},
278     {AtspiInterface::SELECTION, "org.a11y.atspi.Selection"},
279     {AtspiInterface::SOCKET, "org.a11y.atspi.Socket"},
280     {AtspiInterface::TABLE, "org.a11y.atspi.Table"},
281     {AtspiInterface::TABLE_CELL, "org.a11y.atspi.TableCell"},
282     {AtspiInterface::TEXT, "org.a11y.atspi.Text"},
283     {AtspiInterface::VALUE, "org.a11y.atspi.Value"},
284   };
285
286   auto it = interfaceMap.find(interface);
287
288   if(it == interfaceMap.end())
289   {
290     return {};
291   }
292
293   return std::string{it->second};
294 }
295
296 Dali::Actor Accessible::GetCurrentlyHighlightedActor()
297 {
298   return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
299 }
300
301 void Accessible::SetCurrentlyHighlightedActor(Dali::Actor actor)
302 {
303   if(IsUp())
304   {
305     Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor = actor;
306   }
307 }
308
309 bool Accessible::IsHighlighted() const
310 {
311   Dali::Actor self = GetInternalActor();
312
313   return self && self == GetCurrentlyHighlightedActor();
314 }
315
316 Dali::Actor Accessible::GetHighlightActor()
317 {
318   return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
319 }
320
321 void Accessible::SetHighlightActor(Dali::Actor actor)
322 {
323   if(IsUp())
324   {
325     Bridge::GetCurrentBridge()->mData->mHighlightActor = actor;
326   }
327 }
328
329 void Bridge::ForceDown()
330 {
331   auto highlighted = Accessible::GetCurrentlyHighlightedActor();
332   if(highlighted)
333   {
334     auto component = dynamic_cast<Component*>(Accessible::Get(highlighted));
335     if(component)
336     {
337       component->ClearHighlight();
338     }
339   }
340   mData = {};
341 }
342
343 void Bridge::SetIsOnRootLevel(Accessible* owner)
344 {
345   owner->mIsOnRootLevel = true;
346 }
347
348 namespace
349 {
350 class AdaptorAccessible : public ActorAccessible
351 {
352 private:
353   std::unique_ptr<TriggerEventInterface> mRenderNotification = nullptr;
354
355 protected:
356   bool mRoot = false;
357
358 public:
359   AdaptorAccessible(Dali::Actor actor, bool isRoot)
360   : ActorAccessible(actor),
361     mRoot(isRoot)
362   {
363   }
364
365   bool GrabFocus() override
366   {
367     return false;
368   }
369
370   bool GrabHighlight() override
371   {
372     if(!IsUp())
373     {
374       return false;
375     }
376
377     // Only window accessible is able to grab and clear highlight
378     if(!mRoot)
379     {
380       return false;
381     }
382
383     auto self                = Self();
384     auto oldHighlightedActor = GetCurrentlyHighlightedActor();
385     if(self == oldHighlightedActor)
386     {
387       return true;
388     }
389
390     // Clear the old highlight.
391     if(oldHighlightedActor)
392     {
393       auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
394       if(oldHighlightedObject)
395       {
396         oldHighlightedObject->ClearHighlight();
397       }
398     }
399
400     SetCurrentlyHighlightedActor(self);
401
402     auto                             window     = Dali::DevelWindow::Get(self);
403     Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
404     windowImpl.EmitAccessibilityHighlightSignal(true);
405
406     return true;
407   }
408
409   bool ClearHighlight() override
410   {
411     if(!IsUp())
412     {
413       return false;
414     }
415
416     // Only window accessible is able to grab and clear highlight
417     if(!mRoot)
418     {
419       return false;
420     }
421
422     if(!IsHighlighted())
423     {
424       return false;
425     }
426
427     SetCurrentlyHighlightedActor({});
428
429     auto                             window     = Dali::DevelWindow::Get(Self());
430     Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
431     windowImpl.EmitAccessibilityHighlightSignal(false);
432
433     return true;
434   }
435
436   Role GetRole() const override
437   {
438     return mRoot ? Role::WINDOW : Role::REDUNDANT_OBJECT;
439   }
440
441   States GetStates() override
442   {
443     States state;
444     if(mRoot)
445     {
446       auto window             = Dali::DevelWindow::Get(Self());
447       auto visible            = window.IsVisible();
448       state[State::ENABLED]   = true;
449       state[State::SENSITIVE] = true;
450       state[State::SHOWING]   = visible;
451       state[State::VISIBLE]   = true;
452       state[State::ACTIVE]    = visible;
453     }
454     else if (GetParent())
455     {
456       auto parentState      = GetParent()->GetStates();
457       state[State::SHOWING] = parentState[State::SHOWING];
458       state[State::VISIBLE] = parentState[State::VISIBLE];
459     }
460     else
461     {
462       state[State::SHOWING] = false;
463       state[State::VISIBLE] = false;
464     }
465     return state;
466   }
467
468   Attributes GetAttributes() const override
469   {
470     Attributes attributes;
471
472     if(mRoot)
473     {
474       Dali::Window                     window     = Dali::DevelWindow::Get(Self());
475       Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
476       attributes["resID"]                         = windowImpl.GetNativeResourceId();
477     }
478
479     Dali::TypeInfo type;
480     Self().GetTypeInfo(type);
481     attributes["class"] = type.GetName();
482
483     return attributes;
484   }
485
486   bool DoGesture(const GestureInfo& gestureInfo) override
487   {
488     return false;
489   }
490
491   std::vector<Relation> GetRelationSet() override
492   {
493     return {};
494   }
495
496   void SetListenPostRender(bool enabled) override
497   {
498     if (!mRoot)
499     {
500       return;
501     }
502
503     auto window                                 = Dali::DevelWindow::Get(Self());
504     Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
505
506     if(!mRenderNotification)
507     {
508       mRenderNotification = std::unique_ptr<TriggerEventInterface>(
509                                            TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &AdaptorAccessible::OnPostRender),
510                                            TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
511     }
512
513     if (enabled)
514     {
515       windowImpl.SetRenderNotification(mRenderNotification.get());
516     }
517     else
518     {
519       windowImpl.SetRenderNotification(nullptr);
520     }
521   }
522
523   void OnPostRender()
524   {
525     Accessibility::Bridge::GetCurrentBridge()->EmitPostRender(this);
526   }
527 }; // AdaptorAccessible
528
529 using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
530
531 // Save RefObject from an Actor in Accessible::Get()
532 AdaptorAccessiblesType& GetAdaptorAccessibles()
533 {
534   static AdaptorAccessiblesType gAdaptorAccessibles;
535   return gAdaptorAccessibles;
536 }
537
538 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
539   return nullptr;
540 };
541
542 ObjectRegistry objectRegistry;
543 } // namespace
544
545 void Accessible::SetObjectRegistry(ObjectRegistry registry)
546 {
547   objectRegistry = registry;
548   objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
549     GetAdaptorAccessibles().erase(obj);
550   });
551 }
552
553 void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali::Actor)> functor)
554 {
555   convertingFunctor = functor;
556 }
557
558 Accessible* Accessible::Get(Dali::Actor actor)
559 {
560   if(!actor)
561   {
562     return nullptr;
563   }
564
565   auto accessible = convertingFunctor(actor);
566   if(!accessible)
567   {
568     auto pair = GetAdaptorAccessibles().emplace(&actor.GetBaseObject(), nullptr);
569     if(pair.second)
570     {
571       bool                     isRoot = false;
572       Dali::Integration::Scene scene  = Dali::Integration::Scene::Get(actor);
573       if(scene)
574       {
575         isRoot = (actor == scene.GetRootLayer());
576       }
577       pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
578     }
579     accessible = pair.first->second.get();
580   }
581
582   return accessible;
583 }