community
community copied to clipboard
Write an abstraction for `kivy.core.accessibility`, so accessibility tools have a proper API to work with
The new kivy.core.accessibility
provider will expose some classes, to better accommodate accessibility tools.
kivy.core.accessibility.AccessibilityRoleType:
An Enum
, that defines the role of the linked widget (static text, text box, container ...), so the accessibility tools can know how to act with a specific widget.
As an example:
-
AccessibilityRoleType.STATIC_TEXT
-
AccessibilityRoleType.CONTAINER
kivy.core.accessibility.AccessibilityEventType:
An Enum
, which defines the event type that has to be handled by the accessibility provider.
As an example:
-
AccessibilityEventType.NEW
-
AccessibilityEventType.TEXT_CHANGED
kivy.core.accessibility.AccessibilityInterfaceBase:
A base class, that every widget (or set of widgets) can subclass to better accommodate its needs. This serves as an interface between the widget and the accessibility provider (and vice-versa).
The AccessibilityInterfaceBase
can bind to widget properties and send "repacked" events to the accessibility provider, by calling the .update(event)
method of the AccessibilityBase
object.
The AccessibilityBase
can reverse-walk the widget tree, to get the position of the widget linked to the AccessibilityInterfaceBase
that emitted the event. (when an AccessibilityEventType.NEW
is received).
AccessibilityInterfaceBase
will also have widget-specialized methods to send "faked" events to the underlying widget. (pressed, released, text edited, ...)
class AccessibilityInterfaceBase:
def __init__(self, widget: Widget):
self.widget = widget
kivy.core.accessibility.AccessibilityEvent:
This class will serve as a container for the event information.
class AccessibilityEvent:
def __init__(self, event_type: AccessibilityEventType, interface: AccessibilityInterface):
self.event_type = event_type
self.interface = interface
kivy.core.accessibility.AccessibilityBase:
The base implementation for every accessibility core provider implementation (as it happens for other core providers in the Kivy project).
As accessibility is pretty tied to the whole life-cycle of the app, the accessibility provider will be chosen during the App
initialization, and before the Window creation (so the Window can signal to the accessibility provider that is ready).
kivy.core.accessibility.AccessibilityBase
methods:
def install(self, window: Window):
"""
Called only once when `kivy.core.Window` is ready but not yet shown, so the
accessibility tool can attach flawlessly.
"""
def update(self, event: AccessibilityEvent):
"""
Called every time a widget (or another entity) needs to let know the accessibility provider
that something has changed, created or deleted.
"""
Some questions:
- Would keyboard focus be handled by emitting an
EventType
? - Accessible widgets sometimes need to expose children that aren't necessary part of the widget tree, examples include the dropdown menu of a combo box or each line of text in a multi-line text area. Would we modify existing widgets to create these children, or do we come up with a mechanism to create fake, accessible-only widgets? Kivy's dropdown widget should be fine, I haven't looked into editable text though.
- I've heard that multi-window apps might soon come to Kivy: what can we do to make it simpler to identify which window a widget is a descendant of?
Would keyboard focus be handled by emitting an EventType?
Yes.
... each line of text in a multi-line text area ...
Since we're rewriting the TextInput
widget almost from scratch, can you better explain the kind of interaction needed between accessibility tools and the TextInput
widget? I assume you're talking about paragraph separation?
I've heard that multi-window apps might soon come to Kivy: what can we do to make it simpler to identify which window a widget is a descendant of?
We did not added it yet to 3.0.0
schedule, and it will not likely land unless we find a hero. (We need a realistic release date, and 3.0.0
task list already looks pretty big). So, maybe 3.1.0
? BTW I guess we can easily get this info by reverse walking the widget tree, in case we find a hero 😀
can you better explain the kind of interaction needed between accessibility tools and the TextInput widget? I assume you're talking about paragraph separation?
I will mostly talk in the context of AccessKit (which derived its data schema from Chromium):
We have a role called InlineTextBox
which is a container for a piece of text spanning at most one line. Nodes of this kind obviously hold their text content as value, but they also have to have:
- a bounding box,
- the length of each character in bytes,
- the x coordinate of each character,
- the width of each character,
- the length of each word (in characters).
So, paragraphs don't matter here: if a line is too long so that it is split into two, then two nodes will be needed.
Since text decoration and other text formatting attributes apply to an entire node, multiple inline text boxes will be needed if a line contains regular and bold text for instance.
Also worth noting: we will need to receive events when the text selection changes.
To have an idea of the kind of tree updates we have to produce, you can have a look at this test suite.