[NUI][AT-SPI] Add AccessibilityDynamicAttributes (#5299)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / ViewAccessibilityWrappers.cs
1 /*
2  * Copyright(c) 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
18 using System;
19 using System.Runtime.InteropServices;
20 using Tizen.NUI.Accessibility;
21
22 namespace Tizen.NUI.BaseComponents
23 {
24     public partial class View
25     {
26         private static AccessibilityStates AccessibilityInitialStates = new AccessibilityStates();
27
28         private static void RegisterAccessibilityDelegate()
29         {
30             InitializeAccessibilityDelegateAccessibleInterface();
31             InitializeAccessibilityDelegateActionInterface();
32             InitializeAccessibilityDelegateComponentInterface();
33             InitializeAccessibilityDelegateEditableTextInterface();
34             InitializeAccessibilityDelegateSelectionInterface();
35             InitializeAccessibilityDelegateTextInterface();
36             InitializeAccessibilityDelegateValueInterface();
37             InitializeAccessibilityDelegateTizenExtensions();
38
39             var ad   = Interop.ControlDevel.AccessibilityDelegate.Instance;
40             var size = Marshal.SizeOf<Interop.ControlDevel.AccessibilityDelegate>();
41             var ptr  = Marshal.AllocHGlobal(size);
42
43             Marshal.StructureToPtr(ad, ptr, false);
44             Interop.ControlDevel.DaliAccessibilitySetAccessibilityDelegate(ptr, Convert.ToUInt32(size));
45         }
46
47         private static View GetViewFromRefObject(IntPtr refObjectPtr)
48         {
49             return Registry.GetManagedBaseHandleFromRefObject(refObjectPtr) as View;
50         }
51
52         private static T GetInterfaceFromRefObject<T>(IntPtr refObjectPtr)
53         {
54             var view = GetViewFromRefObject(refObjectPtr);
55
56             // NUIViewAccessible::CallMethod<T> checks whether a given interface is implemented
57             // before jumping to managed code, so this condition should always be true.
58             if (view is T atspiInterface)
59             {
60                 return atspiInterface;
61             }
62
63             return default(T);
64         }
65
66         private static IntPtr DuplicateString(string value)
67         {
68             return Interop.ControlDevel.DaliAccessibilityDuplicateString(value ?? "");
69         }
70
71         private static IntPtr DuplicateAccessibilityRange(AccessibilityRange range)
72         {
73             return Interop.ControlDevel.DaliAccessibilityNewRange(range.StartOffset, range.EndOffset, range.Content);
74         }
75
76         private static IntPtr DuplicateAccessibilityRectangle(Rectangle rect)
77         {
78             return Interop.Rectangle.NewRectangle(rect.X, rect.Y, rect.Width, rect.Height);
79         }
80
81         //
82         // Accessible interface
83         //
84
85         private static void InitializeAccessibilityDelegateAccessibleInterface()
86         {
87             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
88
89             ad.CalculateStates = AccessibilityCalculateStatesWrapper;
90             ad.GetAttributes   = AccessibilityGetAttributes; // Not a wrapper, entirely private implementation
91             ad.GetDescription  = AccessibilityGetDescriptionWrapper;
92             ad.GetInterfaces   = AccessibilityGetInterfaces; // Not a wrapper, entirely private implementation
93             ad.GetName         = AccessibilityGetNameWrapper;
94         }
95
96         private static ulong AccessibilityCalculateStatesWrapper(IntPtr self, ulong initialStates)
97         {
98             View view = GetViewFromRefObject(self);
99             if (view == null)
100                 return 0UL;
101
102             ulong bitMask = 0UL;
103
104             lock (AccessibilityInitialStates)
105             {
106                 AccessibilityInitialStates.BitMask = initialStates;
107                 bitMask = view.AccessibilityCalculateStates().BitMask;
108                 AccessibilityInitialStates.BitMask = 0UL;
109             }
110
111             return bitMask;
112         }
113
114         private static void AccessibilityGetAttributes(IntPtr self, Interop.ControlDevel.AccessibilityDelegate.AccessibilityGetAttributesCallback callback, IntPtr userData)
115         {
116             var view = GetViewFromRefObject(self);
117             var attributes = view.AccessibilityAttributes;
118             var classKey = "class";
119
120             if (!attributes.ContainsKey(classKey))
121             {
122                 attributes[classKey] = view.GetType().Name;
123             }
124
125             foreach (var attribute in attributes)
126             {
127                 callback(attribute.Key, attribute.Value, userData);
128             }
129
130             foreach (var attribute in view.AccessibilityDynamicAttributes)
131             {
132                 callback(attribute.Key, attribute.Value.Invoke(), userData);
133             }
134         }
135
136         private static IntPtr AccessibilityGetDescriptionWrapper(IntPtr self)
137         {
138             string description = GetViewFromRefObject(self).AccessibilityGetDescription();
139
140             return DuplicateString(description);
141         }
142
143         private static uint AccessibilityGetInterfaces(IntPtr self)
144         {
145             View view = GetViewFromRefObject(self);
146             uint flags = 0U;
147
148             if (view is IAtspiEditableText)
149             {
150                 flags |= (1U << (int)AccessibilityInterface.EditableText);
151             }
152
153             if (view is IAtspiSelection)
154             {
155                 flags |= (1U << (int)AccessibilityInterface.Selection);
156             }
157
158             if (view is IAtspiText)
159             {
160                 flags |= (1U << (int)AccessibilityInterface.Text);
161             }
162
163             if (view is IAtspiValue)
164             {
165                 flags |= (1U << (int)AccessibilityInterface.Value);
166             }
167
168             return flags;
169         }
170
171         private static IntPtr AccessibilityGetNameWrapper(IntPtr self)
172         {
173             string name = GetViewFromRefObject(self).AccessibilityGetName();
174
175             return DuplicateString(name);
176         }
177
178         //
179         // Action interface
180         //
181
182         private static void InitializeAccessibilityDelegateActionInterface()
183         {
184             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
185
186             ad.DoAction       = AccessibilityDoActionWrapper;
187             ad.GetActionCount = AccessibilityGetActionCountWrapper;
188             ad.GetActionName  = AccessibilityGetActionNameWrapper;
189         }
190
191         private static bool AccessibilityDoActionWrapper(IntPtr self, IntPtr name)
192         {
193             return GetViewFromRefObject(self).AccessibilityDoAction(Marshal.PtrToStringAnsi(name));
194         }
195
196         private static int AccessibilityGetActionCountWrapper(IntPtr self)
197         {
198             return GetViewFromRefObject(self).AccessibilityGetActionCount();
199         }
200
201         private static IntPtr AccessibilityGetActionNameWrapper(IntPtr self, int index)
202         {
203             string name = GetViewFromRefObject(self).AccessibilityGetActionName(index);
204
205             return DuplicateString(name);
206         }
207
208         //
209         // Component interface
210         //
211
212         private static void InitializeAccessibilityDelegateComponentInterface()
213         {
214             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
215
216             ad.IsScrollable = AccessibilityIsScrollableWrapper;
217         }
218
219         private static bool AccessibilityIsScrollableWrapper(IntPtr self)
220         {
221             return GetViewFromRefObject(self).AccessibilityIsScrollable();
222         }
223
224         //
225         // EditableText interface
226         //
227
228         private static void InitializeAccessibilityDelegateEditableTextInterface()
229         {
230             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
231
232             ad.CopyText        = AccessibilityCopyTextWrapper;
233             ad.CutText         = AccessibilityCutTextWrapper;
234             ad.DeleteText      = AccessibilityDeleteTextWrapper;
235             ad.InsertText      = AccessibilityInsertTextWrapper;
236             ad.SetTextContents = AccessibilitySetTextContentsWrapper;
237         }
238
239         private static bool AccessibilityCopyTextWrapper(IntPtr self, int startPosition, int endPosition)
240         {
241             return GetInterfaceFromRefObject<IAtspiEditableText>(self).AccessibilityCopyText(startPosition, endPosition);
242         }
243
244         private static bool AccessibilityCutTextWrapper(IntPtr self, int startPosition, int endPosition)
245         {
246             return GetInterfaceFromRefObject<IAtspiEditableText>(self).AccessibilityCutText(startPosition, endPosition);
247         }
248
249         private static bool AccessibilityDeleteTextWrapper(IntPtr self, int startPosition, int endPosition)
250         {
251             return GetInterfaceFromRefObject<IAtspiEditableText>(self).AccessibilityDeleteText(startPosition, endPosition);
252         }
253
254         private static bool AccessibilityInsertTextWrapper(IntPtr self, int startPosition, IntPtr text)
255         {
256             return GetInterfaceFromRefObject<IAtspiEditableText>(self).AccessibilityInsertText(startPosition, Marshal.PtrToStringAnsi(text));
257         }
258
259         private static bool AccessibilitySetTextContentsWrapper(IntPtr self, IntPtr newContents)
260         {
261             return GetInterfaceFromRefObject<IAtspiEditableText>(self).AccessibilitySetTextContents(Marshal.PtrToStringAnsi(newContents));
262         }
263
264         //
265         // Selection interface
266         //
267
268         private static void InitializeAccessibilityDelegateSelectionInterface()
269         {
270             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
271
272             ad.ClearSelection           = AccessibilityClearSelectionWrapper;
273             ad.DeselectChild            = AccessibilityDeselectChildWrapper;
274             ad.DeselectSelectedChild    = AccessibilityDeselectSelectedChildWrapper;
275             ad.GetSelectedChild         = AccessibilityGetSelectedChildWrapper;
276             ad.GetSelectedChildrenCount = AccessibilityGetSelectedChildrenCountWrapper;
277             ad.IsChildSelected          = AccessibilityIsChildSelectedWrapper;
278             ad.SelectAll                = AccessibilitySelectAllWrapper;
279             ad.SelectChild              = AccessibilitySelectChildWrapper;
280         }
281
282         private static bool AccessibilityClearSelectionWrapper(IntPtr self)
283         {
284             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityClearSelection();
285         }
286
287         private static bool AccessibilityDeselectChildWrapper(IntPtr self, int childIndex)
288         {
289             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityDeselectChild(childIndex);
290         }
291
292         private static bool AccessibilityDeselectSelectedChildWrapper(IntPtr self, int selectedChildIndex)
293         {
294             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityDeselectSelectedChild(selectedChildIndex);
295         }
296
297         private static IntPtr AccessibilityGetSelectedChildWrapper(IntPtr self, int selectedChildIndex)
298         {
299             View child = GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityGetSelectedChild(selectedChildIndex);
300
301             return View.getCPtr(child).Handle;
302         }
303
304         private static int AccessibilityGetSelectedChildrenCountWrapper(IntPtr self)
305         {
306             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityGetSelectedChildrenCount();
307         }
308
309         private static bool AccessibilityIsChildSelectedWrapper(IntPtr self, int childIndex)
310         {
311             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilityIsChildSelected(childIndex);
312         }
313
314         private static bool AccessibilitySelectAllWrapper(IntPtr self)
315         {
316             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilitySelectAll();
317         }
318
319         private static bool AccessibilitySelectChildWrapper(IntPtr self, int childIndex)
320         {
321             return GetInterfaceFromRefObject<IAtspiSelection>(self).AccessibilitySelectChild(childIndex);
322         }
323
324         //
325         // Text interface
326         //
327
328         private static void InitializeAccessibilityDelegateTextInterface()
329         {
330             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
331
332             ad.GetCharacterCount = AccessibilityGetCharacterCountWrapper;
333             ad.GetCursorOffset   = AccessibilityGetCursorOffsetWrapper;
334             ad.GetRangeExtents   = AccessibilityGetRangeExtentsWrapper;
335             ad.GetSelection      = AccessibilityGetSelectionWrapper;
336             ad.GetText           = AccessibilityGetTextWrapper;
337             ad.GetTextAtOffset   = AccessibilityGetTextAtOffsetWrapper;
338             ad.RemoveSelection   = AccessibilityRemoveSelectionWrapper;
339             ad.SetCursorOffset   = AccessibilitySetCursorOffsetWrapper;
340             ad.SetSelection      = AccessibilitySetSelectionWrapper;
341         }
342
343         private static int AccessibilityGetCharacterCountWrapper(IntPtr self)
344         {
345             return GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetCharacterCount();
346         }
347
348         private static int AccessibilityGetCursorOffsetWrapper(IntPtr self)
349         {
350             return GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetCursorOffset();
351         }
352
353         private static IntPtr AccessibilityGetRangeExtentsWrapper(IntPtr self, int startOffset, int endOffset, int coordType)
354         {
355             using Rectangle rect = GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetRangeExtents(startOffset, endOffset, (AccessibilityCoordinateType)coordType);
356
357             return DuplicateAccessibilityRectangle(rect);
358         }
359
360         private static IntPtr AccessibilityGetSelectionWrapper(IntPtr self, int selectionNumber)
361         {
362             AccessibilityRange range = GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetSelection(selectionNumber);
363
364             return DuplicateAccessibilityRange(range);
365         }
366
367         private static IntPtr AccessibilityGetTextWrapper(IntPtr self, int startOffset, int endOffset)
368         {
369             string text = GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetText(startOffset, endOffset);
370
371             return DuplicateString(text);
372         }
373
374         private static IntPtr AccessibilityGetTextAtOffsetWrapper(IntPtr self, int offset, int boundary)
375         {
376             AccessibilityRange range = GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityGetTextAtOffset(offset, (AccessibilityTextBoundary)boundary);
377
378             return DuplicateAccessibilityRange(range);
379         }
380
381         private static bool AccessibilityRemoveSelectionWrapper(IntPtr self, int selectionNumber)
382         {
383             return GetInterfaceFromRefObject<IAtspiText>(self).AccessibilityRemoveSelection(selectionNumber);
384         }
385
386         private static bool AccessibilitySetCursorOffsetWrapper(IntPtr self, int offset)
387         {
388             return GetInterfaceFromRefObject<IAtspiText>(self).AccessibilitySetCursorOffset(offset);
389         }
390
391         private static bool AccessibilitySetSelectionWrapper(IntPtr self, int selectionNumber, int startOffset, int endOffset)
392         {
393             return GetInterfaceFromRefObject<IAtspiText>(self).AccessibilitySetSelection(selectionNumber, startOffset, endOffset);
394         }
395
396         //
397         // Value interface
398         //
399
400         private static void InitializeAccessibilityDelegateValueInterface()
401         {
402             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
403
404             ad.GetCurrent          = AccessibilityGetCurrentWrapper;
405             ad.GetMaximum          = AccessibilityGetMaximumWrapper;
406             ad.GetMinimum          = AccessibilityGetMinimumWrapper;
407             ad.GetMinimumIncrement = AccessibilityGetMinimumIncrementWrapper;
408             ad.GetValueText        = AccessibilityGetValueTextWrapper;
409             ad.SetCurrent          = AccessibilitySetCurrentWrapper;
410         }
411
412         private static double AccessibilityGetCurrentWrapper(IntPtr self)
413         {
414             return GetInterfaceFromRefObject<IAtspiValue>(self).AccessibilityGetCurrent();
415         }
416
417         private static double AccessibilityGetMaximumWrapper(IntPtr self)
418         {
419             return GetInterfaceFromRefObject<IAtspiValue>(self).AccessibilityGetMaximum();
420         }
421
422         private static double AccessibilityGetMinimumWrapper(IntPtr self)
423         {
424             return GetInterfaceFromRefObject<IAtspiValue>(self).AccessibilityGetMinimum();
425         }
426
427         private static double AccessibilityGetMinimumIncrementWrapper(IntPtr self)
428         {
429             return GetInterfaceFromRefObject<IAtspiValue>(self).AccessibilityGetMinimumIncrement();
430         }
431
432         private static IntPtr AccessibilityGetValueTextWrapper(IntPtr self)
433         {
434             var view = GetViewFromRefObject(self);
435             var value = GetInterfaceFromRefObject<IAtspiValue>(self);
436             string text;
437
438             // Mimic the behaviour of the pairs AccessibilityNameRequested & AccessibilityGetName(),
439             // and AccessibilityDescriptionRequested & AccessibilityGetDescription(),
440             // i.e. a higher-priority Accessibility[…]Requested event for application developers,
441             // and a lower-priority AccessibilityGet[…]() virtual method for component developers.
442             // The difference is that event-or-virtual-method dispatching is done in C++ for
443             // Name and Description, and here in this wrapper method for ValueText (because there
444             // is no signal for ValueText in DALi.)
445             if (view.AccessibilityValueTextRequested?.GetInvocationList().Length > 0)
446             {
447                 var args = new AccessibilityValueTextRequestedEventArgs();
448                 view.AccessibilityValueTextRequested.Invoke(view, args);
449                 text = args.Text;
450             }
451             else
452             {
453                 text = value.AccessibilityGetValueText();
454             }
455
456             return DuplicateString(text);
457         }
458
459         private static bool AccessibilitySetCurrentWrapper(IntPtr self, double value)
460         {
461             return GetInterfaceFromRefObject<IAtspiValue>(self).AccessibilitySetCurrent(value);
462         }
463
464         //
465         // Tizen extensions
466         //
467
468         private static void InitializeAccessibilityDelegateTizenExtensions()
469         {
470             var ad = Interop.ControlDevel.AccessibilityDelegate.Instance;
471
472             ad.ScrollToChild            = AccessibilityScrollToChildWrapper;
473         }
474
475         private static bool AccessibilityScrollToChildWrapper(IntPtr self, IntPtr child)
476         {
477             View view = GetViewFromRefObject(self);
478
479             return view.AccessibilityScrollToChild(view.GetInstanceSafely<View>(child));
480         }
481     }
482 }