@@ -38,6 +38,7 @@ For license and copyright information please follow this link:
3838
3939#include < xxhash.h> // XXH64.
4040#include < QtWidgets/QApplication>
41+ #include " base/screen_reader_state.h"
4142
4243[[nodiscard]] PeerListRowId UniqueRowIdFromString (const QString &d) {
4344 return XXH64 (d.data (), d.size () * sizeof (ushort), 0 );
@@ -452,6 +453,10 @@ std::unique_ptr<PeerListRow> PeerListController::createSearchRow(
452453 return nullptr ;
453454}
454455
456+ QString PeerListController::accessibilityName () const {
457+ return u" Peer list" _q;
458+ }
459+
455460std::unique_ptr<PeerListState> PeerListController::saveState () const {
456461 return delegate ()->peerListSaveState ();
457462}
@@ -600,6 +605,10 @@ PeerListRow::PeerListRow(PeerListRowId id)
600605
601606PeerListRow::~PeerListRow () = default ;
602607
608+ bool PeerListRow::checkable () const {
609+ return _checkbox != nullptr ;
610+ }
611+
603612bool PeerListRow::checked () const {
604613 return _checkbox && _checkbox->checked ();
605614}
@@ -1025,6 +1034,11 @@ PeerListContent::PeerListContent(
10251034 update ();
10261035 }, lifetime ());
10271036
1037+ base::ScreenReaderState::Instance ()->activeValue (
1038+ ) | rpl::on_next ([this ](bool active) {
1039+ setFocusPolicy (active ? Qt::TabFocus : Qt::NoFocus);
1040+ }, lifetime ());
1041+
10281042 using UpdateFlag = Data::PeerUpdate::Flag;
10291043 _controller->session ().changes ().peerUpdates (
10301044 UpdateFlag::Name | UpdateFlag::Photo | UpdateFlag::EmojiStatus
@@ -1043,6 +1057,13 @@ PeerListContent::PeerListContent(
10431057 }, lifetime ());
10441058
10451059 _repaintByStatus.setCallback ([this ] { update (); });
1060+
1061+ _selectedIndex.changes (
1062+ ) | rpl::on_next ([=](int index) {
1063+ if (index >= 0 ) {
1064+ accessibilityChildFocused (index);
1065+ }
1066+ }, lifetime ());
10461067}
10471068
10481069void PeerListContent::setMode (Mode mode) {
@@ -1114,6 +1135,12 @@ void PeerListContent::changeCheckState(
11141135 row->setChecked (checked, _st.item .checkbox , animated, [=] {
11151136 updateRow (row);
11161137 });
1138+ if (const auto index = findRowIndex (row, RowIndex ()); index.value >= 0 ) {
1139+ accessibilityChildStateChanged (index.value , {
1140+ .checked = true ,
1141+ .selected = true ,
1142+ });
1143+ }
11171144}
11181145
11191146void PeerListContent::setRowHidden (not_null<PeerListRow*> row, bool hidden) {
@@ -1603,6 +1630,50 @@ int PeerListContent::resizeGetHeight(int newWidth) {
16031630 return belowTop + _belowHeight;
16041631}
16051632
1633+ void PeerListContent::keyPressEvent (QKeyEvent *e) {
1634+ if (e->key () == Qt::Key_Up) {
1635+ selectSkip (-1 );
1636+ e->accept ();
1637+ } else if (e->key () == Qt::Key_Down) {
1638+ selectSkip (1 );
1639+ e->accept ();
1640+ } else if (e->key () == Qt::Key_PageDown) {
1641+ selectSkipPage (height (), 1 );
1642+ e->accept ();
1643+ } else if (e->key () == Qt::Key_PageUp) {
1644+ selectSkipPage (height (), -1 );
1645+ e->accept ();
1646+ } else if (e->key () == Qt::Key_Return || e->key () == Qt::Key_Enter || e->key () == Qt::Key_Space) {
1647+ submitted ();
1648+ e->accept ();
1649+ } else {
1650+ e->ignore ();
1651+ }
1652+ }
1653+
1654+ void PeerListContent::focusInEvent (QFocusEvent *e) {
1655+ RpWidget::focusInEvent (e);
1656+ if (!base::ScreenReaderState::Instance ()->active ()) {
1657+ return ;
1658+ }
1659+ InvokeQueued (this , [=] {
1660+ if (!hasFocus ()) {
1661+ return ;
1662+ }
1663+ const auto count = shownRowsCount ();
1664+ if (count <= 0 ) {
1665+ return ;
1666+ }
1667+ const auto current = _selectedIndex.current ();
1668+ if (current >= 0 && current < count) {
1669+ accessibilityChildFocused (current);
1670+ return ;
1671+ }
1672+ setSelected ({ RowIndex (0 ), 0 });
1673+ accessibilityChildFocused (0 );
1674+ });
1675+ }
1676+
16061677void PeerListContent::enterEventHook (QEnterEvent *e) {
16071678 setMouseTracking (true );
16081679}
@@ -2443,6 +2514,19 @@ PeerListRow *PeerListContent::getRow(RowIndex index) {
24432514 return nullptr ;
24442515}
24452516
2517+ const PeerListRow *PeerListContent::getRow (RowIndex index) const {
2518+ if (index.value >= 0 ) {
2519+ if (showingSearch ()) {
2520+ if (index.value < _filterResults.size ()) {
2521+ return _filterResults[index.value ];
2522+ }
2523+ } else if (index.value < _rows.size ()) {
2524+ return _rows[index.value ].get ();
2525+ }
2526+ }
2527+ return nullptr ;
2528+ }
2529+
24462530PeerListContent::RowIndex PeerListContent::findRowIndex (
24472531 not_null<PeerListRow*> row,
24482532 RowIndex hint) {
@@ -2491,3 +2575,97 @@ void PeerListContentDelegate::peerListShowRowMenu(
24912575 Fn<void (not_null<Ui::PopupMenu *>)> destroyed) {
24922576 _content->showRowMenu (row, highlightRow, std::move (destroyed));
24932577}
2578+
2579+ QAccessible::Role PeerListContent::accessibilityRole () {
2580+ return QAccessible::List;
2581+ }
2582+
2583+ QString PeerListContent::accessibilityName () {
2584+ const auto fromUi = accessibleName ();
2585+ return fromUi.isEmpty () ? _controller->accessibilityName () : fromUi;
2586+ }
2587+
2588+ Ui::AccessibilityState PeerListContent::accessibilityState () const {
2589+ return {};
2590+ }
2591+
2592+ int PeerListContent::accessibilityChildCount () const {
2593+ return shownRowsCount ();
2594+ }
2595+
2596+ QAccessible::Role PeerListContent::accessibilityChildRole () const {
2597+ return QAccessible::ListItem;
2598+ }
2599+
2600+ QString PeerListContent::accessibilityChildName (int index) const {
2601+ if (index >= 0 && index < shownRowsCount ()) {
2602+ const auto row = getRow (RowIndex (index));
2603+ if (!row) {
2604+ return QString ();
2605+ }
2606+ if (!row->name ().isEmpty ()) {
2607+ return row->name ().toString ();
2608+ }
2609+ return const_cast <PeerListRow*>(row)->generateName ();
2610+ }
2611+ return QString ();
2612+ }
2613+
2614+ QAccessible::State PeerListContent::accessibilityChildState (int index) const {
2615+ auto state = QAccessible::State ();
2616+ if (index >= 0 && index < shownRowsCount ()) {
2617+ if (base::ScreenReaderState::Instance ()->active ()) {
2618+ state.focusable = true ;
2619+ }
2620+ state.selectable = true ;
2621+
2622+ const auto row = getRow (RowIndex (index));
2623+ if (row->checkable () && !row->special ()) {
2624+ state.checkable = true ;
2625+ state.checked = row->checked ();
2626+ if (row->checked ()) {
2627+ state.selected = true ;
2628+ }
2629+ }
2630+ const auto top = getRowTop (RowIndex (index));
2631+ const auto bottom = top + _rowHeight;
2632+ if (bottom <= _visibleTop || top >= _visibleBottom) {
2633+ state.invisible = true ;
2634+ }
2635+ if (index == _selectedIndex.current ()) {
2636+ state.focused = true ;
2637+ state.invisible = false ;
2638+ }
2639+ }
2640+ return state;
2641+ }
2642+
2643+ QRect PeerListContent::accessibilityChildRect (int index) const {
2644+ if (index >= 0 && index < shownRowsCount ()) {
2645+ const auto top = getRowTop (RowIndex (index));
2646+ return QRect (0 , top, width (), _st.item .height );
2647+ }
2648+ return QRect ();
2649+ }
2650+
2651+ int PeerListContent::accessibilityChildColumnCount (int row) const {
2652+ return 1 ;
2653+ }
2654+
2655+ QAccessible::Role PeerListContent::accessibilityChildSubItemRole () const {
2656+ return QAccessible::Cell;
2657+ }
2658+
2659+ QString PeerListContent::accessibilityChildSubItemName (int row, int column) const {
2660+ if (column == 0 ) {
2661+ return _controller->accessibilityName ();
2662+ }
2663+ return QString ();
2664+ }
2665+
2666+ QString PeerListContent::accessibilityChildSubItemValue (int row, int column) const {
2667+ if (column == 0 ) {
2668+ return accessibilityChildName (row);
2669+ }
2670+ return QString ();
2671+ }
0 commit comments