feat(accessibility): add screen reader support for message list

This commit is contained in:
Reza Bakhshi Laktasaraei
2026-05-15 02:24:09 +03:30
committed by John Preston
parent 7113392f9b
commit 4e4eeeefb9
5 changed files with 1687 additions and 3 deletions
+78 -2
View File
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_menu_my_groups" = "My Groups";
"lng_menu_my_channels" = "My Channels";
"lng_main_menu" = "Main menu";
"lng_chat_list" = "Chats";
"lng_filter_unread_chats#one" = "{text} ({count} unread chat)";
"lng_filter_unread_chats#other" = "{text} ({count} unread chats)";
@@ -409,6 +410,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_country_ph" = "Search";
"lng_country_none" = "Country not found";
"lng_country_select" = "Select Country";
"lng_country_column_name" = "Country name";
"lng_phone_number" = "Phone number";
"lng_code_ph" = "Code";
@@ -646,6 +648,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_effect_premium_link" = "Telegram Premium";
"lng_languages" = "Language";
"lng_languages_column_native" = "Native name";
"lng_languages_column_name" = "Language name";
"lng_languages_none" = "No languages found.";
"lng_languages_count#one" = "{count} language";
"lng_languages_count#other" = "{count} languages";
@@ -5626,8 +5630,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_search_filter_group" = "Group chats";
"lng_search_filter_channel" = "Channels";
"lng_search_sponsored_button" = "Ad ⋮";
// Screen reader strings for chat list.
"lng_sr_chat_channel" = "Channel";
"lng_sr_chat_group" = "Group";
"lng_sr_chat_bot" = "Bot";
@@ -5659,6 +5661,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sr_chat_subscribed" = "Subscribed";
"lng_sr_chat_forwarded" = "Forwarded";
"lng_sr_chat_story_reply" = "Story reply";
"lng_sr_chat_column_type" = "Type";
"lng_sr_chat_column_name" = "Name";
"lng_sr_chat_column_warning" = "Warning";
@@ -5682,9 +5685,82 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sr_chat_column_closed" = "Closed";
"lng_sr_chat_column_forward" = "Forward";
"lng_sr_chat_column_folders" = "Folders";
"lng_sr_message_list" = "Messages";
"lng_sr_from_me" = "Me";
"lng_sr_message_seen" = "Seen";
"lng_sr_message_not_seen" = "Not seen";
"lng_sr_message_played" = "Played";
"lng_sr_message_not_played" = "Not played";
"lng_sr_message_not_downloaded" = "Not downloaded";
"lng_sr_message_downloading" = "Downloading";
"lng_sr_message_reply_to" = "In reply to {name}: {text}";
"lng_sr_message_edited" = "Edited";
"lng_sr_message_via_bot" = "via @{bot}";
"lng_sr_message_reactions" = "Reactions: {list}";
"lng_sr_message_custom_emoji" = "Custom emoji {emoji}";
"lng_sr_message_column_seen" = "Seen";
"lng_sr_message_column_sender" = "Sender";
"lng_sr_message_column_via_bot" = "Via bot";
"lng_sr_message_column_reply" = "Reply";
"lng_sr_message_column_forward" = "Forward";
"lng_sr_message_column_media_type" = "Media type";
"lng_sr_message_column_download" = "Download";
"lng_sr_message_column_played" = "Played";
"lng_sr_message_column_artist" = "Artist";
"lng_sr_message_column_title" = "Title";
"lng_sr_message_column_filename" = "Filename";
"lng_sr_message_column_duration" = "Duration";
"lng_sr_message_column_dimensions" = "Dimensions";
"lng_sr_message_column_file_size" = "File size";
"lng_sr_message_column_message" = "Message";
"lng_sr_message_column_delivery" = "Delivery";
"lng_sr_message_column_edited" = "Edited";
"lng_sr_message_column_time" = "Time";
"lng_sr_message_column_reactions" = "Reactions";
"lng_sr_message_column_views" = "Views";
"lng_sr_message_column_signature" = "Signature";
"lng_sr_message_column_pinned" = "Pinned";
"lng_sr_message_column_web_site" = "Website";
"lng_sr_message_column_web_title" = "Link title";
"lng_sr_message_column_web_description" = "Link description";
"lng_sr_message_column_poll_question" = "Poll question";
"lng_sr_message_column_poll_options" = "Poll options";
"lng_sr_message_column_poll_status" = "Poll status";
"lng_sr_message_column_contact_name" = "Contact name";
"lng_sr_message_column_contact_phone" = "Contact phone";
"lng_sr_message_column_location" = "Location";
"lng_sr_message_column_sticker_emoji" = "Sticker emoji";
"lng_sr_message_column_game_title" = "Game title";
"lng_sr_message_column_game_description" = "Game description";
"lng_sr_message_column_invoice_title" = "Invoice title";
"lng_sr_message_column_invoice_amount" = "Invoice amount";
"lng_sr_message_column_spoiler" = "Spoiler";
"lng_sr_message_column_dice" = "Dice";
"lng_sr_message_column_giveaway" = "Giveaway";
"lng_sr_message_column_gift" = "Gift";
"lng_sr_message_column_todo_title" = "Todo title";
"lng_sr_message_column_todo_items" = "Todo items";
"lng_sr_message_column_factcheck" = "Fact check";
"lng_sr_message_column_forward_date" = "Forward date";
"lng_sr_message_column_forward_author" = "Forward author";
"lng_sr_message_column_paid_reactions" = "Paid reactions";
"lng_sr_message_pinned" = "Pinned";
"lng_sr_message_spoiler" = "Hidden media";
"lng_sr_message_poll_votes#one" = "{count} vote";
"lng_sr_message_poll_votes#other" = "{count} votes";
"lng_sr_message_todo_completed" = "Completed";
"lng_sr_message_todo_not_completed" = "Not completed";
"lng_sr_message_gift_premium#one" = "Premium gift, {count} day";
"lng_sr_message_gift_premium#other" = "Premium gift, {count} days";
"lng_sr_message_gift_credits#one" = "Star gift, {count} star";
"lng_sr_message_gift_credits#other" = "Star gift, {count} stars";
"lng_sr_message_paid_content" = "Paid content";
"lng_sr_message_live_location" = "Live location";
"lng_sr_message_invoice_paid" = "Paid";
"lng_sr_message_invoice_unpaid" = "Unpaid";
"lng_media_save_progress" = "{ready} of {total} {mb}";
"lng_mediaview_save_as" = "Save As...";
@@ -5863,6 +5863,8 @@ void InnerWidget::announceSelectedFocus() {
}
Ui::AccessibilityState InnerWidget::accessibilityState() const {
// Mark the List container as non-focusable so screen readers
// drill down to the focusable child items (ListItem rows).
return {};
}
File diff suppressed because it is too large Load Diff
@@ -111,6 +111,22 @@ public:
not_null<History*> history);
~HistoryInner();
// Accessibility.
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::List;
}
int accessibilityChildCount() const override;
QString accessibilityChildName(int index) const override;
QAccessible::State accessibilityChildState(int index) const override;
QAccessible::Role accessibilityChildRole() const override;
QRect accessibilityChildRect(int index) const override;
int accessibilityChildColumnCount(int row) const override;
QAccessible::Role accessibilityChildSubItemRole() const override;
QString accessibilityChildSubItemName(
int row, int column) const override;
QString accessibilityChildSubItemValue(
int row, int column) const override;
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] not_null<Ui::ChatTheme*> theme() const {
return _theme.get();
@@ -251,6 +267,7 @@ public:
-> std::unique_ptr<HistoryMainElementDelegateMixin>;
protected:
void focusInEvent(QFocusEvent *e) override;
bool focusNextPrevChild(bool next) override;
bool eventHook(QEvent *e) override; // calls touchEvent when necessary
@@ -267,6 +284,15 @@ protected:
void contextMenuEvent(QContextMenuEvent *e) override;
private:
[[nodiscard]] std::vector<Element*> accessibleElements() const;
[[nodiscard]] int accessibilityUnreadBarIndex() const;
void toggleMessageSelection();
void playPauseFocusedMedia();
[[nodiscard]] QString computeSubItemValue(
int row, int column) const;
[[nodiscard]] const QVector<int> &computeActiveColumns(
int row) const;
void onTouchSelect();
void onTouchScrollTimer();
void markReadMetricsStale();
@@ -484,6 +510,11 @@ private:
// Does any of the shown histories has this flag set.
bool hasPendingResizedItems() const;
int _accessibilityFocusedIndex = -1;
HistoryItem *_accessibilityFocusedItem = nullptr;
mutable int _activeColumnsRow = -1;
mutable QVector<int> _activeColumns;
const not_null<HistoryWidget*> _widget;
const not_null<Ui::ScrollArea*> _scroll;
const not_null<Window::SessionController*> _controller;
@@ -57,6 +57,10 @@ public:
const MTPSendMessageAction &action);
void clear(not_null<UserData*> from);
[[nodiscard]] QString actionText() const {
return _sendActionString;
}
private:
const not_null<History*> _history;
const MsgId _rootId = 0;