2 * Copyright 2022 Samsung Electronics Co., Ltd
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
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>
51 using namespace Dali::Accessibility;
54 const std::string& Dali::Accessibility::Address::GetBus() const
56 return mBus.empty() && Bridge::GetCurrentBridge() ? Bridge::GetCurrentBridge()->GetBusName() : mBus;
59 std::string Accessible::GetLocalizedRoleName() const
64 std::string Accessible::GetRoleName() const
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"},
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"},
94 {Role::IMAGE, "image"},
95 {Role::INTERNAL_FRAME, "internal frame"},
96 {Role::LABEL, "label"},
97 {Role::LAYERED_PANE, "layered pane"},
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"},
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"},
190 auto it = roleMap.find(GetRole());
192 if(it == roleMap.end())
197 return std::string{it->second};
200 AtspiInterfaces Accessible::GetInterfaces() const
204 mInterfaces = DoGetInterfaces();
205 DALI_ASSERT_DEBUG(mInterfaces); // There has to be at least AtspiInterface::ACCESSIBLE
211 std::vector<std::string> Accessible::GetInterfacesAsStrings() const
213 std::vector<std::string> ret;
214 AtspiInterfaces interfaces = GetInterfaces();
216 for(std::size_t i = 0u; i < static_cast<std::size_t>(AtspiInterface::MAX_COUNT); ++i)
218 auto interface = static_cast<AtspiInterface>(i);
220 if(interfaces[interface])
222 auto name = GetInterfaceName(interface);
224 DALI_ASSERT_DEBUG(!name.empty());
225 ret.emplace_back(std::move(name));
232 AtspiInterfaces Accessible::DoGetInterfaces() const
234 AtspiInterfaces interfaces;
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);
254 std::string Accessible::GetInterfaceName(AtspiInterface interface)
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"},
286 auto it = interfaceMap.find(interface);
288 if(it == interfaceMap.end())
293 return std::string{it->second};
296 Dali::Actor Accessible::GetCurrentlyHighlightedActor()
298 return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
301 void Accessible::SetCurrentlyHighlightedActor(Dali::Actor actor)
305 Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor = actor;
309 Dali::Actor Accessible::GetHighlightActor()
311 return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
314 void Accessible::SetHighlightActor(Dali::Actor actor)
318 Bridge::GetCurrentBridge()->mData->mHighlightActor = actor;
322 void Bridge::ForceDown()
324 auto highlighted = Accessible::GetCurrentlyHighlightedActor();
327 auto component = dynamic_cast<Component*>(Accessible::Get(highlighted));
330 component->ClearHighlight();
336 void Bridge::SetIsOnRootLevel(Accessible* owner)
338 owner->mIsOnRootLevel = true;
343 class AdaptorAccessible : public ActorAccessible
346 std::unique_ptr<TriggerEventInterface> mRenderNotification = nullptr;
352 AdaptorAccessible(Dali::Actor actor, bool isRoot)
353 : ActorAccessible(actor),
358 bool GrabFocus() override
363 bool GrabHighlight() override
370 // Only window accessible is able to grab and clear highlight
377 auto oldHighlightedActor = GetCurrentlyHighlightedActor();
378 if(self == oldHighlightedActor)
383 // Clear the old highlight.
384 if(oldHighlightedActor)
386 auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
387 if(oldHighlightedObject)
389 oldHighlightedObject->ClearHighlight();
393 SetCurrentlyHighlightedActor(self);
395 auto window = Dali::DevelWindow::Get(self);
396 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
397 windowImpl.EmitAccessibilityHighlightSignal(true);
402 bool ClearHighlight() override
409 // Only window accessible is able to grab and clear highlight
416 if(self != GetCurrentlyHighlightedActor())
421 SetCurrentlyHighlightedActor({});
423 auto window = Dali::DevelWindow::Get(self);
424 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
425 windowImpl.EmitAccessibilityHighlightSignal(false);
430 Role GetRole() const override
432 return mRoot ? Role::WINDOW : Role::REDUNDANT_OBJECT;
435 States GetStates() override
440 auto window = Dali::DevelWindow::Get(Self());
441 auto visible = window.IsVisible();
442 state[State::ENABLED] = true;
443 state[State::SENSITIVE] = true;
444 state[State::SHOWING] = visible;
445 state[State::VISIBLE] = true;
446 state[State::ACTIVE] = visible;
448 else if (GetParent())
450 auto parentState = GetParent()->GetStates();
451 state[State::SHOWING] = parentState[State::SHOWING];
452 state[State::VISIBLE] = parentState[State::VISIBLE];
456 state[State::SHOWING] = false;
457 state[State::VISIBLE] = false;
462 Attributes GetAttributes() const override
464 Attributes attributes;
468 Dali::Window window = Dali::DevelWindow::Get(Self());
469 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
470 attributes["resID"] = windowImpl.GetNativeResourceId();
474 Self().GetTypeInfo(type);
475 attributes["class"] = type.GetName();
480 bool DoGesture(const GestureInfo& gestureInfo) override
485 std::vector<Relation> GetRelationSet() override
490 void SetListenPostRender(bool enabled) override
497 auto window = Dali::DevelWindow::Get(Self());
498 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
500 if(!mRenderNotification)
502 mRenderNotification = std::unique_ptr<TriggerEventInterface>(
503 TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &AdaptorAccessible::OnPostRender),
504 TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
509 windowImpl.SetRenderNotification(mRenderNotification.get());
513 windowImpl.SetRenderNotification(nullptr);
519 Accessibility::Bridge::GetCurrentBridge()->EmitPostRender(this);
521 }; // AdaptorAccessible
523 using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
525 // Save RefObject from an Actor in Accessible::Get()
526 AdaptorAccessiblesType& GetAdaptorAccessibles()
528 static AdaptorAccessiblesType gAdaptorAccessibles;
529 return gAdaptorAccessibles;
532 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
536 ObjectRegistry objectRegistry;
539 void Accessible::SetObjectRegistry(ObjectRegistry registry)
541 objectRegistry = registry;
542 objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
543 GetAdaptorAccessibles().erase(obj);
547 void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali::Actor)> functor)
549 convertingFunctor = functor;
552 Accessible* Accessible::Get(Dali::Actor actor)
559 auto accessible = convertingFunctor(actor);
562 auto pair = GetAdaptorAccessibles().emplace(&actor.GetBaseObject(), nullptr);
566 Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor);
569 isRoot = (actor == scene.GetRootLayer());
571 pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
573 accessible = pair.first->second.get();