[thanos] Stabilized collapse animation for bulk deletions.

This commit is contained in:
23rd
2026-05-15 16:37:53 +03:00
parent 3a6c1e1f98
commit f5ecb1ebc5
4 changed files with 55 additions and 15 deletions
@@ -4306,6 +4306,15 @@ bool HistoryInner::focusNextPrevChild(bool next) {
}
void HistoryInner::adjustCurrent(int32 y) const {
auto gapShift = 0;
for (const auto &gap : _collapseGaps) {
if (y < gap.absY + gapShift) {
break;
}
gapShift += gap.height;
}
y -= gapShift;
int32 htop = historyTop(), hdrawtop = historyDrawTop(), mtop = migratedTop();
_curHistory = nullptr;
if (mtop >= 0) {
@@ -2870,6 +2870,15 @@ TextSelection ListWidget::getSelectedTextRange(
int ListWidget::findItemIndexByY(int y) const {
Expects(!_items.empty());
auto gapShift = 0;
for (const auto &gap : _collapseGaps) {
if (y < gap.absY + gapShift) {
break;
}
gapShift += gap.height;
}
y -= gapShift;
if (y < _itemsTop) {
return 0;
}
@@ -2891,7 +2900,11 @@ Element *ListWidget::strictFindItemByY(int y) const {
if (_items.empty()) {
return nullptr;
}
return (y >= _itemsTop && y < _itemsTop + _itemsHeight)
auto gapTotal = 0;
for (const auto &gap : _collapseGaps) {
gapTotal += gap.height;
}
return (y >= _itemsTop && y < _itemsTop + _itemsHeight + gapTotal)
? findItemByY(y).get()
: nullptr;
}
@@ -21,8 +21,8 @@ namespace Ui {
namespace {
constexpr auto kBaseMs = 400;
constexpr auto kPerPixelMs = 0.5;
constexpr auto kMaxMs = 1200;
constexpr auto kPerPixelMs = 0.15;
constexpr auto kMaxMs = 600;
} // namespace
@@ -36,6 +36,15 @@ ThanosEffectController::ThanosEffectController(
) | rpl::on_next([=](const auto &items) {
captureItemsBatch(items);
}, lifetime);
_session->data().historyChanged(
) | rpl::on_next([=](not_null<History*> history) {
if (_restoreScrollPending && history == _capturedHistory) {
_restoreScrollPending = false;
_capturedHistory = nullptr;
_delegate.scrollToY(_savedScrollTop);
}
}, lifetime);
}
ThanosEffectController::~ThanosEffectController() = default;
@@ -45,10 +54,21 @@ void ThanosEffectController::captureItemsBatch(
if (!ThanosEffect::Supported()) {
return;
}
_suppressCollapseAnimation = true;
const auto guard = gsl::finally([&] {
_suppressCollapseAnimation = false;
});
auto firstHistory = (History*)nullptr;
for (const auto &item : items) {
if (_delegate.viewForItem(item)) {
firstHistory = item->history();
break;
}
}
if (!firstHistory) {
return;
}
if (!_restoreScrollPending) {
_capturedHistory = firstHistory;
_savedScrollTop = _delegate.scrollArea()->scrollTop();
_restoreScrollPending = true;
}
for (const auto &item : items) {
if (const auto view = _delegate.viewForItem(item)) {
const auto top = _delegate.itemTop(view);
@@ -226,12 +246,6 @@ void ThanosEffectController::startCollapseAnimation(
syncCollapseGapsToHost();
const auto aboveViewport = std::max(0, scrollTop - itemTop);
const auto scrollAdjust = std::min(height, aboveViewport);
if (scrollAdjust > 0) {
_delegate.scrollToY(scrollTop + scrollAdjust);
}
auto totalHeight = 0;
for (const auto &gap : _collapseGaps) {
totalHeight += gap.currentHeight;
@@ -246,7 +260,7 @@ void ThanosEffectController::startCollapseAnimation(
0.,
1.,
duration,
anim::easeOutCirc);
anim::halfSine);
}
void ThanosEffectController::collapseAnimationCallback() {
@@ -24,6 +24,7 @@ namespace HistoryView {
class Element;
} // namespace HistoryView
class History;
class HistoryItem;
namespace Ui {
@@ -93,11 +94,14 @@ private:
base::flat_map<
not_null<const HistoryView::Element*>,
PreCapturedView> _preCaptured;
bool _suppressCollapseAnimation = false;
std::vector<CollapseGap> _renderGaps;
std::vector<CollapseGapState> _collapseGaps;
Animations::Simple _collapseAnimation;
History *_capturedHistory = nullptr;
int _savedScrollTop = 0;
bool _restoreScrollPending = false;
};
} // namespace Ui