dbc41bd588019942621aadcf9abfa44a1fa23de4
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / ViewAccessibility.cs
1 /*
2  * Copyright(c) 2021 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.Collections.Generic;
20 using System.ComponentModel;
21 using System.Runtime.InteropServices;
22 using Tizen.NUI;
23
24 namespace Tizen.NUI.BaseComponents
25 {
26     /// <summary>
27     /// AccessibilityRange is used to store data related with Text.
28     /// </summary>
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class AccessibilityRange
31     {
32         /// <summary>
33         /// Start position in stored text.
34         /// </summary>
35         public int StartOffset { get; set; } = 0;
36
37         /// <summary>
38         /// End position in stored text.
39         /// </summary>
40         public int EndOffset { get; set; } = 0;
41
42         /// <summary>
43         /// Text content in stored text.
44         /// </summary>
45         public string Content { get; set; } = "";
46     }
47
48     /// <summary>
49     /// View is the base class for all views.
50     /// </summary>
51     /// <since_tizen> 3 </since_tizen>
52     public partial class View
53     {
54         ///////////////////////////////////////////////////////////////////
55         // ****************** Accessibility Attributes ****************** //
56         ///////////////////////////////////////////////////////////////////
57
58         /// <summary>
59         /// Dictionary of accessibility attributes (key-value pairs of strings).
60         /// </summary>
61         [EditorBrowsable(EditorBrowsableState.Never)]
62         protected Dictionary<string, string> AccessibilityAttributes { get; } = new Dictionary<string, string>();
63
64         ///////////////////////////////////////////////////////////////////
65         // ************************** Highlight ************************ //
66         ///////////////////////////////////////////////////////////////////
67
68         /// <summary>
69         /// Clears accessibility highlight.
70         /// </summary>
71         /// <returns>True if cleared, otherwise false when it is not possible</returns>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public bool ClearAccessibilityHighlight()
74         {
75             bool result = Interop.ControlDevel.DaliToolkitDevelControlClearAccessibilityHighlight(SwigCPtr);
76             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
77             return result;
78         }
79
80         /// <summary>
81         /// Grabs accessibility highlight.
82         /// </summary>
83         /// <returns>True if cleared, otherwise false when it is not possible</returns>
84         [EditorBrowsable(EditorBrowsableState.Never)]
85         public bool GrabAccessibilityHighlight()
86         {
87             bool result = Interop.ControlDevel.DaliToolkitDevelControlGrabAccessibilityHighlight(SwigCPtr);
88             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
89             return result;
90         }
91
92         /// <summary>
93         /// Flag to check whether this view is highlighted or not.
94         /// </summary>
95         [EditorBrowsable(EditorBrowsableState.Never)]
96         protected bool IsHighlighted
97         {
98             get
99             {
100                 return (this == Accessibility.Accessibility.GetCurrentlyHighlightedView());
101             }
102         }
103
104         ///////////////////////////////////////////////////////////////////
105         // ****************** Accessibility Relations ******************* //
106         ///////////////////////////////////////////////////////////////////
107
108         /// <summary>
109         /// Creates relation between objects.
110         /// </summary>
111         /// <param name="second">Object which will be in relation.</param>
112         /// <param name="relation">Relation type.</param>
113         /// <exception cref="ArgumentNullException">You must pass valid object. NULL could not be in relation.</exception>
114         [EditorBrowsable(EditorBrowsableState.Never)]
115         public void AppendAccessibilityRelation(View second, AccessibilityRelationType relation)
116         {
117             if (second is null)
118             {
119                 throw new ArgumentNullException(nameof(second));
120             }
121
122             Interop.ControlDevel.DaliToolkitDevelControlAppendAccessibilityRelation(SwigCPtr, second.SwigCPtr, (int)relation);
123             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
124         }
125
126         /// <summary>
127         /// Removes accessibility relation.
128         /// </summary>
129         /// <param name="second">Object which will be removed in relation</param>
130         /// <param name="relation">Relation type</param>
131         [EditorBrowsable(EditorBrowsableState.Never)]
132         public void RemoveAccessibilityRelation(View second, AccessibilityRelationType relation)
133         {
134             if (second is null)
135             {
136                 throw new ArgumentNullException(nameof(second));
137             }
138
139             Interop.ControlDevel.DaliToolkitDevelControlRemoveAccessibilityRelation(SwigCPtr, second.SwigCPtr, (int)relation);
140             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
141         }
142
143         /// <summary>
144         /// Removes all previously appended relations.
145         /// </summary>
146         [EditorBrowsable(EditorBrowsableState.Never)]
147         public void ClearAccessibilityRelations()
148         {
149             Interop.ControlDevel.DaliToolkitDevelControlClearAccessibilityRelations(SwigCPtr);
150             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
151         }
152
153         /// <summary>
154         /// Gets accessibility collection connected with the current object.
155         /// </summary>
156         /// <returns>A dictionary mapping a relation type to a set of objects in that relation</returns>
157         [EditorBrowsable(EditorBrowsableState.Never)]
158         public Dictionary<AccessibilityRelationType, List<View>> GetAccessibilityRelations()
159         {
160             var list = new List<KeyValuePair<int, IntPtr>>();
161             var listHandle = GCHandle.Alloc(list);
162             var callback = new Interop.ControlDevel.GetAccessibilityRelationsCallback(GetAccessibilityRelationsCallback);
163
164             Interop.ControlDevel.DaliToolkitDevelControlGetAccessibilityRelations(SwigCPtr, callback, GCHandle.ToIntPtr(listHandle));
165             listHandle.Free();
166             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
167
168             var result = new Dictionary<AccessibilityRelationType, List<View>>();
169
170             foreach (var pair in list)
171             {
172                 var key = (AccessibilityRelationType)pair.Key;
173                 var value = this.GetInstanceSafely<View>(pair.Value);
174
175                 if (!result.ContainsKey(key))
176                 {
177                     result[key] = new List<View>();
178                 }
179
180                 result[key].Add(value);
181             }
182
183             return result;
184         }
185
186         private static void GetAccessibilityRelationsCallback(int relationType, IntPtr relationTarget, IntPtr userData)
187         {
188             var handle = GCHandle.FromIntPtr(userData);
189             var list = (List<KeyValuePair<int, IntPtr>>)handle.Target;
190
191             list.Add(new KeyValuePair<int, IntPtr>(relationType, relationTarget));
192         }
193
194         ///////////////////////////////////////////////////////////////////
195         // ********************* ReadingInfoType *********************** //
196         ///////////////////////////////////////////////////////////////////
197
198         /// <summary>
199         /// Sets accessibility reading information.
200         /// </summary>
201         /// <param name="type">Reading information type</param>
202         [EditorBrowsable(EditorBrowsableState.Never)]
203         public void SetAccessibilityReadingInfoTypes(AccessibilityReadingInfoTypes type)
204         {
205             Interop.ControlDevel.DaliToolkitDevelControlSetAccessibilityReadingInfoTypes(SwigCPtr, (int)type);
206             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
207         }
208
209         /// <summary>
210         /// Gets accessibility reading information.
211         /// </summary>
212         /// <returns>Reading information type</returns>
213         [EditorBrowsable(EditorBrowsableState.Never)]
214         public AccessibilityReadingInfoTypes GetAccessibilityReadingInfoTypes()
215         {
216             AccessibilityReadingInfoTypes result = (AccessibilityReadingInfoTypes)Interop.ControlDevel.DaliToolkitDevelControlGetAccessibilityReadingInfoTypes(SwigCPtr);
217             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
218             return result;
219         }
220
221         ///////////////////////////////////////////////////////////////////
222         // ******************** Accessibility States ******************* //
223         ///////////////////////////////////////////////////////////////////
224
225         /// <summary>
226         /// Notifies sending notifications about the current states to accessibility clients.
227         /// </summary>
228         /// <remarks>
229         /// In essence, this is equivalent to calling EmitAccessibilityStateChangedEvent in a loop for all specified states.
230         /// If recursive mode is specified, all children of the Accessibility object will also re-emit the states.
231         /// </remarks>
232         /// <param name="states">Accessibility States</param>
233         /// <param name="notifyMode">Controls the notification strategy</param>
234         [EditorBrowsable(EditorBrowsableState.Never)]
235         public void NotifyAccessibilityStatesChange(AccessibilityStates states, AccessibilityStatesNotifyMode notifyMode)
236         {
237             if (states is null)
238             {
239                 throw new ArgumentNullException(nameof(states));
240             }
241
242             Interop.ControlDevel.DaliToolkitDevelControlNotifyAccessibilityStateChange(SwigCPtr, states.BitMask, (int)notifyMode);
243             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
244         }
245
246         /// <summary>
247         /// Gets Accessibility States.
248         /// </summary>
249         /// <returns>Accessibility States</returns>
250         [EditorBrowsable(EditorBrowsableState.Never)]
251         public AccessibilityStates GetAccessibilityStates()
252         {
253             var result = new AccessibilityStates {BitMask = Interop.ControlDevel.DaliToolkitDevelControlGetAccessibilityStates(SwigCPtr)};
254             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
255             return result;
256         }
257
258         ///////////////////////////////////////////////////////////////////
259         // ************************ Accessible ************************* //
260         ///////////////////////////////////////////////////////////////////
261
262         /// <summary>
263         /// Emits accessibility property changed event.
264         /// </summary>
265         /// <param name="changeEvent">Property changed event</param>
266         [EditorBrowsable(EditorBrowsableState.Never)]
267         public void EmitAccessibilityEvent(AccessibilityPropertyChangeEvent changeEvent)
268         {
269             Interop.ControlDevel.DaliAccessibilityEmitAccessibilityEvent(SwigCPtr, Convert.ToInt32(changeEvent));
270             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
271         }
272
273         /// <summary>
274         /// Emits accessibility states changed event.
275         /// </summary>
276         /// <param name="state">Accessibility state</param>
277         /// <param name="equal">True if the state is set or enabled, otherwise false</param>
278         [EditorBrowsable(EditorBrowsableState.Never)]
279         public void EmitAccessibilityStateChangedEvent(AccessibilityState state, bool equal)
280         {
281             Interop.ControlDevel.DaliAccessibilityEmitAccessibilityStateChangedEvent(SwigCPtr, (int)state, Convert.ToInt32(equal));
282             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
283         }
284
285         /// <summary>
286         /// Emits accessibility text inserted event.
287         /// </summary>
288         /// <param name="cursorPosition">Text cursor position</param>
289         /// <param name="length">Text length</param>
290         /// <param name="content">Inserted text content</param>
291         [EditorBrowsable(EditorBrowsableState.Never)]
292         public void EmitTextInsertedEvent(int cursorPosition, int length, string content)
293         {
294             Interop.ControlDevel.DaliAccessibilityEmitTextInsertedEvent(SwigCPtr, cursorPosition, length, content);
295             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
296         }
297
298         /// <summary>
299         /// Emits accessibility text deleted event.
300         /// </summary>
301         /// <param name="cursorPosition">Text cursor position</param>
302         /// <param name="length">Text length</param>
303         /// <param name="content">Inserted text content</param>
304         [EditorBrowsable(EditorBrowsableState.Never)]
305         public void EmitTextDeletedEvent(int cursorPosition, int length, string content)
306         {
307             Interop.ControlDevel.DaliAccessibilityEmitTextDeletedEvent(SwigCPtr, cursorPosition, length, content);
308             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
309         }
310
311         /// <summary>
312         /// Emits accessibility text cursor moved event.
313         /// </summary>
314         /// <param name="cursorPosition">The new cursor position</param>
315         [EditorBrowsable(EditorBrowsableState.Never)]
316         public void EmitTextCursorMovedEvent(int cursorPosition)
317         {
318             Interop.ControlDevel.DaliAccessibilityEmitTextCursorMovedEvent(SwigCPtr, cursorPosition);
319             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
320         }
321
322         /// <summary>
323         /// Modifiable collection of suppressed AT-SPI events (D-Bus signals).
324         /// </summary>
325         [EditorBrowsable(EditorBrowsableState.Never)]
326         public AccessibilityEvents AccessibilitySuppressedEvents
327         {
328             get
329             {
330                 return new AccessibilityEvents {Owner = this};
331             }
332         }
333
334         ///////////////////////////////////////////////////////////////////
335         // ************************** Bridge *************************** //
336         ///////////////////////////////////////////////////////////////////
337
338         /// <summary>
339         /// Registers component as a source of an accessibility "default label".
340         /// The "Default label" is a text that could be read by screen-reader immediately
341         /// after the navigation context has changed (window activates, popup shows up, tab changes)
342         /// and before first UI element is highlighted.
343         /// </summary>
344         [EditorBrowsable(EditorBrowsableState.Never)]
345         public void RegisterDefaultLabel()
346         {
347             Interop.ControlDevel.DaliAccessibilityBridgeRegisterDefaultLabel(SwigCPtr);
348             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
349         }
350
351         /// <summary>
352         /// Unregisters component that has been registered previously as a source of an accessibility "default label".
353         /// The "Default label" is a text that could be read by screen-reader immediately
354         /// after the navigation context has changed (window activates, popup shows up, tab changes)
355         /// and before first UI element is highlighted.
356         /// </summary>
357         [EditorBrowsable(EditorBrowsableState.Never)]
358         public void UnregisterDefaultLabel()
359         {
360             Interop.ControlDevel.DaliAccessibilityBridgeUnregisterDefaultLabel(SwigCPtr);
361             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
362         }
363
364         [EditorBrowsable(EditorBrowsableState.Never)]
365         protected override void Dispose(bool disposing)
366         {
367             if (disposed)
368             {
369                 return;
370             }
371
372             internalName = null;
373
374             if (disposing == false)
375             {
376                 if (IsNativeHandleInvalid() || SwigCMemOwn == false)
377                 {
378                     // at this case, implicit nor explicit dispose is not required. No native object is made.
379                     disposed = true;
380                     return;
381                 }
382             }
383
384             if (disposing)
385             {
386                 Unparent();
387             }
388
389             base.Dispose(disposing);
390         }
391
392         [EditorBrowsable(EditorBrowsableState.Never)]
393         protected static readonly string AccessibilityActivateAction = "activate";
394         [EditorBrowsable(EditorBrowsableState.Never)]
395         protected static readonly string AccessibilityReadingSkippedAction = "ReadingSkipped";
396         [EditorBrowsable(EditorBrowsableState.Never)]
397         protected static readonly string AccessibilityReadingCancelledAction = "ReadingCancelled";
398         [EditorBrowsable(EditorBrowsableState.Never)]
399         protected static readonly string AccessibilityReadingStoppedAction = "ReadingStopped";
400         [EditorBrowsable(EditorBrowsableState.Never)]
401         protected static readonly string AccessibilityReadingPausedAction = "ReadingPaused";
402         [EditorBrowsable(EditorBrowsableState.Never)]
403         protected static readonly string AccessibilityReadingResumedAction = "ReadingResumed";
404
405         [EditorBrowsable(EditorBrowsableState.Never)]
406         private static readonly string[] AccessibilityActions = {
407             AccessibilityActivateAction,
408             AccessibilityReadingSkippedAction,
409             AccessibilityReadingCancelledAction,
410             AccessibilityReadingStoppedAction,
411             AccessibilityReadingPausedAction,
412             AccessibilityReadingResumedAction,
413         };
414
415         [EditorBrowsable(EditorBrowsableState.Never)]
416         protected virtual string AccessibilityGetName()
417         {
418             return "";
419         }
420
421         [EditorBrowsable(EditorBrowsableState.Never)]
422         protected virtual string AccessibilityGetDescription()
423         {
424             return "";
425         }
426
427         [EditorBrowsable(EditorBrowsableState.Never)]
428         protected virtual bool AccessibilityDoAction(string name)
429         {
430             if (name == AccessibilityActivateAction)
431             {
432                 if (ActivateSignal?.Empty() == false)
433                 {
434                     ActivateSignal?.Emit();
435                     return true;
436                 }
437                 else
438                 {
439                     return OnAccessibilityActivated();
440                 }
441             }
442             else if (name == AccessibilityReadingSkippedAction)
443             {
444                 if (ReadingSkippedSignal?.Empty() == false)
445                 {
446                     ReadingSkippedSignal?.Emit();
447                     return true;
448                 }
449                 else
450                 {
451                     return OnAccessibilityReadingSkipped();
452                 }
453             }
454             else if (name == AccessibilityReadingCancelledAction)
455             {
456                 if (ReadingCancelledSignal?.Empty() == false)
457                 {
458                     ReadingCancelledSignal?.Emit();
459                     return true;
460                 }
461                 else
462                 {
463                     return OnAccessibilityReadingCancelled();
464                 }
465             }
466             else if (name == AccessibilityReadingStoppedAction)
467             {
468                 if (ReadingStoppedSignal?.Empty() == false)
469                 {
470                     ReadingStoppedSignal?.Emit();
471                     return true;
472                 }
473                 else
474                 {
475                     return OnAccessibilityReadingStopped();
476                 }
477             }
478             else if (name == AccessibilityReadingPausedAction)
479             {
480                 if (ReadingPausedSignal?.Empty() == false)
481                 {
482                     ReadingPausedSignal?.Emit();
483                     return true;
484                 }
485                 else
486                 {
487                     return OnAccessibilityReadingPaused();
488                 }
489             }
490             else if (name == AccessibilityReadingResumedAction)
491             {
492                 if (ReadingResumedSignal?.Empty() == false)
493                 {
494                     ReadingResumedSignal?.Emit();
495                     return true;
496                 }
497                 else
498                 {
499                     return OnAccessibilityReadingResumed();
500                 }
501             }
502             else
503             {
504                 return false;
505             }
506         }
507
508         [EditorBrowsable(EditorBrowsableState.Never)]
509         protected virtual AccessibilityStates AccessibilityCalculateStates()
510         {
511             var states = AccessibilityInitialStates;
512
513             states[AccessibilityState.Focused] = this.State == States.Focused;
514             states[AccessibilityState.Enabled] = this.State != States.Disabled;
515             states[AccessibilityState.Sensitive] = this.Sensitive;
516
517             return states;
518         }
519
520         [EditorBrowsable(EditorBrowsableState.Never)]
521         protected virtual int AccessibilityGetActionCount()
522         {
523             return AccessibilityActions.Length;
524         }
525
526         [EditorBrowsable(EditorBrowsableState.Never)]
527         protected virtual string AccessibilityGetActionName(int index)
528         {
529             if (index >= 0 && index < AccessibilityActions.Length)
530             {
531                 return AccessibilityActions[index];
532             }
533             else
534             {
535                 return "";
536             }
537         }
538
539         [EditorBrowsable(EditorBrowsableState.Never)]
540         protected virtual bool AccessibilityIsScrollable()
541         {
542             return false;
543         }
544
545         [EditorBrowsable(EditorBrowsableState.Never)]
546         protected virtual bool AccessibilityScrollToChild(View child)
547         {
548             return false;
549         }
550
551         /// <summary>
552         /// This method is called when the control accessibility is activated.<br />
553         /// Derived classes should override this to perform custom accessibility activation.<br />
554         /// </summary>
555         /// <returns>True if this control can perform accessibility activation.</returns>
556         [EditorBrowsable(EditorBrowsableState.Never)]
557         protected virtual bool OnAccessibilityActivated()
558         {
559             return false;
560         }
561
562         /// <summary>
563         /// This method is called when reading is skipped.
564         /// </summary>
565         /// <returns>True if information was served.</returns>
566         [EditorBrowsable(EditorBrowsableState.Never)]
567         protected virtual bool OnAccessibilityReadingSkipped()
568         {
569             return false;
570         }
571
572         /// <summary>
573         /// This method is called when reading is cancelled.
574         /// </summary>
575         /// <returns>True if information was served.</returns>
576         [EditorBrowsable(EditorBrowsableState.Never)]
577         protected virtual bool OnAccessibilityReadingCancelled()
578         {
579             return false;
580         }
581
582         /// <summary>
583         /// This method is called when reading is stopped.
584         /// </summary>
585         /// <returns>True if information was served.</returns>
586         [EditorBrowsable(EditorBrowsableState.Never)]
587         protected virtual bool OnAccessibilityReadingStopped()
588         {
589             return false;
590         }
591
592         /// <summary>
593         /// This method is called when reading was paused.
594         /// </summary>
595         /// <returns>True if information was served.</returns>
596         [EditorBrowsable(EditorBrowsableState.Never)]
597         protected virtual bool OnAccessibilityReadingPaused()
598         {
599             return false;
600         }
601
602         /// <summary>
603         /// This method is called when reading is resumed.
604         /// </summary>
605         /// <returns>True if information was served.</returns>
606         [EditorBrowsable(EditorBrowsableState.Never)]
607         protected virtual bool OnAccessibilityReadingResumed()
608         {
609             return false;
610         }
611     }
612 }