mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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 kSaveDraftAnywayTimeout = 5000;
|
||||||
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
|
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
|
||||||
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
||||||
constexpr auto kItemRevealDuration = crl::time(200);
|
|
||||||
constexpr auto kCommonModifiers = 0
|
constexpr auto kCommonModifiers = 0
|
||||||
| Qt::ShiftModifier
|
| Qt::ShiftModifier
|
||||||
| Qt::MetaModifier
|
| Qt::MetaModifier
|
||||||
|
@ -4688,6 +4687,10 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
_itemRevealAnimations.erase(i);
|
_itemRevealAnimations.erase(i);
|
||||||
revealItemsCallback();
|
revealItemsCallback();
|
||||||
}
|
}
|
||||||
|
const auto j = _itemRevealPending.find(item);
|
||||||
|
if (j != _itemRevealPending.end()) {
|
||||||
|
_itemRevealPending.erase(j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::itemEdited(not_null<HistoryItem*> item) {
|
void HistoryWidget::itemEdited(not_null<HistoryItem*> item) {
|
||||||
|
@ -4904,35 +4907,35 @@ void HistoryWidget::revealItemsCallback() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::updateListSize() {
|
void HistoryWidget::startItemRevealAnimations() {
|
||||||
_list->recountHistoryGeometry();
|
|
||||||
auto washidden = _scroll->isHidden();
|
|
||||||
if (washidden) {
|
|
||||||
_scroll->show();
|
|
||||||
}
|
|
||||||
for (const auto item : base::take(_itemRevealPending)) {
|
for (const auto item : base::take(_itemRevealPending)) {
|
||||||
if (const auto view = item->mainView()) {
|
if (const auto view = item->mainView()) {
|
||||||
if (const auto top = _list->itemTop(view); top >= 0) {
|
if (const auto top = _list->itemTop(view); top >= 0) {
|
||||||
if (const auto height = view->height()) {
|
if (const auto height = view->height()) {
|
||||||
if (!_itemRevealAnimations.contains(item)) {
|
if (!_itemRevealAnimations.contains(item)) {
|
||||||
auto &animation = _itemRevealAnimations[item];
|
auto &animation = _itemRevealAnimations[item];
|
||||||
if (!animation.animation.animating()) {
|
animation.startHeight = height;
|
||||||
animation.startHeight
|
_itemsRevealHeight += height;
|
||||||
= animation.currentHeight
|
animation.animation.start(
|
||||||
= height;
|
[=] { revealItemsCallback(); },
|
||||||
_itemsRevealHeight += height;
|
0.,
|
||||||
animation.animation.start(
|
1.,
|
||||||
[=] { revealItemsCallback(); },
|
HistoryView::kItemRevealDuration,
|
||||||
0.,
|
anim::easeOutCirc);
|
||||||
1.,
|
|
||||||
kItemRevealDuration,
|
|
||||||
anim::easeOutCirc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::updateListSize() {
|
||||||
|
_list->recountHistoryGeometry();
|
||||||
|
auto washidden = _scroll->isHidden();
|
||||||
|
if (washidden) {
|
||||||
|
_scroll->show();
|
||||||
|
}
|
||||||
|
startItemRevealAnimations();
|
||||||
_list->setItemsRevealHeight(_itemsRevealHeight);
|
_list->setItemsRevealHeight(_itemsRevealHeight);
|
||||||
_list->updateSize();
|
_list->updateSize();
|
||||||
if (washidden) {
|
if (washidden) {
|
||||||
|
|
|
@ -323,9 +323,8 @@ private:
|
||||||
bool active = false;
|
bool active = false;
|
||||||
};
|
};
|
||||||
struct ItemRevealAnimation {
|
struct ItemRevealAnimation {
|
||||||
int startHeight = 0;
|
|
||||||
int currentHeight = 0;
|
|
||||||
Ui::Animations::Simple animation;
|
Ui::Animations::Simple animation;
|
||||||
|
int startHeight = 0;
|
||||||
};
|
};
|
||||||
enum class TextUpdateEvent {
|
enum class TextUpdateEvent {
|
||||||
SaveDraft = (1 << 0),
|
SaveDraft = (1 << 0),
|
||||||
|
@ -536,6 +535,7 @@ private:
|
||||||
|
|
||||||
void updateHistoryGeometry(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();
|
void updateListSize();
|
||||||
|
void startItemRevealAnimations();
|
||||||
void revealItemsCallback();
|
void revealItemsCallback();
|
||||||
|
|
||||||
// Does any of the shown histories has this flag set.
|
// Does any of the shown histories has this flag set.
|
||||||
|
|
|
@ -340,14 +340,24 @@ void ListWidget::refreshViewer() {
|
||||||
_idsLimit,
|
_idsLimit,
|
||||||
_idsLimit
|
_idsLimit
|
||||||
) | rpl::start_with_next([=](Data::MessagesSlice &&slice) {
|
) | rpl::start_with_next([=](Data::MessagesSlice &&slice) {
|
||||||
_slice = std::move(slice);
|
std::swap(_slice, slice);
|
||||||
refreshRows();
|
refreshRows(slice);
|
||||||
}, _viewerLifetime);
|
}, _viewerLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::refreshRows() {
|
void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||||
saveScrollState();
|
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.clear();
|
||||||
_items.reserve(_slice.ids.size());
|
_items.reserve(_slice.ids.size());
|
||||||
auto nearestIndex = -1;
|
auto nearestIndex = -1;
|
||||||
|
@ -359,12 +369,17 @@ void ListWidget::refreshRows() {
|
||||||
_items.push_back(enforceViewForItem(item));
|
_items.push_back(enforceViewForItem(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto e = end(_items), i = e - addedToEndCount; i != e; ++i) {
|
||||||
|
_itemRevealPending.emplace(*i);
|
||||||
|
}
|
||||||
updateAroundPositionFromNearest(nearestIndex);
|
updateAroundPositionFromNearest(nearestIndex);
|
||||||
|
|
||||||
updateItemsGeometry();
|
updateItemsGeometry();
|
||||||
checkUnreadBarCreation();
|
checkUnreadBarCreation();
|
||||||
restoreScrollState();
|
restoreScrollState();
|
||||||
mouseActionUpdate(QCursor::pos());
|
if (!_itemsRevealHeight) {
|
||||||
|
mouseActionUpdate(QCursor::pos());
|
||||||
|
}
|
||||||
_delegate->listContentRefreshed();
|
_delegate->listContentRefreshed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,7 +1139,9 @@ bool ListWidget::loadedAtBottom() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListWidget::isEmpty() const {
|
bool ListWidget::isEmpty() const {
|
||||||
return loadedAtTop() && loadedAtBottom();
|
return loadedAtTop()
|
||||||
|
&& loadedAtBottom()
|
||||||
|
&& (_itemsHeight + _itemsRevealHeight == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ListWidget::itemMinimalHeight() const {
|
int ListWidget::itemMinimalHeight() const {
|
||||||
|
@ -1405,6 +1422,60 @@ void ListWidget::resizeToWidth(int newWidth, int minHeight) {
|
||||||
restoreScrollPosition();
|
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) {
|
int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -1423,8 +1494,9 @@ int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
itemMinimalHeight(),
|
itemMinimalHeight(),
|
||||||
newHeight / int(_items.size()));
|
newHeight / int(_items.size()));
|
||||||
}
|
}
|
||||||
|
startItemRevealAnimations();
|
||||||
_itemsWidth = newWidth;
|
_itemsWidth = newWidth;
|
||||||
_itemsHeight = newHeight;
|
_itemsHeight = newHeight - _itemsRevealHeight;
|
||||||
_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom)
|
_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom)
|
||||||
? (_minHeight - _itemsHeight - st::historyPaddingBottom)
|
? (_minHeight - _itemsHeight - st::historyPaddingBottom)
|
||||||
: 0;
|
: 0;
|
||||||
|
@ -2726,6 +2798,23 @@ void ListWidget::viewReplaced(not_null<const Element*> was, Element *now) {
|
||||||
_bar.element->createUnreadBar(_barText.value());
|
_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) {
|
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
|
|
|
@ -143,6 +143,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline constexpr auto kItemRevealDuration = crl::time(150);
|
||||||
|
|
||||||
class ListWidget final
|
class ListWidget final
|
||||||
: public Ui::RpWidget
|
: public Ui::RpWidget
|
||||||
, public ElementDelegate
|
, public ElementDelegate
|
||||||
|
@ -296,7 +298,10 @@ private:
|
||||||
inline bool operator!=(const MouseState &other) const {
|
inline bool operator!=(const MouseState &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
struct ItemRevealAnimation {
|
||||||
|
Ui::Animations::Simple animation;
|
||||||
|
int startHeight = 0;
|
||||||
};
|
};
|
||||||
enum class Direction {
|
enum class Direction {
|
||||||
Up,
|
Up,
|
||||||
|
@ -329,7 +334,7 @@ private:
|
||||||
|
|
||||||
void refreshViewer();
|
void refreshViewer();
|
||||||
void updateAroundPositionFromNearest(int nearestIndex);
|
void updateAroundPositionFromNearest(int nearestIndex);
|
||||||
void refreshRows();
|
void refreshRows(const Data::MessagesSlice &old);
|
||||||
ScrollTopState countScrollState() const;
|
ScrollTopState countScrollState() const;
|
||||||
void saveScrollState();
|
void saveScrollState();
|
||||||
void restoreScrollState();
|
void restoreScrollState();
|
||||||
|
@ -458,6 +463,8 @@ private:
|
||||||
void checkUnreadBarCreation();
|
void checkUnreadBarCreation();
|
||||||
void applyUpdatedScrollState();
|
void applyUpdatedScrollState();
|
||||||
void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo);
|
void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo);
|
||||||
|
void startItemRevealAnimations();
|
||||||
|
void revealItemsCallback();
|
||||||
|
|
||||||
void updateHighlightedMessage();
|
void updateHighlightedMessage();
|
||||||
void clearHighlightedMessage();
|
void clearHighlightedMessage();
|
||||||
|
@ -505,6 +512,11 @@ private:
|
||||||
int _itemsWidth = 0;
|
int _itemsWidth = 0;
|
||||||
int _itemsHeight = 0;
|
int _itemsHeight = 0;
|
||||||
int _itemAverageHeight = 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_set<FullMsgId> _animatedStickersPlayed;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
not_null<PeerData*>,
|
not_null<PeerData*>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue