From 3dadcd9352f2b081de8a11c6b42a1aee1ea5d5b0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 13 Aug 2021 21:18:06 +0300 Subject: [PATCH] Animated transition on pattern-on-gradient resize. --- .../chat_helpers/tabbed_section.cpp | 2 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 2 +- .../admin_log/history_admin_log_section.cpp | 12 ++- .../SourceFiles/history/history_widget.cpp | 7 +- .../view/history_view_pinned_section.cpp | 2 +- .../view/history_view_replies_section.cpp | 2 +- .../view/history_view_scheduled_section.cpp | 2 +- .../SourceFiles/info/info_section_widget.cpp | 4 +- .../SourceFiles/info/info_wrap_widget.cpp | 2 +- .../SourceFiles/settings/settings_chat.cpp | 23 +++--- .../SourceFiles/window/section_widget.cpp | 67 ++++++++++----- Telegram/SourceFiles/window/section_widget.h | 13 +-- .../window/themes/window_theme.cpp | 81 +++++++++---------- .../SourceFiles/window/themes/window_theme.h | 14 ++-- .../SourceFiles/window/window_main_menu.cpp | 11 +-- .../window/window_session_controller.cpp | 76 ++++++++++++----- .../window/window_session_controller.h | 28 ++++--- 17 files changed, 213 insertions(+), 135 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp index 3759697e3..a5c6833f7 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp @@ -26,7 +26,7 @@ object_ptr TabbedMemento::createWidget( TabbedSection::TabbedSection( QWidget *parent, not_null controller) -: Window::SectionWidget(parent, controller) +: Window::SectionWidget(parent, controller, PaintedBackground::Custom) , _selector(controller->tabbedSelector()) { _selector->setParent(this); _selector->setRoundRadius(0); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 6cefdb93e..d6342a486 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -164,7 +164,7 @@ void Widget::BottomButton::paintEvent(QPaintEvent *e) { Widget::Widget( QWidget *parent, not_null controller) - : Window::AbstractSectionWidget(parent, controller) +: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Custom) , _api(&controller->session().mtp()) , _searchControls(this) , _mainMenuToggle(_searchControls, st::dialogsMenuToggle) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp index 56086db7b..eb23ebd45 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp @@ -275,11 +275,14 @@ Widget::Widget( QWidget *parent, not_null controller, not_null channel) -: Window::SectionWidget(parent, controller) +: Window::SectionWidget(parent, controller, PaintedBackground::Section) , _scroll(this, st::historyScroll, false) , _fixedBar(this, controller, channel) , _fixedBarShadow(this) -, _whatIsThis(this, tr::lng_admin_log_about(tr::now).toUpper(), st::historyComposeButton) { +, _whatIsThis( + this, + tr::lng_admin_log_about(tr::now).toUpper(), + st::historyComposeButton) { _fixedBar->move(0, 0); _fixedBar->resizeToWidth(width()); _fixedBar->showFilterRequests( @@ -303,6 +306,11 @@ Widget::Widget( updateAdaptiveLayout(); }, lifetime()); + controller->repaintBackgroundRequests( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + _inner = _scroll->setOwnedWidget(object_ptr(this, controller, channel)); _inner->showSearchSignal( ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index f723e1813..b634e6c04 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -171,7 +171,7 @@ const auto kPsaAboutPrefix = "cloud_lng_about_psa_"; HistoryWidget::HistoryWidget( QWidget *parent, not_null controller) -: Window::AbstractSectionWidget(parent, controller) +: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Section) , _api(&controller->session().mtp()) , _updateEditTimeLeftDisplay([=] { updateField(); }) , _fieldBarCancel(this, st::historyReplyCancel) @@ -432,6 +432,11 @@ HistoryWidget::HistoryWidget( } }, lifetime()); + controller->repaintBackgroundRequests( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + session().data().newItemAdded( ) | rpl::start_with_next([=](not_null item) { newItemAdded(item); diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 32548d4c4..d58b93ed1 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -90,7 +90,7 @@ PinnedWidget::PinnedWidget( QWidget *parent, not_null controller, not_null history) -: Window::SectionWidget(parent, controller) +: Window::SectionWidget(parent, controller, PaintedBackground::Section) , _history(history->migrateToOrMe()) , _migratedPeer(_history->peer->migrateFrom()) , _topBar(this, controller) diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 2d1d52204..1e0ed722d 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -146,7 +146,7 @@ RepliesWidget::RepliesWidget( not_null controller, not_null history, MsgId rootId) -: Window::SectionWidget(parent, controller) +: Window::SectionWidget(parent, controller, PaintedBackground::Section) , _history(history) , _rootId(rootId) , _root(lookupRoot()) diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 234c35e2b..296ae3d17 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -90,7 +90,7 @@ ScheduledWidget::ScheduledWidget( QWidget *parent, not_null controller, not_null history) -: Window::SectionWidget(parent, controller) +: Window::SectionWidget(parent, controller, PaintedBackground::Section) , _history(history) , _scroll(this, st::historyScroll, false) , _topBar(this, controller) diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp index 188432308..e8b405c7f 100644 --- a/Telegram/SourceFiles/info/info_section_widget.cpp +++ b/Telegram/SourceFiles/info/info_section_widget.cpp @@ -24,7 +24,7 @@ SectionWidget::SectionWidget( not_null window, Wrap wrap, not_null memento) -: Window::SectionWidget(parent, window) +: Window::SectionWidget(parent, window, PaintedBackground::Custom) , _content(this, window, wrap, memento) { init(); } @@ -34,7 +34,7 @@ SectionWidget::SectionWidget( not_null window, Wrap wrap, not_null memento) -: Window::SectionWidget(parent, window) +: Window::SectionWidget(parent, window, PaintedBackground::Custom) , _content(memento->takeContent(this, wrap)) { init(); } diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index eeec3631d..09e4f126e 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -59,7 +59,7 @@ WrapWidget::WrapWidget( not_null window, Wrap wrap, not_null memento) -: SectionWidget(parent, window) +: SectionWidget(parent, window, PaintedBackground::Custom) , _wrap(wrap) , _controller(createController(window, memento->content())) , _topShadow(this) { diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 3d99f0702..4195a3265 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -571,18 +571,21 @@ void BackgroundRow::updateImage() { p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); } - const auto &pix = background->pixmap(); - if (!pix.isNull()) { - const auto sx = (pix.width() > pix.height()) - ? ((pix.width() - pix.height()) / 2) + const auto &prepared = background->prepared(); + if (!prepared.isNull()) { + const auto sx = (prepared.width() > prepared.height()) + ? ((prepared.width() - prepared.height()) / 2) : 0; - const auto sy = (pix.height() > pix.width()) - ? ((pix.height() - pix.width()) / 2) + const auto sy = (prepared.height() > prepared.width()) + ? ((prepared.height() - prepared.width()) / 2) : 0; - const auto s = (pix.width() > pix.height()) - ? pix.height() - : pix.width(); - p.drawPixmap(0, 0, size, size, pix, sx, sy, s, s); + const auto s = (prepared.width() > prepared.height()) + ? prepared.height() + : prepared.width(); + p.drawImage( + QRect(0, 0, size, size), + prepared, + QRect(sx, sy, s, s)); } } } diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 305fd38eb..f04897aee 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -18,14 +18,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Window { +AbstractSectionWidget::AbstractSectionWidget( + QWidget *parent, + not_null controller, + PaintedBackground paintedBackground) +: RpWidget(parent) +, _controller(controller) { + if (paintedBackground == PaintedBackground::Section) { + controller->repaintBackgroundRequests( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + } +} + Main::Session &AbstractSectionWidget::session() const { return _controller->session(); } SectionWidget::SectionWidget( QWidget *parent, - not_null controller) -: AbstractSectionWidget(parent, controller) { + not_null controller, + PaintedBackground paintedBackground) +: AbstractSectionWidget(parent, controller, paintedBackground) { } void SectionWidget::setGeometryWithTopMoved( @@ -99,18 +114,16 @@ void SectionWidget::PaintBackground( const auto gradient = background->gradientForFill(); const auto fill = QSize(widget->width(), fullHeight); auto fromy = controller->content()->backgroundFromY(); - auto cached = controller->cachedBackground(fill); - const auto goodCache = (cached.area == fill); - const auto useCached = goodCache || !gradient.isNull(); - if (!cached.pixmap.isNull()) { + auto state = controller->backgroundState(fill); + const auto paintCache = [&](const CachedBackground &cache) { const auto to = QRect( - QPoint(cached.x, fromy + cached.y), - cached.pixmap.size() / cIntRetinaFactor()); - if (goodCache) { - p.drawPixmap(to, cached.pixmap); + QPoint(cache.x, fromy + cache.y), + cache.pixmap.size() / cIntRetinaFactor()); + if (cache.area == fill) { + p.drawPixmap(to, cache.pixmap); } else { - const auto sx = fill.width() / float64(cached.area.width()); - const auto sy = fill.height() / float64(cached.area.height()); + const auto sx = fill.width() / float64(cache.area.width()); + const auto sy = fill.height() / float64(cache.area.height()); const auto round = [](float64 value) -> int { return (value >= 0.) ? int(std::ceil(value)) @@ -122,17 +135,27 @@ void SectionWidget::PaintBackground( sto.y(), round((to.x() + to.width()) * sx) - sto.x(), round((to.y() + to.height()) * sy) - sto.y(), - cached.pixmap); + cache.pixmap); } + }; + const auto goodNow = !state.now.pixmap.isNull() + && (state.now.area == fill); + const auto useCache = goodNow || !gradient.isNull(); + if (useCache) { + if (state.shown < 1. && !gradient.isNull()) { + paintCache(state.was); + p.setOpacity(state.shown); + } + paintCache(state.now); return; } const auto patternOpacity = background->paper().patternOpacity(); - const auto &bg = background->pixmap(); - if (!bg.isNull() && !background->tile()) { - auto hq = PainterHighQualityEnabler(p); + const auto &prepared = background->prepared(); + if (!prepared.isNull() && !background->tile()) { + const auto hq = PainterHighQualityEnabler(p); const auto rects = Window::Theme::ComputeBackgroundRects( fill, - bg.size()); + prepared.size()); if (!gradient.isNull()) { p.drawImage(rects.to, gradient); p.setCompositionMode(QPainter::CompositionMode_SoftLight); @@ -140,17 +163,17 @@ void SectionWidget::PaintBackground( } auto to = rects.to; to.moveTop(to.top() + fromy); - p.drawPixmap(to, bg, rects.from); + p.drawImage(to, prepared, rects.from); return; } if (!gradient.isNull()) { - auto hq = PainterHighQualityEnabler(p); + const auto hq = PainterHighQualityEnabler(p); p.drawImage(QRect(QPoint(0, fromy), fill), gradient); p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); } - if (!bg.isNull()) { - auto &tiled = background->pixmapForTiled(); + if (!prepared.isNull()) { + const auto &tiled = background->preparedForTiled(); auto left = clip.left(); auto top = clip.top(); auto right = clip.left() + clip.width(); @@ -163,7 +186,7 @@ void SectionWidget::PaintBackground( auto cy = qCeil((bottom - fromy) / h); for (auto i = sx; i < cx; ++i) { for (auto j = sy; j < cy; ++j) { - p.drawPixmap(QPointF(i * w, fromy + j * h), tiled); + p.drawImage(QPointF(i * w, fromy + j * h), tiled); } } } diff --git a/Telegram/SourceFiles/window/section_widget.h b/Telegram/SourceFiles/window/section_widget.h index cb9ea5abe..68f1036c4 100644 --- a/Telegram/SourceFiles/window/section_widget.h +++ b/Telegram/SourceFiles/window/section_widget.h @@ -40,12 +40,14 @@ class AbstractSectionWidget , public Media::Player::FloatSectionDelegate , protected base::Subscriber { public: + enum class PaintedBackground { + Section, + Custom, + }; AbstractSectionWidget( QWidget *parent, - not_null controller) - : RpWidget(parent) - , _controller(controller) { - } + not_null controller, + PaintedBackground paintedBackground); [[nodiscard]] Main::Session &session() const; [[nodiscard]] not_null controller() const { @@ -84,7 +86,8 @@ class SectionWidget : public AbstractSectionWidget { public: SectionWidget( QWidget *parent, - not_null controller); + not_null controller, + PaintedBackground paintedBackground); virtual Dialogs::RowDescriptor activeChat() const { return {}; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 820cb00d1..49f4bbd60 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -730,8 +730,8 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) { Assert(colorForFill() || !_gradient.isNull() || (!_original.isNull() - && !_pixmap.isNull() - && !_pixmapForTiled.isNull())); + && !_prepared.isNull() + && !_preparedForTiled.isNull())); _updates.fire({ BackgroundUpdate::Type::New, tile() }); // delayed? if (needResetAdjustable) { @@ -763,46 +763,42 @@ void ChatBackground::setPrepared( } _original = std::move(original); + _prepared = std::move(prepared); _gradient = std::move(gradient); - preparePixmaps(std::move(prepared)); + _imageMonoColor = _gradient.isNull() + ? CalculateImageMonoColor(_prepared) + : std::nullopt; + prepareImageForTiled(); } -void ChatBackground::preparePixmaps(QImage image) { - const auto width = image.width(); - const auto height = image.height(); +void ChatBackground::prepareImageForTiled() { + const auto width = _prepared.width(); + const auto height = _prepared.height(); const auto isSmallForTiled = (width > 0 && height > 0) && (width < kMinimumTiledSize || height < kMinimumTiledSize); - if (isSmallForTiled) { - const auto repeatTimesX = qCeil(kMinimumTiledSize / (1. * width)); - const auto repeatTimesY = qCeil(kMinimumTiledSize / (1. * height)); - auto imageForTiled = QImage( - width * repeatTimesX, - height * repeatTimesY, - QImage::Format_ARGB32_Premultiplied); - imageForTiled.setDevicePixelRatio(image.devicePixelRatio()); - auto imageForTiledBytes = imageForTiled.bits(); - auto bytesInLine = width * sizeof(uint32); - for (auto timesY = 0; timesY != repeatTimesY; ++timesY) { - auto imageBytes = image.constBits(); - for (auto y = 0; y != height; ++y) { - for (auto timesX = 0; timesX != repeatTimesX; ++timesX) { - memcpy(imageForTiledBytes, imageBytes, bytesInLine); - imageForTiledBytes += bytesInLine; - } - imageBytes += image.bytesPerLine(); - imageForTiledBytes += imageForTiled.bytesPerLine() - (repeatTimesX * bytesInLine); - } - } - _pixmapForTiled = Ui::PixmapFromImage(std::move(imageForTiled)); - } - _imageMonoColor = _gradient.isNull() - ? CalculateImageMonoColor(image) - : std::nullopt; - _pixmap = image.isNull() - ? QPixmap() - : Ui::PixmapFromImage(std::move(image)); if (!isSmallForTiled) { - _pixmapForTiled = _pixmap; + _preparedForTiled = _prepared; + return; + } + const auto repeatTimesX = qCeil(kMinimumTiledSize / (1. * width)); + const auto repeatTimesY = qCeil(kMinimumTiledSize / (1. * height)); + _preparedForTiled = QImage( + width * repeatTimesX, + height * repeatTimesY, + QImage::Format_ARGB32_Premultiplied); + _preparedForTiled.setDevicePixelRatio(_prepared.devicePixelRatio()); + auto imageForTiledBytes = _preparedForTiled.bits(); + auto bytesInLine = width * sizeof(uint32); + for (auto timesY = 0; timesY != repeatTimesY; ++timesY) { + auto imageBytes = _prepared.constBits(); + for (auto y = 0; y != height; ++y) { + for (auto timesX = 0; timesX != repeatTimesX; ++timesX) { + memcpy(imageForTiledBytes, imageBytes, bytesInLine); + imageForTiledBytes += bytesInLine; + } + imageBytes += _prepared.bytesPerLine(); + imageForTiledBytes += _preparedForTiled.bytesPerLine() - (repeatTimesX * bytesInLine); + } } } @@ -868,7 +864,7 @@ void ChatBackground::adjustPaletteUsingColor(QColor color) { } std::optional ChatBackground::colorForFill() const { - return !_pixmap.isNull() + return !_prepared.isNull() ? imageMonoColor() : !_gradient.isNull() ? std::nullopt @@ -888,13 +884,12 @@ QImage ChatBackground::createCurrentImage() const { result.fill(*fill); return result; } else if (_gradient.isNull()) { - return pixmap().toImage(); - } else if (pixmap().isNull()) { + return _prepared; + } else if (_prepared.isNull()) { return _gradient; } - const auto &pattern = pixmap(); auto result = _gradient.scaled( - pattern.size(), + _prepared.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); result.setDevicePixelRatio(1.); @@ -902,7 +897,7 @@ QImage ChatBackground::createCurrentImage() const { auto p = QPainter(&result); p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(paper().patternOpacity()); - p.drawPixmap(QRect(QPoint(), pattern.size()), pattern); + p.drawImage(QRect(QPoint(), _prepared.size()), _prepared); } return result; } @@ -1044,7 +1039,7 @@ void ChatBackground::setTestingTheme(Instance &&theme) { saveForRevert(); set( Data::details::TestingEditorWallPaper(), - std::move(_pixmap).toImage()); + base::take(_prepared)); } } else if (switchToThemeBackground) { saveForRevert(); diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 30dc7594f..95448f494 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -170,11 +170,11 @@ public: [[nodiscard]] WallPaperId id() const { return _paper.id(); } - [[nodiscard]] const QPixmap &pixmap() const { - return _pixmap; + [[nodiscard]] const QImage &prepared() const { + return _prepared; } - [[nodiscard]] const QPixmap &pixmapForTiled() const { - return _pixmapForTiled; + [[nodiscard]] const QImage &preparedForTiled() const { + return _preparedForTiled; } [[nodiscard]] std::optional colorForFill() const; [[nodiscard]] QImage gradientForFill() const; @@ -197,7 +197,7 @@ private: void initialRead(); void saveForRevert(); void setPrepared(QImage original, QImage prepared, QImage gradient); - void preparePixmaps(QImage image); + void prepareImageForTiled(); void writeNewBackgroundSettings(); void setPaper(const Data::WallPaper &paper); @@ -239,8 +239,8 @@ private: std::optional _paperColor; QImage _gradient; QImage _original; - QPixmap _pixmap; - QPixmap _pixmapForTiled; + QImage _prepared; + QImage _preparedForTiled; bool _nightMode = false; bool _tileDayValue = false; bool _tileNightValue = true; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index af30cda65..f077cb1fe 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -988,10 +988,11 @@ void MainMenu::refreshBackground() { const auto intensityText = IntensityOfColor(st::mainMenuCoverFg->c); const auto background = Window::Theme::Background(); const auto &paper = background->paper(); - const auto &pixmap = background->pixmap(); + const auto &prepared = background->prepared(); - QRect to, from; - const auto rects = Window::Theme::ComputeBackgroundRects(fill, pixmap.size()); + const auto rects = Window::Theme::ComputeBackgroundRects( + fill, + prepared.size()); auto backgroundImage = !paper.backgroundColors().empty() ? Data::GenerateWallPaper( @@ -999,7 +1000,7 @@ void MainMenu::refreshBackground() { paper.backgroundColors(), paper.gradientRotation(), paper.patternOpacity(), - [&](QPainter &p) { p.drawPixmap(to, pixmap, from); }) + [&](QPainter &p) { p.drawImage(rects.to, prepared, rects.from); }) : QImage( fill * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); @@ -1029,7 +1030,7 @@ void MainMenu::refreshBackground() { // Background image. if (!paper.isPattern()) { - p.drawPixmap(to, pixmap, from); + p.drawImage(rects.to, prepared, rects.from); } // Cut off the part of the background that is under text. diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 2513f8508..89f31d847 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -72,6 +72,7 @@ namespace { constexpr auto kMaxChatEntryHistorySize = 50; constexpr auto kCacheBackgroundTimeout = 3 * crl::time(1000); constexpr auto kCacheBackgroundFastTimeout = crl::time(200); +constexpr auto kBackgroundFadeDuration = crl::time(200); } // namespace @@ -1318,15 +1319,16 @@ void SessionController::openDocument( session().data().message(contextId)); } -CachedBackground SessionController::cachedBackground(QSize area) { - if (_cachedBackground.area != area) { +const BackgroundState &SessionController::backgroundState(QSize area) { + _backgroundState.shown = _backgroundFade.value(1.); + if (_backgroundState.now.area != area) { if (_willCacheForArea != area || !_cacheBackgroundTimer.isActive()) { _willCacheForArea = area; _lastAreaChangeTime = crl::now(); _cacheBackgroundTimer.callOnce(kCacheBackgroundFastTimeout); } } - return _cachedBackground; + return _backgroundState; } void SessionController::cacheBackground() { @@ -1342,8 +1344,8 @@ void SessionController::cacheBackground() { } const auto gradient = background->gradientForFill(); const auto patternOpacity = background->paper().patternOpacity(); - const auto &bg = background->pixmap(); - if (background->tile() || bg.isNull()) { + const auto &prepared = background->prepared(); + if (background->tile() || prepared.isNull()) { auto result = gradient.isNull() ? QImage( _willCacheForArea * cIntRetinaFactor(), @@ -1353,34 +1355,34 @@ void SessionController::cacheBackground() { Qt::IgnoreAspectRatio, Qt::SmoothTransformation); result.setDevicePixelRatio(cRetinaFactor()); - if (!bg.isNull()) { + if (!prepared.isNull()) { QPainter p(&result); if (!gradient.isNull()) { p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); } - const auto &tiled = background->pixmapForTiled(); - auto w = tiled.width() / cRetinaFactor(); - auto h = tiled.height() / cRetinaFactor(); + const auto &tiled = background->preparedForTiled(); + const auto w = tiled.width() / cRetinaFactor(); + const auto h = tiled.height() / cRetinaFactor(); auto sx = 0; auto sy = 0; - auto cx = qCeil(_willCacheForArea.width() / w); - auto cy = qCeil(_willCacheForArea.height() / h); + const auto cx = qCeil(_willCacheForArea.width() / w); + const auto cy = qCeil(_willCacheForArea.height() / h); for (int i = sx; i < cx; ++i) { for (int j = sy; j < cy; ++j) { - p.drawPixmap(QPointF(i * w, j * h), tiled); + p.drawImage(QPointF(i * w, j * h), tiled); } } } - _cachedBackground = CachedBackground{ + setCachedBackground({ .pixmap = Ui::PixmapFromImage(std::move(result)), .area = _willCacheForArea, - }; + }); } else { const auto rects = Window::Theme::ComputeBackgroundRects( _willCacheForArea, - bg.size()); - auto image = bg.toImage().copy(rects.from).scaled( + prepared.size()); + auto image = prepared.copy(rects.from).scaled( rects.to.width() * cIntRetinaFactor(), rects.to.height() * cIntRetinaFactor(), Qt::IgnoreAspectRatio, @@ -1399,21 +1401,51 @@ void SessionController::cacheBackground() { p.drawImage(QRect(QPoint(), rects.to.size()), image); } image = QImage(); - _cachedBackground = { + setCachedBackground({ .pixmap = Ui::PixmapFromImage(std::move(result)), .area = _willCacheForArea, .x = rects.to.x(), .y = rects.to.y(), - }; - } - if (!gradient.isNull()) { - content()->update(); + }); } } +void SessionController::setCachedBackground(CachedBackground &&cached) { + const auto background = Window::Theme::Background(); + if (background->gradientForFill().isNull() + || _backgroundState.now.pixmap.isNull()) { + _backgroundFade.stop(); + _backgroundState.shown = 1.; + _backgroundState.now = std::move(cached); + return; + } + // #TODO themes compose several transitions. + _backgroundState.was = std::move(_backgroundState.now); + _backgroundState.now = std::move(cached); + _backgroundState.shown = 0.; + const auto callback = [=] { + if (!_backgroundFade.animating()) { + _backgroundState.was = {}; + _backgroundState.shown = 1.; + } + _repaintBackgroundRequests.fire({}); + }; + _backgroundFade.start( + callback, + 0., + 1., + kBackgroundFadeDuration); +} + void SessionController::clearCachedBackground() { - _cachedBackground = {}; + _backgroundState = {}; + _backgroundFade.stop(); _cacheBackgroundTimer.cancel(); + _repaintBackgroundRequests.fire({}); +} + +rpl::producer<> SessionController::repaintBackgroundRequests() const { + return _repaintBackgroundRequests.events(); } SessionController::~SessionController() { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index a758584b0..1274533ca 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -7,13 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include #include "base/flags.h" #include "base/object_ptr.h" #include "base/weak_ptr.h" #include "base/timer.h" #include "dialogs/dialogs_key.h" -#include "ui/effects/animation_value.h" +#include "ui/effects/animations.h" #include "ui/layers/layer_widget.h" #include "window/window_adaptive.h" @@ -63,6 +62,12 @@ struct CachedBackground { int y = 0; }; +struct BackgroundState { + CachedBackground was; + CachedBackground now; + float64 shown = 1.; +}; + enum class GifPauseReason { Any = 0, InlineResults = (1 << 0), @@ -232,7 +237,6 @@ private: MsgId _showingRepliesRootId = 0; mtpRequestId _showingRepliesRequestId = 0; - }; class SessionController : public SessionNavigation { @@ -296,11 +300,11 @@ public: void floatPlayerAreaUpdated(); struct ColumnLayout { - int bodyWidth; - int dialogsWidth; - int chatWidth; - int thirdWidth; - Adaptive::WindowLayout windowLayout; + int bodyWidth = 0; + int dialogsWidth = 0; + int chatWidth = 0; + int thirdWidth = 0; + Adaptive::WindowLayout windowLayout = Adaptive::WindowLayout(); }; [[nodiscard]] ColumnLayout computeColumnLayout() const; int dialogsSmallColumnWidth() const; @@ -400,7 +404,8 @@ public: void toggleFiltersMenu(bool enabled); [[nodiscard]] rpl::producer<> filtersMenuChanged() const; - [[nodiscard]] CachedBackground cachedBackground(QSize area); + [[nodiscard]] const BackgroundState &backgroundState(QSize area); + [[nodiscard]] rpl::producer<> repaintBackgroundRequests() const; rpl::lifetime &lifetime() { return _lifetime; @@ -433,6 +438,7 @@ private: void cacheBackground(); void clearCachedBackground(); + void setCachedBackground(CachedBackground &&cached); const not_null _window; @@ -461,10 +467,12 @@ private: rpl::event_stream<> _filtersMenuChanged; - CachedBackground _cachedBackground; + BackgroundState _backgroundState; + Ui::Animations::Simple _backgroundFade; QSize _willCacheForArea; crl::time _lastAreaChangeTime = 0; base::Timer _cacheBackgroundTimer; + rpl::event_stream<> _repaintBackgroundRequests; rpl::lifetime _lifetime;