Improve initial visible scroll handling.

This commit is contained in:
John Preston
2026-07-17 16:24:06 +04:00
parent fc1b6a1d1a
commit c46eb8a8a1
3 changed files with 55 additions and 18 deletions
@@ -458,6 +458,14 @@ HistoryInner::HistoryInner(
) | rpl::on_next(
[this](auto item) { itemRemoved(item); },
lifetime());
session().data().newItemAdded(
) | rpl::filter([=](not_null<HistoryItem*> item) {
const auto history = item->history();
return (history == _history)
|| (_migrated && history == _migrated);
}) | rpl::on_next([=] {
checkAnnounceFirstMessages();
}, lifetime());
setupThanosEffect();
session().data().viewRemoved(
) | rpl::on_next(
@@ -840,13 +848,7 @@ void HistoryInner::messagesReceived(
_migrated->addNewerSlice(QVector<MTPMessage>());
}
}
if (_announceFirstMessages && hasFocus()) {
InvokeQueued(this, [=] {
if (_announceFirstMessages && hasFocus()) {
announceAccessibilityFocusedChild();
}
});
}
checkAnnounceFirstMessages();
}
void HistoryInner::messagesReceivedDown(
@@ -6626,17 +6628,30 @@ auto HistoryInner::computeActiveColumns(int row) const
return _activeColumns;
}
void HistoryInner::checkAnnounceFirstMessages() {
if (_announceFirstMessages && hasFocus()) {
InvokeQueued(this, [=] {
if (_announceFirstMessages && hasFocus()) {
announceAccessibilityFocusedChild();
}
});
}
}
void HistoryInner::announceAccessibilityFocusedChild() {
const auto count = accessibilityChildCount();
if (count <= 0) {
// One-shot for chats focused before their first page arrived:
// One-shot for chats focused before their first messages 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;
}
// received slice (or the first live-added message in a genuinely
// empty chat) should announce the focused message. Fired (queued)
// from checkAnnounceFirstMessages and disarmed by any real
// announcement below or in a later focus-in. Deliberately not
// gated by the screen-reader-mode detector: it may still be false
// during startup or for valid clients that are not on its
// allowlist, while the deferred announcement is a no-op without
// an accessibility client and never moves ordinary keyboard focus.
_announceFirstMessages = true;
return;
}
_announceFirstMessages = false;
@@ -323,6 +323,7 @@ private:
void playPauseFocusedMedia();
void setAccessibilityFocusedItem(int index, HistoryItem *item);
void announceAccessibilityFocus(int index);
void checkAnnounceFirstMessages();
void announceAccessibilityFocusedChild();
void applyAccessibilityFocus(int index, bool announceAlways);
[[nodiscard]] auto computeActiveColumns(int row) const
@@ -393,7 +393,9 @@ HistoryWidget::HistoryWidget(
}), lifetime());
_scroll->setBottomContentRequest([=] {
if (!_history || !_history->loadedAtBottom()) {
if (!_history
|| _firstLoadRequest
|| !_history->loadedAtBottom()) {
return false;
}
using Result = Data::SponsoredMessages::AppendResult;
@@ -4797,6 +4799,8 @@ void HistoryWidget::loadMessages() {
bool HistoryWidget::historyLoadedAtTop() const {
if (!_history) {
return true;
} else if (_firstLoadRequest) {
return false;
}
const auto loadMigrated = _migrated
&& (_history->isEmpty()
@@ -4807,8 +4811,16 @@ bool HistoryWidget::historyLoadedAtTop() const {
}
bool HistoryWidget::historyLoadedAtBottom() const {
// While the first load request is pending the (visible) scroll area
// shows a blank list, but getReadyFor() may have already marked the
// history as loaded at bottom before any server page arrived. Report
// unloaded edges for that interval: it keeps the elastic overscroll
// (and the pull-to-next-channel gesture riding on it) away from the
// blank list until the requested messages are actually shown.
if (!_history) {
return true;
} else if (_firstLoadRequest) {
return false;
}
const auto loadMigrated = _migrated
&& !(_migrated->isEmpty()
@@ -8109,12 +8121,11 @@ void HistoryWidget::updateHistoryGeometry(
});
if (!_history
|| (initial && _historyInited)
|| (!initial && !_historyInited)) {
|| (!initial && !_historyInited && !_firstLoadRequest)) {
return;
}
if (_firstLoadRequest || _showAnimation) {
if (_showAnimation) {
_updateHistoryGeometryRequired = true;
// scrollTopMax etc are not working after recountHistoryGeometry()
return;
}
@@ -8211,6 +8222,16 @@ void HistoryWidget::updateHistoryGeometry(
_subsectionTabs->setBoundingRect(
{ 0, subsectionTabsTop, width(), areaHeight });
}
if (_firstLoadRequest) {
// The scroll area stays visible (and possibly focused) while the
// first messages are being loaded, so its viewport geometry above
// is maintained even now. The list layout and the scroll position
// are still deferred until the requested messages arrive:
// scrollTopMax etc are not working after recountHistoryGeometry()
// and the initial scroll position can not be counted yet.
_updateHistoryGeometryRequired = true;
return;
}
updateListSize();
_updateHistoryGeometryRequired = false;