From c789349b244535d1a595b78d90cad6430556fc17 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 19 Jul 2021 20:01:39 +0300 Subject: [PATCH] Support add item animation in ListWidget (sections). --- .../SourceFiles/history/history_widget.cpp | 41 +++---- Telegram/SourceFiles/history/history_widget.h | 4 +- .../history/view/history_view_list_widget.cpp | 101 ++++++++++++++++-- .../history/view/history_view_list_widget.h | 16 ++- 4 files changed, 133 insertions(+), 29 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 53ba504b0..a0b39cbc8 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -154,7 +154,6 @@ constexpr auto kSaveDraftTimeout = 1000; constexpr auto kSaveDraftAnywayTimeout = 5000; constexpr auto kSaveCloudDraftIdleTimeout = 14000; constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200); -constexpr auto kItemRevealDuration = crl::time(200); constexpr auto kCommonModifiers = 0 | Qt::ShiftModifier | Qt::MetaModifier @@ -4688,6 +4687,10 @@ void HistoryWidget::itemRemoved(not_null item) { _itemRevealAnimations.erase(i); revealItemsCallback(); } + const auto j = _itemRevealPending.find(item); + if (j != _itemRevealPending.end()) { + _itemRevealPending.erase(j); + } } void HistoryWidget::itemEdited(not_null item) { @@ -4904,35 +4907,35 @@ void HistoryWidget::revealItemsCallback() { } } -void HistoryWidget::updateListSize() { - _list->recountHistoryGeometry(); - auto washidden = _scroll->isHidden(); - if (washidden) { - _scroll->show(); - } +void HistoryWidget::startItemRevealAnimations() { for (const auto item : base::take(_itemRevealPending)) { if (const auto view = item->mainView()) { if (const auto top = _list->itemTop(view); top >= 0) { if (const auto height = view->height()) { if (!_itemRevealAnimations.contains(item)) { auto &animation = _itemRevealAnimations[item]; - if (!animation.animation.animating()) { - animation.startHeight - = animation.currentHeight - = height; - _itemsRevealHeight += height; - animation.animation.start( - [=] { revealItemsCallback(); }, - 0., - 1., - kItemRevealDuration, - anim::easeOutCirc); - } + animation.startHeight = height; + _itemsRevealHeight += height; + animation.animation.start( + [=] { revealItemsCallback(); }, + 0., + 1., + HistoryView::kItemRevealDuration, + anim::easeOutCirc); } } } } } +} + +void HistoryWidget::updateListSize() { + _list->recountHistoryGeometry(); + auto washidden = _scroll->isHidden(); + if (washidden) { + _scroll->show(); + } + startItemRevealAnimations(); _list->setItemsRevealHeight(_itemsRevealHeight); _list->updateSize(); if (washidden) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index b298bd204..88469d6c7 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -323,9 +323,8 @@ private: bool active = false; }; struct ItemRevealAnimation { - int startHeight = 0; - int currentHeight = 0; Ui::Animations::Simple animation; + int startHeight = 0; }; enum class TextUpdateEvent { SaveDraft = (1 << 0), @@ -536,6 +535,7 @@ private: void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 }); void updateListSize(); + void startItemRevealAnimations(); void revealItemsCallback(); // Does any of the shown histories has this flag set. diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index e1be2af7c..294b66f70 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -340,14 +340,24 @@ void ListWidget::refreshViewer() { _idsLimit, _idsLimit ) | rpl::start_with_next([=](Data::MessagesSlice &&slice) { - _slice = std::move(slice); - refreshRows(); + std::swap(_slice, slice); + refreshRows(slice); }, _viewerLifetime); } -void ListWidget::refreshRows() { +void ListWidget::refreshRows(const Data::MessagesSlice &old) { saveScrollState(); + const auto addedToEndFrom = (old.skippedAfter == 0 + && (_slice.skippedAfter == 0) + && !old.ids.empty()) + ? ranges::find(_slice.ids, old.ids.back()) + : end(_slice.ids); + const auto addedToEndCount = std::max( + int(end(_slice.ids) - addedToEndFrom), + 1 + ) - 1; + _items.clear(); _items.reserve(_slice.ids.size()); auto nearestIndex = -1; @@ -359,12 +369,17 @@ void ListWidget::refreshRows() { _items.push_back(enforceViewForItem(item)); } } + for (auto e = end(_items), i = e - addedToEndCount; i != e; ++i) { + _itemRevealPending.emplace(*i); + } updateAroundPositionFromNearest(nearestIndex); updateItemsGeometry(); checkUnreadBarCreation(); restoreScrollState(); - mouseActionUpdate(QCursor::pos()); + if (!_itemsRevealHeight) { + mouseActionUpdate(QCursor::pos()); + } _delegate->listContentRefreshed(); } @@ -1124,7 +1139,9 @@ bool ListWidget::loadedAtBottom() const { } bool ListWidget::isEmpty() const { - return loadedAtTop() && loadedAtBottom(); + return loadedAtTop() + && loadedAtBottom() + && (_itemsHeight + _itemsRevealHeight == 0); } int ListWidget::itemMinimalHeight() const { @@ -1405,6 +1422,60 @@ void ListWidget::resizeToWidth(int newWidth, int minHeight) { restoreScrollPosition(); } +void ListWidget::startItemRevealAnimations() { + for (const auto view : base::take(_itemRevealPending)) { + if (const auto height = view->height()) { + if (!_itemRevealAnimations.contains(view)) { + auto &animation = _itemRevealAnimations[view]; + animation.startHeight = height; + _itemsRevealHeight += height; + animation.animation.start( + [=] { revealItemsCallback(); }, + 0., + 1., + kItemRevealDuration, + anim::easeOutCirc); + } + } + } +} + +void ListWidget::revealItemsCallback() { + auto revealHeight = 0; + for (auto i = begin(_itemRevealAnimations) + ; i != end(_itemRevealAnimations);) { + if (!i->second.animation.animating()) { + i = _itemRevealAnimations.erase(i); + } else { + revealHeight += anim::interpolate( + i->second.startHeight, + 0, + i->second.animation.value(1.)); + ++i; + } + } + if (_itemsRevealHeight != revealHeight) { + saveScrollState(); + const auto old = std::exchange(_itemsRevealHeight, revealHeight); + const auto delta = old - _itemsRevealHeight; + _itemsHeight += delta; + _itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) + ? (_minHeight - _itemsHeight - st::historyPaddingBottom) + : 0; + const auto wasHeight = height(); + const auto nowHeight = std::min(_minHeight, wasHeight + delta); + if (wasHeight != nowHeight) { + resize(width(), nowHeight); + } + update(); + restoreScrollState(); + + if (!_itemsRevealHeight) { + mouseActionUpdate(QCursor::pos()); + } + } +} + int ListWidget::resizeGetHeight(int newWidth) { update(); @@ -1423,8 +1494,9 @@ int ListWidget::resizeGetHeight(int newWidth) { itemMinimalHeight(), newHeight / int(_items.size())); } + startItemRevealAnimations(); _itemsWidth = newWidth; - _itemsHeight = newHeight; + _itemsHeight = newHeight - _itemsRevealHeight; _itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) ? (_minHeight - _itemsHeight - st::historyPaddingBottom) : 0; @@ -2726,6 +2798,23 @@ void ListWidget::viewReplaced(not_null was, Element *now) { _bar.element->createUnreadBar(_barText.value()); } } + const auto i = _itemRevealPending.find(was); + if (i != end(_itemRevealPending)) { + _itemRevealPending.erase(i); + if (now) { + _itemRevealPending.emplace(now); + } + } + const auto j = _itemRevealAnimations.find(was); + if (j != end(_itemRevealAnimations)) { + auto data = std::move(j->second); + _itemRevealAnimations.erase(j); + if (now) { + _itemRevealAnimations.emplace(now, std::move(data)); + } else { + revealItemsCallback(); + } + } } void ListWidget::itemRemoved(not_null item) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 0c26dd368..a46041f58 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -143,6 +143,8 @@ private: }; +inline constexpr auto kItemRevealDuration = crl::time(150); + class ListWidget final : public Ui::RpWidget , public ElementDelegate @@ -296,7 +298,10 @@ private: inline bool operator!=(const MouseState &other) const { return !(*this == other); } - + }; + struct ItemRevealAnimation { + Ui::Animations::Simple animation; + int startHeight = 0; }; enum class Direction { Up, @@ -329,7 +334,7 @@ private: void refreshViewer(); void updateAroundPositionFromNearest(int nearestIndex); - void refreshRows(); + void refreshRows(const Data::MessagesSlice &old); ScrollTopState countScrollState() const; void saveScrollState(); void restoreScrollState(); @@ -458,6 +463,8 @@ private: void checkUnreadBarCreation(); void applyUpdatedScrollState(); void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo); + void startItemRevealAnimations(); + void revealItemsCallback(); void updateHighlightedMessage(); void clearHighlightedMessage(); @@ -505,6 +512,11 @@ private: int _itemsWidth = 0; int _itemsHeight = 0; int _itemAverageHeight = 0; + base::flat_set> _itemRevealPending; + base::flat_map< + not_null, + ItemRevealAnimation> _itemRevealAnimations; + int _itemsRevealHeight = 0; base::flat_set _animatedStickersPlayed; base::flat_map< not_null,