From a6c1def6fe630f7b3ee836f5e7a6b3e4085dbb65 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 13:36:41 +0400 Subject: [PATCH] Add ripples to top peers. --- Telegram/SourceFiles/dialogs/dialogs.style | 4 +- .../dialogs/ui/top_peers_strip.cpp | 94 +++++++++++++++++-- .../SourceFiles/dialogs/ui/top_peers_strip.h | 6 +- 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 8d219e71c..3f4fffe0a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -595,8 +595,10 @@ topPeers: DialogsStories(dialogsStoriesFull) { photo: 46px; photoLeft: 10px; photoTop: 8px; - nameLeft: 4px; + nameLeft: 6px; } +topPeersRadius: 4px; +topPeersMargin: margins(3px, 3px, 3px, 4px); dialogsStoriesList: DialogsStoriesList { small: dialogsStories; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 03e7cd09e..2c8e53d36 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -40,7 +40,8 @@ struct TopPeersStrip::Entry { TopPeersStrip::TopPeersStrip( not_null parent, rpl::producer content) -: RpWidget(parent) { +: RpWidget(parent) +, _selection(st::topPeersRadius, st::windowBgOver) { resize(0, st::topPeers.height); std::move(content) | rpl::start_with_next([=](const TopPeersList &list) { @@ -102,6 +103,23 @@ void TopPeersStrip::mousePressEvent(QMouseEvent *e) { _mouseDownPosition = _lastMousePosition; _pressed = _selected; + + if (_selected >= 0) { + Assert(_selected < _entries.size()); + auto &entry = _entries[_selected]; + if (!entry.ripple) { + entry.ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::RoundRectMask( + innerRounded().size(), + st::topPeersRadius), + [=] { update(); }); + } + const auto single = outer().width(); + entry.ripple->add(e->pos() - QPoint( + _selected * single - _scrollLeft + st::topPeersMargin.left(), + st::topPeersMargin.top())); + } } void TopPeersStrip::mouseMoveEvent(QMouseEvent *e) { @@ -174,14 +192,20 @@ void TopPeersStrip::mouseReleaseEvent(QMouseEvent *e) { }); const auto pressed = std::exchange(_pressed, -1); + if (pressed >= 0) { + Assert(pressed < _entries.size()); + auto &entry = _entries[pressed]; + if (entry.ripple) { + entry.ripple->lastStop(); + } + } if (finishDragging()) { return; } updateSelected(); - if (_selected == pressed) { - if (_selected < _entries.size()) { - _clicks.fire_copy(_entries[_selected].id); - } + if (_selected >= 0 && _selected == pressed) { + Assert(_selected < _entries.size()); + _clicks.fire_copy(_entries[_selected].id); } } @@ -220,7 +244,14 @@ void TopPeersStrip::removeLocally(uint64 id) { } else if (i->subscribed) { i->userpic->subscribeToUpdates(nullptr); } + const auto index = int(i - begin(_entries)); _entries.erase(i); + if (_selected > index) { + --_selected; + } + if (_pressed > index) { + --_pressed; + } updateScrollMax(); if (_entries.empty()) { _empty = true; @@ -231,6 +262,8 @@ void TopPeersStrip::removeLocally(uint64 id) { void TopPeersStrip::apply(const TopPeersList &list) { auto now = std::vector(); + auto selectedId = (_selected >= 0) ? _entries[_selected].id : 0; + auto pressedId = (_pressed >= 0) ? _entries[_pressed].id : 0; for (const auto &entry : list.entries) { if (_removed.contains(entry.id)) { continue; @@ -253,6 +286,18 @@ void TopPeersStrip::apply(const TopPeersList &list) { } } _entries = std::move(now); + if (selectedId) { + const auto i = ranges::find(_entries, selectedId, &Entry::id); + if (i != end(_entries)) { + _selected = int(i - begin(_entries)); + } + } + if (pressedId) { + const auto i = ranges::find(_entries, pressedId, &Entry::id); + if (i != end(_entries)) { + _pressed = int(i - begin(_entries)); + } + } updateScrollMax(); unsubscribeUserpics(); if (!_entries.empty()) { @@ -302,10 +347,19 @@ void TopPeersStrip::apply(Entry &entry, const TopPeersEntry &data) { } } +QRect TopPeersStrip::outer() const { + const auto &st = st::topPeers; + const auto single = st.photoLeft * 2 + st.photo; + return QRect(0, 0, single, height()); +} + +QRect TopPeersStrip::innerRounded() const { + return outer().marginsRemoved(st::topPeersMargin); +} + void TopPeersStrip::paintEvent(QPaintEvent *e) { auto p = Painter(this); const auto &st = st::topPeers; - const auto line = st.lineTwice / 2; const auto single = st.photoLeft * 2 + st.photo; const auto from = std::min(_scrollLeft / single, int(_entries.size())); @@ -315,12 +369,28 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) { int(_entries.size())); auto x = -_scrollLeft + from * single; + const auto highlighted = (_pressed >= 0) ? _pressed : _selected; for (auto i = from; i != till; ++i) { auto &entry = _entries[i]; + const auto selected = (i == highlighted); + if (selected) { + _selection.paint(p, innerRounded().translated(x, 0)); + } + if (entry.ripple) { + entry.ripple->paint( + p, + x + st::topPeersMargin.left(), + st::topPeersMargin.top(), + width()); + if (entry.ripple->empty()) { + entry.ripple = nullptr; + } + } + if (!entry.subscribed) { subscribeUserpic(entry); } - paintUserpic(p, i, x); + paintUserpic(p, x, i, selected); const auto nameLeft = x + st.nameLeft; const auto nameWidth = single - 2 * st.nameLeft; @@ -335,7 +405,11 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) { } } -void TopPeersStrip::paintUserpic(Painter &p, int index, int x) { +void TopPeersStrip::paintUserpic( + Painter &p, + int x, + int index, + bool selected) { Expects(index >= 0 && index < _entries.size()); auto &entry = _entries[index]; @@ -401,7 +475,7 @@ void TopPeersStrip::paintUserpic(Painter &p, int index, int x) { : (QString::number(entry.badge / 1000) + 'K'); } auto st = Ui::UnreadBadgeStyle(); - st.selected = (_selected == index); + st.selected = selected; st.muted = entry.muted; const auto &counter = entry.badgeString; const auto badge = PaintUnreadBadge(q, counter, size, 0, st); @@ -422,6 +496,7 @@ void TopPeersStrip::contextMenuEvent(QContextMenuEvent *e) { if (_selected < 0 || _entries.empty()) { return; } + Assert(_selected < _entries.size()); _menu = base::make_unique_q( this, st::popupMenuWithIcons); @@ -476,6 +551,7 @@ void TopPeersStrip::updateSelected() { setCursor(over ? style::cur_pointer : style::cur_default); } _selected = selected; + update(); } } diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 5eac0ae03..0eead5cc5 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" #include "ui/widgets/menu/menu_add_action_callback.h" +#include "ui/round_rect.h" #include "ui/rp_widget.h" namespace Ui { @@ -68,8 +69,10 @@ private: bool finishDragging(); void subscribeUserpic(Entry &entry); void unsubscribeUserpics(bool all = false); - void paintUserpic(Painter &p, int index, int x); + void paintUserpic(Painter &p, int x, int index, bool selected); + [[nodiscard]] QRect outer() const; + [[nodiscard]] QRect innerRounded() const; void apply(const TopPeersList &list); void apply(Entry &entry, const TopPeersEntry &data); @@ -92,6 +95,7 @@ private: int _selected = -1; int _pressed = -1; + Ui::RoundRect _selection; base::unique_qptr _menu; base::has_weak_ptr _menuGuard;