diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 407d91ce11..cdd19e22df 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -676,54 +676,56 @@ void HistoryWidget::scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId void HistoryWidget::animatedScrollToItem(MsgId msgId) { Expects(_history != nullptr); - auto animatedScrollAttachedToItem = [this](HistoryItem *item, int scrollTo) { - auto itemTop = _list->itemTop(item); - if (itemTop < 0) { - return false; - } + auto to = App::histItemById(_channel, msgId); + if (_list->itemTop(to) < 0) { + return; + } - auto maxAnimatedDelta = _scroll->height(); - auto transition = anim::sineInOut; - auto scrollTop = _scroll->scrollTop(); - if (scrollTo > scrollTop + maxAnimatedDelta) { - scrollTop = scrollTo - maxAnimatedDelta; - synteticScrollToY(scrollTop); - transition = anim::easeOutCubic; - } else if (scrollTo + maxAnimatedDelta < scrollTop) { - scrollTop = scrollTo + maxAnimatedDelta; - synteticScrollToY(scrollTop); - transition = anim::easeOutCubic; - } - _scrollToMediaMessageAnimation.finish(); - _scrollToMediaMessageAnimation.start([this, itemId = item->fullId()] { - auto itemTop = _list->itemTop(App::histItemById(itemId)); - if (itemTop < 0) { - _scrollToMediaMessageAnimation.finish(); - } else { - synteticScrollToY(qRound(_scrollToMediaMessageAnimation.current()) + itemTop); - } - }, scrollTop - itemTop, scrollTo - itemTop, st::slideDuration, anim::sineInOut); - return true; - }; + auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax()); + animatedScrollToY(scrollTo, to); +} - if (msgId == ShowAtUnreadMsgId) { - // Special case "scroll to bottom". - auto scrollTo = _scroll->scrollTopMax(); +void HistoryWidget::animatedScrollToY(int scrollTo, HistoryItem *attachTo) { + Expects(_history != nullptr); - // Attach our scroll animation to some item. - auto item = _history->isEmpty() ? nullptr : _history->blocks.back()->items.back(); - if (animatedScrollAttachedToItem(item, scrollTo)) { - return; - } - - // If something went wrong we just scroll without animation. + // Attach our scroll animation to some item. + auto itemTop = _list->itemTop(attachTo); + if (itemTop < 0 && !_history->isEmpty()) { + attachTo = _history->blocks.back()->items.back(); + itemTop = _list->itemTop(attachTo); + } + if (itemTop < 0) { synteticScrollToY(scrollTo); return; } - auto to = App::histItemById(_channel, msgId); - auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax()); - animatedScrollAttachedToItem(to, scrollTo); + _scrollToAnimation.finish(); + auto maxAnimatedDelta = _scroll->height(); + auto transition = anim::sineInOut; + auto scrollTop = _scroll->scrollTop(); + if (scrollTo > scrollTop + maxAnimatedDelta) { + scrollTop = scrollTo - maxAnimatedDelta; + synteticScrollToY(scrollTop); + transition = anim::easeOutCubic; + } else if (scrollTo + maxAnimatedDelta < scrollTop) { + scrollTop = scrollTo + maxAnimatedDelta; + synteticScrollToY(scrollTop); + transition = anim::easeOutCubic; + } + _scrollToAnimation.start([this, itemId = attachTo->fullId()] { scrollToAnimationCallback(itemId); }, scrollTop - itemTop, scrollTo - itemTop, st::slideDuration, anim::sineInOut); +} + +void HistoryWidget::scrollToAnimationCallback(FullMsgId attachToId) { + auto itemTop = _list->itemTop(App::histItemById(attachToId)); + if (itemTop < 0) { + _scrollToAnimation.finish(); + } else { + synteticScrollToY(qRound(_scrollToAnimation.current()) + itemTop); + } + if (!_scrollToAnimation.animating()) { + preloadHistoryByScroll(); + checkReplyReturns(); + } } void HistoryWidget::highlightMessage(HistoryItem *context) { @@ -744,8 +746,10 @@ void HistoryWidget::highlightMessage(HistoryItem *context) { } } -int HistoryWidget::itemTopForHighlight(HistoryItem *item) const { +int HistoryWidget::itemTopForHighlight(gsl::not_null item) const { auto itemTop = _list->itemTop(item); + t_assert(itemTop >= 0); + auto heightLeft = (_scroll->height() - item->height()); if (heightLeft <= 0) { return itemTop; @@ -1095,10 +1099,10 @@ void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) { void HistoryWidget::activate() { if (_history) { - if (!_histInited) { - updateListSize(true); + if (!_historyInited) { + updateHistoryGeometry(true); } else if (hasPendingResizedItems()) { - updateListSize(); + updateHistoryGeometry(); } } if (App::wnd()) App::wnd()->setInnerFocus(); @@ -1249,7 +1253,7 @@ void HistoryWidget::notify_migrateUpdated(PeerData *peer) { } else { _migrated = migrated; _list->notifyMigrateUpdated(); - updateListSize(); + updateHistoryGeometry(); } } } else if (_migrated && _migrated->peer == peer && peer->migrateTo() != _peer) { @@ -1697,7 +1701,7 @@ void HistoryWidget::fastShowAtEnd(History *h) { clearAllLoadRequests(); setMsgId(ShowAtUnreadMsgId); - _histInited = false; + _historyInited = false; if (h->isReadyFor(_showAtMsgId)) { historyLoaded(); @@ -1788,9 +1792,10 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } setMsgId(showAtMsgId); - if (_histInited) { - animatedScrollToItem(_showAtMsgId); - highlightMessage(App::histItemById(_channel, _showAtMsgId)); + if (_historyInited) { + auto item = getItemFromHistoryOrMigrated(_showAtMsgId); + animatedScrollToY(countInitialScrollTop(), item); + highlightMessage(item); } else { historyLoaded(); } @@ -1829,6 +1834,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re destroyUnreadBar(); destroyPinnedBar(); + _scrollToAnimation.finish(); _history = _migrated = nullptr; _peer = nullptr; _channel = NoChannel; @@ -1853,7 +1859,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re clearInlineBot(); _showAtMsgId = showAtMsgId; - _histInited = false; + _historyInited = false; if (peerId) { _peer = App::peer(peerId); @@ -2517,7 +2523,7 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages setMsgId(_delayedShowAtMsgId); - _histInited = false; + _historyInited = false; historyLoaded(); } } @@ -2723,35 +2729,63 @@ void HistoryWidget::visibleAreaUpdated() { } void HistoryWidget::preloadHistoryIfNeeded() { - if (_firstLoadRequest || _scroll->isHidden() || !_peer) return; + if (_firstLoadRequest || _scroll->isHidden() || !_peer) { + return; + } updateHistoryDownVisibility(); + if (!_scrollToAnimation.animating()) { + preloadHistoryByScroll(); + checkReplyReturns(); + } - int st = _scroll->scrollTop(), stm = _scroll->scrollTopMax(), sh = _scroll->height(); - if (st + kPreloadHeightsCount * sh >= stm) { + auto scrollTop = _scroll->scrollTop(); + if (scrollTop != _lastScrollTop) { + _lastScrolled = getms(); + _lastScrollTop = scrollTop; + } +} + +void HistoryWidget::preloadHistoryByScroll() { + if (_firstLoadRequest || _scroll->isHidden() || !_peer) { + return; + } + + auto scrollTop = _scroll->scrollTop(); + auto scrollTopMax = _scroll->scrollTopMax(); + auto scrollHeight = _scroll->height(); + if (scrollTop + kPreloadHeightsCount * scrollHeight >= scrollTopMax) { loadMessagesDown(); } - - if (st <= kPreloadHeightsCount * sh) { + if (scrollTop <= kPreloadHeightsCount * scrollHeight) { loadMessages(); } +} +void HistoryWidget::checkReplyReturns() { + if (_firstLoadRequest || _scroll->isHidden() || !_peer) { + return; + } + auto scrollTop = _scroll->scrollTop(); + auto scrollTopMax = _scroll->scrollTopMax(); + auto scrollHeight = _scroll->height(); while (_replyReturn) { - bool below = (_replyReturn->detached() && _replyReturn->history() == _history && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->items.back()->id); - if (!below) below = (_replyReturn->detached() && _replyReturn->history() == _migrated && !_history->isEmpty()); - if (!below) below = (_replyReturn->detached() && _migrated && _replyReturn->history() == _migrated && !_migrated->isEmpty() && _replyReturn->id < _migrated->blocks.back()->items.back()->id); - if (!below && !_replyReturn->detached()) below = (st >= stm) || (_list->itemTop(_replyReturn) < st + sh / 2); + auto below = (_replyReturn->detached() && _replyReturn->history() == _history && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->items.back()->id); + if (!below) { + below = (_replyReturn->detached() && _replyReturn->history() == _migrated && !_history->isEmpty()); + } + if (!below) { + below = (_replyReturn->detached() && _migrated && _replyReturn->history() == _migrated && !_migrated->isEmpty() && _replyReturn->id < _migrated->blocks.back()->items.back()->id); + } + if (!below && !_replyReturn->detached()) { + below = (scrollTop >= scrollTopMax) || (_list->itemTop(_replyReturn) < scrollTop + scrollHeight / 2); + } if (below) { calcNextReplyReturn(); } else { break; } } - - if (st != _lastScrollTop) { - _lastScrolled = getms(); - _lastScrollTop = st; - } } void HistoryWidget::onInlineBotCancel() { @@ -3113,10 +3147,10 @@ void HistoryWidget::doneShow() { updateReportSpamStatus(); updateBotKeyboard(); updateControlsVisibility(); - if (!_histInited) { - updateListSize(true); + if (!_historyInited) { + updateHistoryGeometry(true); } else if (hasPendingResizedItems()) { - updateListSize(); + updateHistoryGeometry(); } preloadHistoryIfNeeded(); if (App::wnd()) { @@ -4150,7 +4184,7 @@ void HistoryWidget::inlineBotChanged() { void HistoryWidget::onFieldResize() { moveFieldControls(); - updateListSize(); + updateHistoryGeometry(); updateField(); } @@ -4637,7 +4671,7 @@ void HistoryWidget::onReportSpamClear() { void HistoryWidget::peerMessagesUpdated(PeerId peer) { if (_peer && _list && peer == _peer->id) { - updateListSize(); + updateHistoryGeometry(); updateBotKeyboard(); if (!_scroll->isHidden()) { bool unblock = isBlocked(), botStart = isBotStart(), joinChannel = isJoinChannel(), muteUnmute = isMuteUnmute(); @@ -4712,8 +4746,8 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { } void HistoryWidget::notify_handlePendingHistoryUpdate() { - if (hasPendingResizedItems()) { - updateListSize(); + if (hasPendingResizedItems() || _updateHistoryGeometryRequired) { + updateHistoryGeometry(); _list->update(); } } @@ -4746,7 +4780,7 @@ void HistoryWidget::updateControlsGeometry() { _reportSpamPanel->setGeometryToLeft(0, _scroll->y(), _chatWidth, _reportSpamPanel->height()); } - updateListSize(false, false, { ScrollChangeAdd, App::main() ? App::main()->contentScrollAddToY() : 0 }); + updateHistoryGeometry(false, false, { ScrollChangeAdd, App::main() ? App::main()->contentScrollAddToY() : 0 }); updateFieldSize(); @@ -4818,8 +4852,64 @@ MsgId HistoryWidget::replyToId() const { return _replyToId ? _replyToId : (_kbReplyTo ? _kbReplyTo->id : 0); } -void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollChange &change) { - if (!_history || (initial && _histInited) || (!initial && !_histInited)) return; +int HistoryWidget::countInitialScrollTop() { + auto result = ScrollMax; + if (_history->scrollTopItem || (_migrated && _migrated->scrollTopItem)) { + result = _list->historyScrollTop(); + } else if (_showAtMsgId && (_showAtMsgId > 0 && -_showAtMsgId < ServerMaxMsgId)) { + auto item = getItemFromHistoryOrMigrated(_showAtMsgId); + auto itemTop = _list->itemTop(item); + if (itemTop < 0) { + setMsgId(0); + return countInitialScrollTop(); + } else { + result = itemTopForHighlight(item); + highlightMessage(item); + } + } else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) { + result = unreadBarTop(); + } else { + return countAutomaticScrollTop(); + } + return qMin(result, _scroll->scrollTopMax()); +} + +int HistoryWidget::countAutomaticScrollTop() { + auto result = ScrollMax; + if (_migrated && _migrated->showFrom) { + result = _list->itemTop(_migrated->showFrom); + if (result < _scroll->scrollTopMax() + HistoryMessageUnreadBar::height() - HistoryMessageUnreadBar::marginTop()) { + _migrated->addUnreadBar(); + if (hasPendingResizedItems()) { + updateListSize(); + } + if (_migrated->unreadBar) { + setMsgId(ShowAtUnreadMsgId); + result = countInitialScrollTop(); + App::wnd()->checkHistoryActivation(); + return result; + } + } + } else if (_history->showFrom) { + result = _list->itemTop(_history->showFrom); + if (result < _scroll->scrollTopMax() + HistoryMessageUnreadBar::height() - HistoryMessageUnreadBar::marginTop()) { + _history->addUnreadBar(); + if (hasPendingResizedItems()) { + updateListSize(); + } + if (_history->unreadBar) { + setMsgId(ShowAtUnreadMsgId); + result = countInitialScrollTop(); + App::wnd()->checkHistoryActivation(); + return result; + } + } + } + return qMin(result, _scroll->scrollTopMax()); +} + +void HistoryWidget::updateHistoryGeometry(bool initial, bool loadedDown, const ScrollChange &change) { + if (!_history || (initial && _historyInited) || (!initial && !_historyInited)) return; if (_firstLoadRequest || _a_show.animating()) { return; // scrollTopMax etc are not working after recountHeight() } @@ -4858,16 +4948,8 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh controller()->floatPlayerAreaUpdated().notify(true); } - _list->recountHeight(); - - bool washidden = _scroll->isHidden(); - if (washidden) { - _scroll->show(); - } - _list->updateSize(); - if (washidden) { - _scroll->hide(); - } + updateListSize(); + _updateHistoryGeometryRequired = false; if ((!initial && !wasAtBottom) || (loadedDown && (!_history->showFrom || _history->unreadBar || _history->loadedAtBottom()) && (!_migrated || !_migrated->showFrom || _migrated->unreadBar || _history->loadedAtBottom()))) { int toY = _list->historyScrollTop(); @@ -4891,71 +4973,29 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh } if (initial) { - _histInited = true; + _historyInited = true; } - - int32 toY = ScrollMax; - if (initial && (_history->scrollTopItem || (_migrated && _migrated->scrollTopItem))) { - toY = _list->historyScrollTop(); - } else if (initial && _migrated && _showAtMsgId < 0 && -_showAtMsgId < ServerMaxMsgId) { - auto item = App::histItemById(0, -_showAtMsgId); - auto iy = _list->itemTop(item); - if (iy < 0) { - setMsgId(0); - _histInited = false; - return updateListSize(initial, false, change); - } else { - toY = itemTopForHighlight(item); - highlightMessage(item); - } - } else if (initial && _showAtMsgId > 0) { - auto item = App::histItemById(_channel, _showAtMsgId); - auto iy = _list->itemTop(item); - if (iy < 0) { - setMsgId(0); - _histInited = false; - return updateListSize(initial, false, change); - } else { - toY = itemTopForHighlight(item); - highlightMessage(item); - } - } else if (initial && (_history->unreadBar || (_migrated && _migrated->unreadBar))) { - toY = unreadBarTop(); - } else if (_migrated && _migrated->showFrom) { - toY = _list->itemTop(_migrated->showFrom); - if (toY < _scroll->scrollTopMax() + HistoryMessageUnreadBar::height() - HistoryMessageUnreadBar::marginTop()) { - _migrated->addUnreadBar(); - if (_migrated->unreadBar) { - setMsgId(ShowAtUnreadMsgId); - _histInited = false; - updateListSize(true); - App::wnd()->checkHistoryActivation(); - return; - } - } - } else if (_history->showFrom) { - toY = _list->itemTop(_history->showFrom); - if (toY < _scroll->scrollTopMax() + st::historyUnreadBarHeight) { - _history->addUnreadBar(); - if (_history->unreadBar) { - setMsgId(ShowAtUnreadMsgId); - _histInited = false; - updateListSize(true); - App::wnd()->checkHistoryActivation(); - return; - } - } - } else { - } - auto scrollMax = _scroll->scrollTopMax(); - accumulate_min(toY, scrollMax); - if (_scroll->scrollTop() == toY) { + auto newScrollTop = initial ? countInitialScrollTop() : countAutomaticScrollTop(); + if (_scroll->scrollTop() == newScrollTop) { visibleAreaUpdated(); } else { - synteticScrollToY(toY); + synteticScrollToY(newScrollTop); } } +void HistoryWidget::updateListSize() { + _list->recountHeight(); + auto washidden = _scroll->isHidden(); + if (washidden) { + _scroll->show(); + } + _list->updateSize(); + if (washidden) { + _scroll->hide(); + } + _updateHistoryGeometryRequired = true; +} + int HistoryWidget::unreadBarTop() const { auto getUnreadBar = [this]() -> HistoryItem* { if (_migrated && _migrated->unreadBar) { @@ -4979,9 +5019,9 @@ int HistoryWidget::unreadBarTop() const { void HistoryWidget::addMessagesToFront(PeerData *peer, const QVector &messages) { _list->messagesReceived(peer, messages); if (!_firstLoadRequest) { - updateListSize(); + updateHistoryGeometry(); if (_animActiveTimer.isActive() && _activeAnimMsgId > 0 && _migrated && !_migrated->isEmpty() && _migrated->loadedAtBottom() && _migrated->blocks.back()->items.back()->isGroupMigrate() && _list->historyTop() != _list->historyDrawTop() && _history) { - HistoryItem *animActiveItem = App::histItemById(_history->channelId(), _activeAnimMsgId); + auto animActiveItem = App::histItemById(_history->channelId(), _activeAnimMsgId); if (animActiveItem && animActiveItem->isGroupMigrate()) { _activeAnimMsgId = -_migrated->blocks.back()->items.back()->id; } @@ -4993,7 +5033,7 @@ void HistoryWidget::addMessagesToFront(PeerData *peer, const QVector void HistoryWidget::addMessagesToBack(PeerData *peer, const QVector &messages) { _list->messagesReceivedDown(peer, messages); if (!_firstLoadRequest) { - updateListSize(false, true, { ScrollChangeNoJumpToBottom, 0 }); + updateHistoryGeometry(false, true, { ScrollChangeNoJumpToBottom, 0 }); } } @@ -5976,7 +6016,7 @@ void HistoryWidget::peerUpdated(PeerData *data) { if (pinnedMsgVisibilityUpdated()) { resize = true; } - updateListSize(); + updateHistoryGeometry(); if (_peer->isChannel()) updateReportSpamStatus(); if (App::api()) { if (data->isChat() && data->asChat()->noParticipantInfo()) { @@ -6102,13 +6142,22 @@ void HistoryWidget::onClearSelected() { if (_list) _list->clearSelectedItems(); } +HistoryItem *HistoryWidget::getItemFromHistoryOrMigrated(MsgId genericMsgId) const { + if (genericMsgId < 0 && -genericMsgId < ServerMaxMsgId && _migrated) { + return App::histItemById(_migrated->channelId(), -genericMsgId); + } + return App::histItemById(_channel, genericMsgId); +} + void HistoryWidget::onAnimActiveStep() { if (!_history || !_activeAnimMsgId || (_activeAnimMsgId < 0 && (!_migrated || -_activeAnimMsgId >= ServerMaxMsgId))) { return _animActiveTimer.stop(); } - HistoryItem *item = (_activeAnimMsgId < 0 && -_activeAnimMsgId < ServerMaxMsgId && _migrated) ? App::histItemById(_migrated->channelId(), -_activeAnimMsgId) : App::histItemById(_channel, _activeAnimMsgId); - if (!item || item->detached()) return _animActiveTimer.stop(); + auto item = getItemFromHistoryOrMigrated(_activeAnimMsgId); + if (!item || item->detached()) { + return _animActiveTimer.stop(); + } if (getms() - _animActiveStart > st::activeFadeInDuration + st::activeFadeOutDuration) { stopAnimActive(); @@ -6144,7 +6193,7 @@ void HistoryWidget::updateTopBarSelection() { _nonEmptySelection = (selectedState.count > 0) || selectedState.textSelected; _topBar->showSelected(selectedState); updateControlsVisibility(); - updateListSize(); + updateHistoryGeometry(); if (!Ui::isLayerShown() && !App::passcoded()) { if (_nonEmptySelection || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 9c0c8e1a2b..575b5db305 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -636,7 +636,8 @@ private: ScrollChangeType type; int value; }; - void updateListSize(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 }); + void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 }); + void updateListSize(); // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const { @@ -646,7 +647,7 @@ private: // Counts scrollTop for placing the scroll right at the unread // messages bar, choosing from _history and _migrated unreadBar. int unreadBarTop() const; - int itemTopForHighlight(HistoryItem *item) const; + int itemTopForHighlight(gsl::not_null item) const; void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId); // Scroll to current y without updating the _lastUserScrolled time. @@ -698,13 +699,20 @@ private: setFieldText(TextWithTags(), events, undoHistoryAction); } + HistoryItem *getItemFromHistoryOrMigrated(MsgId genericMsgId) const; void animatedScrollToItem(MsgId msgId); + void animatedScrollToY(int scrollTo, HistoryItem *attachTo = nullptr); void highlightMessage(HistoryItem *context); void updateDragAreas(); // when scroll position or scroll area size changed this method // updates the boundings of the visible area in HistoryInner void visibleAreaUpdated(); + int countInitialScrollTop(); + int countAutomaticScrollTop(); + void preloadHistoryByScroll(); + void checkReplyReturns(); + void scrollToAnimationCallback(FullMsgId attachToId); bool readyToForward() const; bool hasSilentToggle() const; @@ -730,7 +738,8 @@ private: QPointer _list; History *_migrated = nullptr; History *_history = nullptr; - bool _histInited = false; // initial updateListSize() called + bool _historyInited = false; // Initial updateHistoryGeometry() was called. + bool _updateHistoryGeometryRequired = false; // If updateListSize() was called without updateHistoryGeometry(). int _addToScroll = 0; int _lastScrollTop = 0; // gifs optimization @@ -739,7 +748,7 @@ private: TimeMs _lastUserScrolled = 0; bool _synteticScrollEvent = false; - Animation _scrollToMediaMessageAnimation; + Animation _scrollToAnimation; Animation _historyDownShown; bool _historyDownIsShown = false;