@@ -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,87 @@ class CountrySelectBox::Inner : public RpWidget {
4854 return _mustScrollTo.events ();
4955 }
5056
57+ QAccessible::Role accessibilityRole () override {
58+ return QAccessible::List;
59+ }
60+
61+ QAccessible::Role accessibilityChildRole () const override {
62+ return QAccessible::ListItem;
63+ }
64+
65+ QAccessible::State accessibilityChildState (int index) const override {
66+ QAccessible::State state;
67+ state.selectable = true ;
68+ if (base::ScreenReaderState::Instance ()->active ()) {
69+ state.focusable = true ;
70+ }
71+ if (index == _selected) {
72+ state.selected = true ;
73+ state.active = true ;
74+ if (hasFocus ()) {
75+ state.focused = true ;
76+ }
77+ }
78+ return state;
79+ }
80+
81+ int accessibilityChildCount () const override {
82+ return int (current ().size ());
83+ }
84+
85+ QString accessibilityChildName (int index) const override {
86+ const auto &list = current ();
87+ if (index < 0 || index >= int (list.size ())) {
88+ return {};
89+ }
90+ if (_type == Type::Phones) {
91+ return list[index].country + u" , +" _q + list[index].code ;
92+ }
93+ return list[index].country ;
94+ }
95+
96+ QRect accessibilityChildRect (int index) const override {
97+ const auto &list = current ();
98+ if (index < 0 || index >= int (list.size ())) {
99+ return QRect ();
100+ }
101+ return QRect (0 , st::countriesSkip + index * _rowHeight, width (), _rowHeight);
102+ }
103+
104+ int accessibilityChildColumnCount (int row) const override {
105+ return (_type == Type::Phones) ? 2 : 1 ;
106+ }
107+
108+ QAccessible::Role accessibilityChildSubItemRole () const override {
109+ return QAccessible::Cell;
110+ }
111+
112+ QString accessibilityChildSubItemName (int row, int column) const override {
113+ if (column == 0 ) {
114+ return tr::lng_sr_country_column_name (tr::now);
115+ } else if (column == 1 && _type == Type::Phones) {
116+ return tr::lng_country_code (tr::now);
117+ }
118+ return {};
119+ }
120+
121+ QString accessibilityChildSubItemValue (int row, int column) const override {
122+ const auto &list = current ();
123+ if (row < 0 || row >= int (list.size ())) {
124+ return {};
125+ }
126+ if (column == 0 ) {
127+ return list[row].country ;
128+ } else if (column == 1 && _type == Type::Phones) {
129+ return u" +" _q + list[row].code ;
130+ }
131+ return {};
132+ }
133+
51134protected:
135+ void focusInEvent (QFocusEvent *e) override ;
52136 void paintEvent (QPaintEvent *e) override ;
137+ void keyPressEvent (QKeyEvent *e) override ;
53138 void enterEventHook (QEnterEvent *e) override ;
54139 void leaveEventHook (QEvent *e) override ;
55140 void mouseMoveEvent (QMouseEvent *e) override ;
@@ -203,6 +288,13 @@ CountrySelectBox::Inner::Inner(
203288 _filter = u" a" _q;
204289 updateFilter (filter);
205290 }, lifetime ());
291+
292+ setAccessibleName (tr::lng_country_select (tr::now));
293+
294+ base::ScreenReaderState::Instance ()->activeValue (
295+ ) | rpl::on_next ([=](bool active) {
296+ setFocusPolicy (active ? Qt::TabFocus : Qt::NoFocus);
297+ }, lifetime ());
206298}
207299
208300void CountrySelectBox::Inner::init () {
@@ -262,6 +354,27 @@ void CountrySelectBox::Inner::init() {
262354 }
263355}
264356
357+ void CountrySelectBox::Inner::focusInEvent (QFocusEvent *e) {
358+ // Select first item when focus enters.
359+ const auto &list = current ();
360+ if (_selected < 0 && !list.empty ()) {
361+ _selected = 0 ;
362+ updateSelectedRow ();
363+ }
364+
365+ RpWidget::focusInEvent (e);
366+
367+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
368+ const auto index = _selected;
369+ InvokeQueued (this , [=] {
370+ if (_selected != index || !hasFocus ()) {
371+ return ;
372+ }
373+ accessibilityChildFocused (index);
374+ });
375+ }
376+ }
377+
265378void CountrySelectBox::Inner::paintEvent (QPaintEvent *e) {
266379 Painter p (this );
267380 QRect r (e->rect ());
@@ -316,6 +429,50 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
316429 }
317430}
318431
432+ void CountrySelectBox::Inner::keyPressEvent (QKeyEvent *e) {
433+ if (e->key () == Qt::Key_Down) {
434+ selectSkip (1 );
435+ } else if (e->key () == Qt::Key_Up) {
436+ selectSkip (-1 );
437+ } else if (e->key () == Qt::Key_PageDown || e->key () == Qt::Key_PageUp) {
438+ const auto visibleHeight = visibleRegion ().boundingRect ().height ();
439+ const auto rowsPerPage = std::max (visibleHeight / _rowHeight, 1 );
440+ selectSkip (e->key () == Qt::Key_PageDown ? rowsPerPage : -rowsPerPage);
441+ } else if (e->key () == Qt::Key_Home) {
442+ const auto &list = current ();
443+ if (!list.empty ()) {
444+ _selected = 0 ;
445+ _mustScrollTo.fire (ScrollToRequest (
446+ st::countriesSkip,
447+ st::countriesSkip + _rowHeight));
448+ update ();
449+ if (base::ScreenReaderState::Instance ()->active ()) {
450+ accessibilityChildNameChanged (_selected);
451+ accessibilityChildFocused (_selected);
452+ }
453+ }
454+ } else if (e->key () == Qt::Key_End) {
455+ const auto &list = current ();
456+ if (!list.empty ()) {
457+ _selected = int (list.size ()) - 1 ;
458+ _mustScrollTo.fire (ScrollToRequest (
459+ st::countriesSkip + _selected * _rowHeight,
460+ st::countriesSkip + (_selected + 1 ) * _rowHeight));
461+ update ();
462+ if (base::ScreenReaderState::Instance ()->active ()) {
463+ accessibilityChildNameChanged (_selected);
464+ accessibilityChildFocused (_selected);
465+ }
466+ }
467+ } else if (!e->isAutoRepeat ()
468+ && (e->key () == Qt::Key_Return
469+ || e->key () == Qt::Key_Enter)) {
470+ chooseCountry ();
471+ } else {
472+ RpWidget::keyPressEvent (e);
473+ }
474+ }
475+
319476void CountrySelectBox::Inner::enterEventHook (QEnterEvent *e) {
320477 setMouseTracking (true );
321478}
@@ -426,6 +583,10 @@ void CountrySelectBox::Inner::selectSkip(int32 dir) {
426583 st::countriesSkip + (_selected + 1 ) * _rowHeight));
427584 }
428585 update ();
586+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
587+ accessibilityChildNameChanged (_selected);
588+ accessibilityChildFocused (_selected);
589+ }
429590}
430591
431592void CountrySelectBox::Inner::selectSkipPage (int32 h, int32 dir) {
@@ -457,6 +618,10 @@ void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
457618 updateSelectedRow ();
458619 _selected = selected;
459620 updateSelectedRow ();
621+ if (_selected >= 0 && base::ScreenReaderState::Instance ()->active ()) {
622+ accessibilityChildNameChanged (_selected);
623+ accessibilityChildFocused (_selected);
624+ }
460625 }
461626}
462627
0 commit comments