Skip to content

Make the chat folder tabs and the chat list column accessible - #31098

Open
rezabakhshilaktasaraei wants to merge 6 commits into
telegramdesktop:devfrom
rezabakhshilaktasaraei:chat-filters-tabs-accessibility
Open

rezabakhshilaktasaraei wants to merge 6 commits into
telegramdesktop:devfrom
rezabakhshilaktasaraei:chat-filters-tabs-accessibility

Conversation

@rezabakhshilaktasaraei

@rezabakhshilaktasaraei rezabakhshilaktasaraei commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #31097 and builds on top of it: that PR exposes the painted sections of Ui::DiscreteSlider as accessible tabs, this one completes the chat folder tabs strip specifically. It contains no code from #31097 - it only compiles once that lands, since it overrides a virtual and subscribes to a producer introduced there. With #31097 the strips switch to the tab the arrows land on; the folder tabs keep the browse-then-commit model through the opt-out added there (the last commit here), since switching folders reloads the chat list and the strip may include locked ones - the arrows only browse and announce, Enter or Space commits.

Keyboard or screen reader activation of a locked (premium) folder now behaves like a click on it: the premium promo opens instead of switching to a folder the user cannot use. Folder tab names include the unread count via the existing lng_filter_unread_chats key and locked folders get the lng_sr_folder_locked name and description, mirroring the folders sidebar. Unread count changes fire a name-changed event so a screen reader focused on a tab picks them up, and the strip container is named with lng_filters_title instead of being announced as a bare tab control. Browsing with arrows also scrolls the strip to keep the browsed tab visible, reusing the same scrollToIndex animation the strip already uses when the active filter changes. No new lang keys.

The folder context menu (edit, mark as read, remove) was reachable with a right click only. contextMenuEvent looked up the tab under e->pos(), but a menu asked for from the keyboard comes with the center of an empty input method rect, so the lookup ran past every section and the request was dropped - the menu key did nothing. A keyboard request now takes the tab the browse position is on, or the active one when it is on none, and skips locked folders like the mouse path does. The menu is also anchored on that tab (through Ui::ContextMenuPosition from desktop-app/lib_ui#348) instead of the mouse cursor, which may sit nowhere near the strip; a mouse-invoked menu keeps opening at the cursor.

The third commit orders the chat list column in the Tab chain by position, with setVisualTabOrder. The bars above the list and the folder tabs strip are created only once they are needed, long after the list and the buttons below it, so Tab used to reach the folder tabs after the list instead of before it. It now follows the column: main menu, search, the bars, the folder tabs, the list, jump to top, the buttons at the bottom.

Tested with NVDA on Windows 10 with both PRs applied: browsing the folder tabs reads titles with unread counts and scrolls overflowing tabs into view, Enter activates the browsed folder or opens the premium box on a locked one, the menu key opens the menu of the browsed folder, and Tab walks the column top to bottom.

@rezabakhshilaktasaraei
rezabakhshilaktasaraei force-pushed the chat-filters-tabs-accessibility branch 2 times, most recently from f6a73fd to 1120827 Compare August 22, 2026 01:07
@rezabakhshilaktasaraei rezabakhshilaktasaraei changed the title Add accessible names and locked folder handling to chat filters tabs Aug 22, 2026
@john-preston

Copy link
Copy Markdown
Member

Verdict: CHANGES_REQUIRED. Keep the implementation, add the small locked-tab selection-state correction below. No target-specific ordinary mouse/keyboard regression was established. Prerequisite defects remain separately tracked in #31097 and #345; this assessment assumes they are corrected.

The strip now opts out of automatic activation: arrows browse and scroll folders, while Enter/Space commits. Locked activation opens the existing Premium prompt. Accessible names include unread counts or the locked-folder explanation, and unread changes emit child-name events. Keyboard context menus use the browsed folder and its rectangle; mouse menus retain their previous cursor position. The chat-list column opts into the existing visual Tab-order helper.

P2 — Locked folders still claim to support selection. At chat_filters_tabs_slider.cpp:495, the new override deliberately opens the Premium prompt instead of activating a locked section. However, the class adds only name/description overrides, leaving #31097's child state reporting selectable = true for every tab.

Reproduce with a non-Premium account that has a locked folder, then request SelectionItem.Select on that folder through an accessibility client. The integrated selection interface accepts the advertised state, dispatches pressAction and returns true. The painted item action reaches this new locked override. The existing Qt 5 selection backport converts true to S_OK. The client is told selection succeeded, yet the selected folder remains unchanged and a Premium prompt appears. This is a source-confirmed contract mismatch, not a claim that runtime speech was observed.

Add ChatsFiltersTabs::accessibilityChildState(int), start with the base state, and clear selectable for a locked index. Preserve focusability, browsing and the press action so users can still hear why the folder is unavailable and intentionally open subscription options. Do not mark the whole tab disabled. The existing sidebar makes the same distinction, but the selection call path is the reason for the correction.

The context menu key already picked the browsed tab, but the menu
still popped at the mouse cursor, which may sit nowhere near the tab
or outside the window altogether. The slider now sends the position to
show the menu at along with the tab index - the tab itself for a
keyboard-invoked menu, the cursor as before for a mouse-invoked one.
@rezabakhshilaktasaraei
rezabakhshilaktasaraei force-pushed the chat-filters-tabs-accessibility branch from f4bd0db to 3fccc49 Compare September 25, 2026 21:29
@rezabakhshilaktasaraei

Copy link
Copy Markdown
Contributor Author

Done: ChatsFiltersTabs::accessibilityChildState() starts from the base state and clears selectable for a locked folder, so a selection request on it is refused instead of reported as done; the folder stays focusable and browsable, keeps its name and explanation, and its press action still opens the premium prompt. Added to the first commit.

A folder chosen from the tabs in one column clears the section stack,
and the history shown then asks the chat list for the focus: the tabs
keep it, and the user goes on to the list with Tab when done.
@rezabakhshilaktasaraei
rezabakhshilaktasaraei force-pushed the chat-filters-tabs-accessibility branch from 3fccc49 to 50c83b1 Compare September 26, 2026 04:22
@rezabakhshilaktasaraei

Copy link
Copy Markdown
Contributor Author

One more change in the last commit, found while testing: in one-column layout a folder chosen from the tabs clears the section stack, and the history shown then asks the chat list for the focus (HistoryWidget::doneShow → setInnerFocus), which with a screen reader took it off the tabs into the list. Widget::setInnerFocus now keeps the focus on the tabs when they hold it; Tab goes on to the list as before.

This branch has not been deployed

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

Labels

None yet

2 participants