From 215303cc693f8a8d64ac29213ef6b837cb019fe7 Mon Sep 17 00:00:00 2001 From: mukthar777 Date: Mon, 9 Feb 2026 09:02:55 +0530 Subject: [PATCH] accessibility: implement button labeling across info sections --- Telegram/Resources/langs/lang.strings | 4 +++ Telegram/SourceFiles/info/info_top_bar.cpp | 31 +++++++++++++++++ .../SourceFiles/info/info_wrap_widget.cpp | 5 +++ .../info/profile/info_profile_actions.cpp | 31 +++++++++++++++++ .../info/profile/info_profile_badge.cpp | 20 +++++++++++ .../info/profile/info_profile_members.cpp | 21 ++++++++---- .../profile/info_profile_music_button.cpp | 1 + .../info/profile/info_profile_top_bar.cpp | 33 +++++++++++++++++-- .../SourceFiles/ui/controls/stars_rating.cpp | 1 + 9 files changed, 138 insertions(+), 9 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2a1fe55aad..ee75d5e9c9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -7608,5 +7608,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sr_cancel_search" = "Cancel search"; "lng_sr_clear_search" = "Clear search"; "lng_sr_scroll_to_top" = "Scroll to top"; +"lng_sr_verified_badge" = "Verified"; +"lng_sr_bot_verified_badge" = "Verified Bot"; +"lng_sr_profile_menu" = "Profile menu"; +"lng_sr_close_panel" = "Close panel"; // Keys finished diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 3ebf9a613b..f89dceea3c 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -134,6 +134,7 @@ void TopBar::enableBackButton() { st::infoTopBarScale); _back->setDuration(st::infoTopBarDuration); _back->toggle(!selectionMode(), anim::type::instant); + _back->entity()->setAccessibleName(tr::lng_go_back(tr::now)); _back->entity()->clicks( ) | rpl::to_empty | rpl::start_to_stream(_backClicks, _back->lifetime()); @@ -265,6 +266,7 @@ void TopBar::createSearchView( auto button = base::make_unique_q(this, _st.search); auto search = button.get(); + search->setAccessibleName(tr::lng_dlg_filter(tr::now)); search->addClickHandler([=] { showSearch(); }); auto searchWrap = pushButton(std::move(button)); registerToggleControlCallback(searchWrap, [=] { @@ -276,10 +278,21 @@ void TopBar::createSearchView( auto cancel = Ui::CreateChild( wrap, _st.searchRow.fieldCancel); + cancel->setAccessibleName(tr::lng_sr_cancel_search(tr::now)); registerToggleControlCallback(cancel, [=] { return !selectionMode() && searchMode(); }); + const auto updateCancelName = [=] { + const auto hasText = !field->getLastText().isEmpty(); + cancel->setAccessibleName(hasText + ? tr::lng_sr_clear_search(tr::now) + : tr::lng_sr_cancel_search(tr::now)); + }; + field->changes( + ) | rpl::on_next(updateCancelName, cancel->lifetime()); + updateCancelName(); + cancel->addClickHandler([=] { if (!field->getLastText().isEmpty()) { field->setText(QString()); @@ -637,6 +650,12 @@ void TopBar::updateSelectionState() { (_allStoriesInProfile ? &_st.storiesArchive.iconOver : &_st.storiesSave.iconOver)); + _toggleStoryInProfile->entity()->setAccessibleName(_allStoriesInProfile + ? tr::lng_mediaview_archive_story(tr::now) + : tr::lng_mediaview_save_to_profile(tr::now)); + _toggleStoryPin->entity()->setAccessibleName(_canUnpinStories + ? tr::lng_context_unpin_from_top(tr::now) + : tr::lng_context_pin_to_top(tr::now)); _toggleStoryPin->toggle(_canToggleStoryPin, anim::type::instant); _toggleStoryPin->entity()->setIconOverride( _canUnpinStories ? &_st.storiesUnpin.icon : nullptr, @@ -663,6 +682,8 @@ void TopBar::createSelectionControls() { object_ptr(this, _st.mediaCancel), st::infoTopBarScale)); _cancelSelection->setDuration(st::infoTopBarDuration); + _cancelSelection->entity()->setAccessibleName( + tr::lng_context_clear_selection(tr::now)); _cancelSelection->entity()->clicks( ) | rpl::map_to( SelectionAction::Clear @@ -693,6 +714,8 @@ void TopBar::createSelectionControls() { _forward.data(), [this] { return selectionMode() && _canForward; }); _forward->setDuration(st::infoTopBarDuration); + _forward->entity()->setAccessibleName( + tr::lng_context_forward_selected(tr::now)); _forward->entity()->clicks( ) | rpl::map_to( SelectionAction::Forward @@ -709,6 +732,8 @@ void TopBar::createSelectionControls() { _delete.data(), [this] { return selectionMode() && _canDelete; }); _delete->setDuration(st::infoTopBarDuration); + _delete->entity()->setAccessibleName( + tr::lng_context_delete_selected(tr::now)); _delete->entity()->clicks( ) | rpl::map_to( SelectionAction::Delete @@ -728,6 +753,9 @@ void TopBar::createSelectionControls() { _toggleStoryInProfile.data(), [this] { return selectionMode() && _canToggleStoryPin; }); _toggleStoryInProfile->setDuration(st::infoTopBarDuration); + _toggleStoryInProfile->entity()->setAccessibleName(_allStoriesInProfile + ? tr::lng_mediaview_archive_story(tr::now) + : tr::lng_mediaview_save_to_profile(tr::now)); _toggleStoryInProfile->entity()->clicks( ) | rpl::map([=] { return _allStoriesInProfile @@ -754,6 +782,9 @@ void TopBar::createSelectionControls() { _toggleStoryPin.data(), [this] { return selectionMode() && _canToggleStoryPin; }); _toggleStoryPin->setDuration(st::infoTopBarDuration); + _toggleStoryPin->entity()->setAccessibleName(_canUnpinStories + ? tr::lng_context_unpin_from_top(tr::now) + : tr::lng_context_pin_to_top(tr::now)); _toggleStoryPin->entity()->clicks( ) | rpl::map_to( SelectionAction::ToggleStoryPin diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 99730a1f00..4cc5413e7c 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -378,6 +378,7 @@ void WrapWidget::createTopBar() { base::make_unique_q( _topBar, st::infoTopBarClose)); + close->setAccessibleName(tr::lng_sr_close_panel(tr::now)); close->addClickHandler([this] { _controller->parentController()->closeThirdSection(); }); @@ -392,6 +393,7 @@ void WrapWidget::createTopBar() { base::make_unique_q( _topBar, st::infoLayerTopBarClose)); + close->setAccessibleName(tr::lng_sr_close_panel(tr::now)); close->addClickHandler([this] { checkBeforeClose([=] { _controller->parentController()->hideSpecialLayer(); @@ -432,6 +434,7 @@ void WrapWidget::setupTopBarMenuToggle() { : st::infoTopBarSearch; const auto button = _topBar->addButton( base::make_unique_q(_topBar, st)); + button->setAccessibleName(tr::lng_dlg_filter(tr::now)); button->addClickHandler([=] { _controller->showSettings(::Settings::Search::Id()); }); @@ -445,6 +448,7 @@ void WrapWidget::setupTopBarMenuToggle() { : st::infoTopBarQr; const auto button = _topBar->addButton( base::make_unique_q(_topBar, st)); + button->setAccessibleName(tr::lng_group_invite_context_qr(tr::now)); button->addClickHandler([show, self] { Ui::DefaultShowFillPeerQrBoxCallback(show, self); }); @@ -531,6 +535,7 @@ void WrapWidget::addTopBarMenuButton() { (wrap() == Wrap::Layer ? st::infoLayerTopBarMenu : st::infoTopBarMenu)))); + _topBarMenuToggle->setAccessibleName(tr::lng_sr_profile_menu(tr::now)); _topBarMenuToggle->addClickHandler([this] { showTopBarMenu(false); }); diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index 1aac712d38..8ba8b99835 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -578,6 +578,33 @@ base::options::toggle ShowChannelJoinedBelowAbout({ openedWrap->resize(width, std::max(h1, size.height()) - added); }, openedWrap->lifetime()); + rpl::combine( + state->opened.value(), + state->opensIn.value(), + state->expanded.value(), + dayHoursTextValue(state->day.value()) + ) | rpl::on_next([=]( + bool opened, + TimeId opensIn, + bool expanded, + const QString &timing) { + const auto status = (opened + ? tr::lng_info_work_open + : tr::lng_info_work_closed)(tr::now); + const auto when = (!opensIn || expanded) + ? timing + : (opensIn >= 86400) + ? tr::lng_info_hours_opens_in_days(tr::now, lt_count, opensIn / 86400) + : (opensIn >= 3600) + ? tr::lng_info_hours_opens_in_hours(tr::now, lt_count, opensIn / 3600) + : tr::lng_info_hours_opens_in_minutes( + tr::now, + lt_count, + std::max(opensIn / 60, 1)); + button->setAccessibleName( + tr::lng_info_hours_label(tr::now) + ": " + status + ", " + when); + }, inner->lifetime()); + const auto labelWrap = inner->add(object_ptr(inner)); const auto label = Ui::CreateChild( labelWrap, @@ -1653,6 +1680,7 @@ object_ptr DetailsFiller::setupInfo() { const auto qrButton = Ui::CreateChild( usernameLine.text->parentWidget(), st::infoProfileLabeledButtonQr); + qrButton->setAccessibleName(tr::lng_group_invite_context_qr(tr::now)); UsernamesValue(_peer) | rpl::on_next([=](const auto &u) { qrButton->setVisible(!u.empty()); }, qrButton->lifetime()); @@ -1733,6 +1761,7 @@ object_ptr DetailsFiller::setupInfo() { const auto qr = Ui::CreateChild( linkLine.text->parentWidget(), st::infoProfileLabeledButtonQr); + qr->setAccessibleName(tr::lng_group_invite_context_qr(tr::now)); UsernamesValue(_peer) | rpl::on_next([=](const auto &u) { qr->setVisible(!u.empty()); }, qr->lifetime()); @@ -2045,6 +2074,7 @@ object_ptr DetailsFiller::setupPersonalChannel( button->lower(); inner->lifetime().make_state>( button); + button->setAccessibleName(tr::lng_profile_view_channel(tr::now)); } inner->setAttribute(Qt::WA_TransparentForMouseEvents); Ui::AddSkip(messageChannelWrap->entity()); @@ -2868,6 +2898,7 @@ void SetupAddChannelMember( auto add = Ui::CreateChild( parent.get(), st::infoMembersAddMember); + add->setAccessibleName(tr::lng_channel_add_members(tr::now)); add->showOn(CanAddMemberValue(channel)); add->addClickHandler([=] { Window::PeerMenuAddChannelMembers(navigation, channel); diff --git a/Telegram/SourceFiles/info/profile/info_profile_badge.cpp b/Telegram/SourceFiles/info/profile/info_profile_badge.cpp index 7a997f1543..496a0e1357 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_badge.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_badge.cpp @@ -81,6 +81,26 @@ void Badge::setContent(Content content) { return; } _view.create(_parent); + _view->setAccessibleName([&] { + switch (_content.badge) { + case BadgeType::Verified: + return tr::lng_sr_verified_badge(tr::now); + case BadgeType::BotVerified: + return tr::lng_sr_bot_verified_badge(tr::now); + case BadgeType::Premium: + if (_content.emojiStatusId) { + return tr::lng_profile_bot_emoji_status_access(tr::now); + } + return tr::lng_premium_summary_title(tr::now); + case BadgeType::Scam: + return tr::lng_scam_badge(tr::now); + case BadgeType::Fake: + return tr::lng_fake_badge(tr::now); + case BadgeType::Direct: + return tr::lng_direct_badge(tr::now); + } + Unexpected("badge type"); + }()); _view->show(); switch (_content.badge) { case BadgeType::Verified: diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp index 25900c1ec4..1f3cc86cc7 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp @@ -131,6 +131,7 @@ void Members::setupHeader() { _openMembers = Ui::CreateChild( parent, rpl::single(QString())); + // _openMembers->setAccessibleName(tr::lng_manage_peer_members(tr::now)); object_ptr( parent, @@ -142,12 +143,14 @@ void Members::setupHeader() { _addMember = Ui::CreateChild( _openMembers, st::infoMembersAddMember); + _addMember->setAccessibleName(tr::lng_channel_add_members(tr::now)); //_searchField = _controller->searchFieldController()->createField( // parent, // st::infoMembersSearchField); _search = Ui::CreateChild( _openMembers, st::infoMembersSearch); + _search->setAccessibleName(tr::lng_participant_filter(tr::now)); //_cancelSearch = Ui::CreateChild( // parent, // st::infoMembersCancelSearch); @@ -169,15 +172,19 @@ object_ptr Members::setupTitle() { auto visible = _peer->isMegagroup() ? CanViewParticipantsValue(_peer->asMegagroup()) : rpl::single(true); + auto text = rpl::conditional( + std::move(visible), + tr::lng_chat_status_members( + lt_count_decimal, + MembersCountValue(_peer) | tr::to_count(), + tr::upper), + tr::lng_channel_admins(tr::upper)); + rpl::duplicate(text) | rpl::on_next([=](const QString &v) { + _openMembers->setAccessibleName(v); + }, _openMembers->lifetime()); auto result = object_ptr( _titleWrap, - rpl::conditional( - std::move(visible), - tr::lng_chat_status_members( - lt_count_decimal, - MembersCountValue(_peer) | tr::to_count(), - tr::upper), - tr::lng_channel_admins(tr::upper)), + std::move(text), st::infoBlockHeaderLabel); result->setAttribute(Qt::WA_TransparentForMouseEvents); return result; diff --git a/Telegram/SourceFiles/info/profile/info_profile_music_button.cpp b/Telegram/SourceFiles/info/profile/info_profile_music_button.cpp index 93ec22e280..ff6d94f5df 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_music_button.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_music_button.cpp @@ -41,6 +41,7 @@ void MusicButton::updateData(MusicButtonData data) { _title.setText( st::defaultTextStyle, result.text.mid(performerLength, result.text.size())); + setAccessibleName(result.text); update(); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp b/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp index 2f902339ee..c681ffa5c9 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp @@ -116,6 +116,7 @@ public: : Ui::AbstractButton(parent) , _hasStories(std::move(hasStories)) { installEventFilter(this); + setAccessibleName(tr::lng_mediaview_profile_photo(tr::now)); } QString tooltipText() const override { @@ -701,6 +702,7 @@ void TopBar::setupActions(not_null controller) { moreButton->setClickedCallback([=] { showTopBarMenu(controller, false); }); + moreButton->setAccessibleName(tr::lng_profile_action_short_more(tr::now)); _actionMore = moreButton; _actions->add(moreButton); buttons.push_back(moreButton); @@ -751,6 +753,7 @@ void TopBar::setupActions(not_null controller) { peer->id, Window::SectionShow::Way::Forward); }); + message->setAccessibleName(tr::lng_profile_action_short_message(tr::now)); buttons.push_back(message); _actions->add(message); } @@ -763,6 +766,7 @@ void TopBar::setupActions(not_null controller) { join->setClickedCallback([=] { channel->session().api().joinChannel(channel); }); + join->setAccessibleName(tr::lng_profile_action_short_join(tr::now)); buttons.push_back(join); _actions->add(join); } else if (const auto channel = peer->monoforumBroadcast()) { @@ -775,6 +779,7 @@ void TopBar::setupActions(not_null controller) { channel, Window::SectionShow::Way::Forward); }); + message->setAccessibleName(tr::lng_profile_action_short_channel(tr::now)); buttons.push_back(message); _actions->add(message); } @@ -783,6 +788,7 @@ void TopBar::setupActions(not_null controller) { this, tr::lng_profile_action_short_mute(tr::now), st::infoProfileTopBarActionMessage); + notifications->setAccessibleName(tr::lng_profile_action_short_mute(tr::now)); notifications->convertToToggle( st::infoProfileTopBarActionUnmute, st::infoProfileTopBarActionMute, @@ -800,9 +806,11 @@ void TopBar::setupActions(not_null controller) { : NotificationsEnabledValue(peer) ) | rpl::on_next([=](bool enabled) { notifications->toggle(enabled); - notifications->setText(enabled + const auto text = enabled ? tr::lng_profile_action_short_mute(tr::now) - : tr::lng_profile_action_short_unmute(tr::now)); + : tr::lng_profile_action_short_unmute(tr::now); + notifications->setText(text); + notifications->setAccessibleName(text); }, notifications->lifetime()); notifications->finishAnimating(); @@ -863,6 +871,7 @@ void TopBar::setupActions(not_null controller) { call->setClickedCallback([=] { Core::App().calls().startOutgoingCall(user, {}); }); + call->setAccessibleName(tr::lng_profile_action_short_call(tr::now)); buttons.push_back(call); _actions->add(call); } @@ -885,6 +894,7 @@ void TopBar::setupActions(not_null controller) { chat, Window::SectionShow::Way::Forward); }); + discuss->setAccessibleName(tr::lng_profile_action_short_discuss(tr::now)); _actions->add(discuss); buttons.push_back(discuss); } @@ -907,6 +917,7 @@ void TopBar::setupActions(not_null controller) { window->showEditPeerBox(peer); } }); + manage->setAccessibleName(tr::lng_profile_action_short_manage(tr::now)); buttons.push_back(manage); _actions->add(manage); } @@ -935,6 +946,7 @@ void TopBar::setupActions(not_null controller) { giftButton->setClickedCallback([=] { Ui::ShowStarGiftBox(controller, peer); }); + giftButton->setAccessibleName(tr::lng_profile_action_short_gift(tr::now)); _actions->add(giftButton); buttons.push_back(giftButton); } @@ -956,6 +968,7 @@ void TopBar::setupActions(not_null controller) { reportButton->setClickedCallback([=] { ShowReportMessageBox(show, peer, {}, {}); }); + reportButton->setAccessibleName(tr::lng_profile_action_short_report(tr::now)); _actions->add(reportButton); buttons.push_back(reportButton); } @@ -969,6 +982,7 @@ void TopBar::setupActions(not_null controller) { st::infoProfileTopBarActionLeave); leaveButton->setClickedCallback( Window::DeleteAndLeaveHandler(controller, peer)); + leaveButton->setAccessibleName(tr::lng_profile_action_short_leave(tr::now)); _actions->add(leaveButton); buttons.push_back(leaveButton); } @@ -1863,6 +1877,7 @@ void TopBar::setupButtons( st::infoTopBarScale); _back->QWidget::show(); _back->setDuration(0); + _back->entity()->setAccessibleName(tr::lng_go_back(tr::now)); _back->toggleOn(isLayer || isSide ? (_backToggles.value() | rpl::type_erased) : rpl::single(wrap == Wrap::Narrow)); @@ -1878,6 +1893,7 @@ void TopBar::setupButtons( shouldUseColored ? st::infoTopBarColoredClose : st::infoTopBarBlackClose); + _close->setAccessibleName(tr::lng_sr_close_panel(tr::now)); _close->show(); _close->addClickHandler(isSide ? Fn ([=] { controller->closeThirdSection(); }) @@ -1915,6 +1931,7 @@ void TopBar::addTopBarEditButton( _topBarButton->addClickHandler([=] { controller->showSettings(::Settings::InformationId()); }); + _topBarButton->setAccessibleName(tr::lng_settings_information(tr::now)); widthValue() | rpl::on_next([=] { if (_close) { @@ -2309,6 +2326,18 @@ void TopBar::setupNewGifts( } entry.position = positions[i]; entry.button = base::make_unique_q(this); + entry.button->setAccessibleName([&] { + const auto base = tr::lng_profile_action_short_gift(tr::now); + + if (const auto &unique = gift.info.unique) { + const auto name = Data::UniqueGiftName(*unique); + if (!name.isEmpty()) { + return base + ": " + name; + } + } + + return base; + }()); entry.button->show(); entry.button->setClickedCallback([=, giftData = gift, peer = _peer] { diff --git a/Telegram/SourceFiles/ui/controls/stars_rating.cpp b/Telegram/SourceFiles/ui/controls/stars_rating.cpp index fb3c20353b..7fa17b8ba1 100644 --- a/Telegram/SourceFiles/ui/controls/stars_rating.cpp +++ b/Telegram/SourceFiles/ui/controls/stars_rating.cpp @@ -467,6 +467,7 @@ void StarsRating::updateData(Data::StarsRating rating) { _currentLevel = rating.level; } updateWidth(); + _widget->setAccessibleName(tr::lng_boost_level(tr::now, lt_count, rating.level)); } void StarsRating::updateWidth() {