Skip to content

chatdlg: replace QTextBrowser with QListView for screen reader accessibility#3750

Open
mcfnord wants to merge 1 commit into
jamulussoftware:mainfrom
mcfnord:chatdlg-voiceover-fix
Open

chatdlg: replace QTextBrowser with QListView for screen reader accessibility#3750
mcfnord wants to merge 1 commit into
jamulussoftware:mainfrom
mcfnord:chatdlg-voiceover-fix

Conversation

@mcfnord

@mcfnord mcfnord commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Replaces QTextBrowser with QListView + QStyledItemDelegate in the chat dialog so that each message is a discrete item in the OS accessibility tree.

With QTextBrowser, VoiceOver (and other screen readers that consume QAccessible) sees the entire chat history as a single text block. With QListView, each appended message becomes a QAccessible::ListItem, letting the user arrow-key through individual messages.

ChatDelegate renders each item as HTML via QTextDocument, preserving the existing rich-text formatting. Link clicks are detected in eventFilter by installing a filter on the viewport and using QTextDocument::documentLayout()->anchorAt() to hit-test the click position. Hovering over a link shows a pointing-hand cursor. Clicking routes through the existing OnAnchorClicked confirmation dialog. Right-click or Ctrl+C copies the selected message's plain text to the clipboard.

macOS VoiceOver fixes (addresses the silence reported in testing)

Two issues prevented VoiceOver from speaking on macOS:

  1. Qt::AccessibleTextRoleQt::DisplayRole stores raw HTML. VoiceOver reads this role verbatim and sees angle-bracket markup, producing silence or garbled output. Fix: QTextDocument::toPlainText() strips the tags, and the result is stored as Qt::AccessibleTextRole on each item — the role VoiceOver actually queries for the item's accessible name.

  2. Correct accessibility events — Replaced the previous QAccessibleValueChangeEvent (which fired before HTML processing and before the row existed) with QAccessibleTableModelChangeEvent::RowsInserted targeting the exact new row, followed by QAccessibleValueChangeEvent carrying the plain text. This is the correct signal sequence for live-region-style auto-announcement of incoming messages on macOS.

Fixes an issue?

Addresses #3613.

Does this change need documentation?

No documentation changes needed.

Status

Builds and runs. VoiceOver on macOS should now speak individual messages when navigating the list and announce incoming messages automatically. Please test with VoiceOver — specifically: does arrowing to a message read it aloud, and does an incoming message get announced without user action?

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.

…ibility

Replaces QTextBrowser with QListView + QStyledItemDelegate in the chat
dialog so that each message is a discrete item in the OS accessibility
tree. With QTextBrowser, VoiceOver (and other screen readers) sees the
entire chat history as one opaque text block. With QListView, each
appended message becomes a QAccessible::ListItem, letting the user
arrow-key through individual messages.

ChatDelegate renders each item as HTML via QTextDocument, preserving
existing rich-text formatting. Link clicks are detected in eventFilter
via QTextDocument::documentLayout()->anchorAt(). Hovering over a link
shows a pointing-hand cursor. Clicking routes through the existing
OnAnchorClicked confirmation dialog. Right-click or Ctrl+C copies the
selected message's plain text to the clipboard.

Accessibility fixes for macOS VoiceOver:
- Qt::AccessibleTextRole is set to the HTML-stripped plain text on each
  item. VoiceOver reads this role when narrating a list item; without it,
  it reads the raw HTML markup verbatim and produces silence or garbled
  output.
- QAccessibleTableModelChangeEvent::RowsInserted fires after each append
  so the AT stack knows a new row exists, followed by
  QAccessibleValueChangeEvent carrying the plain text — the correct signal
  sequence for live-region-style auto-announcement of incoming messages.

Fixes jamulussoftware#3613.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mcfnord

mcfnord commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Someone please try this on a Mac using VoiceOver.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant