From 4d32a1658cca26828f8b0c549bcec468f2866fea Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 6 Jun 2026 16:23:15 +0300 Subject: [PATCH] Fixed 3D premium covers drawing over shown boxes. --- Telegram/SourceFiles/mainwindow.cpp | 10 +++ Telegram/SourceFiles/mainwindow.h | 2 + .../settings/sections/settings_business.cpp | 15 ++-- .../settings/sections/settings_credits.cpp | 8 +-- .../settings/sections/settings_premium.cpp | 19 ++--- .../ui/effects/premium_3d_cover.cpp | 66 +++++++++++++++-- .../SourceFiles/ui/effects/premium_3d_cover.h | 6 ++ .../SourceFiles/ui/effects/premium_star.cpp | 72 ++++++++++++++++++- .../SourceFiles/ui/effects/premium_star.h | 8 ++- .../SourceFiles/window/window_controller.cpp | 4 ++ .../SourceFiles/window/window_controller.h | 1 + .../window/window_session_controller.cpp | 4 ++ .../window/window_session_controller.h | 1 + Telegram/lib_ui | 2 +- 14 files changed, 179 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 2627c02c7c..3fd3d9ad77 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -424,6 +424,11 @@ void MainWindow::ensureLayerCreated() { destroyLayer(); }, _layer->lifetime()); + _layer->boxShownValue( + ) | rpl::on_next([=](bool shown) { + _boxShown = shown; + }, _layer->lifetime()); + if (const auto controller = sessionController()) { controller->enableGifPauseReason(Window::GifPauseReason::Layer); } @@ -435,6 +440,7 @@ void MainWindow::destroyLayer() { } auto layer = base::take(_layer); + _boxShown = false; const auto resetFocus = Ui::InFocusChain(layer); if (resetFocus) { setFocus(); @@ -502,6 +508,10 @@ bool MainWindow::ui_isLayerShown() const { return _layer != nullptr; } +rpl::producer MainWindow::ui_boxShownValue() const { + return _boxShown.value(); +} + bool MainWindow::showMediaPreview( Data::FileOrigin origin, not_null document) { diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index ea36b41e56..9f04a76e6a 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -102,6 +102,7 @@ public: void ui_hideSettingsAndLayer(anim::type animated); void ui_removeLayerBlackout(); [[nodiscard]] bool ui_isLayerShown() const; + [[nodiscard]] rpl::producer ui_boxShownValue() const; bool showMediaPreview( Data::FileOrigin origin, not_null document); @@ -133,6 +134,7 @@ private: object_ptr _intro = { nullptr }; object_ptr _main = { nullptr }; base::unique_qptr _layer; + rpl::variable _boxShown = false; object_ptr _mediaPreview = { nullptr }; object_ptr _testingThemeWarning = { nullptr }; diff --git a/Telegram/SourceFiles/settings/sections/settings_business.cpp b/Telegram/SourceFiles/settings/sections/settings_business.cpp index e2339cd3f1..50365b657b 100644 --- a/Telegram/SourceFiles/settings/sections/settings_business.cpp +++ b/Telegram/SourceFiles/settings/sections/settings_business.cpp @@ -619,16 +619,7 @@ void BuildBusinessSectionContent( const auto alwaysAvailable = (feature == PremiumFeature::BusinessBots); if (!alwaysAvailable && !session->premium()) { - if (state && state->setPaused) { - state->setPaused(true); - } - const auto hidden = crl::guard(content, [=] { - if (state && state->setPaused) { - state->setPaused(false); - } - }); - - ShowPremiumPreviewToBuy(controller, feature, hidden); + ShowPremiumPreviewToBuy(controller, feature, nullptr); return; } else if (!isReady(feature)) { *waitingToShow = feature; @@ -819,6 +810,10 @@ base::weak_qptr Business::createPinnedToTop( _subscribe->setGlarePaused(paused); } }; + controller()->boxShownValue( + ) | rpl::on_next([=](bool shown) { + _state->setPaused(shown); + }, content->lifetime()); _wrap.value( ) | rpl::on_next([=](Info::Wrap wrap) { diff --git a/Telegram/SourceFiles/settings/sections/settings_credits.cpp b/Telegram/SourceFiles/settings/sections/settings_credits.cpp index a85468c573..b9ac22e07e 100644 --- a/Telegram/SourceFiles/settings/sections/settings_credits.cpp +++ b/Telegram/SourceFiles/settings/sections/settings_credits.cpp @@ -138,7 +138,6 @@ private: base::unique_qptr _close; rpl::variable _backToggles; rpl::variable _wrap; - Fn _setPaused; rpl::event_stream<> _showBack; rpl::event_stream<> _showFinished; @@ -705,9 +704,10 @@ base::weak_qptr Credits::createPinnedToTop( .showFinished = _showFinished.events(), }); }(); - _setPaused = [=](bool paused) { - content->setPaused(paused); - }; + controller()->boxShownValue( + ) | rpl::on_next([=](bool shown) { + content->setPaused(shown); + }, content->lifetime()); _wrap.value( ) | rpl::on_next([=](Info::Wrap wrap) { diff --git a/Telegram/SourceFiles/settings/sections/settings_premium.cpp b/Telegram/SourceFiles/settings/sections/settings_premium.cpp index 2c4679482c..6a1bd1a935 100644 --- a/Telegram/SourceFiles/settings/sections/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/sections/settings_premium.cpp @@ -1299,19 +1299,8 @@ void BuildPremiumSectionContent( state->ref, state->radioGroup); - auto buttonCallback = [controller, state](PremiumFeature section) { - if (state->setPaused) { - state->setPaused(true); - } - const auto hidden = crl::guard( - (QObject*)controller->widget(), - [state] { - if (state->setPaused) { - state->setPaused(false); - } - }); - - ShowPremiumPreviewToBuy(controller, section, hidden); + auto buttonCallback = [controller](PremiumFeature section) { + ShowPremiumPreviewToBuy(controller, section, nullptr); }; AddSummaryPremium( ctx.container, @@ -1571,6 +1560,10 @@ base::weak_qptr Premium::createPinnedToTop( _subscribe->setGlarePaused(paused); } }; + controller()->boxShownValue( + ) | rpl::on_next([=](bool shown) { + _state->setPaused(shown); + }, content->lifetime()); _wrap.value( ) | rpl::on_next([=](Info::Wrap wrap) { diff --git a/Telegram/SourceFiles/ui/effects/premium_3d_cover.cpp b/Telegram/SourceFiles/ui/effects/premium_3d_cover.cpp index af85ee8086..0978a8a4f7 100644 --- a/Telegram/SourceFiles/ui/effects/premium_3d_cover.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_3d_cover.cpp @@ -64,8 +64,48 @@ void Object3dCover::setPaused(bool paused) { } _paused = paused; if (_paused) { - stopAnimation(); - } else if (!isHidden()) { + freeze(); + } else { + unfreeze(); + } +} + +void Object3dCover::freeze() { + stopAnimation(); + _pausedAt = crl::now(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + const auto w = surfaceWidget(); + if (!w || w->isHidden()) { + return; + } + const auto rhi = qobject_cast(w); + auto image = rhi ? rhi->grabFramebuffer() : QImage(); + if (image.isNull()) { + return; + } + _frozen = std::move(image); + pushState(); + w->update(); + update(); +#endif // Qt >= 6.7 +} + +void Object3dCover::unfreeze() { + if (!_frozen.isNull()) { + _frozen = QImage(); + update(); + } + if (_pausedAt) { + const auto delta = crl::now() - _pausedAt; + if (_gesture) { + _gesture->start += delta; + } + if (_idleAt) { + _idleAt += delta; + } + _pausedAt = 0; + } + if (!isHidden()) { startAnimation(); } } @@ -205,7 +245,7 @@ void Object3dCover::pushState() { float(_yaw), float(_pitch), float(_time), - float(_opacity), + _paused ? 0.f : float(_opacity), _night); } @@ -315,6 +355,14 @@ void Object3dCover::startTapTilt(QPoint position) { play(std::move(gesture)); } +void Object3dCover::paintEvent(QPaintEvent *e) { + if (_frozen.isNull()) { + return; + } + auto p = QPainter(this); + p.drawImage(rect(), _frozen); +} + void Object3dCover::resizeEvent(QResizeEvent *e) { if (const auto w = surfaceWidget()) { w->setGeometry(rect()); @@ -326,14 +374,20 @@ void Object3dCover::showEvent(QShowEvent *e) { if (const auto w = surfaceWidget()) { w->show(); } - _yaw = _pitch = 0.; - _gesture.reset(); - scheduleIdle(0); + if (!_entered) { + _entered = true; + _yaw = _pitch = 0.; + _gesture.reset(); + scheduleIdle(0); + } startAnimation(); } void Object3dCover::hideEvent(QHideEvent *e) { stopAnimation(); + if (_entered) { + return; + } _gesture.reset(); cancelIdle(); _yaw = _pitch = 0.; diff --git a/Telegram/SourceFiles/ui/effects/premium_3d_cover.h b/Telegram/SourceFiles/ui/effects/premium_3d_cover.h index 06881bfc3d..60ba3137a1 100644 --- a/Telegram/SourceFiles/ui/effects/premium_3d_cover.h +++ b/Telegram/SourceFiles/ui/effects/premium_3d_cover.h @@ -47,6 +47,7 @@ protected: float alpha, bool night) = 0; + void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void showEvent(QShowEvent *e) override; void hideEvent(QHideEvent *e) override; @@ -79,6 +80,8 @@ private: void ensureSurface(); [[nodiscard]] QWidget *surfaceWidget() const; + void freeze(); + void unfreeze(); void startAnimation(); void stopAnimation(); void frame(); @@ -96,6 +99,7 @@ private: const Descriptor _descriptor; std::unique_ptr _surface; + QImage _frozen; Ui::Animations::Basic _animation; crl::time _lastFrame = 0; @@ -116,6 +120,8 @@ private: rpl::event_stream _flung; bool _paused = false; + bool _entered = false; + crl::time _pausedAt = 0; }; diff --git a/Telegram/SourceFiles/ui/effects/premium_star.cpp b/Telegram/SourceFiles/ui/effects/premium_star.cpp index 2af067400c..47d97acb2d 100644 --- a/Telegram/SourceFiles/ui/effects/premium_star.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_star.cpp @@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/call_delayed.h" #include "base/random.h" +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +#include +#endif // Qt >= 6.7 + namespace Ui::Premium { namespace { @@ -81,8 +85,48 @@ void Star::setPaused(bool paused) { } _paused = paused; if (_paused) { - stopAnimation(); - } else if (!isHidden()) { + freeze(); + } else { + unfreeze(); + } +} + +void Star::freeze() { + stopAnimation(); + _pausedAt = crl::now(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + const auto w = surfaceWidget(); + if (!w || w->isHidden()) { + return; + } + const auto rhi = qobject_cast(w); + auto image = rhi ? rhi->grabFramebuffer() : QImage(); + if (image.isNull()) { + return; + } + _frozen = std::move(image); + pushState(); + w->update(); + update(); +#endif // Qt >= 6.7 +} + +void Star::unfreeze() { + if (!_frozen.isNull()) { + _frozen = QImage(); + update(); + } + if (_pausedAt) { + const auto delta = crl::now() - _pausedAt; + if (_gesture) { + _gesture->start += delta; + } + if (_idleAt) { + _idleAt += delta; + } + _pausedAt = 0; + } + if (!isHidden()) { startAnimation(); } } @@ -221,7 +265,7 @@ void Star::pushState() { .pitch = float(_pitch), .bob = float(_bob), .shimmer = float(_shimmer), - .alpha = float(_fadeIn * _opacity), + .alpha = _paused ? 0.f : float(_fadeIn * _opacity), }); } #endif // Qt >= 6.7 @@ -410,6 +454,14 @@ void Star::sleepGesture() { play(std::move(gesture)); } +void Star::paintEvent(QPaintEvent *e) { + if (_frozen.isNull()) { + return; + } + auto p = QPainter(this); + p.drawImage(rect(), _frozen); +} + void Star::resizeEvent(QResizeEvent *e) { if (const auto w = surfaceWidget()) { w->setGeometry(rect()); @@ -417,6 +469,10 @@ void Star::resizeEvent(QResizeEvent *e) { } void Star::startEnter() { + if (_entered) { + return; + } + _entered = true; base::call_delayed(kEnterDelay, this, [=] { if (isHidden()) { return; @@ -433,6 +489,13 @@ void Star::startEnter() { void Star::showEvent(QShowEvent *e) { ensureSurface(); + if (_entered) { + if (const auto w = surfaceWidget()) { + w->show(); + } + startAnimation(); + return; + } _yaw = kEnterYaw; _pitch = _bob = 0.; _gesture.reset(); @@ -445,6 +508,9 @@ void Star::showEvent(QShowEvent *e) { void Star::hideEvent(QHideEvent *e) { stopAnimation(); + if (_entered) { + return; + } _gesture.reset(); cancelIdle(); _yaw = _pitch = _bob = 0.; diff --git a/Telegram/SourceFiles/ui/effects/premium_star.h b/Telegram/SourceFiles/ui/effects/premium_star.h index aa889e889f..99bef90eda 100644 --- a/Telegram/SourceFiles/ui/effects/premium_star.h +++ b/Telegram/SourceFiles/ui/effects/premium_star.h @@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" -#include - namespace Ui::Premium { class StarRenderer; @@ -32,6 +30,7 @@ public: [[nodiscard]] rpl::producer flungStrength() const; protected: + void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void showEvent(QShowEvent *e) override; void hideEvent(QHideEvent *e) override; @@ -67,6 +66,8 @@ private: void ensureSurface(); [[nodiscard]] QWidget *surfaceWidget() const; + void freeze(); + void unfreeze(); void startAnimation(); void stopAnimation(); void frame(); @@ -87,6 +88,7 @@ private: StarRenderer *_renderer = nullptr; std::unique_ptr _surface; + QImage _frozen; Ui::Animations::Basic _animation; crl::time _lastFrame = 0; @@ -111,6 +113,8 @@ private: rpl::event_stream _flung; bool _paused = false; + bool _entered = false; + crl::time _pausedAt = 0; }; diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 47f88da2fc..c925818269 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -467,6 +467,10 @@ bool Controller::isLayerShown() const { return _widget.ui_isLayerShown(); } +rpl::producer Controller::boxShownValue() const { + return _widget.ui_boxShownValue(); +} + void Controller::sideBarChanged() { _widget.recountGeometryConstraints(); } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index e412176e2e..29fb373430 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -99,6 +99,7 @@ public: void hideLayer(anim::type animated = anim::type::normal); void hideSettingsAndLayer(anim::type animated = anim::type::normal); [[nodiscard]] bool isLayerShown() const; + [[nodiscard]] rpl::producer boxShownValue() const; template < typename BoxType, diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 7a283f5723..465002fb45 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -3129,6 +3129,10 @@ bool SessionController::isLayerShown() const { return _window->isLayerShown(); } +rpl::producer SessionController::boxShownValue() const { + return _window->boxShownValue(); +} + void SessionController::registerActiveLayerSection(SectionWidget *section) { _activeLayerSection = section; } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 9ff7406eb2..6d43ec06a1 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -555,6 +555,7 @@ public: } void removeLayerBlackout(); [[nodiscard]] bool isLayerShown() const; + [[nodiscard]] rpl::producer boxShownValue() const; void registerActiveLayerSection(SectionWidget *section); void unregisterActiveLayerSection(SectionWidget *section); [[nodiscard]] SectionWidget *activeLayerSection() const; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index e0e6e7d01c..ebaddb2d4b 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit e0e6e7d01c65cef834f61f96f8925591ae7f0b73 +Subproject commit ebaddb2d4bd1e13763c9107b7585398d6cb65eb2