Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write an abstraction for kivy.core.accessibility, so accessibility tools have a proper API to work with #8599

Open
Tracked by #8596
misl6 opened this issue Feb 1, 2024 · 3 comments

Comments

@misl6
Copy link
Member

misl6 commented Feb 1, 2024

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.
     """
@DataTriny
Copy link
Contributor

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?

@misl6
Copy link
Member Author

misl6 commented Feb 6, 2024

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 😀

@DataTriny
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

No branches or pull requests

2 participants