diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 722d725320..5eef0492a8 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -4921,39 +4921,43 @@ void InnerWidget::selectSkip(int32 direction) { _hashtagSelected = _filteredSelected = _peerSearchSelected = _previewSelected = -1; } } - if (base::in_range(_hashtagSelected, 0, _hashtagResults.size())) { - const auto from = _hashtagSelected * st::mentionHeight; - scrollToItem(from, st::mentionHeight); - } else if (base::in_range(_filteredSelected, 0, _filterResults.size())) { - const auto &result = _filterResults[_filteredSelected]; - const auto from = filteredOffset() + result.top; - scrollToItem(from, result.row->height()); - } else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) { - const auto from = peerSearchOffset() - + _peerSearchSelected * st::dialogsRowHeight - + (_peerSearchSelected ? 0 : -st::searchedBarHeight); - const auto height = st::dialogsRowHeight - + (_peerSearchSelected ? 0 : st::searchedBarHeight); - scrollToItem(from, height); - } else if (base::in_range(_previewSelected, 0, _previewResults.size())) { - const auto from = previewOffset() - + _previewSelected * _st->height - + (_previewSelected ? 0 : -st::searchedBarHeight); - const auto height = _st->height - + (_previewSelected ? 0 : st::searchedBarHeight); - scrollToItem(from, height); - } else { - const auto from = searchedOffset() - + _searchedSelected * _st->height - + (_searchedSelected ? 0 : -st::searchedBarHeight); - const auto height = _st->height - + (_searchedSelected ? 0 : st::searchedBarHeight); - scrollToItem(from, height); - } + scrollToFilteredSelected(); } update(); } +void InnerWidget::scrollToFilteredSelected() { + if (base::in_range(_hashtagSelected, 0, _hashtagResults.size())) { + const auto from = _hashtagSelected * st::mentionHeight; + scrollToItem(from, st::mentionHeight); + } else if (base::in_range(_filteredSelected, 0, _filterResults.size())) { + const auto &result = _filterResults[_filteredSelected]; + const auto from = filteredOffset() + result.top; + scrollToItem(from, result.row->height()); + } else if (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size())) { + const auto from = peerSearchOffset() + + _peerSearchSelected * st::dialogsRowHeight + + (_peerSearchSelected ? 0 : -st::searchedBarHeight); + const auto height = st::dialogsRowHeight + + (_peerSearchSelected ? 0 : st::searchedBarHeight); + scrollToItem(from, height); + } else if (base::in_range(_previewSelected, 0, _previewResults.size())) { + const auto from = previewOffset() + + _previewSelected * _st->height + + (_previewSelected ? 0 : -st::searchedBarHeight); + const auto height = _st->height + + (_previewSelected ? 0 : st::searchedBarHeight); + scrollToItem(from, height); + } else if (base::in_range(_searchedSelected, 0, _searchResults.size())) { + const auto from = searchedOffset() + + _searchedSelected * _st->height + + (_searchedSelected ? 0 : -st::searchedBarHeight); + const auto height = _st->height + + (_searchedSelected ? 0 : st::searchedBarHeight); + scrollToItem(from, height); + } +} + void InnerWidget::scrollToEntry(const RowDescriptor &entry) { if (_state == WidgetState::Default) { if (auto row = _shownList->getRow(entry.key)) { @@ -6131,6 +6135,64 @@ void InnerWidget::announceSelectedFocus() { } } +void InnerWidget::accessibilityChildSetFocus(int index) { + // UIA invokes provider actions (SetFocus) on a background thread, + // so hop to the main thread before touching any widget state. + crl::on_main(this, [=] { + if (!Ui::ScreenReaderModeActive()) { + return; + } + // The rows are virtual (no real QWidget), so the screen reader's + // SetFocus can't move real keyboard focus to a row. Translate it + // into our internal selection, then either grab keyboard focus + // (focusInEvent announces the row) or announce it directly. + clearMouseSelection(); + if (_state == WidgetState::Default) { + if (index < 0 || index >= _shownList->size()) { + return; + } + _collapsedSelected = -1; + _selected = (_shownList->cbegin() + index)->get(); + scrollToDefaultSelected(); + } else if (_state == WidgetState::Filtered) { + const auto ref = filteredChildAt(index); + if (!ref) { + return; + } + _hashtagSelected = _filteredSelected = _peerSearchSelected + = _previewSelected = _searchedSelected = -1; + switch (ref->cohort) { + case AccessibilityCohort::Hashtag: + _hashtagSelected = ref->local; + break; + case AccessibilityCohort::Filtered: + _filteredSelected = ref->local; + break; + case AccessibilityCohort::PeerSearch: + _peerSearchSelected = ref->local; + break; + case AccessibilityCohort::Preview: + _previewSelected = ref->local; + break; + case AccessibilityCohort::Searched: + _searchedSelected = ref->local; + break; + } + scrollToFilteredSelected(); + } else { + return; + } + update(); + // Selection is already set, so if focus moves here focusInEvent + // announces this very row; otherwise announce it ourselves. + if (hasFocus()) { + announceSelectedFocus(); + } else { + setFocus(); + } + }); +} + Ui::AccessibilityState InnerWidget::accessibilityState() const { // Mark the List container as non-focusable so screen readers // drill down to the focusable child items (ListItem rows). diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index deaba41a5a..70a5f2145b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -253,6 +253,7 @@ public: QAccessible::Role accessibilityChildSubItemRole() const override; QString accessibilityChildSubItemName(int row, int column) const override; QString accessibilityChildSubItemValue(int row, int column) const override; + void accessibilityChildSetFocus(int index) override; protected: void visibleTopBottomUpdated( @@ -353,6 +354,7 @@ private: void preloadRowsData(); void scrollToItem(int top, int height); void scrollToDefaultSelected(); + void scrollToFilteredSelected(); void setCollapsedPressed(int pressed); void setPressed( Row *pressed,