From 64ac6b18bf344014b5ac299a54d9220a2879ac89 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 13 Oct 2020 20:15:52 +0300 Subject: [PATCH] Move SingleMediaPreview to td_ui. --- .../SourceFiles/boxes/edit_caption_box.cpp | 4 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 262 +----------------- .../passport/passport_panel_edit_scans.cpp | 3 +- .../SourceFiles/storage/localimageloader.cpp | 2 +- .../storage/storage_media_prepare.cpp | 15 +- .../storage/storage_media_prepare.h | 1 - .../ui/chat/attach/attach_prepare.cpp | 7 + .../ui/chat/attach/attach_prepare.h | 1 + .../attach/attach_single_media_preview.cpp | 235 ++++++++++++++++ .../chat/attach/attach_single_media_preview.h | 66 +++++ Telegram/cmake/td_ui.cmake | 3 + 11 files changed, 327 insertions(+), 272 deletions(-) create mode 100644 Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp create mode 100644 Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index e3f6e530fc..1a216e3bd5 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -498,7 +498,7 @@ void EditCaptionBox::updateEditPreview() { auto shouldAsDoc = true; auto docPhotoSize = QSize(); if (const auto image = std::get_if(fileMedia)) { - shouldAsDoc = !Storage::ValidateThumbDimensions( + shouldAsDoc = !Ui::ValidateThumbDimensions( image->data.width(), image->data.height()); if (shouldAsDoc) { @@ -694,7 +694,7 @@ bool EditCaptionBox::fileFromClipboard(not_null data) { using Info = Ui::PreparedFileInformation; const auto fileMedia = &file->information->media; if (const auto image = std::get_if(fileMedia)) { - return !Storage::ValidateThumbDimensions( + return !Ui::ValidateThumbDimensions( image->data.width(), image->data.height()); } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index f6ac6012b9..16a3898097 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_album_preview.h" #include "ui/chat/attach/attach_single_file_preview.h" +#include "ui/chat/attach/attach_single_media_preview.h" #include "ui/text/format_values.h" #include "ui/grouped_layout.h" #include "ui/text/text_options.h" @@ -56,8 +57,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -constexpr auto kMinPreviewWidth = 20; - using Ui::SendFilesWay; inline bool CanAddUrls(const QList &urls) { @@ -95,259 +94,6 @@ void FileDialogCallback( callback(std::move(*list)); } -class SingleMediaPreview : public Ui::RpWidget { -public: - static SingleMediaPreview *Create( - QWidget *parent, - not_null controller, - const Ui::PreparedFile &file); - - SingleMediaPreview( - QWidget *parent, - not_null controller, - QImage preview, - bool animated, - bool sticker, - const QString &animatedPreviewPath); - - bool canSendAsPhoto() const { - return _canSendAsPhoto; - } - - rpl::producer desiredHeightValue() const override; - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - void preparePreview( - QImage preview, - const QString &animatedPreviewPath); - void prepareAnimatedPreview(const QString &animatedPreviewPath); - void clipCallback(Media::Clip::Notification notification); - - not_null _controller; - bool _animated = false; - bool _sticker = false; - bool _canSendAsPhoto = false; - QPixmap _preview; - int _previewLeft = 0; - int _previewWidth = 0; - int _previewHeight = 0; - Media::Clip::ReaderPointer _gifPreview; - std::unique_ptr _lottiePreview; - -}; - -SingleMediaPreview *SingleMediaPreview::Create( - QWidget *parent, - not_null controller, - const Ui::PreparedFile &file) { - auto preview = QImage(); - bool animated = false; - bool animationPreview = false; - if (const auto image = std::get_if( - &file.information->media)) { - preview = image->data; - animated = animationPreview = image->animated; - } else if (const auto video = std::get_if( - &file.information->media)) { - preview = video->thumbnail; - animated = true; - animationPreview = video->isGifv; - } - if (preview.isNull()) { - return nullptr; - } else if (!animated && !Storage::ValidateThumbDimensions( - preview.width(), - preview.height())) { - return nullptr; - } - return Ui::CreateChild( - parent, - controller, - preview, - animated, - Core::IsMimeSticker(file.information->filemime), - animationPreview ? file.path : QString()); -} - -SingleMediaPreview::SingleMediaPreview( - QWidget *parent, - not_null controller, - QImage preview, - bool animated, - bool sticker, - const QString &animatedPreviewPath) -: RpWidget(parent) -, _controller(controller) -, _animated(animated) -, _sticker(sticker) { - Expects(!preview.isNull()); - - _canSendAsPhoto = !_animated - && !_sticker - && Storage::ValidateThumbDimensions( - preview.width(), - preview.height()); - - preparePreview(preview, animatedPreviewPath); -} - -void SingleMediaPreview::preparePreview( - QImage preview, - const QString &animatedPreviewPath) { - auto maxW = 0; - auto maxH = 0; - if (_animated && !_sticker) { - auto limitW = st::sendMediaPreviewSize; - auto limitH = st::confirmMaxHeight; - maxW = qMax(preview.width(), 1); - maxH = qMax(preview.height(), 1); - if (maxW * limitH > maxH * limitW) { - if (maxW < limitW) { - maxH = maxH * limitW / maxW; - maxW = limitW; - } - } else { - if (maxH < limitH) { - maxW = maxW * limitH / maxH; - maxH = limitH; - } - } - preview = Images::prepare( - preview, - maxW * cIntRetinaFactor(), - maxH * cIntRetinaFactor(), - Images::Option::Smooth | Images::Option::Blurred, - maxW, - maxH); - } - auto originalWidth = preview.width(); - auto originalHeight = preview.height(); - if (!originalWidth || !originalHeight) { - originalWidth = originalHeight = 1; - } - _previewWidth = st::sendMediaPreviewSize; - if (preview.width() < _previewWidth) { - _previewWidth = qMax(preview.width(), kMinPreviewWidth); - } - auto maxthumbh = qMin(qRound(1.5 * _previewWidth), st::confirmMaxHeight); - _previewHeight = qRound(originalHeight * float64(_previewWidth) / originalWidth); - if (_previewHeight > maxthumbh) { - _previewWidth = qRound(_previewWidth * float64(maxthumbh) / _previewHeight); - accumulate_max(_previewWidth, kMinPreviewWidth); - _previewHeight = maxthumbh; - } - _previewLeft = (st::boxWideWidth - _previewWidth) / 2; - - preview = std::move(preview).scaled( - _previewWidth * cIntRetinaFactor(), - _previewHeight * cIntRetinaFactor(), - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - preview = Images::prepareOpaque(std::move(preview)); - _preview = App::pixmapFromImageInPlace(std::move(preview)); - _preview.setDevicePixelRatio(cRetinaFactor()); - - prepareAnimatedPreview(animatedPreviewPath); -} - -void SingleMediaPreview::prepareAnimatedPreview( - const QString &animatedPreviewPath) { - if (_sticker && _animated) { - const auto box = QSize(_previewWidth, _previewHeight) - * cIntRetinaFactor(); - _lottiePreview = std::make_unique( - Lottie::ReadContent(QByteArray(), animatedPreviewPath), - Lottie::FrameRequest{ box }); - _lottiePreview->updates( - ) | rpl::start_with_next([=] { - update(); - }, lifetime()); - } else if (!animatedPreviewPath.isEmpty()) { - auto callback = [=](Media::Clip::Notification notification) { - clipCallback(notification); - }; - _gifPreview = Media::Clip::MakeReader( - animatedPreviewPath, - std::move(callback)); - } -} - -void SingleMediaPreview::clipCallback(Media::Clip::Notification notification) { - using namespace Media::Clip; - switch (notification) { - case NotificationReinit: { - if (_gifPreview && _gifPreview->state() == State::Error) { - _gifPreview.setBad(); - } - - if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) { - auto s = QSize(_previewWidth, _previewHeight); - _gifPreview->start(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None); - } - - update(); - } break; - - case NotificationRepaint: { - if (_gifPreview && !_gifPreview->currentDisplayed()) { - update(); - } - } break; - } -} - -void SingleMediaPreview::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_sticker) { - if (_previewLeft > st::boxPhotoPadding.left()) { - p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _previewLeft - st::boxPhotoPadding.left(), _previewHeight, st::confirmBg); - } - if (_previewLeft + _previewWidth < width() - st::boxPhotoPadding.right()) { - p.fillRect(_previewLeft + _previewWidth, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _previewLeft - _previewWidth, _previewHeight, st::confirmBg); - } - } - if (_gifPreview && _gifPreview->started()) { - auto s = QSize(_previewWidth, _previewHeight); - auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer); - auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now()); - p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), frame); - } else if (_lottiePreview && _lottiePreview->ready()) { - const auto frame = _lottiePreview->frame(); - const auto size = frame.size() / cIntRetinaFactor(); - p.drawImage( - QRect( - _previewLeft + (_previewWidth - size.width()) / 2, - st::boxPhotoPadding.top() + (_previewHeight - size.height()) / 2, - size.width(), - size.height()), - frame); - _lottiePreview->markFrameShown(); - } else { - p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), _preview); - } - if (_animated && !_gifPreview && !_lottiePreview) { - auto inner = QRect(_previewLeft + (_previewWidth - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_previewHeight - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); - p.setPen(Qt::NoPen); - p.setBrush(st::msgDateImgBg); - - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(inner); - } - - auto icon = &st::historyFileInPlay; - icon->paintInCenter(p, inner); - } -} - -rpl::producer SingleMediaPreview::desiredHeightValue() const { - return rpl::single(st::boxPhotoPadding.top() + _previewHeight); -} - rpl::producer FieldPlaceholder( const Ui::PreparedList &list, SendFilesWay way) { @@ -410,7 +156,11 @@ void SendFilesBox::prepareSingleFilePreview() { Expects(IsSingleItem(_list)); const auto &file = _list.files[0]; - const auto media = SingleMediaPreview::Create(this, _controller, file); + const auto controller = _controller; + const auto media = Ui::SingleMediaPreview::Create(this, [=] { + return controller->isGifPausedAtLeastFor( + Window::GifPauseReason::Layer); + }, file); if (media) { if (!media->canSendAsPhoto()) { _compressConfirm = CompressConfirm::None; diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp index 6bdad1c90e..85b77c7122 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "ui/chat/attach/attach_prepare.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "ui/text/text_options.h" #include "core/file_utilities.h" @@ -41,7 +42,7 @@ std::variant ProcessImage(QByteArray &&bytes) { auto image = App::readImage(base::take(bytes)); if (image.isNull()) { return ReadScanError::CantReadImage; - } else if (!Storage::ValidateThumbDimensions(image.width(), image.height())) { + } else if (!Ui::ValidateThumbDimensions(image.width(), image.height())) { return ReadScanError::BadImageSize; } if (std::max(image.width(), image.height()) > kMaxDimensions) { diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 782e73fb36..d9c8a52a78 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -40,7 +40,7 @@ constexpr auto kThumbnailQuality = 87; constexpr auto kThumbnailSize = 320; constexpr auto kPhotoUploadPartSize = 32 * 1024; -using Storage::ValidateThumbDimensions; +using Ui::ValidateThumbDimensions; struct PreparedFileThumbnail { uint64 id = 0; diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.cpp b/Telegram/SourceFiles/storage/storage_media_prepare.cpp index 629ea47663..ebd65091c1 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.cpp +++ b/Telegram/SourceFiles/storage/storage_media_prepare.cpp @@ -43,13 +43,13 @@ bool ValidPhotoForAlbum( } const auto width = image.data.width(); const auto height = image.data.height(); - return ValidateThumbDimensions(width, height); + return Ui::ValidateThumbDimensions(width, height); } bool ValidVideoForAlbum(const PreparedFileInformation::Video &video) { const auto width = video.thumbnail.width(); const auto height = video.thumbnail.height(); - return ValidateThumbDimensions(width, height); + return Ui::ValidateThumbDimensions(width, height); } QSize PrepareShownDimensions(const QImage &preview) { @@ -163,13 +163,6 @@ bool ValidateDragData(not_null data, bool isAlbum) { return true; } -bool ValidateThumbDimensions(int width, int height) { - return (width > 0) - && (height > 0) - && (width < 20 * height) - && (height < 20 * width); -} - MimeDataState ComputeMimeDataState(const QMimeData *data) { if (!data || data->hasFormat(qsl("application/x-td-forward"))) { return MimeDataState::None; @@ -272,7 +265,7 @@ PreparedList PrepareMediaFromImage( QByteArray &&content, int previewWidth) { auto result = Storage::PreparedList(); - result.allFilesForCompress = ValidateThumbDimensions( + result.allFilesForCompress = Ui::ValidateThumbDimensions( image.width(), image.height()); auto file = PreparedFile(QString()); @@ -345,7 +338,7 @@ std::optional PreparedFileFromFilesDialog( const auto media = &file.information->media; const auto valid = v::match(*media, [](const Info::Image &data) { - return Storage::ValidateThumbDimensions( + return Ui::ValidateThumbDimensions( data.data.width(), data.data.height()) && !data.animated; diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.h b/Telegram/SourceFiles/storage/storage_media_prepare.h index d07d90abe9..98b298a3b8 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.h +++ b/Telegram/SourceFiles/storage/storage_media_prepare.h @@ -32,7 +32,6 @@ std::optional PreparedFileFromFilesDialog( int previewWidth); MimeDataState ComputeMimeDataState(const QMimeData *data); bool ValidateDragData(not_null data, bool isAlbum); -bool ValidateThumbDimensions(int width, int height); Ui::PreparedList PrepareMediaList(const QList &files, int previewWidth); Ui::PreparedList PrepareMediaList(const QStringList &files, int previewWidth); Ui::PreparedList PrepareMediaFromImage( diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index 94a03753d6..06712deb95 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -88,4 +88,11 @@ int MaxAlbumItems() { return kMaxAlbumCount; } +bool ValidateThumbDimensions(int width, int height) { + return (width > 0) + && (height > 0) + && (width < 20 * height) + && (height < 20 * width); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index 2853996cd9..05f9e3db08 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -81,5 +81,6 @@ struct PreparedList { }; [[nodiscard]] int MaxAlbumItems(); +[[nodiscard]] bool ValidateThumbDimensions(int width, int height); } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp new file mode 100644 index 0000000000..18b41cbb9e --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp @@ -0,0 +1,235 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/chat/attach/attach_single_media_preview.h" + +#include "ui/chat/attach/attach_prepare.h" +#include "core/mime_type.h" +#include "lottie/lottie_single_player.h" +#include "styles/style_boxes.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +constexpr auto kMinPreviewWidth = 20; + +} // namespace + +SingleMediaPreview *SingleMediaPreview::Create( + QWidget *parent, + Fn gifPaused, + const PreparedFile &file) { + auto preview = QImage(); + bool animated = false; + bool animationPreview = false; + if (const auto image = std::get_if( + &file.information->media)) { + preview = image->data; + animated = animationPreview = image->animated; + } else if (const auto video = std::get_if( + &file.information->media)) { + preview = video->thumbnail; + animated = true; + animationPreview = video->isGifv; + } + if (preview.isNull()) { + return nullptr; + } else if (!animated && !ValidateThumbDimensions( + preview.width(), + preview.height())) { + return nullptr; + } + return CreateChild( + parent, + std::move(gifPaused), + preview, + animated, + Core::IsMimeSticker(file.information->filemime), + animationPreview ? file.path : QString()); +} + +SingleMediaPreview::SingleMediaPreview( + QWidget *parent, + Fn gifPaused, + QImage preview, + bool animated, + bool sticker, + const QString &animatedPreviewPath) +: RpWidget(parent) +, _gifPaused(std::move(gifPaused)) +, _animated(animated) +, _sticker(sticker) { + Expects(!preview.isNull()); + + _canSendAsPhoto = !_animated + && !_sticker + && ValidateThumbDimensions( + preview.width(), + preview.height()); + + preparePreview(preview, animatedPreviewPath); +} + +SingleMediaPreview::~SingleMediaPreview() = default; + +void SingleMediaPreview::preparePreview( + QImage preview, + const QString &animatedPreviewPath) { + auto maxW = 0; + auto maxH = 0; + if (_animated && !_sticker) { + auto limitW = st::sendMediaPreviewSize; + auto limitH = st::confirmMaxHeight; + maxW = qMax(preview.width(), 1); + maxH = qMax(preview.height(), 1); + if (maxW * limitH > maxH * limitW) { + if (maxW < limitW) { + maxH = maxH * limitW / maxW; + maxW = limitW; + } + } else { + if (maxH < limitH) { + maxW = maxW * limitH / maxH; + maxH = limitH; + } + } + preview = Images::prepare( + preview, + maxW * style::DevicePixelRatio(), + maxH * style::DevicePixelRatio(), + Images::Option::Smooth | Images::Option::Blurred, + maxW, + maxH); + } + auto originalWidth = preview.width(); + auto originalHeight = preview.height(); + if (!originalWidth || !originalHeight) { + originalWidth = originalHeight = 1; + } + _previewWidth = st::sendMediaPreviewSize; + if (preview.width() < _previewWidth) { + _previewWidth = qMax(preview.width(), kMinPreviewWidth); + } + auto maxthumbh = qMin(qRound(1.5 * _previewWidth), st::confirmMaxHeight); + _previewHeight = qRound(originalHeight * float64(_previewWidth) / originalWidth); + if (_previewHeight > maxthumbh) { + _previewWidth = qRound(_previewWidth * float64(maxthumbh) / _previewHeight); + accumulate_max(_previewWidth, kMinPreviewWidth); + _previewHeight = maxthumbh; + } + _previewLeft = (st::boxWideWidth - _previewWidth) / 2; + + preview = std::move(preview).scaled( + _previewWidth * style::DevicePixelRatio(), + _previewHeight * style::DevicePixelRatio(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + preview = Images::prepareOpaque(std::move(preview)); + _preview = PixmapFromImage(std::move(preview)); + _preview.setDevicePixelRatio(style::DevicePixelRatio()); + + prepareAnimatedPreview(animatedPreviewPath); +} + +void SingleMediaPreview::prepareAnimatedPreview( + const QString &animatedPreviewPath) { + if (_sticker && _animated) { + const auto box = QSize(_previewWidth, _previewHeight) + * style::DevicePixelRatio(); + _lottiePreview = std::make_unique( + Lottie::ReadContent(QByteArray(), animatedPreviewPath), + Lottie::FrameRequest{ box }); + _lottiePreview->updates( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + } else if (!animatedPreviewPath.isEmpty()) { + auto callback = [=](Media::Clip::Notification notification) { + clipCallback(notification); + }; + _gifPreview = Media::Clip::MakeReader( + animatedPreviewPath, + std::move(callback)); + } +} + +void SingleMediaPreview::clipCallback(Media::Clip::Notification notification) { + using namespace Media::Clip; + switch (notification) { + case NotificationReinit: { + if (_gifPreview && _gifPreview->state() == State::Error) { + _gifPreview.setBad(); + } + + if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) { + auto s = QSize(_previewWidth, _previewHeight); + _gifPreview->start(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None); + } + + update(); + } break; + + case NotificationRepaint: { + if (_gifPreview && !_gifPreview->currentDisplayed()) { + update(); + } + } break; + } +} + +void SingleMediaPreview::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_sticker) { + if (_previewLeft > st::boxPhotoPadding.left()) { + p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _previewLeft - st::boxPhotoPadding.left(), _previewHeight, st::confirmBg); + } + if (_previewLeft + _previewWidth < width() - st::boxPhotoPadding.right()) { + p.fillRect(_previewLeft + _previewWidth, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _previewLeft - _previewWidth, _previewHeight, st::confirmBg); + } + } + if (_gifPreview && _gifPreview->started()) { + auto s = QSize(_previewWidth, _previewHeight); + auto paused = _gifPaused(); + auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now()); + p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), frame); + } else if (_lottiePreview && _lottiePreview->ready()) { + const auto frame = _lottiePreview->frame(); + const auto size = frame.size() / style::DevicePixelRatio(); + p.drawImage( + QRect( + _previewLeft + (_previewWidth - size.width()) / 2, + st::boxPhotoPadding.top() + (_previewHeight - size.height()) / 2, + size.width(), + size.height()), + frame); + _lottiePreview->markFrameShown(); + } else { + p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), _preview); + } + if (_animated && !_gifPreview && !_lottiePreview) { + auto inner = QRect(_previewLeft + (_previewWidth - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_previewHeight - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + p.setPen(Qt::NoPen); + p.setBrush(st::msgDateImgBg); + + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(inner); + } + + auto icon = &st::historyFileInPlay; + icon->paintInCenter(p, inner); + } +} + +rpl::producer SingleMediaPreview::desiredHeightValue() const { + return rpl::single(st::boxPhotoPadding.top() + _previewHeight); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h new file mode 100644 index 0000000000..17e035e8e8 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h @@ -0,0 +1,66 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "media/clip/media_clip_reader.h" + +namespace Lottie { +class SinglePlayer; +} // namespace Lottie + +namespace Ui { + +struct PreparedFile; + +class SingleMediaPreview : public RpWidget { +public: + static SingleMediaPreview *Create( + QWidget *parent, + Fn gifPaused, + const PreparedFile &file); + + SingleMediaPreview( + QWidget *parent, + Fn gifPaused, + QImage preview, + bool animated, + bool sticker, + const QString &animatedPreviewPath); + ~SingleMediaPreview(); + + bool canSendAsPhoto() const { + return _canSendAsPhoto; + } + + rpl::producer desiredHeightValue() const override; + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + void preparePreview( + QImage preview, + const QString &animatedPreviewPath); + void prepareAnimatedPreview(const QString &animatedPreviewPath); + void clipCallback(Media::Clip::Notification notification); + + Fn _gifPaused; + bool _animated = false; + bool _sticker = false; + bool _canSendAsPhoto = false; + QPixmap _preview; + int _previewLeft = 0; + int _previewWidth = 0; + int _previewHeight = 0; + Media::Clip::ReaderPointer _gifPreview; + std::unique_ptr _lottiePreview; + +}; + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 8a19ce621e..ac073df9cb 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -75,6 +75,8 @@ PRIVATE ui/chat/attach/attach_prepare.h ui/chat/attach/attach_single_file_preview.cpp ui/chat/attach/attach_single_file_preview.h + ui/chat/attach/attach_single_media_preview.cpp + ui/chat/attach/attach_single_media_preview.h ui/chat/message_bar.cpp ui/chat/message_bar.h ui/chat/pinned_bar.cpp @@ -107,4 +109,5 @@ PUBLIC tdesktop::td_lang desktop-app::lib_ui desktop-app::lib_ffmpeg + desktop-app::lib_lottie )