Merge "[ATSPI] Add more descriptions to Bridge objects" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / adaptor-framework / accessibility-impl.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 // CLASS HEADER
18
19 // EXTERNAL INCLUDES
20 #include <dali/integration-api/debug.h>
21 #include <dali/public-api/actors/actor.h>
22 #include <dali/public-api/actors/layer.h>
23 #include <dali/public-api/object/base-object.h>
24 #include <dali/public-api/object/object-registry.h>
25 #include <dali/public-api/object/type-info.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/object/weak-handle.h>
28
29 // INTERNAL INCLUDES
30 #include <dali/devel-api/adaptor-framework/accessibility-impl.h>
31 #include <dali/devel-api/adaptor-framework/window-devel.h>
32 #include <dali/internal/adaptor/common/adaptor-impl.h>
33 #include <dali/public-api/dali-adaptor-common.h>
34
35 using namespace Dali::Accessibility;
36 using namespace Dali;
37
38 const std::string& Dali::Accessibility::Address::GetBus() const
39 {
40   return mBus.empty() && Bridge::GetCurrentBridge() ? Bridge::GetCurrentBridge()->GetBusName() : mBus;
41 }
42
43 std::string EmptyAccessibleWithAddress::GetRoleName()
44 {
45   return "";
46 }
47
48 std::string Accessible::GetLocalizedRoleName()
49 {
50   return GetRoleName();
51 }
52
53 std::string Accessible::GetRoleName()
54 {
55   switch(GetRole())
56   {
57     case Role::INVALID:
58     {
59       return "invalid";
60     }
61     case Role::ACCELERATOR_LABEL:
62     {
63       return "accelerator label";
64     }
65     case Role::ALERT:
66     {
67       return "alert";
68     }
69     case Role::ANIMATION:
70     {
71       return "animation";
72     }
73     case Role::ARROW:
74     {
75       return "arrow";
76     }
77     case Role::CALENDAR:
78     {
79       return "calendar";
80     }
81     case Role::CANVAS:
82     {
83       return "canvas";
84     }
85     case Role::CHECK_BOX:
86     {
87       return "check box";
88     }
89     case Role::CHECK_MENU_ITEM:
90     {
91       return "check menu item";
92     }
93     case Role::COLOR_CHOOSER:
94     {
95       return "color chooser";
96     }
97     case Role::COLUMN_HEADER:
98     {
99       return "column header";
100     }
101     case Role::COMBO_BOX:
102     {
103       return "combo box";
104     }
105     case Role::DATE_EDITOR:
106     {
107       return "date editor";
108     }
109     case Role::DESKTOP_ICON:
110     {
111       return "desktop icon";
112     }
113     case Role::DESKTOP_FRAME:
114     {
115       return "desktop frame";
116     }
117     case Role::DIAL:
118     {
119       return "dial";
120     }
121     case Role::DIALOG:
122     {
123       return "dialog";
124     }
125     case Role::DIRECTORY_PANE:
126     {
127       return "directory pane";
128     }
129     case Role::DRAWING_AREA:
130     {
131       return "drawing area";
132     }
133     case Role::FILE_CHOOSER:
134     {
135       return "file chooser";
136     }
137     case Role::FILLER:
138     {
139       return "filler";
140     }
141     case Role::FOCUS_TRAVERSABLE:
142     {
143       return "focus traversable";
144     }
145     case Role::FONT_CHOOSER:
146     {
147       return "font chooser";
148     }
149     case Role::FRAME:
150     {
151       return "frame";
152     }
153     case Role::GLASS_PANE:
154     {
155       return "glass pane";
156     }
157     case Role::HTML_CONTAINER:
158     {
159       return "html container";
160     }
161     case Role::ICON:
162     {
163       return "icon";
164     }
165     case Role::IMAGE:
166     {
167       return "image";
168     }
169     case Role::INTERNAL_FRAME:
170     {
171       return "internal frame";
172     }
173     case Role::LABEL:
174     {
175       return "label";
176     }
177     case Role::LAYERED_PANE:
178     {
179       return "layered pane";
180     }
181     case Role::LIST:
182     {
183       return "list";
184     }
185     case Role::LIST_ITEM:
186     {
187       return "list item";
188     }
189     case Role::MENU:
190     {
191       return "menu";
192     }
193     case Role::MENU_BAR:
194     {
195       return "menu bar";
196     }
197     case Role::MENU_ITEM:
198     {
199       return "menu item";
200     }
201     case Role::OPTION_PANE:
202     {
203       return "option pane";
204     }
205     case Role::PAGE_TAB:
206     {
207       return "page tab";
208     }
209     case Role::PAGE_TAB_LIST:
210     {
211       return "page tab list";
212     }
213     case Role::PANEL:
214     {
215       return "panel";
216     }
217     case Role::PASSWORD_TEXT:
218     {
219       return "password text";
220     }
221     case Role::POPUP_MENU:
222     {
223       return "popup menu";
224     }
225     case Role::PROGRESS_BAR:
226     {
227       return "progress bar";
228     }
229     case Role::PUSH_BUTTON:
230     {
231       return "push button";
232     }
233     case Role::RADIO_BUTTON:
234     {
235       return "radio button";
236     }
237     case Role::RADIO_MENU_ITEM:
238     {
239       return "radio menu item";
240     }
241     case Role::ROOT_PANE:
242     {
243       return "root pane";
244     }
245     case Role::ROW_HEADER:
246     {
247       return "row header";
248     }
249     case Role::SCROLL_BAR:
250     {
251       return "scroll bar";
252     }
253     case Role::SCROLL_PANE:
254     {
255       return "scroll pane";
256     }
257     case Role::SEPARATOR:
258     {
259       return "separator";
260     }
261     case Role::SLIDER:
262     {
263       return "slider";
264     }
265     case Role::SPIN_BUTTON:
266     {
267       return "spin button";
268     }
269     case Role::SPLIT_PANE:
270     {
271       return "split pane";
272     }
273     case Role::STATUS_BAR:
274     {
275       return "status bar";
276     }
277     case Role::TABLE:
278     {
279       return "table";
280     }
281     case Role::TABLE_CELL:
282     {
283       return "table cell";
284     }
285     case Role::TABLE_COLUMN_HEADER:
286     {
287       return "table column header";
288     }
289     case Role::TABLE_ROW_HEADER:
290     {
291       return "table row header";
292     }
293     case Role::TEAROFF_MENU_ITEM:
294     {
295       return "tearoff menu item";
296     }
297     case Role::TERMINAL:
298     {
299       return "terminal";
300     }
301     case Role::TEXT:
302     {
303       return "text";
304     }
305     case Role::TOGGLE_BUTTON:
306     {
307       return "toggle button";
308     }
309     case Role::TOOL_BAR:
310     {
311       return "tool bar";
312     }
313     case Role::TOOL_TIP:
314     {
315       return "tool tip";
316     }
317     case Role::TREE:
318     {
319       return "tree";
320     }
321     case Role::TREE_TABLE:
322     {
323       return "tree table";
324     }
325     case Role::UNKNOWN:
326     {
327       return "unknown";
328     }
329     case Role::VIEWPORT:
330     {
331       return "viewport";
332     }
333     case Role::WINDOW:
334     {
335       return "window";
336     }
337     case Role::EXTENDED:
338     {
339       return "extended";
340     }
341     case Role::HEADER:
342     {
343       return "header";
344     }
345     case Role::FOOTER:
346     {
347       return "footer";
348     }
349     case Role::PARAGRAPH:
350     {
351       return "paragraph";
352     }
353     case Role::RULER:
354     {
355       return "ruler";
356     }
357     case Role::APPLICATION:
358     {
359       return "application";
360     }
361     case Role::AUTOCOMPLETE:
362     {
363       return "autocomplete";
364     }
365     case Role::EDITBAR:
366     {
367       return "edit bar";
368     }
369     case Role::EMBEDDED:
370     {
371       return "embedded";
372     }
373     case Role::ENTRY:
374     {
375       return "entry";
376     }
377     case Role::CHART:
378     {
379       return "chart";
380     }
381     case Role::CAPTION:
382     {
383       return "caution";
384     }
385     case Role::DOCUMENT_FRAME:
386     {
387       return "document frame";
388     }
389     case Role::HEADING:
390     {
391       return "heading";
392     }
393     case Role::PAGE:
394     {
395       return "page";
396     }
397     case Role::SECTION:
398     {
399       return "section";
400     }
401     case Role::REDUNDANT_OBJECT:
402     {
403       return "redundant object";
404     }
405     case Role::FORM:
406     {
407       return "form";
408     }
409     case Role::LINK:
410     {
411       return "link";
412     }
413     case Role::INPUT_METHOD_WINDOW:
414     {
415       return "input method window";
416     }
417     case Role::TABLE_ROW:
418     {
419       return "table row";
420     }
421     case Role::TREE_ITEM:
422     {
423       return "tree item";
424     }
425     case Role::DOCUMENT_SPREADSHEET:
426     {
427       return "document spreadsheet";
428     }
429     case Role::DOCUMENT_PRESENTATION:
430     {
431       return "document presentation";
432     }
433     case Role::DOCUMENT_TEXT:
434     {
435       return "document text";
436     }
437     case Role::DOCUMENT_WEB:
438     {
439       return "document web";
440     }
441     case Role::DOCUMENT_EMAIL:
442     {
443       return "document email";
444     }
445     case Role::COMMENT:
446     {
447       return "comment";
448     }
449     case Role::LIST_BOX:
450     {
451       return "list box";
452     }
453     case Role::GROUPING:
454     {
455       return "grouping";
456     }
457     case Role::IMAGE_MAP:
458     {
459       return "image map";
460     }
461     case Role::NOTIFICATION:
462     {
463       return "notification";
464     }
465     case Role::INFO_BAR:
466     {
467       return "info bar";
468     }
469     case Role::LEVEL_BAR:
470     {
471       return "level bar";
472     }
473     case Role::TITLE_BAR:
474     {
475       return "title bar";
476     }
477     case Role::BLOCK_QUOTE:
478     {
479       return "block quote";
480     }
481     case Role::AUDIO:
482     {
483       return "audio";
484     }
485     case Role::VIDEO:
486     {
487       return "video";
488     }
489     case Role::DEFINITION:
490     {
491       return "definition";
492     }
493     case Role::ARTICLE:
494     {
495       return "article";
496     }
497     case Role::LANDMARK:
498     {
499       return "landmark";
500     }
501     case Role::LOG:
502     {
503       return "log";
504     }
505     case Role::MARQUEE:
506     {
507       return "marquee";
508     }
509     case Role::MATH:
510     {
511       return "math";
512     }
513     case Role::RATING:
514     {
515       return "rating";
516     }
517     case Role::TIMER:
518     {
519       return "timer";
520     }
521     case Role::STATIC:
522     {
523       return "static";
524     }
525     case Role::MATH_FRACTION:
526     {
527       return "math fraction";
528     }
529     case Role::MATH_ROOT:
530     {
531       return "math root";
532     }
533     case Role::SUBSCRIPT:
534     {
535       return "subscript";
536     }
537     case Role::SUPERSCRIPT:
538     {
539       return "superscript";
540     }
541     case Role::MAX_COUNT:
542     {
543       break;
544     }
545   }
546   return "";
547 }
548
549 Dali::Actor Accessible::GetCurrentlyHighlightedActor()
550 {
551   return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
552 }
553
554 void Accessible::SetCurrentlyHighlightedActor(Dali::Actor actor)
555 {
556   if(IsUp())
557   {
558     Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor = actor;
559   }
560 }
561
562 Dali::Actor Accessible::GetHighlightActor()
563 {
564   return IsUp() ? Bridge::GetCurrentBridge()->mData->mHighlightActor : Dali::Actor{};
565 }
566
567 void Accessible::SetHighlightActor(Dali::Actor actor)
568 {
569   if(IsUp())
570   {
571     Bridge::GetCurrentBridge()->mData->mHighlightActor = actor;
572   }
573 }
574
575 void Bridge::ForceDown()
576 {
577   auto highlighted = Accessible::GetCurrentlyHighlightedActor();
578   if(highlighted)
579   {
580     auto component = dynamic_cast<Component*>(Accessible::Get(highlighted));
581     if(component)
582     {
583       component->ClearHighlight();
584     }
585   }
586   mData = {};
587 }
588
589 void Bridge::SetIsOnRootLevel(Accessible* owner)
590 {
591   owner->mIsOnRootLevel = true;
592 }
593
594 namespace
595 {
596 class NonControlAccessible : public virtual Accessible, public virtual Collection, public virtual Component
597 {
598 protected:
599   Dali::WeakHandle<Dali::Actor> mSelf;
600   bool                          mRoot = false;
601
602   Dali::Actor Self()
603   {
604     auto handle = mSelf.GetHandle();
605
606     // NonControlAccessible is deleted on ObjectDestroyedSignal
607     // for the respective actor (see `nonControlAccessibles`).
608     DALI_ASSERT_ALWAYS(handle);
609
610     return handle;
611   }
612
613 public:
614   NonControlAccessible(Dali::Actor actor, bool isRoot)
615   : mSelf(actor),
616     mRoot(isRoot)
617   {
618   }
619
620   Dali::Rect<> GetExtents(Dali::Accessibility::CoordinateType type) override
621   {
622     Dali::Actor actor                   = Self();
623     Vector2     screenPosition          = actor.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
624     Vector3     size                    = actor.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * actor.GetCurrentProperty<Vector3>(Actor::Property::WORLD_SCALE);
625     bool        positionUsesAnchorPoint = actor.GetProperty(Actor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
626     Vector3     anchorPointOffSet       = size * (positionUsesAnchorPoint ? actor.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
627     Vector2     position                = Vector2(screenPosition.x - anchorPointOffSet.x, screenPosition.y - anchorPointOffSet.y);
628
629     if(type == Dali::Accessibility::CoordinateType::WINDOW)
630     {
631       return {position.x, position.y, size.x, size.y};
632     }
633     else // Dali::Accessibility::CoordinateType::SCREEN
634     {
635       auto window = Dali::DevelWindow::Get(actor);
636       auto windowPosition = window.GetPosition();
637       return {position.x + windowPosition.GetX(), position.y + windowPosition.GetY(), size.x, size.y};
638     }
639   }
640
641   Dali::Accessibility::ComponentLayer GetLayer() override
642   {
643     return Dali::Accessibility::ComponentLayer::WINDOW;
644   }
645
646   int16_t GetMdiZOrder() override
647   {
648     return 0;
649   }
650
651   double GetAlpha() override
652   {
653     return 0;
654   }
655
656   bool GrabFocus() override
657   {
658     return false;
659   }
660
661   bool GrabHighlight() override
662   {
663     return false;
664   }
665
666   bool ClearHighlight() override
667   {
668     return false;
669   }
670
671   bool IsScrollable() override
672   {
673     return false;
674   }
675
676   std::string GetName() override
677   {
678     return Self().GetProperty<std::string>(Dali::Actor::Property::NAME);
679   }
680
681   std::string GetDescription() override
682   {
683     return "";
684   }
685
686   Accessible* GetParent() override
687   {
688     if(IsOnRootLevel())
689     {
690       auto data = GetBridgeData();
691       return data->mBridge->GetApplication();
692     }
693     return Get(Self().GetParent());
694   }
695
696   size_t GetChildCount() override
697   {
698     return static_cast<size_t>(Self().GetChildCount());
699   }
700
701   Accessible* GetChildAtIndex(size_t index) override
702   {
703     auto numberOfChildren = static_cast<size_t>(Self().GetChildCount());
704     if(index >= numberOfChildren)
705     {
706       throw std::domain_error{"invalid index " + std::to_string(index) + " for object with " + std::to_string(numberOfChildren) + " children"};
707     }
708     return Get(Self().GetChildAt(static_cast<unsigned int>(index)));
709   }
710
711   size_t GetIndexInParent() override
712   {
713     auto parent = Self().GetParent();
714     if(!parent)
715     {
716       return 0;
717     }
718     auto size = static_cast<size_t>(parent.GetChildCount());
719     for(auto i = 0u; i < size; ++i)
720     {
721       if(parent.GetChildAt(i) == Self())
722       {
723         return i;
724       }
725     }
726     throw std::domain_error{"actor is not a child of it's parent"};
727   }
728
729   Role GetRole() override
730   {
731     return mRoot ? Role::WINDOW : Role::REDUNDANT_OBJECT;
732   }
733
734   States GetStates() override
735   {
736     States state;
737     if(mRoot)
738     {
739       auto window = Dali::DevelWindow::Get(Self());
740       auto visible = window.IsVisible();
741       state[State::ENABLED]   = true;
742       state[State::SENSITIVE] = true;
743       state[State::SHOWING]   = visible;
744       state[State::VISIBLE]   = true;
745       state[State::ACTIVE]    = visible;
746     }
747     else
748     {
749       auto parentState = GetParent()->GetStates();
750       state[State::SHOWING] = parentState[State::SHOWING];
751       state[State::VISIBLE] = parentState[State::VISIBLE];
752     }
753     return state;
754   }
755
756   Attributes GetAttributes() override
757   {
758     Dali::TypeInfo type;
759     Self().GetTypeInfo(type);
760     return
761     {
762       {"class", type.GetName()},
763     };
764   }
765
766   bool DoGesture(const GestureInfo& gestureInfo) override
767   {
768     return false;
769   }
770
771   std::vector<Relation> GetRelationSet() override
772   {
773     return {};
774   }
775 };
776
777 using NonControlAccessiblesType = std::unordered_map<const Dali::RefObject*, std::unique_ptr<NonControlAccessible> >;
778
779 NonControlAccessiblesType nonControlAccessibles;
780
781 std::function<Accessible*(Dali::Actor)> convertingFunctor = [](Dali::Actor) -> Accessible* {
782   return nullptr;
783 };
784
785 ObjectRegistry objectRegistry;
786 } // namespace
787
788 void Accessible::SetObjectRegistry(ObjectRegistry registry)
789 {
790   objectRegistry = registry;
791 }
792
793 void Accessible::RegisterControlAccessibilityGetter(std::function<Accessible*(Dali::Actor)> functor)
794 {
795   convertingFunctor = functor;
796 }
797
798 Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
799 {
800   if(!actor)
801   {
802     return nullptr;
803   }
804
805   auto accessible = convertingFunctor(actor);
806   if(!accessible)
807   {
808     if(nonControlAccessibles.empty() && objectRegistry)
809     {
810       objectRegistry.ObjectDestroyedSignal().Connect([](const Dali::RefObject* obj) {
811         nonControlAccessibles.erase(obj);
812       });
813     }
814     auto pair = nonControlAccessibles.emplace(&actor.GetBaseObject(), nullptr);
815     if(pair.second)
816     {
817       pair.first->second.reset(new NonControlAccessible(actor, isRoot));
818     }
819     accessible = pair.first->second.get();
820   }
821   return accessible;
822 }