Added AbstractSingleMediaPreview.

This commit is contained in:
23rd 2021-07-12 14:02:35 +03:00 committed by John Preston
parent 19139a9a5f
commit 86db29cec7
5 changed files with 317 additions and 219 deletions

View file

@ -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<AbstractButton>(this))
, _controls(base::make_unique_q<AttachControlsWidget>(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

View file

@ -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<AbstractButton> _photoEditorButton;
const base::unique_qptr<AttachControlsWidget> _controls;
rpl::event_stream<> _modifyRequests;
};
} // namespace Ui

View file

@ -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<AbstractButton>(this))
, _controls(base::make_unique_q<AttachControlsWidget>(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::SinglePlayer>(
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

View file

@ -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<bool()> _gifPaused;
bool _animated = false;
bool _sticker = false;
QPixmap _preview;
int _previewLeft = 0;
int _previewTop = 0;
int _previewWidth = 0;
int _previewHeight = 0;
const Fn<bool()> _gifPaused;
const bool _sticker = false;
Media::Clip::ReaderPointer _gifPreview;
std::unique_ptr<Lottie::SinglePlayer> _lottiePreview;
const int _minThumbH;
const base::unique_qptr<AbstractButton> _photoEditorButton;
const base::unique_qptr<AttachControlsWidget> _controls;
rpl::event_stream<> _modifyRequests;
};
} // namespace Ui

View file

@ -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