From 86db29cec701a9878598af5e54811156533b5650 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 12 Jul 2021 14:02:35 +0300 Subject: [PATCH] Added AbstractSingleMediaPreview. --- .../attach_abstract_single_media_preview.cpp | 201 +++++++++++++++ .../attach_abstract_single_media_preview.h | 61 +++++ .../attach/attach_single_media_preview.cpp | 231 ++++-------------- .../chat/attach/attach_single_media_preview.h | 41 +--- Telegram/cmake/td_ui.cmake | 2 + 5 files changed, 317 insertions(+), 219 deletions(-) create mode 100644 Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp create mode 100644 Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp new file mode 100644 index 000000000..742f3c111 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp @@ -0,0 +1,201 @@ +/* +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_abstract_single_media_preview.h" + +#include "editor/photo_editor_common.h" +#include "ui/chat/attach/attach_controls.h" +#include "ui/image/image_prepare.h" +#include "ui/widgets/buttons.h" +#include "styles/style_boxes.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +constexpr auto kMinPreviewWidth = 20; + +} // namespace + +AbstractSingleMediaPreview::AbstractSingleMediaPreview(QWidget *parent) +: RpWidget(parent) +, _minThumbH(st::sendBoxAlbumGroupSize.height() + + st::sendBoxAlbumGroupSkipTop * 2) +, _photoEditorButton(base::make_unique_q(this)) +, _controls(base::make_unique_q(this)) { +} + +AbstractSingleMediaPreview::~AbstractSingleMediaPreview() = default; + +rpl::producer<> AbstractSingleMediaPreview::deleteRequests() const { + return _controls->deleteRequests(); +} + +rpl::producer<> AbstractSingleMediaPreview::editRequests() const { + return _controls->editRequests(); +} + +rpl::producer<> AbstractSingleMediaPreview::modifyRequests() const { + return _photoEditorButton->clicks() | rpl::to_empty; +} + +void AbstractSingleMediaPreview::preparePreview(QImage preview) { + auto maxW = 0; + auto maxH = 0; + if (_animated && drawBackground()) { + 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; + if (_previewHeight < _minThumbH) { + _previewTop = (_minThumbH - _previewHeight) / 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()); + + _photoEditorButton->resize(_previewWidth, _previewHeight); + _photoEditorButton->moveToLeft(_previewLeft, _previewTop); + _photoEditorButton->setVisible(isPhoto()); + + resize(width(), std::max(_previewHeight, _minThumbH)); +} + +void AbstractSingleMediaPreview::resizeEvent(QResizeEvent *e) { + _controls->moveToRight( + st::boxPhotoPadding.right() + st::sendBoxAlbumGroupSkipRight, + st::sendBoxAlbumGroupSkipTop, + width()); +} + +void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (drawBackground()) { + const auto &padding = st::boxPhotoPadding; + if (_previewLeft > padding.left()) { + p.fillRect( + padding.left(), + _previewTop, + _previewLeft - padding.left(), + _previewHeight, + st::confirmBg); + } + if ((_previewLeft + _previewWidth) < (width() - padding.right())) { + p.fillRect( + _previewLeft + _previewWidth, + _previewTop, + width() - padding.right() - _previewLeft - _previewWidth, + _previewHeight, + st::confirmBg); + } + if (_previewTop > 0) { + p.fillRect( + padding.left(), + 0, + width() - padding.right() - padding.left(), + height(), + st::confirmBg); + } + } + if (!tryPaintAnimation(p)) { + p.drawPixmap(_previewLeft, _previewTop, _preview); + } + if (_animated && !isAnimatedPreviewReady()) { + const auto innerSize = st::msgFileLayout.thumbSize; + auto inner = QRect( + _previewLeft + (_previewWidth - innerSize) / 2, + _previewTop + (_previewHeight - innerSize) / 2, + innerSize, + innerSize); + p.setPen(Qt::NoPen); + p.setBrush(st::msgDateImgBg); + + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(inner); + } + + auto icon = &st::historyFileInPlay; + icon->paintInCenter(p, inner); + } +} + +int AbstractSingleMediaPreview::previewLeft() const { + return _previewLeft; +} + +int AbstractSingleMediaPreview::previewTop() const { + return _previewTop; +} + +int AbstractSingleMediaPreview::previewWidth() const { + return _previewWidth; +} + +int AbstractSingleMediaPreview::previewHeight() const { + return _previewHeight; +} + +void AbstractSingleMediaPreview::setAnimated(bool animated) { + _animated = animated; +} + +bool AbstractSingleMediaPreview::isPhoto() const { + return drawBackground() + && !isAnimatedPreviewReady() + && !_animated; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h new file mode 100644 index 000000000..688d169ac --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h @@ -0,0 +1,61 @@ +/* +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 "ui/abstract_button.h" + +namespace Ui { + +class AttachControlsWidget; + +class AbstractSingleMediaPreview : public RpWidget { +public: + AbstractSingleMediaPreview(QWidget *parent); + ~AbstractSingleMediaPreview(); + + [[nodiscard]] rpl::producer<> deleteRequests() const; + [[nodiscard]] rpl::producer<> editRequests() const; + [[nodiscard]] rpl::producer<> modifyRequests() const; + + [[nodiscard]] bool isPhoto() const; + +protected: + virtual bool drawBackground() const = 0; + virtual bool tryPaintAnimation(Painter &p) = 0; + virtual bool isAnimatedPreviewReady() const = 0; + + void preparePreview(QImage preview); + + int previewLeft() const; + int previewTop() const; + int previewWidth() const; + int previewHeight() const; + + void setAnimated(bool animated); + +private: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + + bool _animated = false; + QPixmap _preview; + int _previewLeft = 0; + int _previewTop = 0; + int _previewWidth = 0; + int _previewHeight = 0; + + const int _minThumbH; + const base::unique_qptr _photoEditorButton; + const base::unique_qptr _controls; + + rpl::event_stream<> _modifyRequests; + +}; + +} // 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 index da13b1509..f9addd862 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp @@ -8,22 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_single_media_preview.h" #include "editor/photo_editor_common.h" -#include "ui/chat/attach/attach_controls.h" #include "ui/chat/attach/attach_prepare.h" -#include "ui/widgets/buttons.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" -#include "styles/palette.h" namespace Ui { -namespace { - -constexpr auto kMinPreviewWidth = 20; - -} // namespace SingleMediaPreview *SingleMediaPreview::Create( QWidget *parent, @@ -65,119 +54,59 @@ SingleMediaPreview::SingleMediaPreview( bool animated, bool sticker, const QString &animatedPreviewPath) -: RpWidget(parent) +: AbstractSingleMediaPreview(parent) , _gifPaused(std::move(gifPaused)) -, _animated(animated) -, _sticker(sticker) -, _minThumbH(st::sendBoxAlbumGroupSize.height() - + st::sendBoxAlbumGroupSkipTop * 2) -, _photoEditorButton(base::make_unique_q(this)) -, _controls(base::make_unique_q(this)) { +, _sticker(sticker) { Expects(!preview.isNull()); + setAnimated(animated); - preparePreview(preview, animatedPreviewPath); + prepareAnimatedPreview(animatedPreviewPath, animated); + preparePreview(preview); } -SingleMediaPreview::~SingleMediaPreview() = default; - -rpl::producer<> SingleMediaPreview::deleteRequests() const { - return _controls->deleteRequests(); +bool SingleMediaPreview::drawBackground() const { + return !_sticker; } -rpl::producer<> SingleMediaPreview::editRequests() const { - return _controls->editRequests(); +bool SingleMediaPreview::tryPaintAnimation(Painter &p) { + 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(), previewTop(), frame); + return true; + } 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, + (previewHeight() - size.height()) / 2, + size.width(), + size.height()), + frame); + _lottiePreview->markFrameShown(); + return true; + } + return false; } -rpl::producer<> SingleMediaPreview::modifyRequests() const { - return _photoEditorButton->clicks() | rpl::to_empty; -} - -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; - if (_previewHeight < _minThumbH) { - _previewTop = (_minThumbH - _previewHeight) / 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); - - _photoEditorButton->resize(_previewWidth, _previewHeight); - _photoEditorButton->moveToLeft(_previewLeft, _previewTop); - _photoEditorButton->setVisible(!_sticker - && !_gifPreview - && !_lottiePreview - && !_animated); - - resize(width(), std::max(_previewHeight, _minThumbH)); -} - -void SingleMediaPreview::resizeEvent(QResizeEvent *e) { - _controls->moveToRight( - st::boxPhotoPadding.right() + st::sendBoxAlbumGroupSkipRight, - st::sendBoxAlbumGroupSkipTop, - width()); +bool SingleMediaPreview::isAnimatedPreviewReady() const { + return _gifPreview || _lottiePreview; } void SingleMediaPreview::prepareAnimatedPreview( - const QString &animatedPreviewPath) { - if (_sticker && _animated) { - const auto box = QSize(_previewWidth, _previewHeight) + const QString &animatedPreviewPath, + bool animated) { + if (_sticker && animated) { + const auto box = QSize(previewWidth(), previewHeight()) * style::DevicePixelRatio(); _lottiePreview = std::make_unique( Lottie::ReadContent(QByteArray(), animatedPreviewPath), @@ -206,7 +135,7 @@ void SingleMediaPreview::clipCallback( } if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) { - auto s = QSize(_previewWidth, _previewHeight); + const auto s = QSize(previewWidth(), previewHeight()); _gifPreview->start( s.width(), s.height(), @@ -227,80 +156,4 @@ void SingleMediaPreview::clipCallback( } } -void SingleMediaPreview::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_sticker) { - const auto &padding = st::boxPhotoPadding; - if (_previewLeft > padding.left()) { - p.fillRect( - padding.left(), - _previewTop, - _previewLeft - padding.left(), - _previewHeight, - st::confirmBg); - } - if ((_previewLeft + _previewWidth) < (width() - padding.right())) { - p.fillRect( - _previewLeft + _previewWidth, - _previewTop, - width() - padding.right() - _previewLeft - _previewWidth, - _previewHeight, - st::confirmBg); - } - if (_previewTop > 0) { - p.fillRect( - padding.left(), - 0, - width() - padding.right() - padding.left(), - height(), - 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, _previewTop, 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, - (_previewHeight - size.height()) / 2, - size.width(), - size.height()), - frame); - _lottiePreview->markFrameShown(); - } else { - p.drawPixmap(_previewLeft, _previewTop, _preview); - } - if (_animated && !_gifPreview && !_lottiePreview) { - const auto innerSize = st::msgFileLayout.thumbSize; - auto inner = QRect( - _previewLeft + (_previewWidth - innerSize) / 2, - _previewTop + (_previewHeight - innerSize) / 2, - innerSize, - innerSize); - p.setPen(Qt::NoPen); - p.setBrush(st::msgDateImgBg); - - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(inner); - } - - auto icon = &st::historyFileInPlay; - icon->paintInCenter(p, inner); - } -} - } // 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 index 3c54da015..2d3f83958 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h @@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/rp_widget.h" -#include "ui/abstract_button.h" +#include "ui/chat/attach/attach_abstract_single_media_preview.h" #include "media/clip/media_clip_reader.h" namespace Lottie { @@ -17,11 +16,9 @@ class SinglePlayer; namespace Ui { -class AttachControlsWidget; struct PreparedFile; -class IconButton; -class SingleMediaPreview final : public RpWidget { +class SingleMediaPreview final : public AbstractSingleMediaPreview { public: static SingleMediaPreview *Create( QWidget *parent, @@ -35,39 +32,23 @@ public: bool animated, bool sticker, const QString &animatedPreviewPath); - ~SingleMediaPreview(); - [[nodiscard]] rpl::producer<> deleteRequests() const; - [[nodiscard]] rpl::producer<> editRequests() const; - [[nodiscard]] rpl::producer<> modifyRequests() const; +protected: + bool drawBackground() const override; + bool tryPaintAnimation(Painter &p) override; + bool isAnimatedPreviewReady() const override; private: - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - - void preparePreview( - QImage preview, - const QString &animatedPreviewPath); - void prepareAnimatedPreview(const QString &animatedPreviewPath); + void prepareAnimatedPreview( + const QString &animatedPreviewPath, + bool animated); void clipCallback(Media::Clip::Notification notification); - Fn _gifPaused; - bool _animated = false; - bool _sticker = false; - QPixmap _preview; - int _previewLeft = 0; - int _previewTop = 0; - int _previewWidth = 0; - int _previewHeight = 0; + const Fn _gifPaused; + const bool _sticker = false; Media::Clip::ReaderPointer _gifPreview; std::unique_ptr _lottiePreview; - const int _minThumbH; - const base::unique_qptr _photoEditorButton; - const base::unique_qptr _controls; - - rpl::event_stream<> _modifyRequests; - }; } // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 002bff38d..60d21e872 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -108,6 +108,8 @@ PRIVATE ui/boxes/single_choice_box.h ui/chat/attach/attach_abstract_single_file_preview.cpp ui/chat/attach/attach_abstract_single_file_preview.h + ui/chat/attach/attach_abstract_single_media_preview.cpp + ui/chat/attach/attach_abstract_single_media_preview.h ui/chat/attach/attach_album_preview.cpp ui/chat/attach/attach_album_preview.h ui/chat/attach/attach_album_thumbnail.cpp