From cde70b98079e685b35ebf5b3213b5a4c239f881c Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 14 May 2024 18:16:25 +0400 Subject: [PATCH] Play effects in a separate layer over MainWidget. --- .../history/history_inner_widget.cpp | 8 +- .../view/history_view_emoji_interactions.cpp | 83 ++++++++++++++++--- .../view/history_view_emoji_interactions.h | 20 ++++- .../history/view/history_view_list_widget.cpp | 8 +- 4 files changed, 92 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 38483247d9..1687b50490 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/call_delayed.h" #include "main/main_session.h" #include "main/main_session_settings.h" +#include "mainwidget.h" #include "menu/menu_item_download_files.h" #include "core/application.h" #include "apiwrap.h" @@ -340,6 +341,8 @@ HistoryInner::HistoryInner( , _history(history) , _elementDelegate(_history->delegateMixin()->delegate()) , _emojiInteractions(std::make_unique( + this, + controller->content(), &controller->session(), [=](not_null view) { return itemTop(view); })) , _migrated(history->migrateFrom()) @@ -393,10 +396,6 @@ HistoryInner::HistoryInner( _emojiInteractions->play(std::move(request), view); } }, lifetime()); - _emojiInteractions->updateRequests( - ) | rpl::start_with_next([=](QRect rect) { - update(rect); - }, lifetime()); _emojiInteractions->playStarted( ) | rpl::start_with_next([=](QString &&emoji) { _controller->emojiInteractions().playStarted(_peer, std::move(emoji)); @@ -1238,7 +1237,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) { p.setOpacity(1.); _reactionsManager->paint(p, context); - _emojiInteractions->paint(p); } bool HistoryInner::eventHook(QEvent *e) { diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp index efda8ead15..de775e17df 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp @@ -45,9 +45,13 @@ constexpr auto kDropDelayedAfterDelay = crl::time(2000); } // namespace EmojiInteractions::EmojiInteractions( + not_null parent, + not_null layerParent, not_null session, Fn)> itemTop) -: _session(session) +: _parent(parent) +, _layerParent(layerParent) +, _session(session) , _itemTop(std::move(itemTop)) { _session->data().viewRemoved( ) | rpl::filter([=] { @@ -282,6 +286,18 @@ void EmojiInteractions::play( return; } + if (!_layer) { + _layer = base::make_unique_q(_layerParent); + const auto raw = _layer.get(); + raw->setAttribute(Qt::WA_TransparentForMouseEvents); + raw->show(); + raw->paintRequest() | rpl::start_with_next([=](QRect clip) { + paint(raw, clip); + }, raw->lifetime()); + } + refreshLayerShift(); + _layer->setGeometry(_layerParent->rect()); + auto lottie = document->session().emojiStickersPack().effectPlayer( document, data, @@ -302,11 +318,12 @@ void EmojiInteractions::play( const auto i = ranges::find(_plays, raw, [](const Play &p) { return p.lottie.get(); }); - const auto rect = computeRect(*i).translated(shift); - if (rect.y() + rect.height() >= _visibleTop - && rect.y() <= _visibleBottom) { - _updateRequests.fire_copy(rect); + auto update = computeRect(*i).translated(shift + _layerShift); + if (!i->lastTarget.isEmpty()) { + update = i->lastTarget.united(update); } + _layer->update(update); + i->lastTarget = QRect(); }); }, lottie->lifetime()); _plays.push_back({ @@ -331,6 +348,16 @@ void EmojiInteractions::play( } } +void EmojiInteractions::refreshLayerShift() { + _layerShift = Ui::MapFrom(_layerParent, _parent, QPoint(0, 0)); +} + +void EmojiInteractions::refreshLayerGeometryAndUpdate(QRect rect) { + if (!rect.isEmpty()) { + _layer->update(rect.translated(_layerShift)); + } +} + void EmojiInteractions::visibleAreaUpdated( int visibleTop, int visibleBottom) { @@ -375,12 +402,34 @@ QRect EmojiInteractions::computeRect(const Play &play) const { return QRect(QPoint(left, top), size).translated(play.shift); } -void EmojiInteractions::paint(QPainter &p) { +void EmojiInteractions::paint(not_null layer, QRect clip) { + refreshLayerShift(); + const auto factor = style::DevicePixelRatio(); + const auto whole = layer->rect(); + + auto p = QPainter(layer); + + auto updated = QRect(); + const auto addRect = [&](QRect rect) { + if (updated.isEmpty()) { + updated = rect; + } else { + updated = rect.united(updated); + } + }; for (auto &play : _plays) { if (!play.lottie->ready()) { continue; } + const auto target = computeRect(play).translated(_layerShift); + if (!target.intersects(whole)) { + play.finished = true; + addRect(target); + continue; + } else if (!target.intersects(clip)) { + continue; + } auto request = Lottie::FrameRequest(); request.box = play.outer * factor; const auto rightAligned = play.view->hasRightLayout(); @@ -394,18 +443,18 @@ void EmojiInteractions::paint(QPainter &p) { play.framesCount = information.framesCount; play.frameRate = information.frameRate; } - const auto rect = computeRect(play); if (play.started && !play.frame) { play.finished = true; - _updateRequests.fire_copy(rect); + addRect(target); continue; } else if (play.frame > 0) { play.started = true; } p.drawImage( - QRect(rect.topLeft(), frame.image.size() / factor), + QRect(target.topLeft(), frame.image.size() / factor), frame.image); play.lottie->markFrameShown(); + play.lastTarget = target.translated(_layerShift); } _plays.erase(ranges::remove_if(_plays, [](const Play &play) { if (!play.finished) { @@ -414,6 +463,18 @@ void EmojiInteractions::paint(QPainter &p) { return true; }), end(_plays)); checkDelayed(); + + if (_plays.empty()) { + layer->hide(); + if (_layer.get() == layer) { + crl::on_main([moved = std::move(_layer)] {}); + } + } else if (!updated.isEmpty()) { + const auto translated = updated.translated(_layerShift); + if (translated.intersects(whole)) { + _layer->update(translated); + } + } } void EmojiInteractions::checkDelayed() { @@ -456,10 +517,6 @@ void EmojiInteractions::checkDelayed() { good.incoming); } -rpl::producer EmojiInteractions::updateRequests() const { - return _updateRequests.events(); -} - rpl::producer EmojiInteractions::playStarted() const { return _playStarted.events(); } diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h index 679b102a58..6c1f25e8d2 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/unique_qptr.h" + namespace Data { class DocumentMedia; } // namespace Data @@ -27,6 +29,10 @@ namespace Stickers { enum class EffectType : uint8; } // namespace Stickers +namespace Ui { +class RpWidget; +} // namespace Ui + namespace HistoryView { class Element; @@ -34,6 +40,8 @@ class Element; class EmojiInteractions final { public: EmojiInteractions( + not_null parent, + not_null layerParent, not_null session, Fn)> itemTop); ~EmojiInteractions(); @@ -50,14 +58,14 @@ public: void playEffectOnRead(not_null view); void playEffect(not_null view); - void paint(QPainter &p); - [[nodiscard]] rpl::producer updateRequests() const; + void paint(not_null layer, QRect clip); [[nodiscard]] rpl::producer playStarted() const; private: struct Play { not_null view; std::unique_ptr lottie; + mutable QRect lastTarget; QPoint shift; QSize inner; QSize outer; @@ -111,15 +119,21 @@ private: const ResolvedEffect &resolved); void checkPendingEffects(); + void refreshLayerShift(); + void refreshLayerGeometryAndUpdate(QRect rect); + + const not_null _parent; + const not_null _layerParent; const not_null _session; const Fn)> _itemTop; + base::unique_qptr _layer; + QPoint _layerShift; int _visibleTop = 0; int _visibleBottom = 0; std::vector _plays; std::vector _delayed; - rpl::event_stream _updateRequests; rpl::event_stream _playStarted; std::vector> _pendingEffects; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index c8eacd8e35..1b8905dfaa 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -375,6 +375,8 @@ ListWidget::ListWidget( , _delegate(delegate) , _session(session) , _emojiInteractions(std::make_unique( + this, + _delegate->listWindow()->content(), session, [=](not_null view) { return itemTop(view); })) , _context(_delegate->listContext()) @@ -501,11 +503,6 @@ ListWidget::ListWidget( _isChatWide = wide; }, lifetime()); - _emojiInteractions->updateRequests( - ) | rpl::start_with_next([=](QRect rect) { - update(rect); - }, lifetime()); - _selectScroll.scrolls( ) | rpl::start_with_next([=](int d) { delegate->listScrollTo(_visibleTop + d, false); @@ -2278,7 +2275,6 @@ void ListWidget::paintEvent(QPaintEvent *e) { if (_reactionsManager) { _reactionsManager->paint(p, context); } - _emojiInteractions->paint(p); } void ListWidget::paintUserpics(