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/text.h>
44 #include <dali/devel-api/atspi-interfaces/value.h>
45 #include <dali/internal/adaptor/common/adaptor-impl.h>
46 #include <dali/internal/window-system/common/window-impl.h>
47 #include <dali/public-api/dali-adaptor-common.h>
49 using namespace Dali::Accessibility;
52 const std::string& Dali::Accessibility::Address::GetBus() const
54 return mBus.empty() && Bridge::GetCurrentBridge() ? Bridge::GetCurrentBridge()->GetBusName() : mBus;
57 std::string Accessible::GetLocalizedRoleName() const
62 std::string Accessible::GetRoleName() const
64 static const std::unordered_map<Role, std::string_view> roleMap{
65 {Role::INVALID, "invalid"},
66 {Role::ACCELERATOR_LABEL, "accelerator label"},
67 {Role::ALERT, "alert"},
68 {Role::ANIMATION, "animation"},
69 {Role::ARROW, "arrow"},
70 {Role::CALENDAR, "calendar"},
71 {Role::CANVAS, "canvas"},
72 {Role::CHECK_BOX, "check box"},
73 {Role::CHECK_MENU_ITEM, "check menu item"},
74 {Role::COLOR_CHOOSER, "color chooser"},
75 {Role::COLUMN_HEADER, "column header"},
76 {Role::COMBO_BOX, "combo box"},
77 {Role::DATE_EDITOR, "date editor"},
78 {Role::DESKTOP_ICON, "desktop icon"},
79 {Role::DESKTOP_FRAME, "desktop frame"},
81 {Role::DIALOG, "dialog"},
82 {Role::DIRECTORY_PANE, "directory pane"},
83 {Role::DRAWING_AREA, "drawing area"},
84 {Role::FILE_CHOOSER, "file chooser"},
85 {Role::FILLER, "filler"},
86 {Role::FOCUS_TRAVERSABLE, "focus traversable"},
87 {Role::FONT_CHOOSER, "font chooser"},
88 {Role::FRAME, "frame"},
89 {Role::GLASS_PANE, "glass pane"},
90 {Role::HTML_CONTAINER, "html container"},
92 {Role::IMAGE, "image"},
93 {Role::INTERNAL_FRAME, "internal frame"},
94 {Role::LABEL, "label"},
95 {Role::LAYERED_PANE, "layered pane"},
97 {Role::LIST_ITEM, "list item"},
99 {Role::MENU_BAR, "menu bar"},
100 {Role::MENU_ITEM, "menu item"},
101 {Role::OPTION_PANE, "option pane"},
102 {Role::PAGE_TAB, "page tab"},
103 {Role::PAGE_TAB_LIST, "page tab list"},
104 {Role::PANEL, "panel"},
105 {Role::PASSWORD_TEXT, "password text"},
106 {Role::POPUP_MENU, "popup menu"},
107 {Role::PROGRESS_BAR, "progress bar"},
108 {Role::PUSH_BUTTON, "push button"},
109 {Role::RADIO_BUTTON, "radio button"},
110 {Role::RADIO_MENU_ITEM, "radio menu item"},
111 {Role::ROOT_PANE, "root pane"},
112 {Role::ROW_HEADER, "row header"},
113 {Role::SCROLL_BAR, "scroll bar"},
114 {Role::SCROLL_PANE, "scroll pane"},
115 {Role::SEPARATOR, "separator"},
116 {Role::SLIDER, "slider"},
117 {Role::SPIN_BUTTON, "spin button"},
118 {Role::SPLIT_PANE, "split pane"},
119 {Role::STATUS_BAR, "status bar"},
120 {Role::TABLE, "table"},
121 {Role::TABLE_CELL, "table cell"},
122 {Role::TABLE_COLUMN_HEADER, "table column header"},
123 {Role::TABLE_ROW_HEADER, "table row header"},
124 {Role::TEAROFF_MENU_ITEM, "tearoff menu item"},
125 {Role::TERMINAL, "terminal"},
126 {Role::TEXT, "text"},
127 {Role::TOGGLE_BUTTON, "toggle button"},
128 {Role::TOOL_BAR, "tool bar"},
129 {Role::TOOL_TIP, "tool tip"},
130 {Role::TREE, "tree"},
131 {Role::TREE_TABLE, "tree table"},
132 {Role::UNKNOWN, "unknown"},
133 {Role::VIEWPORT, "viewport"},
134 {Role::WINDOW, "window"},
135 {Role::EXTENDED, "extended"},
136 {Role::HEADER, "header"},
137 {Role::FOOTER, "footer"},
138 {Role::PARAGRAPH, "paragraph"},
139 {Role::RULER, "ruler"},
140 {Role::APPLICATION, "application"},
141 {Role::AUTOCOMPLETE, "autocomplete"},
142 {Role::EDITBAR, "edit bar"},
143 {Role::EMBEDDED, "embedded"},
144 {Role::ENTRY, "entry"},
145 {Role::CHART, "chart"},
146 {Role::CAPTION, "caution"},
147 {Role::DOCUMENT_FRAME, "document frame"},
148 {Role::HEADING, "heading"},
149 {Role::PAGE, "page"},
150 {Role::SECTION, "section"},
151 {Role::REDUNDANT_OBJECT, "redundant object"},
152 {Role::FORM, "form"},
153 {Role::LINK, "link"},
154 {Role::INPUT_METHOD_WINDOW, "input method window"},
155 {Role::TABLE_ROW, "table row"},
156 {Role::TREE_ITEM, "tree item"},
157 {Role::DOCUMENT_SPREADSHEET, "document spreadsheet"},
158 {Role::DOCUMENT_PRESENTATION, "document presentation"},
159 {Role::DOCUMENT_TEXT, "document text"},
160 {Role::DOCUMENT_WEB, "document web"},
161 {Role::DOCUMENT_EMAIL, "document email"},
162 {Role::COMMENT, "comment"},
163 {Role::LIST_BOX, "list box"},
164 {Role::GROUPING, "grouping"},
165 {Role::IMAGE_MAP, "image map"},
166 {Role::NOTIFICATION, "notification"},
167 {Role::INFO_BAR, "info bar"},
168 {Role::LEVEL_BAR, "level bar"},
169 {Role::TITLE_BAR, "title bar"},
170 {Role::BLOCK_QUOTE, "block quote"},
171 {Role::AUDIO, "audio"},
172 {Role::VIDEO, "video"},
173 {Role::DEFINITION, "definition"},
174 {Role::ARTICLE, "article"},
175 {Role::LANDMARK, "landmark"},
177 {Role::MARQUEE, "marquee"},
178 {Role::MATH, "math"},
179 {Role::RATING, "rating"},
180 {Role::TIMER, "timer"},
181 {Role::STATIC, "static"},
182 {Role::MATH_FRACTION, "math fraction"},
183 {Role::MATH_ROOT, "math root"},
184 {Role::SUBSCRIPT, "subscript"},
185 {Role::SUPERSCRIPT, "superscript"},
188 auto it = roleMap.find(GetRole());
190 if(it == roleMap.end())
195 return std::string{it->second};
198 AtspiInterfaces Accessible::GetInterfaces() const
202 mInterfaces = DoGetInterfaces();
203 DALI_ASSERT_DEBUG(mInterfaces); // There has to be at least AtspiInterface::ACCESSIBLE
209 std::vector<std::string> Accessible::GetInterfacesAsStrings() const
211 std::vector<std::string> ret;
212 AtspiInterfaces interfaces = GetInterfaces();
214 for(std::size_t i = 0u; i < static_cast<std::size_t>(AtspiInterface::MAX_COUNT); ++i)
216 auto interface = static_cast<AtspiInterface>(i);
218 if(interfaces[interface])
220 auto name = GetInterfaceName(interface);
222 DALI_ASSERT_DEBUG(!name.empty());
223 ret.emplace_back(std::move(name));
230 AtspiInterfaces Accessible::DoGetInterfaces() const
232 AtspiInterfaces interfaces;
234 interfaces[AtspiInterface::ACCESSIBLE] = true;
235 interfaces[AtspiInterface::ACTION] = dynamic_cast<const Action*>(this);
236 interfaces[AtspiInterface::APPLICATION] = dynamic_cast<const Application*>(this);
237 interfaces[AtspiInterface::COLLECTION] = dynamic_cast<const Collection*>(this);
238 interfaces[AtspiInterface::COMPONENT] = dynamic_cast<const Component*>(this);
239 interfaces[AtspiInterface::EDITABLE_TEXT] = dynamic_cast<const EditableText*>(this);
240 interfaces[AtspiInterface::HYPERLINK] = dynamic_cast<const Hyperlink*>(this);
241 interfaces[AtspiInterface::HYPERTEXT] = dynamic_cast<const Hypertext*>(this);
242 interfaces[AtspiInterface::SELECTION] = dynamic_cast<const Selection*>(this);
243 interfaces[AtspiInterface::SOCKET] = dynamic_cast<const Socket*>(this);
244 interfaces[AtspiInterface::TEXT] = dynamic_cast<const Text*>(this);
245 interfaces[AtspiInterface::VALUE] = dynamic_cast<const Value*>(this);
250 std::string Accessible::GetInterfaceName(AtspiInterface interface)
252 static const std::unordered_map<AtspiInterface, std::string_view> interfaceMap{
253 {AtspiInterface::ACCESSIBLE, "org.a11y.atspi.Accessible"},
254 {AtspiInterface::ACTION, "org.a11y.atspi.Action"},
255 {AtspiInterface::APPLICATION, "org.a11y.atspi.Application"},
256 {AtspiInterface::CACHE, "org.a11y.atspi.Cache"},
257 {AtspiInterface::COLLECTION, "org.a11y.atspi.Collection"},
258 {AtspiInterface::COMPONENT, "org.a11y.atspi.Component"},
259 {AtspiInterface::DEVICE_EVENT_CONTROLLER, "org.a11y.atspi.DeviceEventController"},
260 {AtspiInterface::DEVICE_EVENT_LISTENER, "org.a11y.atspi.DeviceEventListener"},
261 {AtspiInterface::DOCUMENT, "org.a11y.atspi.Document"},
262 {AtspiInterface::EDITABLE_TEXT, "org.a11y.atspi.EditableText"},
263 {AtspiInterface::EVENT_DOCUMENT, "org.a11y.atspi.Event.Document"},
264 {AtspiInterface::EVENT_FOCUS, "org.a11y.atspi.Event.Focus"},
265 {AtspiInterface::EVENT_KEYBOARD, "org.a11y.atspi.Event.Keyboard"},
266 {AtspiInterface::EVENT_MOUSE, "org.a11y.atspi.Event.Mouse"},
267 {AtspiInterface::EVENT_OBJECT, "org.a11y.atspi.Event.Object"},
268 {AtspiInterface::EVENT_TERMINAL, "org.a11y.atspi.Event.Terminal"},
269 {AtspiInterface::EVENT_WINDOW, "org.a11y.atspi.Event.Window"},
270 {AtspiInterface::HYPERLINK, "org.a11y.atspi.Hyperlink"},
271 {AtspiInterface::HYPERTEXT, "org.a11y.atspi.Hypertext"},
272 {AtspiInterface::IMAGE, "org.a11y.atspi.Image"},
273 {AtspiInterface::REGISTRY, "org.a11y.atspi.Registry"},
274 {AtspiInterface::SELECTION, "org.a11y.atspi.Selection"},
275 {AtspiInterface::SOCKET, "org.a11y.atspi.Socket"},
276 {AtspiInterface::TABLE, "org.a11y.atspi.Table"},
277 {AtspiInterface::TABLE_CELL, "org.a11y.atspi.TableCell"},
278 {AtspiInterface::TEXT, "org.a11y.atspi.Text"},
279 {AtspiInterface::VALUE, "org.a11y.atspi.Value"},
282 auto it = interfaceMap.find(interface);
284 if(it == interfaceMap.end())
289 return std::string{it->second};
292 Dali::Actor Accessible::GetCurrentlyHighlightedActor()
294 return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
297 void Accessible::SetCurrentlyHighlightedActor(Dali::Actor actor)
301 Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor = actor;
305 Dali::Actor Accessible::GetHighlightActor()
307 return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
310 void Accessible::SetHighlightActor(Dali::Actor actor)
314 Bridge::GetCurrentBridge()->mData->mHighlightActor = actor;
318 void Bridge::ForceDown()
320 auto highlighted = Accessible::GetCurrentlyHighlightedActor();
323 auto component = dynamic_cast<Component*>(Accessible::Get(highlighted));
326 component->ClearHighlight();
332 void Bridge::SetIsOnRootLevel(Accessible* owner)
334 owner->mIsOnRootLevel = true;
339 class AdaptorAccessible : public ActorAccessible
342 std::unique_ptr<TriggerEventInterface> mRenderNotification = nullptr;
348 AdaptorAccessible(Dali::Actor actor, bool isRoot)
349 : ActorAccessible(actor),
354 bool GrabFocus() override
359 bool GrabHighlight() override
366 // Only window accessible is able to grab and clear highlight
373 auto oldHighlightedActor = GetCurrentlyHighlightedActor();
374 if(self == oldHighlightedActor)
379 // Clear the old highlight.
380 if(oldHighlightedActor)
382 auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
383 if(oldHighlightedObject)
385 oldHighlightedObject->ClearHighlight();
389 SetCurrentlyHighlightedActor(self);
391 auto window = Dali::DevelWindow::Get(self);
392 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
393 windowImpl.EmitAccessibilityHighlightSignal(true);
398 bool ClearHighlight() override
405 // Only window accessible is able to grab and clear highlight
412 if(self != GetCurrentlyHighlightedActor())
417 SetCurrentlyHighlightedActor({});
419 auto window = Dali::DevelWindow::Get(self);
420 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
421 windowImpl.EmitAccessibilityHighlightSignal(false);
426 Role GetRole() const override
428 return mRoot ? Role::WINDOW : Role::REDUNDANT_OBJECT;
431 States GetStates() override
436 auto window = Dali::DevelWindow::Get(Self());
437 auto visible = window.IsVisible();
438 state[State::ENABLED] = true;
439 state[State::SENSITIVE] = true;
440 state[State::SHOWING] = visible;
441 state[State::VISIBLE] = true;
442 state[State::ACTIVE] = visible;
446 auto parentState = GetParent()->GetStates();
447 state[State::SHOWING] = parentState[State::SHOWING];
448 state[State::VISIBLE] = parentState[State::VISIBLE];
453 Attributes GetAttributes() const override
455 Attributes attributes;
459 Dali::Window window = Dali::DevelWindow::Get(Self());
460 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
461 attributes["resID"] = windowImpl.GetNativeResourceId();
465 Self().GetTypeInfo(type);
466 attributes["class"] = type.GetName();
471 bool DoGesture(const GestureInfo& gestureInfo) override
476 std::vector<Relation> GetRelationSet() override
481 void SetListenPostRender(bool enabled) override
488 auto window = Dali::DevelWindow::Get(Self());
489 Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
491 if(!mRenderNotification)
493 mRenderNotification = std::unique_ptr<TriggerEventInterface>(
494 TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &AdaptorAccessible::OnPostRender),
495 TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
500 windowImpl.SetRenderNotification(mRenderNotification.get());
504 windowImpl.SetRenderNotification(nullptr);
510 Accessibility::Bridge::GetCurrentBridge()->EmitPostRender(this);
512 }; // AdaptorAccessible
514 using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
516 // Save RefObject from an Actor in Accessible::Get()
517 AdaptorAccessiblesType& GetAdaptorAccessibles()
519 static AdaptorAccessiblesType gAdaptorAccessibles;
520 return gAdaptorAccessibles;
523 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
527 ObjectRegistry objectRegistry;
530 void Accessible::SetObjectRegistry(ObjectRegistry registry)
532 objectRegistry = registry;
533 objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
534 GetAdaptorAccessibles().erase(obj);
538 void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali::Actor)> functor)
540 convertingFunctor = functor;
543 Accessible* Accessible::Get(Dali::Actor actor)
550 auto accessible = convertingFunctor(actor);
553 auto pair = GetAdaptorAccessibles().emplace(&actor.GetBaseObject(), nullptr);
557 Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor);
560 isRoot = (actor == scene.GetRootLayer());
562 pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
564 accessible = pair.first->second.get();