mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-10 05:33:50 +00:00
Keep the message list visible and focused while a chat loads
Opening a chat whose history was not loaded yet hid the scroll area and parked keyboard focus on the unnamed HistoryWidget container until the first messages arrived, so screen readers announced it as a bare "grouping" for the whole load. Keep the scroll visible during the first load for everyone, like the newer sections do: the empty message list takes focus right away and the container-parking branch goes away entirely. With a screen reader the list announces the focused message once the first slice of messages arrives, the same as when opening an already loaded chat.
This commit is contained in:
committed by
John Preston
parent
a82e5156b8
commit
12e79ed954
@@ -840,6 +840,13 @@ void HistoryInner::messagesReceived(
|
||||
_migrated->addNewerSlice(QVector<MTPMessage>());
|
||||
}
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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<HistoryView::MessageSubItem> &;
|
||||
@@ -571,6 +572,7 @@ private:
|
||||
int _accessibilityFocusedIndex = -1;
|
||||
HistoryItem *_accessibilityFocusedItem = nullptr;
|
||||
HistoryItem *_accessibilitySelectionAnchor = nullptr;
|
||||
bool _announceFirstMessages = false;
|
||||
mutable base::flat_map<
|
||||
not_null<const HistoryItem*>,
|
||||
quintptr> _accessibilityIdentities;
|
||||
|
||||
@@ -3175,7 +3175,6 @@ void HistoryWidget::showHistory(
|
||||
}
|
||||
}
|
||||
|
||||
_scroll->hide();
|
||||
_list = _scroll->setOwnedWidget(
|
||||
object_ptr<HistoryInner>(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);
|
||||
|
||||
Reference in New Issue
Block a user