[Lazarus] LCL and Lazarus Accessibility - restarting the discussion
marco at marcozehe.de
Fri Mar 5 11:07:45 CET 2021
My name is Marco, I am new to this list and new to Free Pascal and Lazarus. I have previously been involved with Delphi. I am, among other things, responsible for the accessibility implementation in TVirtualStringTree, which I made in 2007.
I am interested in restarting an effort to make the LCL components and Lazarus itself more accessible to screen reader users. There are a lot of things that work, but unfortunately a lot that don't yet, and some assumptions about current implementations/values aren't up to what accessibility actually requires. An example: The way AccessibleDescription is mapped on different platforms, is creative, but not always correct. :-)
I wanted to get started with this issue: https://bugs.freepascal.org/view.php?id=24135, TBitBtn not exposing its text as the Accessible Name. This is the primary information NVDA and other screen readers on Windows use to speak what a button does. In LCL, there is currently no equivalent, AccDescription and AccValue are being used interchangeably for some of the derived classes, but that is actually not so correct. For most standard controls, the Caption property maps, which is correct.
For TBitBtn, the attempt to set the window text failed because of text duplication. Question: Have you tried to set the text as the window title in the parameters in TWin32WSBitBtn.CreateHandle instead? I am blind myself, so cannot say if that would have any negative visual side effects, or if this is even supported.
If this does not work, which would be surprising since in Delphi, TBitBtn's caption does get exposed to screen readers by default, the only other option we have is to create a dynamic Accessibility Annotation through OLE COM. A simple example is here on the Microsoft Docs page: https://docs.microsoft.com/en-us/windows/win32/winauto/using-direct-annotation.
Only in our case, we would set the AccName property to the caption upon creation, or the text each time TWin32WSBitBtn.SetText is called. The way you draw the text is not being picked up by screen readers or the Microsoft accessibility proxy that gets created automatically. But with the above method, we could override this in a simple enough fashion without having to implement the whole IAccessible interface, or - God forbid - UI Automation, Microsoft's newer and much more complicated accessibility APIs.
Second: I then looked at this other issue about role inconsistencies: https://bugs.freepascal.org/view.php?id=26910. I currently do not yet understand how the LCLAccessibilityRoles are mapped to Win32 equivalents in the first place. I see some mappings for COCOA, QT4 and QT5, and others, but not Win32. For Win32, I see only empty class procedure definitions for the various properties, so I must assume all others are so far derived from the CreateWindow calls with the proper class names. What I do not see, as stated in that original report, is that everything is an ANIMATION role. So something must have changed already but not documented in that issue.
In any case, the above example by Microsoft provides a method to annotate custom drawn components to become accessible, too. There should be a default mapping for roles in place for Win32, the caption should be mapped to the name in most cases, and a hint, if present, or a tooltip, to AccDescription. For edits, as discussed somewhere on a forum, which have an associated TLabel or TStaticText, the AccName would have to be that TLabel's or TStaticText's caption. The association would have to be through TLabel's or TStaticText's FocusControl property, don't know if TEdit and similar support a reverse lookup yet, e. g. if they're being focus-controlled by some other component. But that would be the way forward.
For some components like list views or tree views, some of the more advanced mappings described here might have to take place: https://docs.microsoft.com/en-us/windows/win32/winauto/value-map-annotation.
In any case, this requires the use of COM. Don't know if that is standard in the LCL yet. The trigger is usually that an accessibility client sends a WM_GETOBJECT message to a control to get its accessible information. Some, like TBitBtn, already expose an accessible object, some, like the AutoComplete list in the Components List of the Lazarus IDE, do not. They appear as a generic window with plain text.
I would love to work on this, but would definitely need some guidance. For one: How are roles mapped on Win32, if at all yet?
And second: Is there any sample code yet that I could use to get up and running with COM quickly in the LCL?
Thank you for reading, and sorry for the long read!
More information about the lazarus