diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 7ed79e9058..f444d21e58 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -840,6 +840,13 @@ void HistoryInner::messagesReceived( _migrated->addNewerSlice(QVector()); } } + if (_announceFirstMessages && hasFocus()) { + InvokeQueued(this, [=] { + if (_announceFirstMessages && hasFocus()) { + announceAccessibilityFocusedChild(); + } + }); + } } void HistoryInner::messagesReceivedDown( @@ -6619,67 +6626,79 @@ auto HistoryInner::computeActiveColumns(int row) const return _activeColumns; } +void HistoryInner::announceAccessibilityFocusedChild() { + const auto count = accessibilityChildCount(); + if (count <= 0) { + // One-shot for chats focused before their first page arrived: + // while the empty list holds focus, remember that the first + // received slice should announce the focused message. Fired + // (queued) from messagesReceived and disarmed by any real + // announcement below or in a later focus-in. + if (Ui::ScreenReaderModeActive()) { + _announceFirstMessages = true; + } + return; + } + _announceFirstMessages = false; + if (_accessibilityFocusedItem) { + const auto elements = accessibleElements(); + const auto barIndex = accessibilityUnreadBarIndex(); + auto found = -1; + for (auto i = 0, n = int(elements.size()); i < n; ++i) { + if (elements[i]->data().get() + == _accessibilityFocusedItem) { + found = (barIndex >= 0 && i >= barIndex) + ? (i + 1) + : i; + break; + } + } + if (found >= 0 && found < count) { + _accessibilityFocusedIndex = found; + announceAccessibilityFocus(found); + return; + } + // The cached focused item is no longer in the list (it + // was removed or fell out of the loaded slice since we + // last had focus). Invalidate the index together with the + // item: announcing whatever row occupies the old index + // would leave later actions bound to a row the user never + // heard about once the list shifts again. The auto-select + // branch below establishes a fresh focus instead. + _accessibilityFocusedItem = nullptr; + _accessibilityFocusedIndex = -1; + } else if (_accessibilityFocusedIndex >= 0) { + // A nonnegative index with no cached item means the unread + // bar was focused. Follow the bar to wherever it sits now, + // or fall through to pick a fresh focus target when it is + // gone: the row that occupies the old index was never + // announced to the user. + _accessibilityFocusedIndex = accessibilityUnreadBarIndex(); + } + if (_accessibilityFocusedIndex >= 0 + && _accessibilityFocusedIndex < count) { + announceAccessibilityFocus(_accessibilityFocusedIndex); + return; + } + const auto barIndex = accessibilityUnreadBarIndex(); + const auto index = (barIndex >= 0 && barIndex + 1 < count) + ? (barIndex + 1) + : (count - 1); + const auto elements = accessibleElements(); + const auto item = accessibilityItemAtIndex( + index, + elements, + barIndex); + setAccessibilityFocusedItem(index, item); +} + void HistoryInner::focusInEvent(QFocusEvent *e) { RpWidget::focusInEvent(e); InvokeQueued(this, [=] { - if (!hasFocus()) { - return; + if (hasFocus()) { + announceAccessibilityFocusedChild(); } - const auto count = accessibilityChildCount(); - if (count <= 0) { - return; - } - if (_accessibilityFocusedItem) { - const auto elements = accessibleElements(); - const auto barIndex = accessibilityUnreadBarIndex(); - auto found = -1; - for (auto i = 0, n = int(elements.size()); i < n; ++i) { - if (elements[i]->data().get() - == _accessibilityFocusedItem) { - found = (barIndex >= 0 && i >= barIndex) - ? (i + 1) - : i; - break; - } - } - if (found >= 0 && found < count) { - _accessibilityFocusedIndex = found; - announceAccessibilityFocus(found); - return; - } - // The cached focused item is no longer in the list (it - // was removed or fell out of the loaded slice since we - // last had focus). Invalidate the index together with the - // item: announcing whatever row occupies the old index - // would leave later actions bound to a row the user never - // heard about once the list shifts again. The auto-select - // branch below establishes a fresh focus instead. - _accessibilityFocusedItem = nullptr; - _accessibilityFocusedIndex = -1; - } else if (_accessibilityFocusedIndex >= 0) { - // A nonnegative index with no cached item means the unread - // bar was focused. Follow the bar to wherever it sits now, - // or fall through to pick a fresh focus target when it is - // gone: the row that occupies the old index was never - // announced to the user. - _accessibilityFocusedIndex = accessibilityUnreadBarIndex(); - } - if (_accessibilityFocusedIndex >= 0 - && _accessibilityFocusedIndex < count) { - announceAccessibilityFocus(_accessibilityFocusedIndex); - return; - } - const auto barIndex = accessibilityUnreadBarIndex(); - const auto index = (barIndex >= 0 && barIndex + 1 < count) - ? (barIndex + 1) - : (count - 1); - const auto elements = accessibleElements(); - const auto item = accessibilityItemAtIndex( - index, - elements, - barIndex); - setAccessibilityFocusedItem(index, item); }); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 4befb952e9..b500735ade 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -323,6 +323,7 @@ private: void playPauseFocusedMedia(); void setAccessibilityFocusedItem(int index, HistoryItem *item); void announceAccessibilityFocus(int index); + void announceAccessibilityFocusedChild(); void applyAccessibilityFocus(int index, bool announceAlways); [[nodiscard]] auto computeActiveColumns(int row) const -> const std::vector &; @@ -571,6 +572,7 @@ private: int _accessibilityFocusedIndex = -1; HistoryItem *_accessibilityFocusedItem = nullptr; HistoryItem *_accessibilitySelectionAnchor = nullptr; + bool _announceFirstMessages = false; mutable base::flat_map< not_null, quintptr> _accessibilityIdentities; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 63ac548e05..10fda20956 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3175,7 +3175,6 @@ void HistoryWidget::showHistory( } } - _scroll->hide(); _list = _scroll->setOwnedWidget( object_ptr(this, _scroll, controller(), _history)); _pullToNext->attachToContent(_list); @@ -3931,13 +3930,7 @@ void HistoryWidget::updateControlsVisibility() { return; } - if (_firstLoadRequest && !_scroll->isHidden()) { - if (Ui::InFocusChain(_scroll.data())) { - // Don't loose focus back to chats list. - setFocus(); - } - _scroll->hide(); - } else if (!_firstLoadRequest && _scroll->isHidden()) { + if (_scroll->isHidden()) { _scroll->show(); } _topBars->show(); @@ -5010,7 +5003,7 @@ bool HistoryWidget::isItemCompletelyHidden(HistoryItem *item) const { } void HistoryWidget::visibleAreaUpdated() { - if (_list && !_scroll->isHidden()) { + if (_list && !_firstLoadRequest && !_scroll->isHidden()) { const auto scrollTop = _scroll->scrollTop(); const auto scrollBottom = scrollTop + _scroll->height(); _list->visibleAreaUpdated(scrollTop, scrollBottom);