@@ -8,16 +8,22 @@ For license and copyright information please follow this link:
88#include " ui/boxes/country_select_box.h"
99
1010#include " lang/lang_keys.h"
11+ #include " base/screen_reader_state.h"
12+ #include " ui/accessible/ui_accessible_item.h"
1113#include " ui/widgets/scroll_area.h"
1214#include " ui/widgets/multi_select.h"
1315#include " ui/effects/ripple_animation.h"
1416#include " ui/painter.h"
17+ #include " base/invoke_queued.h"
1518#include " countries/countries_instance.h"
1619#include " styles/style_layers.h"
1720#include " styles/style_boxes.h"
1821#include " styles/style_intro.h"
1922
23+ #include < unordered_map>
24+
2025#include < QtCore/QRegularExpression>
26+ #include < QtWidgets/QApplication>
2127
2228namespace Ui {
2329namespace {
@@ -48,8 +54,89 @@ class CountrySelectBox::Inner : public RpWidget {
4854 return _mustScrollTo.events ();
4955 }
5056
57+ QAccessible::Role accessibilityRole () override {
58+ return QAccessible::List;
59+ }
60+
61+ Ui::AccessibilityState accessibilityState () const override {
62+ return { .readOnly = true , .focusable = 0 };
63+ }
64+
65+ QAccessible::Role accessibilityChildRole () const override {
66+ return QAccessible::ListItem;
67+ }
68+
69+ QAccessible::State accessibilityChildState (int index) const override {
70+ QAccessible::State state;
71+ state.selectable = true ;
72+ state.focusable = true ;
73+ if (index == _selected) {
74+ state.selected = true ;
75+ state.active = true ;
76+ if (hasFocus ()) {
77+ state.focused = true ;
78+ }
79+ }
80+ return state;
81+ }
82+
83+ int accessibilityChildCount () const override {
84+ return int (current ().size ());
85+ }
86+
87+ QString accessibilityChildName (int index) const override {
88+ const auto &list = current ();
89+ if (index < 0 || index >= int (list.size ())) {
90+ return {};
91+ }
92+ if (_type == Type::Phones) {
93+ return list[index].country + u" , +" _q + list[index].code ;
94+ }
95+ return list[index].country ;
96+ }
97+
98+ QRect accessibilityChildRect (int index) const override {
99+ const auto &list = current ();
100+ if (index < 0 || index >= int (list.size ())) {
101+ return QRect ();
102+ }
103+ return QRect (0 , st::countriesSkip + index * _rowHeight, width (), _rowHeight);
104+ }
105+
106+ int accessibilityChildColumnCount (int row) const override {
107+ return (_type == Type::Phones) ? 2 : 1 ;
108+ }
109+
110+ QAccessible::Role accessibilityChildSubItemRole () const override {
111+ return QAccessible::Cell;
112+ }
113+
114+ QString accessibilityChildSubItemName (int row, int column) const override {
115+ if (column == 0 ) {
116+ return tr::lng_country_column_name (tr::now);
117+ } else if (column == 1 && _type == Type::Phones) {
118+ return tr::lng_country_code (tr::now);
119+ }
120+ return {};
121+ }
122+
123+ QString accessibilityChildSubItemValue (int row, int column) const override {
124+ const auto &list = current ();
125+ if (row < 0 || row >= int (list.size ())) {
126+ return {};
127+ }
128+ if (column == 0 ) {
129+ return list[row].country ;
130+ } else if (column == 1 && _type == Type::Phones) {
131+ return u" +" _q + list[row].code ;
132+ }
133+ return {};
134+ }
135+
51136protected:
137+ void focusInEvent (QFocusEvent *e) override ;
52138 void paintEvent (QPaintEvent *e) override ;
139+ void keyPressEvent (QKeyEvent *e) override ;
53140 void enterEventHook (QEnterEvent *e) override ;
54141 void leaveEventHook (QEvent *e) override ;
55142 void mouseMoveEvent (QMouseEvent *e) override ;
@@ -203,6 +290,13 @@ CountrySelectBox::Inner::Inner(
203290 _filter = u" a" _q;
204291 updateFilter (filter);
205292 }, lifetime ());
293+
294+ setAccessibleName (tr::lng_country_select (tr::now));
295+
296+ base::ScreenReaderState::Instance ()->activeValue (
297+ ) | rpl::on_next ([=](bool active) {
298+ setFocusPolicy (active ? Qt::TabFocus : Qt::NoFocus);
299+ }, lifetime ());
206300}
207301
208302void CountrySelectBox::Inner::init () {
@@ -262,6 +356,28 @@ void CountrySelectBox::Inner::init() {
262356 }
263357}
264358
359+ void CountrySelectBox::Inner::focusInEvent (QFocusEvent *e) {
360+ // Select first item when focus enters.
361+ const auto &list = current ();
362+ if (_selected < 0 && !list.empty ()) {
363+ _selected = 0 ;
364+ updateSelectedRow ();
365+ }
366+
367+ RpWidget::focusInEvent (e);
368+
369+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
370+ const auto index = _selected;
371+ InvokeQueued (this , [=] {
372+ if (_selected != index || !hasFocus ()) {
373+ return ;
374+ }
375+ accessibilityChildStateChanged (index, { .focused = true });
376+ accessibilityChildFocused (index);
377+ });
378+ }
379+ }
380+
265381void CountrySelectBox::Inner::paintEvent (QPaintEvent *e) {
266382 Painter p (this );
267383 QRect r (e->rect ());
@@ -316,6 +432,50 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
316432 }
317433}
318434
435+ void CountrySelectBox::Inner::keyPressEvent (QKeyEvent *e) {
436+ if (e->key () == Qt::Key_Down) {
437+ selectSkip (1 );
438+ } else if (e->key () == Qt::Key_Up) {
439+ selectSkip (-1 );
440+ } else if (e->key () == Qt::Key_PageDown || e->key () == Qt::Key_PageUp) {
441+ const auto visibleHeight = visibleRegion ().boundingRect ().height ();
442+ const auto rowsPerPage = std::max (visibleHeight / _rowHeight, 1 );
443+ selectSkip (e->key () == Qt::Key_PageDown ? rowsPerPage : -rowsPerPage);
444+ } else if (e->key () == Qt::Key_Home) {
445+ const auto &list = current ();
446+ if (!list.empty ()) {
447+ _selected = 0 ;
448+ _mustScrollTo.fire (ScrollToRequest (
449+ st::countriesSkip,
450+ st::countriesSkip + _rowHeight));
451+ update ();
452+ if (base::ScreenReaderState::Instance ()->active ()) {
453+ accessibilityChildNameChanged (_selected);
454+ accessibilityChildFocused (_selected);
455+ }
456+ }
457+ } else if (e->key () == Qt::Key_End) {
458+ const auto &list = current ();
459+ if (!list.empty ()) {
460+ _selected = int (list.size ()) - 1 ;
461+ _mustScrollTo.fire (ScrollToRequest (
462+ st::countriesSkip + _selected * _rowHeight,
463+ st::countriesSkip + (_selected + 1 ) * _rowHeight));
464+ update ();
465+ if (base::ScreenReaderState::Instance ()->active ()) {
466+ accessibilityChildNameChanged (_selected);
467+ accessibilityChildFocused (_selected);
468+ }
469+ }
470+ } else if (!e->isAutoRepeat ()
471+ && (e->key () == Qt::Key_Return
472+ || e->key () == Qt::Key_Enter)) {
473+ chooseCountry ();
474+ } else {
475+ RpWidget::keyPressEvent (e);
476+ }
477+ }
478+
319479void CountrySelectBox::Inner::enterEventHook (QEnterEvent *e) {
320480 setMouseTracking (true );
321481}
@@ -426,6 +586,10 @@ void CountrySelectBox::Inner::selectSkip(int32 dir) {
426586 st::countriesSkip + (_selected + 1 ) * _rowHeight));
427587 }
428588 update ();
589+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
590+ accessibilityChildNameChanged (_selected);
591+ accessibilityChildFocused (_selected);
592+ }
429593}
430594
431595void CountrySelectBox::Inner::selectSkipPage (int32 h, int32 dir) {
@@ -457,6 +621,10 @@ void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
457621 updateSelectedRow ();
458622 _selected = selected;
459623 updateSelectedRow ();
624+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
625+ accessibilityChildNameChanged (_selected);
626+ accessibilityChildFocused (_selected);
627+ }
460628 }
461629}
462630
0 commit comments