7caed8e450ed3375a8c3e161c7e48f895d7e894f
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / adaptor-framework / accessibility.cpp
1 /*
2  * Copyright 2020  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 <dali/public-api/object/weak-handle.h>
26 #include <string_view>
27 #include <unordered_map>
28
29 // INTERNAL INCLUDES
30 #include <dali/devel-api/adaptor-framework/accessibility-bridge.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/collection.h>
35 #include <dali/devel-api/atspi-interfaces/component.h>
36 #include <dali/internal/adaptor/common/adaptor-impl.h>
37 #include <dali/public-api/dali-adaptor-common.h>
38
39 using namespace Dali::Accessibility;
40 using namespace Dali;
41
42 const std::string& Dali::Accessibility::Address::GetBus() const
43 {
44   return mBus.empty() && Bridge::GetCurrentBridge() ? Bridge::GetCurrentBridge()->GetBusName() : mBus;
45 }
46
47 std::string Accessible::GetLocalizedRoleName() const
48 {
49   return GetRoleName();
50 }
51
52 std::string Accessible::GetRoleName() const
53 {
54   static const std::unordered_map<Role, std::string_view> roleMap{
55     {Role::INVALID, "invalid"},
56     {Role::ACCELERATOR_LABEL, "accelerator label"},
57     {Role::ALERT, "alert"},
58     {Role::ANIMATION, "animation"},
59     {Role::ARROW, "arrow"},
60     {Role::CALENDAR, "calendar"},
61     {Role::CANVAS, "canvas"},
62     {Role::CHECK_BOX, "check box"},
63     {Role::CHECK_MENU_ITEM, "check menu item"},
64     {Role::COLOR_CHOOSER, "color chooser"},
65     {Role::COLUMN_HEADER, "column header"},
66     {Role::COMBO_BOX, "combo box"},
67     {Role::DATE_EDITOR, "date editor"},
68     {Role::DESKTOP_ICON, "desktop icon"},
69     {Role::DESKTOP_FRAME, "desktop frame"},
70     {Role::DIAL, "dial"},
71     {Role::DIALOG, "dialog"},
72     {Role::DIRECTORY_PANE, "directory pane"},
73     {Role::DRAWING_AREA, "drawing area"},
74     {Role::FILE_CHOOSER, "file chooser"},
75     {Role::FILLER, "filler"},
76     {Role::FOCUS_TRAVERSABLE, "focus traversable"},
77     {Role::FONT_CHOOSER, "font chooser"},
78     {Role::FRAME, "frame"},
79     {Role::GLASS_PANE, "glass pane"},
80     {Role::HTML_CONTAINER, "html container"},
81     {Role::ICON, "icon"},
82     {Role::IMAGE, "image"},
83     {Role::INTERNAL_FRAME, "internal frame"},
84     {Role::LABEL, "label"},
85     {Role::LAYERED_PANE, "layered pane"},
86     {Role::LIST, "list"},
87     {Role::LIST_ITEM, "list item"},
88     {Role::MENU, "menu"},
89     {Role::MENU_BAR, "menu bar"},
90     {Role::MENU_ITEM, "menu item"},
91     {Role::OPTION_PANE, "option pane"},
92     {Role::PAGE_TAB, "page tab"},
93     {Role::PAGE_TAB_LIST, "page tab list"},
94     {Role::PANEL, "panel"},
95     {Role::PASSWORD_TEXT, "password text"},
96     {Role::POPUP_MENU, "popup menu"},
97     {Role::PROGRESS_BAR, "progress bar"},
98     {Role::PUSH_BUTTON, "push button"},
99     {Role::RADIO_BUTTON, "radio button"},
100     {Role::RADIO_MENU_ITEM, "radio menu item"},
101     {Role::ROOT_PANE, "root pane"},
102     {Role::ROW_HEADER, "row header"},
103     {Role::SCROLL_BAR, "scroll bar"},
104     {Role::SCROLL_PANE, "scroll pane"},
105     {Role::SEPARATOR, "separator"},
106     {Role::SLIDER, "slider"},
107     {Role::SPIN_BUTTON, "spin button"},
108     {Role::SPLIT_PANE, "split pane"},
109     {Role::STATUS_BAR, "status bar"},
110     {Role::TABLE, "table"},
111     {Role::TABLE_CELL, "table cell"},
112     {Role::TABLE_COLUMN_HEADER, "table column header"},
113     {Role::TABLE_ROW_HEADER, "table row header"},
114     {Role::TEAROFF_MENU_ITEM, "tearoff menu item"},
115     {Role::TERMINAL, "terminal"},
116     {Role::TEXT, "text"},
117     {Role::TOGGLE_BUTTON, "toggle button"},
118     {Role::TOOL_BAR, "tool bar"},
119     {Role::TOOL_TIP, "tool tip"},
120     {Role::TREE, "tree"},
121     {Role::TREE_TABLE, "tree table"},
122     {Role::UNKNOWN, "unknown"},
123     {Role::VIEWPORT, "viewport"},
124     {Role::WINDOW, "window"},
125     {Role::EXTENDED, "extended"},
126     {Role::HEADER, "header"},
127     {Role::FOOTER, "footer"},
128     {Role::PARAGRAPH, "paragraph"},
129     {Role::RULER, "ruler"},
130     {Role::APPLICATION, "application"},
131     {Role::AUTOCOMPLETE, "autocomplete"},
132     {Role::EDITBAR, "edit bar"},
133     {Role::EMBEDDED, "embedded"},
134     {Role::ENTRY, "entry"},
135     {Role::CHART, "chart"},
136     {Role::CAPTION, "caution"},
137     {Role::DOCUMENT_FRAME, "document frame"},
138     {Role::HEADING, "heading"},
139     {Role::PAGE, "page"},
140     {Role::SECTION, "section"},
141     {Role::REDUNDANT_OBJECT, "redundant object"},
142     {Role::FORM, "form"},
143     {Role::LINK, "link"},
144     {Role::INPUT_METHOD_WINDOW, "input method window"},
145     {Role::TABLE_ROW, "table row"},
146     {Role::TREE_ITEM, "tree item"},
147     {Role::DOCUMENT_SPREADSHEET, "document spreadsheet"},
148     {Role::DOCUMENT_PRESENTATION, "document presentation"},
149     {Role::DOCUMENT_TEXT, "document text"},
150     {Role::DOCUMENT_WEB, "document web"},
151     {Role::DOCUMENT_EMAIL, "document email"},
152     {Role::COMMENT, "comment"},
153     {Role::LIST_BOX, "list box"},
154     {Role::GROUPING, "grouping"},
155     {Role::IMAGE_MAP, "image map"},
156     {Role::NOTIFICATION, "notification"},
157     {Role::INFO_BAR, "info bar"},
158     {Role::LEVEL_BAR, "level bar"},
159     {Role::TITLE_BAR, "title bar"},
160     {Role::BLOCK_QUOTE, "block quote"},
161     {Role::AUDIO, "audio"},
162     {Role::VIDEO, "video"},
163     {Role::DEFINITION, "definition"},
164     {Role::ARTICLE, "article"},
165     {Role::LANDMARK, "landmark"},
166     {Role::LOG, "log"},
167     {Role::MARQUEE, "marquee"},
168     {Role::MATH, "math"},
169     {Role::RATING, "rating"},
170     {Role::TIMER, "timer"},
171     {Role::STATIC, "static"},
172     {Role::MATH_FRACTION, "math fraction"},
173     {Role::MATH_ROOT, "math root"},
174     {Role::SUBSCRIPT, "subscript"},
175     {Role::SUPERSCRIPT, "superscript"},
176   };
177
178   auto it = roleMap.find(GetRole());
179
180   if(it == roleMap.end())
181   {
182     return {};
183   }
184
185   return std::string{it->second};
186 }
187
188 Dali::Actor Accessible::GetCurrentlyHighlightedActor()
189 {
190   return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
191 }
192
193 void Accessible::SetCurrentlyHighlightedActor(Dali::Actor actor)
194 {
195   if(IsUp())
196   {
197     Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor = actor;
198   }
199 }
200
201 Dali::Actor Accessible::GetHighlightActor()
202 {
203   return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
204 }
205
206 void Accessible::SetHighlightActor(Dali::Actor actor)
207 {
208   if(IsUp())
209   {
210     Bridge::GetCurrentBridge()->mData->mHighlightActor = actor;
211   }
212 }
213
214 void Bridge::ForceDown()
215 {
216   auto highlighted = Accessible::GetCurrentlyHighlightedActor();
217   if(highlighted)
218   {
219     auto component = dynamic_cast<Component*>(Accessible::Get(highlighted));
220     if(component)
221     {
222       component->ClearHighlight();
223     }
224   }
225   mData = {};
226 }
227
228 void Bridge::SetIsOnRootLevel(Accessible* owner)
229 {
230   owner->mIsOnRootLevel = true;
231 }
232
233 namespace
234 {
235 class AdaptorAccessible : public virtual Accessible, public virtual Collection, public virtual Component
236 {
237 protected:
238   Dali::WeakHandle<Dali::Actor> mSelf;
239   bool                          mRoot = false;
240
241   Dali::Actor Self() const
242   {
243     auto handle = mSelf.GetHandle();
244
245     // AdaptorAccessible is deleted on ObjectDestroyedSignal
246     // for the respective actor (see `nonControlAccessibles`).
247     DALI_ASSERT_ALWAYS(handle);
248
249     return handle;
250   }
251
252 public:
253   AdaptorAccessible(Dali::Actor actor, bool isRoot)
254   : mSelf(actor),
255     mRoot(isRoot)
256   {
257   }
258
259   Dali::Rect<> GetExtents(Dali::Accessibility::CoordinateType type) const override
260   {
261     Dali::Actor actor                   = Self();
262     Vector2     screenPosition          = actor.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
263     Vector3     size                    = actor.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * actor.GetCurrentProperty<Vector3>(Actor::Property::WORLD_SCALE);
264     bool        positionUsesAnchorPoint = actor.GetProperty(Actor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
265     Vector3     anchorPointOffSet       = size * (positionUsesAnchorPoint ? actor.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
266     Vector2     position                = Vector2(screenPosition.x - anchorPointOffSet.x, screenPosition.y - anchorPointOffSet.y);
267
268     if(type == Dali::Accessibility::CoordinateType::WINDOW)
269     {
270       return {position.x, position.y, size.x, size.y};
271     }
272     else // Dali::Accessibility::CoordinateType::SCREEN
273     {
274       auto window         = Dali::DevelWindow::Get(actor);
275       auto windowPosition = window.GetPosition();
276       return {position.x + windowPosition.GetX(), position.y + windowPosition.GetY(), size.x, size.y};
277     }
278   }
279
280   Dali::Accessibility::ComponentLayer GetLayer() const override
281   {
282     return Dali::Accessibility::ComponentLayer::WINDOW;
283   }
284
285   int16_t GetMdiZOrder() const override
286   {
287     return 0;
288   }
289
290   double GetAlpha() const override
291   {
292     return 0;
293   }
294
295   bool GrabFocus() override
296   {
297     return false;
298   }
299
300   bool GrabHighlight() override
301   {
302     return false;
303   }
304
305   bool ClearHighlight() override
306   {
307     return false;
308   }
309
310   bool IsScrollable() const override
311   {
312     return false;
313   }
314
315   std::string GetName() const override
316   {
317     return Self().GetProperty<std::string>(Dali::Actor::Property::NAME);
318   }
319
320   std::string GetDescription() const override
321   {
322     return "";
323   }
324
325   Accessible* GetParent() override
326   {
327     if(IsOnRootLevel())
328     {
329       auto data = GetBridgeData();
330       return data->mBridge->GetApplication();
331     }
332     return Get(Self().GetParent());
333   }
334
335   size_t GetChildCount() const override
336   {
337     return static_cast<size_t>(Self().GetChildCount());
338   }
339
340   Accessible* GetChildAtIndex(size_t index) override
341   {
342     auto numberOfChildren = static_cast<size_t>(Self().GetChildCount());
343     if(index >= numberOfChildren)
344     {
345       throw std::domain_error{"invalid index " + std::to_string(index) + " for object with " + std::to_string(numberOfChildren) + " children"};
346     }
347     return Get(Self().GetChildAt(static_cast<unsigned int>(index)));
348   }
349
350   size_t GetIndexInParent() override
351   {
352     auto parent = Self().GetParent();
353     if(!parent)
354     {
355       return 0;
356     }
357     auto size = static_cast<size_t>(parent.GetChildCount());
358     for(auto i = 0u; i < size; ++i)
359     {
360       if(parent.GetChildAt(i) == Self())
361       {
362         return i;
363       }
364     }
365     throw std::domain_error{"actor is not a child of it's parent"};
366   }
367
368   Role GetRole() const override
369   {
370     return mRoot ? Role::WINDOW : Role::REDUNDANT_OBJECT;
371   }
372
373   States GetStates() override
374   {
375     States state;
376     if(mRoot)
377     {
378       auto window             = Dali::DevelWindow::Get(Self());
379       auto visible            = window.IsVisible();
380       state[State::ENABLED]   = true;
381       state[State::SENSITIVE] = true;
382       state[State::SHOWING]   = visible;
383       state[State::VISIBLE]   = true;
384       state[State::ACTIVE]    = visible;
385     }
386     else
387     {
388       auto parentState      = GetParent()->GetStates();
389       state[State::SHOWING] = parentState[State::SHOWING];
390       state[State::VISIBLE] = parentState[State::VISIBLE];
391     }
392     return state;
393   }
394
395   Attributes GetAttributes() const override
396   {
397     Dali::TypeInfo type;
398     Self().GetTypeInfo(type);
399     return {
400       {"class", type.GetName()},
401     };
402   }
403
404   bool DoGesture(const GestureInfo& gestureInfo) override
405   {
406     return false;
407   }
408
409   std::vector<Relation> GetRelationSet() override
410   {
411     return {};
412   }
413
414   Dali::Actor GetInternalActor() override
415   {
416     return mSelf.GetHandle();
417   }
418 }; // AdaptorAccessible
419
420 using AdaptorAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<AdaptorAccessible> >;
421
422 // Save RefObject from an Actor in Accessible::Get()
423 AdaptorAccessiblesType gAdaptorAccessibles;
424
425 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
426   return nullptr;
427 };
428
429 ObjectRegistry objectRegistry;
430 } // namespace
431
432 void Accessible::SetObjectRegistry(ObjectRegistry registry)
433 {
434   objectRegistry = registry;
435 }
436
437 void Accessible::RegisterExternalAccessibleGetter(std::function<Accessible*(Dali::Actor)> functor)
438 {
439   convertingFunctor = functor;
440 }
441
442 Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
443 {
444   if(!actor)
445   {
446     return nullptr;
447   }
448
449   auto accessible = convertingFunctor(actor);
450   if(!accessible)
451   {
452     if(gAdaptorAccessibles.empty() && objectRegistry)
453     {
454       objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
455         gAdaptorAccessibles.erase(obj);
456       });
457     }
458     auto pair = gAdaptorAccessibles.emplace(&actor.GetBaseObject(), nullptr);
459     if(pair.second)
460     {
461       pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
462     }
463     accessible = pair.first->second.get();
464   }
465
466   return accessible;
467 }