From d02819db13b17e61dfd7087891be2a995eaa2440 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 30 Dec 2022 13:37:34 +0400 Subject: [PATCH] Support spoilers in reply previews / pinned bar. --- Telegram/SourceFiles/data/data_document.cpp | 13 +-- Telegram/SourceFiles/data/data_document.h | 5 +- .../SourceFiles/data/data_media_types.cpp | 17 ++-- Telegram/SourceFiles/data/data_photo.cpp | 13 +-- Telegram/SourceFiles/data/data_photo.h | 5 +- .../SourceFiles/data/data_reply_preview.cpp | 90 ++++++++++++------- .../SourceFiles/data/data_reply_preview.h | 16 ++-- .../history/history_item_components.cpp | 26 +++++- .../history/history_item_components.h | 1 + .../SourceFiles/history/history_widget.cpp | 36 ++++++-- Telegram/SourceFiles/history/history_widget.h | 2 + .../controls/history_view_forward_panel.cpp | 37 ++++---- .../controls/history_view_forward_panel.h | 5 ++ .../history/view/history_view_pinned_bar.cpp | 11 ++- .../view/history_view_webpage_preview.cpp | 4 +- Telegram/SourceFiles/ui/chat/message_bar.cpp | 73 +++++++++++++-- Telegram/SourceFiles/ui/chat/message_bar.h | 27 ++++-- 17 files changed, 280 insertions(+), 101 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index b9953da2b..84219bc0a 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1198,26 +1198,29 @@ bool DocumentData::isStickerSetInstalled() const { Image *DocumentData::getReplyPreview( Data::FileOrigin origin, - not_null context) { + not_null context, + bool spoiler) { if (!hasThumbnail()) { return nullptr; } else if (!_replyPreview) { _replyPreview = std::make_unique(this); } - return _replyPreview->image(origin, context); + return _replyPreview->image(origin, context, spoiler); } Image *DocumentData::getReplyPreview(not_null item) { - return getReplyPreview(item->fullId(), item->history()->peer); + const auto media = item->media(); + const auto spoiler = media && media->hasSpoiler(); + return getReplyPreview(item->fullId(), item->history()->peer, spoiler); } -bool DocumentData::replyPreviewLoaded() const { +bool DocumentData::replyPreviewLoaded(bool spoiler) const { if (!hasThumbnail()) { return true; } else if (!_replyPreview) { return false; } - return _replyPreview->loaded(); + return _replyPreview->loaded(spoiler); } StickerData *DocumentData::sticker() const { diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index c66ad024e..41dea232f 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -142,9 +142,10 @@ public: [[nodiscard]] Image *getReplyPreview( Data::FileOrigin origin, - not_null context); + not_null context, + bool spoiler); [[nodiscard]] Image *getReplyPreview(not_null item); - [[nodiscard]] bool replyPreviewLoaded() const; + [[nodiscard]] bool replyPreviewLoaded(bool spoiler) const; [[nodiscard]] StickerData *sticker() const; [[nodiscard]] Data::FileOrigin stickerSetOrigin() const; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index b6b881b22..55e6dd172 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -618,7 +618,7 @@ Image *MediaPhoto::replyPreview() const { } bool MediaPhoto::replyPreviewLoaded() const { - return _photo->replyPreviewLoaded(); + return _photo->replyPreviewLoaded(_spoiler); } TextWithEntities MediaPhoto::notificationText() const { @@ -854,7 +854,7 @@ Image *MediaFile::replyPreview() const { } bool MediaFile::replyPreviewLoaded() const { - return _document->replyPreviewLoaded(); + return _document->replyPreviewLoaded(_spoiler); } ItemPreview MediaFile::toPreview(ToPreviewOptions options) const { @@ -1479,10 +1479,11 @@ Image *MediaWebPage::replyPreview() const { } bool MediaWebPage::replyPreviewLoaded() const { + const auto spoiler = false; if (const auto document = MediaWebPage::document()) { - return document->replyPreviewLoaded(); + return document->replyPreviewLoaded(spoiler); } else if (const auto photo = MediaWebPage::photo()) { - return photo->replyPreviewLoaded(); + return photo->replyPreviewLoaded(spoiler); } return true; } @@ -1552,10 +1553,11 @@ Image *MediaGame::replyPreview() const { } bool MediaGame::replyPreviewLoaded() const { + const auto spoiler = false; if (const auto document = _game->document) { - return document->replyPreviewLoaded(); + return document->replyPreviewLoaded(spoiler); } else if (const auto photo = _game->photo) { - return photo->replyPreviewLoaded(); + return photo->replyPreviewLoaded(spoiler); } return true; } @@ -1675,8 +1677,9 @@ Image *MediaInvoice::replyPreview() const { } bool MediaInvoice::replyPreviewLoaded() const { + const auto spoiler = false; if (const auto photo = _invoice.photo) { - return photo->replyPreviewLoaded(); + return photo->replyPreviewLoaded(spoiler); } return true; } diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 146335b77..a091f5bee 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -209,22 +209,25 @@ bool PhotoData::uploading() const { Image *PhotoData::getReplyPreview( Data::FileOrigin origin, - not_null context) { + not_null context, + bool spoiler) { if (!_replyPreview) { _replyPreview = std::make_unique(this); } - return _replyPreview->image(origin, context); + return _replyPreview->image(origin, context, spoiler); } Image *PhotoData::getReplyPreview(not_null item) { - return getReplyPreview(item->fullId(), item->history()->peer); + const auto media = item->media(); + const auto spoiler = media && media->hasSpoiler(); + return getReplyPreview(item->fullId(), item->history()->peer, spoiler); } -bool PhotoData::replyPreviewLoaded() const { +bool PhotoData::replyPreviewLoaded(bool spoiler) const { if (!_replyPreview) { return false; } - return _replyPreview->loaded(); + return _replyPreview->loaded(spoiler); } void PhotoData::setRemoteLocation( diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index aeec93009..33d990723 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -66,9 +66,10 @@ public: [[nodiscard]] Image *getReplyPreview( Data::FileOrigin origin, - not_null context); + not_null context, + bool spoiler); [[nodiscard]] Image *getReplyPreview(not_null item); - [[nodiscard]] bool replyPreviewLoaded() const; + [[nodiscard]] bool replyPreviewLoaded(bool spoiler) const; void setRemoteLocation( int32 dc, diff --git a/Telegram/SourceFiles/data/data_reply_preview.cpp b/Telegram/SourceFiles/data/data_reply_preview.cpp index 4e558784c..2778676aa 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.cpp +++ b/Telegram/SourceFiles/data/data_reply_preview.cpp @@ -27,7 +27,11 @@ ReplyPreview::ReplyPreview(not_null photo) ReplyPreview::~ReplyPreview() = default; -void ReplyPreview::prepare(not_null image, Images::Options options) { +void ReplyPreview::prepare( + not_null image, + Images::Options options, + bool spoiler) { + using namespace Images; if (image->isNull()) { return; } @@ -41,24 +45,34 @@ void ReplyPreview::prepare(not_null image, Images::Options options) { : QSize( st::msgReplyBarSize.height(), h * st::msgReplyBarSize.height() / w); - thumbSize *= cIntRetinaFactor(); - options |= Images::Option::TransparentBackground; + thumbSize *= style::DevicePixelRatio(); + options |= Option::TransparentBackground; auto outerSize = st::msgReplyBarSize.height(); - auto bitmap = image->pixNoCache( - thumbSize, - { .options = options, .outer = { outerSize, outerSize } }); - _image = std::make_unique(bitmap.toImage()); - _good = ((options & Images::Option::Blur) == 0); + auto original = spoiler + ? image->original().scaled( + { 40, 40 }, + Qt::KeepAspectRatio, + Qt::SmoothTransformation) + : image->original(); + auto prepared = Prepare(std::move(original), thumbSize, { + .options = options | (spoiler ? Option::Blur : Option()), + .outer = { outerSize, outerSize }, + }); + (spoiler ? _spoilered : _regular) = std::make_unique( + std::move(prepared)); + _good = spoiler || ((options & Option::Blur) == 0); } Image *ReplyPreview::image( Data::FileOrigin origin, - not_null context) { - if (_checked) { - return _image.get(); - } - if (_document) { - if (!_image || (!_good && _document->hasThumbnail())) { + not_null context, + bool spoiler) { + auto &image = spoiler ? _spoilered : _regular; + auto &checked = spoiler ? _checkedSpoilered : _checkedRegular; + if (checked) { + return image.get(); + } else if (_document) { + if (!image || (!_good && _document->hasThumbnail())) { if (!_documentMedia) { _documentMedia = _document->createMediaView(); _documentMedia->thumbnailWanted(origin); @@ -67,51 +81,67 @@ Image *ReplyPreview::image( const auto option = _document->isVideoMessage() ? Images::Option::RoundCircle : Images::Option::None; - if (thumbnail) { + if (spoiler) { + if (const auto image = _documentMedia->thumbnailInline()) { + prepare(image, option, true); + } else if (thumbnail) { + prepare(thumbnail, option, true); + } + } else if (thumbnail) { prepare(thumbnail, option); - } else if (!_image) { + } else if (!image) { if (const auto image = _documentMedia->thumbnailInline()) { prepare(image, option | Images::Option::Blur); } } if (_good || !_document->hasThumbnail()) { - _checked = true; + checked = true; _documentMedia = nullptr; } } } else { Assert(_photo != nullptr); - if (!_image || !_good) { + if (!image || !_good) { const auto inlineThumbnailBytes = _photo->inlineThumbnailBytes(); if (!_photoMedia) { _photoMedia = _photo->createMediaView(); } + using Size = PhotoSize; const auto loadThumbnail = inlineThumbnailBytes.isEmpty() - || _photoMedia->autoLoadThumbnailAllowed(context); + || (!spoiler + && _photoMedia->autoLoadThumbnailAllowed(context)); if (loadThumbnail) { - _photoMedia->wanted(PhotoSize::Small, origin); + _photoMedia->wanted(Size::Small, origin); } - if (const auto small = _photoMedia->image(PhotoSize::Small)) { - prepare(small, Images::Option(0)); - } else if (const auto large = _photoMedia->image( - PhotoSize::Large)) { - prepare(large, Images::Option(0)); - } else if (!_image) { + if (spoiler) { + const auto option = Images::Option::Blur; + if (const auto blurred = _photoMedia->thumbnailInline()) { + prepare(blurred, {}, true); + } else if (const auto small = _photoMedia->image(Size::Small)) { + prepare(small, {}, true); + } else if (const auto large = _photoMedia->image(Size::Large)) { + prepare(large, {}, true); + } + } else if (const auto small = _photoMedia->image(Size::Small)) { + prepare(small, {}); + } else if (const auto large = _photoMedia->image(Size::Large)) { + prepare(large, {}); + } else if (!image) { if (const auto blurred = _photoMedia->thumbnailInline()) { prepare(blurred, Images::Option::Blur); } } if (_good) { - _checked = true; + checked = true; _photoMedia = nullptr; } } } - return _image.get(); + return image.get(); } -bool ReplyPreview::loaded() const { - return _checked; +bool ReplyPreview::loaded(bool spoiler) const { + return spoiler ? _checkedSpoilered : _checkedRegular; } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_reply_preview.h b/Telegram/SourceFiles/data/data_reply_preview.h index 04abeb433..5c51b86a8 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.h +++ b/Telegram/SourceFiles/data/data_reply_preview.h @@ -25,19 +25,25 @@ public: [[nodiscard]] Image *image( Data::FileOrigin origin, - not_null context); - [[nodiscard]] bool loaded() const; + not_null context, + bool spoiler); + [[nodiscard]] bool loaded(bool spoiler) const; private: - void prepare(not_null image, Images::Options options); + void prepare( + not_null image, + Images::Options options, + bool spoiler = false); - std::unique_ptr _image; + std::unique_ptr _regular; + std::unique_ptr _spoilered; PhotoData *_photo = nullptr; DocumentData *_document = nullptr; std::shared_ptr _photoMedia; std::shared_ptr _documentMedia; bool _good = false; - bool _checked = false; + bool _checkedRegular = false; + bool _checkedSpoilered = false; }; diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 532a04359..8556c255e 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -283,9 +283,10 @@ bool HistoryMessageReply::updateData( } if (replyToMsg) { + const auto repaint = [=] { holder->customEmojiRepaint(); }; const auto context = Core::MarkedTextContext{ .session = &holder->history()->session(), - .customEmojiRepaint = [=] { holder->customEmojiRepaint(); }, + .customEmojiRepaint = repaint, }; replyToText.setMarkedText( st::messageTextStyle, @@ -312,9 +313,17 @@ bool HistoryMessageReply::updateData( ? replyToMsg->from()->id : PeerId(0); } + + const auto media = replyToMsg->media(); + if (!media || !media->hasReplyPreview() || !media->hasSpoiler()) { + spoiler = nullptr; + } else if (!spoiler) { + spoiler = std::make_unique(repaint); + } } else if (force) { replyToMsgId = 0; replyToColorKey = PeerId(0); + spoiler = nullptr; } if (force) { holder->history()->owner().requestItemResize(holder); @@ -463,14 +472,15 @@ void HistoryMessageReply::paint( if (w > st::msgReplyBarSkip) { if (replyToMsg) { - auto hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false; + const auto media = replyToMsg->media(); + auto hasPreview = media && media->hasReplyPreview(); if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) { hasPreview = false; } auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; if (hasPreview) { - if (const auto image = replyToMsg->media()->replyPreview()) { + if (const auto image = media->replyPreview()) { auto to = style::rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x); const auto preview = image->pixSingle( image->size() / style::DevicePixelRatio(), @@ -482,6 +492,16 @@ void HistoryMessageReply::paint( .outer = to.size(), }); p.drawPixmap(to.x(), to.y(), preview); + if (spoiler) { + holder->clearCustomEmojiRepaint(); + Ui::FillSpoilerRect( + p, + to, + Ui::DefaultImageSpoiler().frame( + spoiler->index( + context.now, + context.paused))); + } } } if (w > st::msgReplyBarSkip + previewSkip) { diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 1626a90cb..e20da970b 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -246,6 +246,7 @@ struct HistoryMessageReply WebPageId replyToWebPageId = 0; ReplyToMessagePointer replyToMsg; std::unique_ptr replyToVia; + std::unique_ptr spoiler; ClickHandlerPtr replyToLnk; mutable Ui::Text::String replyToName, replyToText; mutable int replyToVersion = 0; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c0c19ad5f..d59109d47 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7490,20 +7490,44 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { p.setInactive( controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any)); p.fillRect(myrtlrect(0, backy, width(), backh), st::historyReplyBg); + + const auto media = (!drawWebPagePreview && drawMsgText) + ? drawMsgText->media() + : nullptr; + const auto hasPreview = media && media->hasReplyPreview(); + const auto preview = hasPreview ? media->replyPreview() : nullptr; + const auto spoilered = preview && media->hasSpoiler(); + if (!spoilered) { + _replySpoiler = nullptr; + } else if (!_replySpoiler) { + _replySpoiler = std::make_unique([=] { + updateField(); + }); + } + if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { + const auto now = crl::now(); + const auto paused = p.inactive(); auto replyLeft = st::historyReplySkip; (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawWebPagePreview) { if (drawMsgText) { - if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) { - if (const auto image = drawMsgText->media()->replyPreview()) { + if (hasPreview) { + if (preview) { auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - p.drawPixmap(to.x(), to.y(), image->pixSingle( - image->size() / style::DevicePixelRatio(), + p.drawPixmap(to.x(), to.y(), preview->pixSingle( + preview->size() / style::DevicePixelRatio(), { .options = Images::Option::RoundSmall, .outer = to.size(), })); + if (_replySpoiler) { + Ui::FillSpoilerRect( + p, + to, + Ui::DefaultImageSpoiler().frame( + _replySpoiler->index(now, paused))); + } } replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } @@ -7521,8 +7545,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { .availableWidth = width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right(), .palette = &st::historyComposeAreaPalette, .spoiler = Ui::Text::DefaultSpoilerCache(), - .now = crl::now(), - .paused = p.inactive(), + .now = now, + .paused = paused, .elisionLines = 1, }); } else { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 3429b79b0..e0fd4985b 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -70,6 +70,7 @@ class RequestsBar; struct PreparedList; class SendFilesWay; class SendAsButton; +class SpoilerAnimation; enum class ReportReason; class ChooseThemeController; class ContinuousScroll; @@ -626,6 +627,7 @@ private: HistoryItem *_replyEditMsg = nullptr; Ui::Text::String _replyEditMsgText; + std::unique_ptr _replySpoiler; mutable base::Timer _updateEditTimeLeftDisplay; object_ptr _fieldBarCancel; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp index 3265403d4..91ff80e94 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_forum_topic.h" #include "main/main_session.h" #include "ui/chat/forward_options_box.h" +#include "ui/effects/spoiler_mess.h" #include "ui/text/text_options.h" #include "ui/text/text_utilities.h" #include "ui/painter.h" @@ -306,33 +307,35 @@ void ForwardPanel::paint( return; } const_cast(this)->checkTexts(); + const auto now = crl::now(); + const auto paused = p.inactive(); const auto firstItem = _data.items.front(); const auto firstMedia = firstItem->media(); const auto hasPreview = (_data.items.size() < 2) && firstMedia && firstMedia->hasReplyPreview(); const auto preview = hasPreview ? firstMedia->replyPreview() : nullptr; + const auto spoiler = preview && firstMedia->hasSpoiler(); + if (!spoiler) { + _spoiler = nullptr; + } else if (!_spoiler) { + _spoiler = std::make_unique(_repaint); + } if (preview) { auto to = QRect( x, y + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - if (preview->width() == preview->height()) { - p.drawPixmap(to.x(), to.y(), preview->pix()); - } else { - auto from = (preview->width() > preview->height()) - ? QRect( - (preview->width() - preview->height()) / 2, - 0, - preview->height(), - preview->height()) - : QRect( - 0, - (preview->height() - preview->width()) / 2, - preview->width(), - preview->width()); - p.drawPixmap(to, preview->pix(), from); + p.drawPixmap(to.x(), to.y(), preview->pixSingle( + preview->size() / style::DevicePixelRatio(), + { + .options = Images::Option::RoundSmall, + .outer = to.size(), + })); + if (_spoiler) { + Ui::FillSpoilerRect(p, to, Ui::DefaultImageSpoiler().frame( + _spoiler->index(now, paused))); } const auto skip = st::msgReplyBarSize.height() + st::msgReplyBarSkip @@ -355,8 +358,8 @@ void ForwardPanel::paint( .availableWidth = available, .palette = &st::historyComposeAreaPalette, .spoiler = Ui::Text::DefaultSpoilerCache(), - .now = crl::now(), - .paused = p.inactive(), + .now = now, + .paused = paused, .elisionLines = 1, }); } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h index 41aca59e3..af052ec8a 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class Painter; class HistoryItem; +namespace Ui { +class SpoilerAnimation; +} // namespace Ui + namespace Data { class Thread; } // namespace Data @@ -57,6 +61,7 @@ private: rpl::event_stream<> _itemsUpdated; Ui::Text::String _from, _text; + mutable std::unique_ptr _spoiler; int _nameVersion = 0; }; diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp index 5634ce82c..7ec3b365c 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp @@ -38,8 +38,9 @@ namespace { [[nodiscard]] Ui::MessageBarContent ContentWithPreview( not_null item, Image *preview, + bool spoiler, Fn repaint) { - auto result = ContentWithoutPreview(item, std::move(repaint)); + auto result = ContentWithoutPreview(item, repaint); if (!preview) { static const auto kEmpty = [&] { const auto size = st::historyReplyHeight * cIntRetinaFactor(); @@ -51,8 +52,10 @@ namespace { return result; }(); result.preview = kEmpty; + result.spoilerRepaint = nullptr; } else { result.preview = preview->original(); + result.spoilerRepaint = spoiler ? repaint : nullptr; } return result; } @@ -90,7 +93,11 @@ namespace { }) | rpl::then( rpl::single(kFullLoaded) ) | rpl::map([=] { - return ContentWithPreview(item, media->replyPreview(), repaint); + return ContentWithPreview( + item, + media->replyPreview(), + media->hasSpoiler(), + repaint); }); }) | rpl::flatten_latest(); } diff --git a/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp b/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp index 96faf53b8..0d71e756a 100644 --- a/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp @@ -71,8 +71,8 @@ bool DrawWebPageDataPreview( } const auto preview = photo - ? photo->getReplyPreview(Data::FileOrigin(), context) - : document->getReplyPreview(Data::FileOrigin(), context); + ? photo->getReplyPreview(Data::FileOrigin(), context, false) + : document->getReplyPreview(Data::FileOrigin(), context, false); if (preview) { const auto w = preview->width(); const auto h = preview->height(); diff --git a/Telegram/SourceFiles/ui/chat/message_bar.cpp b/Telegram/SourceFiles/ui/chat/message_bar.cpp index e2a7e02ed..0d20dc8ef 100644 --- a/Telegram/SourceFiles/ui/chat/message_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/message_bar.cpp @@ -140,6 +140,7 @@ void MessageBar::tweenTo(MessageBarContent &&content) { ? RectPart::Bottom : RectPart::None; animation.imageFrom = grabImagePart(); + animation.spoilerFrom = std::move(_spoiler); animation.bodyOrTextFrom = grabBodyOrTextPart(animation.bodyAnimation); const auto sameLength = SameFirstPartLength( _content.title, @@ -208,6 +209,12 @@ void MessageBar::updateFromContent(MessageBarContent &&content) { Ui::DialogTextOptions(), _content.context); _image = prepareImage(_content.preview); + if (!_content.spoilerRepaint) { + _spoiler = nullptr; + } else if (!_spoiler) { + _spoiler = std::make_unique( + _content.spoilerRepaint); + } } QRect MessageBar::imageRect() const { @@ -258,10 +265,21 @@ auto MessageBar::makeGrabGuard() { auto imageShown = _animation ? std::move(_animation->imageShown) : Ui::Animations::Simple(); - return gsl::finally([&, shown = std::move(imageShown)]() mutable { + auto spoiler = std::move(_spoiler); + auto fromSpoiler = _animation + ? std::move(_animation->spoilerFrom) + : nullptr; + return gsl::finally([ + &, + shown = std::move(imageShown), + spoiler = std::move(spoiler), + fromSpoiler = std::move(fromSpoiler) + ]() mutable { if (_animation) { _animation->imageShown = std::move(shown); + _animation->spoilerFrom = std::move(fromSpoiler); } + _spoiler = std::move(spoiler); }); } @@ -358,12 +376,20 @@ void MessageBar::paint(Painter &p) { : (_animation->movingTo == RectPart::Top) ? (shiftTo - shiftFull) : (shiftTo + shiftFull); + const auto now = crl::now(); + const auto paused = p.inactive(); paintLeftBar(p); if (!_animation) { if (!_image.isNull()) { - p.drawPixmap(image, _image); + paintImageWithSpoiler( + p, + image, + _image, + _spoiler.get(), + now, + paused); } } else if (!_animation->imageTo.isNull() || (!_animation->imageFrom.isNull() @@ -381,14 +407,30 @@ void MessageBar::paint(Painter &p) { }(); if (_animation->bodyMoved.animating()) { p.setOpacity(1. - progress); - p.drawPixmap( + paintImageWithSpoiler( + p, rect.translated(0, shiftFrom), - _animation->imageFrom); + _animation->imageFrom, + _animation->spoilerFrom.get(), + now, + paused); p.setOpacity(progress); - p.drawPixmap(rect.translated(0, shiftTo), _animation->imageTo); + paintImageWithSpoiler( + p, + rect.translated(0, shiftTo), + _animation->imageTo, + _spoiler.get(), + now, + paused); p.setOpacity(1.); } else { - p.drawPixmap(rect, _image); + paintImageWithSpoiler( + p, + rect, + _image, + _spoiler.get(), + now, + paused); } } if (!_animation || _animation->bodyAnimation == BodyAnimation::None) { @@ -409,8 +451,8 @@ void MessageBar::paint(Painter &p) { .availableWidth = body.width(), .palette = &_st.textPalette, .spoiler = Ui::Text::DefaultSpoilerCache(), - .now = crl::now(), - .paused = p.inactive(), + .now = now, + .paused = paused, .elisionLines = 1, }); } @@ -510,6 +552,21 @@ void MessageBar::ensureGradientsCreated(int size) { _topBarGradient = Images::PixmapFast(std::move(top)); } +void MessageBar::paintImageWithSpoiler( + QPainter &p, + QRect rect, + const QPixmap &image, + SpoilerAnimation *spoiler, + crl::time now, + bool paused) const { + p.drawPixmap(rect, image); + if (spoiler) { + const auto frame = DefaultImageSpoiler().frame( + spoiler->index(now, paused)); + FillSpoilerRect(p, rect, frame); + } +} + void MessageBar::paintLeftBar(Painter &p) { const auto state = countBarState(); const auto gradientSize = int(std::ceil(state.size * 2.5)); diff --git a/Telegram/SourceFiles/ui/chat/message_bar.h b/Telegram/SourceFiles/ui/chat/message_bar.h index 83fb393a6..99a8101b2 100644 --- a/Telegram/SourceFiles/ui/chat/message_bar.h +++ b/Telegram/SourceFiles/ui/chat/message_bar.h @@ -18,6 +18,8 @@ struct MessageBar; namespace Ui { +class SpoilerAnimation; + struct MessageBarContent { int index = 0; int count = 1; @@ -25,6 +27,7 @@ struct MessageBarContent { TextWithEntities text; std::any context; QImage preview; + Fn spoilerRepaint; style::margins margins; }; @@ -38,7 +41,7 @@ public: void set(MessageBarContent &&content); void set(rpl::producer content); - [[nodiscard]] not_null widget() { + [[nodiscard]] not_null widget() { return &_widget; } @@ -52,10 +55,10 @@ private: None, }; struct Animation { - Ui::Animations::Simple bodyMoved; - Ui::Animations::Simple imageShown; - Ui::Animations::Simple barScroll; - Ui::Animations::Simple barTop; + Animations::Simple bodyMoved; + Animations::Simple imageShown; + Animations::Simple barScroll; + Animations::Simple barTop; QPixmap bodyOrTextFrom; QPixmap bodyOrTextTo; QPixmap titleSame; @@ -63,6 +66,7 @@ private: QPixmap titleTo; QPixmap imageFrom; QPixmap imageTo; + std::unique_ptr spoilerFrom; BodyAnimation bodyAnimation = BodyAnimation::None; RectPart movingTo = RectPart::None; }; @@ -98,19 +102,28 @@ private: [[nodiscard]] BarState countBarState() const; void ensureGradientsCreated(int size); + void paintImageWithSpoiler( + QPainter &p, + QRect rect, + const QPixmap &image, + SpoilerAnimation *spoiler, + crl::time now, + bool paused) const; + [[nodiscard]] static BodyAnimation DetectBodyAnimationType( Animation *currentAnimation, const MessageBarContent ¤tContent, const MessageBarContent &nextContent); const style::MessageBar &_st; - Ui::RpWidget _widget; + RpWidget _widget; Fn _customEmojiPaused; MessageBarContent _content; rpl::lifetime _contentLifetime; - Ui::Text::String _title, _text; + Text::String _title, _text; QPixmap _image, _topBarGradient, _bottomBarGradient; std::unique_ptr _animation; + std::unique_ptr _spoiler; bool _customEmojiRepaintScheduled = false; };