feat(accessibility): add screen reader support for country select box

This commit is contained in:
Reza Bakhshi Laktasaraei
2026-02-15 16:48:06 +03:30
committed by John Preston
parent b5f3ea60b7
commit 3fbe30231e
2 changed files with 167 additions and 0 deletions
+2
View File
@@ -7917,4 +7917,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mac_hold_to_quit" = "Hold {text} to Quit";
"lng_sr_country_column_name" = "Country name";
// Keys finished
@@ -8,16 +8,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/country_select_box.h"
#include "lang/lang_keys.h"
#include "base/screen_reader_state.h"
#include "ui/accessible/ui_accessible_item.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/multi_select.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "base/invoke_queued.h"
#include "countries/countries_instance.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
#include <unordered_map>
#include <QtCore/QRegularExpression>
#include <QtWidgets/QApplication>
namespace Ui {
namespace {
@@ -48,8 +54,87 @@ public:
return _mustScrollTo.events();
}
QAccessible::Role accessibilityRole() override {
return QAccessible::List;
}
QAccessible::Role accessibilityChildRole() const override {
return QAccessible::ListItem;
}
QAccessible::State accessibilityChildState(int index) const override {
QAccessible::State state;
state.selectable = true;
if (base::ScreenReaderState::Instance()->active()) {
state.focusable = true;
}
if (index == _selected) {
state.selected = true;
state.active = true;
if (hasFocus()) {
state.focused = true;
}
}
return state;
}
int accessibilityChildCount() const override {
return int(current().size());
}
QString accessibilityChildName(int index) const override {
const auto &list = current();
if (index < 0 || index >= int(list.size())) {
return {};
}
if (_type == Type::Phones) {
return list[index].country + u", +"_q + list[index].code;
}
return list[index].country;
}
QRect accessibilityChildRect(int index) const override {
const auto &list = current();
if (index < 0 || index >= int(list.size())) {
return QRect();
}
return QRect(0, st::countriesSkip + index * _rowHeight, width(), _rowHeight);
}
int accessibilityChildColumnCount(int row) const override {
return (_type == Type::Phones) ? 2 : 1;
}
QAccessible::Role accessibilityChildSubItemRole() const override {
return QAccessible::Cell;
}
QString accessibilityChildSubItemName(int row, int column) const override {
if (column == 0) {
return tr::lng_sr_country_column_name(tr::now);
} else if (column == 1 && _type == Type::Phones) {
return tr::lng_country_code(tr::now);
}
return {};
}
QString accessibilityChildSubItemValue(int row, int column) const override {
const auto &list = current();
if (row < 0 || row >= int(list.size())) {
return {};
}
if (column == 0) {
return list[row].country;
} else if (column == 1 && _type == Type::Phones) {
return u"+"_q + list[row].code;
}
return {};
}
protected:
void focusInEvent(QFocusEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void enterEventHook(QEnterEvent *e) override;
void leaveEventHook(QEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
@@ -203,6 +288,13 @@ CountrySelectBox::Inner::Inner(
_filter = u"a"_q;
updateFilter(filter);
}, lifetime());
setAccessibleName(tr::lng_country_select(tr::now));
base::ScreenReaderState::Instance()->activeValue(
) | rpl::on_next([=](bool active) {
setFocusPolicy(active ? Qt::TabFocus : Qt::NoFocus);
}, lifetime());
}
void CountrySelectBox::Inner::init() {
@@ -262,6 +354,27 @@ void CountrySelectBox::Inner::init() {
}
}
void CountrySelectBox::Inner::focusInEvent(QFocusEvent *e) {
// Select first item when focus enters.
const auto &list = current();
if (_selected < 0 && !list.empty()) {
_selected = 0;
updateSelectedRow();
}
RpWidget::focusInEvent(e);
if (_selected >= 0 && base::ScreenReaderState::Instance()->active()) {
const auto index = _selected;
InvokeQueued(this, [=] {
if (_selected != index || !hasFocus()) {
return;
}
accessibilityChildFocused(index);
});
}
}
void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
@@ -316,6 +429,50 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
}
}
void CountrySelectBox::Inner::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Down) {
selectSkip(1);
} else if (e->key() == Qt::Key_Up) {
selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_PageUp) {
const auto visibleHeight = visibleRegion().boundingRect().height();
const auto rowsPerPage = std::max(visibleHeight / _rowHeight, 1);
selectSkip(e->key() == Qt::Key_PageDown ? rowsPerPage : -rowsPerPage);
} else if (e->key() == Qt::Key_Home) {
const auto &list = current();
if (!list.empty()) {
_selected = 0;
_mustScrollTo.fire(ScrollToRequest(
st::countriesSkip,
st::countriesSkip + _rowHeight));
update();
if (base::ScreenReaderState::Instance()->active()) {
accessibilityChildNameChanged(_selected);
accessibilityChildFocused(_selected);
}
}
} else if (e->key() == Qt::Key_End) {
const auto &list = current();
if (!list.empty()) {
_selected = int(list.size()) - 1;
_mustScrollTo.fire(ScrollToRequest(
st::countriesSkip + _selected * _rowHeight,
st::countriesSkip + (_selected + 1) * _rowHeight));
update();
if (base::ScreenReaderState::Instance()->active()) {
accessibilityChildNameChanged(_selected);
accessibilityChildFocused(_selected);
}
}
} else if (!e->isAutoRepeat()
&& (e->key() == Qt::Key_Return
|| e->key() == Qt::Key_Enter)) {
chooseCountry();
} else {
RpWidget::keyPressEvent(e);
}
}
void CountrySelectBox::Inner::enterEventHook(QEnterEvent *e) {
setMouseTracking(true);
}
@@ -426,6 +583,10 @@ void CountrySelectBox::Inner::selectSkip(int32 dir) {
st::countriesSkip + (_selected + 1) * _rowHeight));
}
update();
if (_selected >= 0 && base::ScreenReaderState::Instance()->active()) {
accessibilityChildNameChanged(_selected);
accessibilityChildFocused(_selected);
}
}
void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) {
@@ -457,6 +618,10 @@ void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
updateSelectedRow();
_selected = selected;
updateSelectedRow();
if (_selected >= 0 && base::ScreenReaderState::Instance()->active()) {
accessibilityChildNameChanged(_selected);
accessibilityChildFocused(_selected);
}
}
}