From ffa63860e235e6389e691992284c0868e5418ea3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 27 Jun 2026 19:44:31 +0300 Subject: [PATCH] Cached peer list rows into opaque images while scrolling. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/boxes/peer_list_box.cpp | 47 +++++++++++++++-- Telegram/SourceFiles/boxes/peer_list_box.h | 9 ++++ .../boxes/peer_list_scroll_cache.cpp | 33 ++++++++++++ .../boxes/peer_list_scroll_cache.h | 51 +++++++++++++++++++ 5 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/peer_list_scroll_cache.cpp create mode 100644 Telegram/SourceFiles/boxes/peer_list_scroll_cache.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 42d1f0bd39..b1411f4f0e 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -317,6 +317,8 @@ PRIVATE boxes/moderate_messages_box.h boxes/peer_list_box.cpp boxes/peer_list_box.h + boxes/peer_list_scroll_cache.cpp + boxes/peer_list_scroll_cache.h boxes/peer_list_controllers.cpp boxes/peer_list_controllers.h boxes/peer_list_section_headers.cpp diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index fa32511c3b..0f76ca8ca5 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -1135,7 +1135,8 @@ PeerListContent::PeerListContent( : RpWidget(parent) , _st(controller->computeListSt()) , _controller(controller) -, _rowHeight(_st.item.height) { +, _rowHeight(_st.item.height) +, _rowsScrollCache([this] { update(); }) { _controller->session().downloaderTaskFinished( ) | rpl::on_next([=] { update(); @@ -1968,7 +1969,6 @@ crl::time PeerListContent::paintRow( ? std::max(refreshStatusAt - now, crl::time(1)) : 0; - const auto peer = row->special() ? nullptr : row->peer().get(); const auto active = (_contexted.index.value >= 0) ? _contexted : (_pressed.index.value >= 0) @@ -1982,6 +1982,41 @@ crl::time PeerListContent::paintRow( return refreshStatusIn; } + const auto activeElement = (active.index == index) ? active.element : 0; + if (_rowsScrollCache.scrolling() + && !selected + && !activeElement + && width() > 0 + && row->opacity() == 1.) { + const auto ratio = style::DevicePixelRatio(); + _rowsScrollCache.paintRow( + p, + row->id(), + QSize(width(), _rowHeight) * ratio, + ratio, + [&](QImage &image) { + auto q = Painter(&image); + paintRowContent(q, now, index, false, 0); + }); + return refreshStatusIn; + } + paintRowContent(p, now, index, selected, activeElement); + return refreshStatusIn; +} + +void PeerListContent::paintRowContent( + Painter &p, + crl::time now, + RowIndex index, + bool selected, + int activeElement) { + const auto row = getRow(index); + Assert(row != nullptr); + + const auto &st = row->computeSt(_st.item); + const auto outerWidth = width(); + const auto peer = row->special() ? nullptr : row->peer().get(); + const auto opacity = row->opacity(); const auto &bg = selected ? st.button.textBgOver @@ -2093,9 +2128,7 @@ crl::time PeerListContent::paintRow( p, width(), selected, - (active.index == index) ? active.element : 0); - - return refreshStatusIn; + activeElement); } PeerListContent::SkipResult PeerListContent::selectSkip(int direction) { @@ -2424,6 +2457,10 @@ void PeerListContent::setIgnoreHiddenRowsOnSearch(bool value) { void PeerListContent::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { + if ((_visibleTop != visibleTop || _visibleBottom != visibleBottom) + && _mode != Mode::Custom) { + _rowsScrollCache.markScrolling(); + } _visibleTop = visibleTop; _visibleBottom = visibleBottom; loadProfilePhotos(); diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index a59dd1cebc..d55fb65520 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/userpic_view.h" #include "ui/layers/box_content.h" #include "base/timer.h" +#include "boxes/peer_list_scroll_cache.h" namespace style { struct PeerList; @@ -870,6 +871,12 @@ private: Fn)> destroyed = nullptr); crl::time paintRow(Painter &p, crl::time now, RowIndex index); + void paintRowContent( + Painter &p, + crl::time now, + RowIndex index, + bool selected, + int activeElement); [[nodiscard]] bool sectionsShown() const; void refreshSectionHeaders(); @@ -951,6 +958,8 @@ private: base::Timer _repaintByStatus; base::unique_qptr _contextMenu; + PeerListRowsScrollCache _rowsScrollCache; + }; class PeerListContentDelegate : public PeerListDelegate { diff --git a/Telegram/SourceFiles/boxes/peer_list_scroll_cache.cpp b/Telegram/SourceFiles/boxes/peer_list_scroll_cache.cpp new file mode 100644 index 0000000000..0d537a840a --- /dev/null +++ b/Telegram/SourceFiles/boxes/peer_list_scroll_cache.cpp @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/peer_list_scroll_cache.h" + +namespace { + +constexpr auto kStopTimeout = crl::time(120); + +} // namespace + +PeerListRowsScrollCache::PeerListRowsScrollCache(Fn stopped) { + _stopTimer.setCallback([=] { + _scrolling = false; + _images.clear(); + stopped(); + }); +} + +void PeerListRowsScrollCache::markScrolling() { + _scrolling = true; + _stopTimer.callOnce(kStopTimeout); +} + +void PeerListRowsScrollCache::invalidate(uint64 rowId) { + if (const auto i = _images.find(rowId); i != end(_images)) { + _images.erase(i); + } +} diff --git a/Telegram/SourceFiles/boxes/peer_list_scroll_cache.h b/Telegram/SourceFiles/boxes/peer_list_scroll_cache.h new file mode 100644 index 0000000000..3f49e14400 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peer_list_scroll_cache.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/timer.h" +#include "base/flat_map.h" + +class PeerListRowsScrollCache final { +public: + explicit PeerListRowsScrollCache(Fn stopped); + + void markScrolling(); + [[nodiscard]] bool scrolling() const { + return _scrolling; + } + + template + void paintRow( + QPainter &p, + uint64 rowId, + QSize physicalSize, + int ratio, + PaintToImage &&paintToImage) { + auto &image = _images[rowId]; + if (image.size() != physicalSize) { + if (_images.size() > kLimit) { + _images.clear(); + image = _images[rowId]; + } + image = QImage(physicalSize, QImage::Format_RGB32); + image.setDevicePixelRatio(ratio); + paintToImage(image); + } + p.drawImage(0, 0, image); + } + + void invalidate(uint64 rowId); + +private: + static constexpr auto kLimit = 256; + + base::flat_map _images; + base::Timer _stopTimer; + bool _scrolling = false; + +};