mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Support add item animation in ListWidget (sections).
This commit is contained in:
parent
7bbc4b7191
commit
c789349b24
4 changed files with 133 additions and 29 deletions
|
@ -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<const HistoryItem*> item) {
|
|||
_itemRevealAnimations.erase(i);
|
||||
revealItemsCallback();
|
||||
}
|
||||
const auto j = _itemRevealPending.find(item);
|
||||
if (j != _itemRevealPending.end()) {
|
||||
_itemRevealPending.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::itemEdited(not_null<HistoryItem*> 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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<const Element*> 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<const HistoryItem*> item) {
|
||||
|
|
|
@ -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<not_null<Element*>> _itemRevealPending;
|
||||
base::flat_map<
|
||||
not_null<Element*>,
|
||||
ItemRevealAnimation> _itemRevealAnimations;
|
||||
int _itemsRevealHeight = 0;
|
||||
base::flat_set<FullMsgId> _animatedStickersPlayed;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
|
|
Loading…
Add table
Reference in a new issue