mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support displaying of photo spoilers.
This commit is contained in:
parent
ae819eb1a6
commit
25746d195c
26 changed files with 315 additions and 109 deletions
|
@ -667,6 +667,8 @@ PRIVATE
|
||||||
history/view/media/history_view_media_common.h
|
history/view/media/history_view_media_common.h
|
||||||
history/view/media/history_view_media_grouped.cpp
|
history/view/media/history_view_media_grouped.cpp
|
||||||
history/view/media/history_view_media_grouped.h
|
history/view/media/history_view_media_grouped.h
|
||||||
|
history/view/media/history_view_media_spoiler.cpp
|
||||||
|
history/view/media/history_view_media_spoiler.h
|
||||||
history/view/media/history_view_media_unwrapped.cpp
|
history/view/media/history_view_media_unwrapped.cpp
|
||||||
history/view/media/history_view_media_unwrapped.h
|
history/view/media/history_view_media_unwrapped.h
|
||||||
history/view/media/history_view_photo.cpp
|
history/view/media/history_view_photo.cpp
|
||||||
|
|
|
@ -530,9 +530,11 @@ ItemPreview Media::toGroupPreview(
|
||||||
|
|
||||||
MediaPhoto::MediaPhoto(
|
MediaPhoto::MediaPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PhotoData*> photo)
|
not_null<PhotoData*> photo,
|
||||||
|
bool spoiler)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _photo(photo) {
|
, _photo(photo)
|
||||||
|
, _spoiler(spoiler) {
|
||||||
parent->history()->owner().registerPhotoItem(_photo, parent);
|
parent->history()->owner().registerPhotoItem(_photo, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +558,7 @@ MediaPhoto::~MediaPhoto() {
|
||||||
std::unique_ptr<Media> MediaPhoto::clone(not_null<HistoryItem*> parent) {
|
std::unique_ptr<Media> MediaPhoto::clone(not_null<HistoryItem*> parent) {
|
||||||
return _chat
|
return _chat
|
||||||
? std::make_unique<MediaPhoto>(parent, _chat, _photo)
|
? std::make_unique<MediaPhoto>(parent, _chat, _photo)
|
||||||
: std::make_unique<MediaPhoto>(parent, _photo);
|
: std::make_unique<MediaPhoto>(parent, _photo, _spoiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
PhotoData *MediaPhoto::photo() const {
|
PhotoData *MediaPhoto::photo() const {
|
||||||
|
@ -722,17 +724,20 @@ std::unique_ptr<HistoryView::Media> MediaPhoto::createView(
|
||||||
return std::make_unique<HistoryView::Photo>(
|
return std::make_unique<HistoryView::Photo>(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
_photo);
|
_photo,
|
||||||
|
_spoiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaFile::MediaFile(
|
MediaFile::MediaFile(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
bool skipPremiumEffect)
|
bool skipPremiumEffect,
|
||||||
|
bool spoiler)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _document(document)
|
, _document(document)
|
||||||
, _emoji(document->sticker() ? document->sticker()->alt : QString())
|
, _emoji(document->sticker() ? document->sticker()->alt : QString())
|
||||||
, _skipPremiumEffect(skipPremiumEffect) {
|
, _skipPremiumEffect(skipPremiumEffect)
|
||||||
|
, _spoiler(spoiler) {
|
||||||
parent->history()->owner().registerDocumentItem(_document, parent);
|
parent->history()->owner().registerDocumentItem(_document, parent);
|
||||||
|
|
||||||
if (!_emoji.isEmpty()) {
|
if (!_emoji.isEmpty()) {
|
||||||
|
@ -755,7 +760,8 @@ std::unique_ptr<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
|
||||||
return std::make_unique<MediaFile>(
|
return std::make_unique<MediaFile>(
|
||||||
parent,
|
parent,
|
||||||
_document,
|
_document,
|
||||||
!_document->session().premium());
|
!_document->session().premium(),
|
||||||
|
_spoiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentData *MediaFile::document() const {
|
DocumentData *MediaFile::document() const {
|
||||||
|
@ -1096,13 +1102,15 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
|
||||||
return std::make_unique<HistoryView::Gif>(
|
return std::make_unique<HistoryView::Gif>(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
_document);
|
_document,
|
||||||
|
_spoiler);
|
||||||
}
|
}
|
||||||
} else if (_document->isAnimation() || _document->isVideoFile()) {
|
} else if (_document->isAnimation() || _document->isVideoFile()) {
|
||||||
return std::make_unique<HistoryView::Gif>(
|
return std::make_unique<HistoryView::Gif>(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
_document);
|
_document,
|
||||||
|
_spoiler);
|
||||||
} else if (_document->isTheme() && _document->hasThumbnail()) {
|
} else if (_document->isTheme() && _document->hasThumbnail()) {
|
||||||
return std::make_unique<HistoryView::ThemeDocument>(
|
return std::make_unique<HistoryView::ThemeDocument>(
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -165,7 +165,8 @@ class MediaPhoto final : public Media {
|
||||||
public:
|
public:
|
||||||
MediaPhoto(
|
MediaPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PhotoData*> photo);
|
not_null<PhotoData*> photo,
|
||||||
|
bool spoiler);
|
||||||
MediaPhoto(
|
MediaPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PeerData*> chat,
|
not_null<PeerData*> chat,
|
||||||
|
@ -200,6 +201,7 @@ public:
|
||||||
private:
|
private:
|
||||||
not_null<PhotoData*> _photo;
|
not_null<PhotoData*> _photo;
|
||||||
PeerData *_chat = nullptr;
|
PeerData *_chat = nullptr;
|
||||||
|
bool _spoiler = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,7 +210,8 @@ public:
|
||||||
MediaFile(
|
MediaFile(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
bool skipPremiumEffect);
|
bool skipPremiumEffect,
|
||||||
|
bool spoiler);
|
||||||
~MediaFile();
|
~MediaFile();
|
||||||
|
|
||||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
@ -242,6 +245,7 @@ private:
|
||||||
not_null<DocumentData*> _document;
|
not_null<DocumentData*> _document;
|
||||||
QString _emoji;
|
QString _emoji;
|
||||||
bool _skipPremiumEffect = false;
|
bool _skipPremiumEffect = false;
|
||||||
|
bool _spoiler = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,8 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||||
return photo->match([&](const MTPDphoto &photo) -> Result {
|
return photo->match([&](const MTPDphoto &photo) -> Result {
|
||||||
return std::make_unique<Data::MediaPhoto>(
|
return std::make_unique<Data::MediaPhoto>(
|
||||||
item,
|
item,
|
||||||
item->history()->owner().processPhoto(photo));
|
item->history()->owner().processPhoto(photo),
|
||||||
|
media.is_spoiler());
|
||||||
}, [](const MTPDphotoEmpty &) -> Result {
|
}, [](const MTPDphotoEmpty &) -> Result {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
});
|
});
|
||||||
|
@ -205,7 +206,8 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||||
return std::make_unique<Data::MediaFile>(
|
return std::make_unique<Data::MediaFile>(
|
||||||
item,
|
item,
|
||||||
item->history()->owner().processDocument(document),
|
item->history()->owner().processDocument(document),
|
||||||
media.is_nopremium());
|
media.is_nopremium(),
|
||||||
|
media.is_spoiler());
|
||||||
}, [](const MTPDdocumentEmpty &) -> Result {
|
}, [](const MTPDdocumentEmpty &) -> Result {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
});
|
});
|
||||||
|
@ -513,10 +515,12 @@ HistoryItem::HistoryItem(
|
||||||
std::move(markup));
|
std::move(markup));
|
||||||
|
|
||||||
const auto skipPremiumEffect = !history->session().premium();
|
const auto skipPremiumEffect = !history->session().premium();
|
||||||
|
const auto spoiler = false;
|
||||||
_media = std::make_unique<Data::MediaFile>(
|
_media = std::make_unique<Data::MediaFile>(
|
||||||
this,
|
this,
|
||||||
document,
|
document,
|
||||||
skipPremiumEffect);
|
skipPremiumEffect,
|
||||||
|
spoiler);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,7 +549,8 @@ HistoryItem::HistoryItem(
|
||||||
postAuthor,
|
postAuthor,
|
||||||
std::move(markup));
|
std::move(markup));
|
||||||
|
|
||||||
_media = std::make_unique<Data::MediaPhoto>(this, photo);
|
const auto spoiler = false;
|
||||||
|
_media = std::make_unique<Data::MediaPhoto>(this, photo, spoiler);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -431,6 +431,9 @@ void Element::hideSpoilers() {
|
||||||
if (_text.hasSpoilers()) {
|
if (_text.hasSpoilers()) {
|
||||||
_text.setSpoilerRevealed(false, anim::type::instant);
|
_text.setSpoilerRevealed(false, anim::type::instant);
|
||||||
}
|
}
|
||||||
|
if (_media) {
|
||||||
|
_media->hideSpoilers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::customEmojiRepaint() {
|
void Element::customEmojiRepaint() {
|
||||||
|
|
|
@ -7,11 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/history_view_spoiler_click_handler.h"
|
#include "history/view/history_view_spoiler_click_handler.h"
|
||||||
|
|
||||||
#include "core/click_handler_types.h" // ClickHandlerContext
|
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "window/window_session_controller.h"
|
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -22,18 +21,13 @@ void FillTextWithAnimatedSpoilers(
|
||||||
if (text.hasSpoilers()) {
|
if (text.hasSpoilers()) {
|
||||||
text.setSpoilerLinkFilter([weak = base::make_weak(view)](
|
text.setSpoilerLinkFilter([weak = base::make_weak(view)](
|
||||||
const ClickContext &context) {
|
const ClickContext &context) {
|
||||||
const auto my = context.other.value<ClickHandlerContext>();
|
|
||||||
const auto button = context.button;
|
const auto button = context.button;
|
||||||
const auto view = weak.get();
|
const auto view = weak.get();
|
||||||
if (button != Qt::LeftButton || !view || !my.elementDelegate) {
|
if (button != Qt::LeftButton || !view) {
|
||||||
return false;
|
return false;
|
||||||
} else if (const auto d = my.elementDelegate()) {
|
|
||||||
if (const auto controller = my.sessionWindow.get()) {
|
|
||||||
controller->session().data().registerShownSpoiler(view);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
view->history()->owner().registerShownSpoiler(view);
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1454,6 +1454,12 @@ TextWithEntities Document::getCaption() const {
|
||||||
return TextWithEntities();
|
return TextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::hideSpoilers() {
|
||||||
|
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||||
|
captioned->caption.setSpoilerRevealed(false, anim::type::instant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ui::Text::String Document::createCaption() {
|
Ui::Text::String Document::createCaption() {
|
||||||
return File::createCaption(_realParent);
|
return File::createCaption(_realParent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities getCaption() const override;
|
TextWithEntities getCaption() const override;
|
||||||
|
void hideSpoilers() override;
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ ExtendedPreview::ExtendedPreview(
|
||||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||||
const auto item = parent->data();
|
const auto item = parent->data();
|
||||||
_caption = createCaption(item);
|
_caption = createCaption(item);
|
||||||
_link = MakeInvoiceLink(item);
|
_spoiler.link = MakeInvoiceLink(item);
|
||||||
resolveButtonText();
|
resolveButtonText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,15 +97,15 @@ void ExtendedPreview::ensureThumbnailRead() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExtendedPreview::hasHeavyPart() const {
|
bool ExtendedPreview::hasHeavyPart() const {
|
||||||
return _animation || !_inlineThumbnail.isNull();
|
return _spoiler.animation || !_inlineThumbnail.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtendedPreview::unloadHeavyPart() {
|
void ExtendedPreview::unloadHeavyPart() {
|
||||||
_inlineThumbnail
|
_inlineThumbnail
|
||||||
= _imageCache
|
= _spoiler.background
|
||||||
= _cornerCache
|
= _spoiler.cornerCache
|
||||||
= _buttonBackground = QImage();
|
= _buttonBackground = QImage();
|
||||||
_animation = nullptr;
|
_spoiler.animation = nullptr;
|
||||||
_caption.unloadPersistentAnimation();
|
_caption.unloadPersistentAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,8 +217,8 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
|
||||||
fillImageShadow(p, rthumb, *rounding, context);
|
fillImageShadow(p, rthumb, *rounding, context);
|
||||||
}
|
}
|
||||||
validateImageCache(rthumb.size(), rounding);
|
validateImageCache(rthumb.size(), rounding);
|
||||||
p.drawImage(rthumb.topLeft(), _imageCache);
|
p.drawImage(rthumb.topLeft(), _spoiler.background);
|
||||||
fillSpoilerMess(p, rthumb, rounding, context);
|
fillImageSpoiler(p, &_spoiler, rthumb, context);
|
||||||
paintButton(p, rthumb, context);
|
paintButton(p, rthumb, context);
|
||||||
if (context.selected()) {
|
if (context.selected()) {
|
||||||
fillImageOverlay(p, rthumb, rounding, context);
|
fillImageOverlay(p, rthumb, rounding, context);
|
||||||
|
@ -263,14 +263,14 @@ void ExtendedPreview::validateImageCache(
|
||||||
QSize outer,
|
QSize outer,
|
||||||
std::optional<Ui::BubbleRounding> rounding) const {
|
std::optional<Ui::BubbleRounding> rounding) const {
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
if (_imageCache.size() == (outer * ratio)
|
if (_spoiler.background.size() == (outer * ratio)
|
||||||
&& _imageCacheRounding == rounding) {
|
&& _spoiler.backgroundRounding == rounding) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_imageCache = Images::Round(
|
_spoiler.background = Images::Round(
|
||||||
prepareImageCache(outer),
|
prepareImageCache(outer),
|
||||||
MediaRoundingMask(rounding));
|
MediaRoundingMask(rounding));
|
||||||
_imageCacheRounding = rounding;
|
_spoiler.backgroundRounding = rounding;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage ExtendedPreview::prepareImageCache(QSize outer) const {
|
QImage ExtendedPreview::prepareImageCache(QSize outer) const {
|
||||||
|
@ -278,28 +278,6 @@ QImage ExtendedPreview::prepareImageCache(QSize outer) const {
|
||||||
return PrepareWithBlurredBackground(outer, {}, {}, _inlineThumbnail);
|
return PrepareWithBlurredBackground(outer, {}, {}, _inlineThumbnail);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtendedPreview::fillSpoilerMess(
|
|
||||||
QPainter &p,
|
|
||||||
QRect rect,
|
|
||||||
std::optional<Ui::BubbleRounding> rounding,
|
|
||||||
const PaintContext &context) const {
|
|
||||||
if (!_animation) {
|
|
||||||
_animation = std::make_unique<Ui::SpoilerAnimation>([=] {
|
|
||||||
_parent->customEmojiRepaint();
|
|
||||||
});
|
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
|
||||||
}
|
|
||||||
_parent->clearCustomEmojiRepaint();
|
|
||||||
const auto &spoiler = Ui::DefaultImageSpoiler();
|
|
||||||
const auto index = _animation->index(context.now, context.paused);
|
|
||||||
Ui::FillSpoilerRect(
|
|
||||||
p,
|
|
||||||
rect,
|
|
||||||
MediaRoundingMask(rounding),
|
|
||||||
spoiler.frame(index),
|
|
||||||
_cornerCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExtendedPreview::paintButton(
|
void ExtendedPreview::paintButton(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
QRect outer,
|
QRect outer,
|
||||||
|
@ -318,13 +296,14 @@ void ExtendedPreview::paintButton(
|
||||||
const auto size = QSize(width, height);
|
const auto size = QSize(width, height);
|
||||||
if (_buttonBackground.size() != size * ratio
|
if (_buttonBackground.size() != size * ratio
|
||||||
|| _buttonBackgroundOverlay != overlay) {
|
|| _buttonBackgroundOverlay != overlay) {
|
||||||
if (_imageCache.width() < width * ratio
|
auto &background = _spoiler.background;
|
||||||
|| _imageCache.height() < height * ratio) {
|
if (background.width() < width * ratio
|
||||||
|
|| background.height() < height * ratio) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_buttonBackground = _imageCache.copy(QRect(
|
_buttonBackground = background.copy(QRect(
|
||||||
(_imageCache.width() - width * ratio) / 2,
|
(background.width() - width * ratio) / 2,
|
||||||
(_imageCache.height() - height * ratio) / 2,
|
(background.height() - height * ratio) / 2,
|
||||||
width * ratio,
|
width * ratio,
|
||||||
height * ratio));
|
height * ratio));
|
||||||
_buttonBackground.setDevicePixelRatio(ratio);
|
_buttonBackground.setDevicePixelRatio(ratio);
|
||||||
|
@ -374,7 +353,7 @@ TextState ExtendedPreview::textState(QPoint point, StateRequest request) const {
|
||||||
painth -= st::mediaCaptionSkip;
|
painth -= st::mediaCaptionSkip;
|
||||||
}
|
}
|
||||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||||
result.link = _link;
|
result.link = _spoiler.link;
|
||||||
}
|
}
|
||||||
if (_caption.isEmpty() && _parent->media() == this) {
|
if (_caption.isEmpty() && _parent->media() == this) {
|
||||||
auto fullRight = paintx + paintw;
|
auto fullRight = paintx + paintw;
|
||||||
|
@ -402,11 +381,11 @@ TextState ExtendedPreview::textState(QPoint point, StateRequest request) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExtendedPreview::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
|
bool ExtendedPreview::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
|
||||||
return p == _link;
|
return p == _spoiler.link;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExtendedPreview::dragItemByHandler(const ClickHandlerPtr &p) const {
|
bool ExtendedPreview::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||||
return p == _link;
|
return p == _spoiler.link;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExtendedPreview::needInfoDisplay() const {
|
bool ExtendedPreview::needInfoDisplay() const {
|
||||||
|
@ -420,6 +399,10 @@ TextForMimeData ExtendedPreview::selectedText(TextSelection selection) const {
|
||||||
return _caption.toTextForMimeData(selection);
|
return _caption.toTextForMimeData(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::hideSpoilers() {
|
||||||
|
_caption.setSpoilerRevealed(false, anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
bool ExtendedPreview::needsBubble() const {
|
bool ExtendedPreview::needsBubble() const {
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
|
#include "history/view/media/history_view_media_spoiler.h"
|
||||||
|
|
||||||
enum class ImageRoundRadius;
|
enum class ImageRoundRadius;
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ public:
|
||||||
TextWithEntities getCaption() const override {
|
TextWithEntities getCaption() const override {
|
||||||
return _caption.toTextWithEntities();
|
return _caption.toTextWithEntities();
|
||||||
}
|
}
|
||||||
|
void hideSpoilers() override;
|
||||||
bool needsBubble() const override;
|
bool needsBubble() const override;
|
||||||
bool customInfoLayout() const override {
|
bool customInfoLayout() const override {
|
||||||
return _caption.isEmpty();
|
return _caption.isEmpty();
|
||||||
|
@ -87,23 +89,13 @@ private:
|
||||||
QRect outer,
|
QRect outer,
|
||||||
const PaintContext &context) const;
|
const PaintContext &context) const;
|
||||||
|
|
||||||
void fillSpoilerMess(
|
|
||||||
QPainter &p,
|
|
||||||
QRect rect,
|
|
||||||
std::optional<Ui::BubbleRounding> rounding,
|
|
||||||
const PaintContext &context) const;
|
|
||||||
|
|
||||||
const not_null<Data::Invoice*> _invoice;
|
const not_null<Data::Invoice*> _invoice;
|
||||||
ClickHandlerPtr _link;
|
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
mutable std::unique_ptr<Ui::SpoilerAnimation> _animation;
|
mutable MediaSpoiler _spoiler;
|
||||||
mutable QImage _inlineThumbnail;
|
mutable QImage _inlineThumbnail;
|
||||||
mutable QImage _imageCache;
|
|
||||||
mutable QImage _cornerCache;
|
|
||||||
mutable QImage _buttonBackground;
|
mutable QImage _buttonBackground;
|
||||||
mutable QColor _buttonBackgroundOverlay;
|
mutable QColor _buttonBackgroundOverlay;
|
||||||
mutable Ui::Text::String _buttonText;
|
mutable Ui::Text::String _buttonText;
|
||||||
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
|
||||||
mutable bool _imageCacheInvalid = false;
|
mutable bool _imageCacheInvalid = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ class FileClickHandler;
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class File : public Media, public base::has_weak_ptr {
|
class File : public Media {
|
||||||
public:
|
public:
|
||||||
File(
|
File(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
|
|
|
@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_transcribe_button.h"
|
#include "history/view/history_view_transcribe_button.h"
|
||||||
#include "history/view/media/history_view_media_common.h"
|
#include "history/view/media/history_view_media_common.h"
|
||||||
|
#include "history/view/media/history_view_media_spoiler.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "core/application.h" // Application::showDocument.
|
#include "core/application.h" // Application::showDocument.
|
||||||
#include "ui/chat/attach/attach_prepare.h"
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
|
@ -38,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
|
#include "ui/effects/spoiler_mess.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -80,10 +82,12 @@ Gif::Streamed::Streamed(
|
||||||
Gif::Gif(
|
Gif::Gif(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<DocumentData*> document)
|
not_null<DocumentData*> document,
|
||||||
|
bool spoiler)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(document)
|
, _data(document)
|
||||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||||
|
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr)
|
||||||
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
||||||
setDocumentLinks(_data, realParent);
|
setDocumentLinks(_data, realParent);
|
||||||
setStatusSize(Ui::FileStatusSizeReady);
|
setStatusSize(Ui::FileStatusSizeReady);
|
||||||
|
@ -476,6 +480,13 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
||||||
p.drawImage(rthumb, _thumbCache);
|
p.drawImage(rthumb, _thumbCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isRound && _spoiler) {
|
||||||
|
fillImageSpoiler(
|
||||||
|
p,
|
||||||
|
_spoiler.get(),
|
||||||
|
rthumb,
|
||||||
|
context);
|
||||||
|
}
|
||||||
if (context.selected()) {
|
if (context.selected()) {
|
||||||
if (isRound) {
|
if (isRound) {
|
||||||
Ui::FillComplexEllipse(p, st, rthumb);
|
Ui::FillComplexEllipse(p, st, rthumb);
|
||||||
|
@ -1312,6 +1323,13 @@ bool Gif::uploading() const {
|
||||||
return _data->uploading();
|
return _data->uploading();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gif::hideSpoilers() {
|
||||||
|
_caption.setSpoilerRevealed(false, anim::type::instant);
|
||||||
|
if (_spoiler) {
|
||||||
|
_spoiler->revealed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Gif::needsBubble() const {
|
bool Gif::needsBubble() const {
|
||||||
if (_data->isVideoMessage()) {
|
if (_data->isVideoMessage()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1538,12 +1556,16 @@ void Gif::parentTextUpdated() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gif::hasHeavyPart() const {
|
bool Gif::hasHeavyPart() const {
|
||||||
return _streamed || _dataMedia;
|
return (_spoiler && _spoiler->animation) || _streamed || _dataMedia;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gif::unloadHeavyPart() {
|
void Gif::unloadHeavyPart() {
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
_dataMedia = nullptr;
|
_dataMedia = nullptr;
|
||||||
|
if (_spoiler) {
|
||||||
|
_spoiler->background = _spoiler->cornerCache = QImage();
|
||||||
|
_spoiler->animation = nullptr;
|
||||||
|
}
|
||||||
_thumbCache = QImage();
|
_thumbCache = QImage();
|
||||||
_videoThumbnailFrame = nullptr;
|
_videoThumbnailFrame = nullptr;
|
||||||
_caption.unloadPersistentAnimation();
|
_caption.unloadPersistentAnimation();
|
||||||
|
|
|
@ -44,7 +44,8 @@ public:
|
||||||
Gif(
|
Gif(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document,
|
||||||
|
bool spoiler);
|
||||||
~Gif();
|
~Gif();
|
||||||
|
|
||||||
void draw(Painter &p, const PaintContext &context) const override;
|
void draw(Painter &p, const PaintContext &context) const override;
|
||||||
|
@ -98,6 +99,7 @@ public:
|
||||||
TextWithEntities getCaption() const override {
|
TextWithEntities getCaption() const override {
|
||||||
return _caption.toTextWithEntities();
|
return _caption.toTextWithEntities();
|
||||||
}
|
}
|
||||||
|
void hideSpoilers() override;
|
||||||
bool needsBubble() const override;
|
bool needsBubble() const override;
|
||||||
bool unwrapped() const override;
|
bool unwrapped() const override;
|
||||||
bool customInfoLayout() const override {
|
bool customInfoLayout() const override {
|
||||||
|
@ -206,6 +208,7 @@ private:
|
||||||
const not_null<DocumentData*> _data;
|
const not_null<DocumentData*> _data;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
std::unique_ptr<Streamed> _streamed;
|
std::unique_ptr<Streamed> _streamed;
|
||||||
|
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||||
mutable std::unique_ptr<TranscribeButton> _transcribe;
|
mutable std::unique_ptr<TranscribeButton> _transcribe;
|
||||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||||
mutable std::unique_ptr<Image> _videoThumbnailFrame;
|
mutable std::unique_ptr<Image> _videoThumbnailFrame;
|
||||||
|
|
|
@ -34,10 +34,12 @@ Invoice::Invoice(
|
||||||
|
|
||||||
void Invoice::fillFromData(not_null<Data::Invoice*> invoice) {
|
void Invoice::fillFromData(not_null<Data::Invoice*> invoice) {
|
||||||
if (invoice->photo) {
|
if (invoice->photo) {
|
||||||
|
const auto spoiler = false;
|
||||||
_attach = std::make_unique<Photo>(
|
_attach = std::make_unique<Photo>(
|
||||||
_parent,
|
_parent,
|
||||||
_parent->data(),
|
_parent->data(),
|
||||||
invoice->photo);
|
invoice->photo,
|
||||||
|
spoiler);
|
||||||
} else {
|
} else {
|
||||||
_attach = nullptr;
|
_attach = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_spoiler_click_handler.h"
|
#include "history/view/history_view_spoiler_click_handler.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
|
#include "history/view/media/history_view_media_spoiler.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -246,6 +247,46 @@ void Media::fillImageOverlay(
|
||||||
Ui::FillComplexOverlayRect(p, rect, st->msgSelectOverlay(), corners);
|
Ui::FillComplexOverlayRect(p, rect, st->msgSelectOverlay(), corners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Media::fillImageSpoiler(
|
||||||
|
QPainter &p,
|
||||||
|
not_null<MediaSpoiler*> spoiler,
|
||||||
|
QRect rect,
|
||||||
|
const PaintContext &context) const {
|
||||||
|
if (!spoiler->animation) {
|
||||||
|
spoiler->animation = std::make_unique<Ui::SpoilerAnimation>([=] {
|
||||||
|
_parent->customEmojiRepaint();
|
||||||
|
});
|
||||||
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
}
|
||||||
|
_parent->clearCustomEmojiRepaint();
|
||||||
|
Ui::FillSpoilerRect(
|
||||||
|
p,
|
||||||
|
rect,
|
||||||
|
MediaRoundingMask(spoiler->backgroundRounding),
|
||||||
|
Ui::DefaultImageSpoiler().frame(
|
||||||
|
spoiler->animation->index(context.now, context.paused)),
|
||||||
|
spoiler->cornerCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Media::createSpoilerLink(not_null<MediaSpoiler*> spoiler) {
|
||||||
|
const auto weak = base::make_weak(this);
|
||||||
|
spoiler->link = std::make_shared<LambdaClickHandler>([=](
|
||||||
|
const ClickContext &context) {
|
||||||
|
const auto button = context.button;
|
||||||
|
const auto media = weak.get();
|
||||||
|
if (button != Qt::LeftButton || !media || spoiler->revealed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto view = media->parent();
|
||||||
|
spoiler->revealed = true;
|
||||||
|
spoiler->revealAnimation.start([=] {
|
||||||
|
media->history()->owner().requestViewRepaint(view);
|
||||||
|
}, 0., 1., st::fadeWrapDuration);
|
||||||
|
media->history()->owner().requestViewRepaint(view);
|
||||||
|
media->history()->owner().registerShownSpoiler(view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Media::repaint() const {
|
void Media::repaint() const {
|
||||||
history()->owner().requestViewRepaint(_parent);
|
history()->owner().requestViewRepaint(_parent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct ColorReplacements;
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
struct BubbleSelectionInterval;
|
struct BubbleSelectionInterval;
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
|
class SpoilerAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Images {
|
namespace Images {
|
||||||
|
@ -45,6 +46,7 @@ enum class CursorState : char;
|
||||||
enum class InfoDisplayType : char;
|
enum class InfoDisplayType : char;
|
||||||
struct TextState;
|
struct TextState;
|
||||||
struct StateRequest;
|
struct StateRequest;
|
||||||
|
struct MediaSpoiler;
|
||||||
class StickerPlayer;
|
class StickerPlayer;
|
||||||
class Element;
|
class Element;
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ enum class MediaInBubbleState : uchar {
|
||||||
TimeId duration,
|
TimeId duration,
|
||||||
const QString &base);
|
const QString &base);
|
||||||
|
|
||||||
class Media : public Object {
|
class Media : public Object, public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
||||||
}
|
}
|
||||||
|
@ -209,6 +211,8 @@ public:
|
||||||
[[nodiscard]] virtual TextWithEntities getCaption() const {
|
[[nodiscard]] virtual TextWithEntities getCaption() const {
|
||||||
return TextWithEntities();
|
return TextWithEntities();
|
||||||
}
|
}
|
||||||
|
virtual void hideSpoilers() {
|
||||||
|
}
|
||||||
[[nodiscard]] virtual bool needsBubble() const = 0;
|
[[nodiscard]] virtual bool needsBubble() const = 0;
|
||||||
[[nodiscard]] virtual bool unwrapped() const {
|
[[nodiscard]] virtual bool unwrapped() const {
|
||||||
return false;
|
return false;
|
||||||
|
@ -343,6 +347,12 @@ protected:
|
||||||
QRect rect,
|
QRect rect,
|
||||||
std::optional<Ui::BubbleRounding> rounding, // nullopt if in WebPage.
|
std::optional<Ui::BubbleRounding> rounding, // nullopt if in WebPage.
|
||||||
const PaintContext &context) const;
|
const PaintContext &context) const;
|
||||||
|
void fillImageSpoiler(
|
||||||
|
QPainter &p,
|
||||||
|
not_null<MediaSpoiler*> spoiler,
|
||||||
|
QRect rect,
|
||||||
|
const PaintContext &context) const;
|
||||||
|
void createSpoilerLink(not_null<MediaSpoiler*> spoiler);
|
||||||
|
|
||||||
void repaint() const;
|
void repaint() const;
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ std::unique_ptr<Media> CreateAttach(
|
||||||
if (!collage.empty()) {
|
if (!collage.empty()) {
|
||||||
return std::make_unique<GroupedMedia>(parent, collage);
|
return std::make_unique<GroupedMedia>(parent, collage);
|
||||||
} else if (document) {
|
} else if (document) {
|
||||||
|
const auto spoiler = false;
|
||||||
if (document->sticker()) {
|
if (document->sticker()) {
|
||||||
const auto skipPremiumEffect = true;
|
const auto skipPremiumEffect = true;
|
||||||
return std::make_unique<UnwrappedMedia>(
|
return std::make_unique<UnwrappedMedia>(
|
||||||
|
@ -71,7 +72,11 @@ std::unique_ptr<Media> CreateAttach(
|
||||||
document,
|
document,
|
||||||
skipPremiumEffect));
|
skipPremiumEffect));
|
||||||
} else if (document->isAnimation() || document->isVideoFile()) {
|
} else if (document->isAnimation() || document->isVideoFile()) {
|
||||||
return std::make_unique<Gif>(parent, parent->data(), document);
|
return std::make_unique<Gif>(
|
||||||
|
parent,
|
||||||
|
parent->data(),
|
||||||
|
document,
|
||||||
|
spoiler);
|
||||||
} else if (document->isWallPaper() || document->isTheme()) {
|
} else if (document->isWallPaper() || document->isTheme()) {
|
||||||
return std::make_unique<ThemeDocument>(
|
return std::make_unique<ThemeDocument>(
|
||||||
parent,
|
parent,
|
||||||
|
@ -80,10 +85,12 @@ std::unique_ptr<Media> CreateAttach(
|
||||||
}
|
}
|
||||||
return std::make_unique<Document>(parent, parent->data(), document);
|
return std::make_unique<Document>(parent, parent->data(), document);
|
||||||
} else if (photo) {
|
} else if (photo) {
|
||||||
|
const auto spoiler = false;
|
||||||
return std::make_unique<Photo>(
|
return std::make_unique<Photo>(
|
||||||
parent,
|
parent,
|
||||||
parent->data(),
|
parent->data(),
|
||||||
photo);
|
photo,
|
||||||
|
spoiler);
|
||||||
} else if (const auto params = ThemeDocument::ParamsFromUrl(webpageUrl)) {
|
} else if (const auto params = ThemeDocument::ParamsFromUrl(webpageUrl)) {
|
||||||
return std::make_unique<ThemeDocument>(parent, nullptr, params);
|
return std::make_unique<ThemeDocument>(parent, nullptr, params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -694,6 +694,13 @@ TextWithEntities GroupedMedia::getCaption() const {
|
||||||
return main()->getCaption();
|
return main()->getCaption();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GroupedMedia::hideSpoilers() {
|
||||||
|
_caption.setSpoilerRevealed(false, anim::type::instant);
|
||||||
|
for (const auto &part : _parts) {
|
||||||
|
part.content->hideSpoilers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask GroupedMedia::sharedMediaTypes() const {
|
Storage::SharedMediaTypesMask GroupedMedia::sharedMediaTypes() const {
|
||||||
return main()->sharedMediaTypes();
|
return main()->sharedMediaTypes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ public:
|
||||||
bool pressed) override;
|
bool pressed) override;
|
||||||
|
|
||||||
TextWithEntities getCaption() const override;
|
TextWithEntities getCaption() const override;
|
||||||
|
void hideSpoilers() override;
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||||
|
|
||||||
bool overrideEditedDate() const override {
|
bool overrideEditedDate() const override {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
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 "history/view/media/history_view_media_spoiler.h"
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
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/chat/message_bubble.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
|
||||||
|
struct MediaSpoiler {
|
||||||
|
ClickHandlerPtr link;
|
||||||
|
std::unique_ptr<Ui::SpoilerAnimation> animation;
|
||||||
|
QImage cornerCache;
|
||||||
|
QImage background;
|
||||||
|
std::optional<Ui::BubbleRounding> backgroundRounding;
|
||||||
|
Ui::Animations::Simple revealAnimation;
|
||||||
|
bool revealed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/media/history_view_media_common.h"
|
#include "history/view/media/history_view_media_common.h"
|
||||||
|
#include "history/view/media/history_view_media_spoiler.h"
|
||||||
#include "media/streaming/media_streaming_instance.h"
|
#include "media/streaming/media_streaming_instance.h"
|
||||||
#include "media/streaming/media_streaming_player.h"
|
#include "media/streaming/media_streaming_player.h"
|
||||||
#include "media/streaming/media_streaming_document.h"
|
#include "media/streaming/media_streaming_document.h"
|
||||||
|
@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
|
#include "ui/effects/spoiler_mess.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
|
@ -58,10 +60,12 @@ Photo::Streamed::Streamed(
|
||||||
Photo::Photo(
|
Photo::Photo(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<PhotoData*> photo)
|
not_null<PhotoData*> photo,
|
||||||
|
bool spoiler)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(photo)
|
, _data(photo)
|
||||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||||
|
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
|
||||||
_caption = createCaption(realParent);
|
_caption = createCaption(realParent);
|
||||||
create(realParent->fullId());
|
create(realParent->fullId());
|
||||||
}
|
}
|
||||||
|
@ -110,6 +114,9 @@ void Photo::create(FullMsgId contextId, PeerData *chat) {
|
||||||
|| _data->hasExact(PhotoSize::Thumbnail))) {
|
|| _data->hasExact(PhotoSize::Thumbnail))) {
|
||||||
_data->load(PhotoSize::Small, contextId);
|
_data->load(PhotoSize::Small, contextId);
|
||||||
}
|
}
|
||||||
|
if (_spoiler) {
|
||||||
|
createSpoilerLink(_spoiler.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::ensureDataMediaCreated() const {
|
void Photo::ensureDataMediaCreated() const {
|
||||||
|
@ -132,12 +139,16 @@ void Photo::dataMediaCreated() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Photo::hasHeavyPart() const {
|
bool Photo::hasHeavyPart() const {
|
||||||
return _streamed || _dataMedia;
|
return (_spoiler && _spoiler->animation) || _streamed || _dataMedia;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::unloadHeavyPart() {
|
void Photo::unloadHeavyPart() {
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
_dataMedia = nullptr;
|
_dataMedia = nullptr;
|
||||||
|
if (_spoiler) {
|
||||||
|
_spoiler->background = _spoiler->cornerCache = QImage();
|
||||||
|
_spoiler->animation = nullptr;
|
||||||
|
}
|
||||||
_imageCache = QImage();
|
_imageCache = QImage();
|
||||||
_caption.unloadPersistentAnimation();
|
_caption.unloadPersistentAnimation();
|
||||||
}
|
}
|
||||||
|
@ -276,8 +287,22 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
Assert(rounding.has_value());
|
Assert(rounding.has_value());
|
||||||
fillImageShadow(p, rthumb, *rounding, context);
|
fillImageShadow(p, rthumb, *rounding, context);
|
||||||
}
|
}
|
||||||
validateImageCache(rthumb.size(), rounding);
|
const auto revealed = _spoiler
|
||||||
p.drawImage(rthumb.topLeft(), _imageCache);
|
? _spoiler->revealAnimation.value(_spoiler->revealed ? 1. : 0.)
|
||||||
|
: 1.;
|
||||||
|
if (revealed < 1.) {
|
||||||
|
validateSpoilerImageCache(rthumb.size(), rounding);
|
||||||
|
}
|
||||||
|
if (revealed > 0.) {
|
||||||
|
validateImageCache(rthumb.size(), rounding);
|
||||||
|
p.drawImage(rthumb.topLeft(), _imageCache);
|
||||||
|
}
|
||||||
|
if (revealed < 1.) {
|
||||||
|
p.setOpacity(1. - revealed);
|
||||||
|
p.drawImage(rthumb.topLeft(), _spoiler->background);
|
||||||
|
fillImageSpoiler(p, _spoiler.get(), rthumb, context);
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
if (context.selected()) {
|
if (context.selected()) {
|
||||||
fillImageOverlay(p, rthumb, rounding, context);
|
fillImageOverlay(p, rthumb, rounding, context);
|
||||||
}
|
}
|
||||||
|
@ -414,9 +439,30 @@ void Photo::validateImageCache(
|
||||||
_imageCacheBlurred = blurredValue;
|
_imageCacheBlurred = blurredValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Photo::validateSpoilerImageCache(
|
||||||
|
QSize outer,
|
||||||
|
std::optional<Ui::BubbleRounding> rounding) const {
|
||||||
|
Expects(_spoiler != nullptr);
|
||||||
|
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
if (_spoiler->background.size() == (outer * ratio)
|
||||||
|
&& _spoiler->backgroundRounding == rounding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_spoiler->background = Images::Round(
|
||||||
|
prepareImageCacheWithLarge(outer, nullptr),
|
||||||
|
MediaRoundingMask(rounding));
|
||||||
|
_spoiler->backgroundRounding = rounding;
|
||||||
|
}
|
||||||
|
|
||||||
QImage Photo::prepareImageCache(QSize outer) const {
|
QImage Photo::prepareImageCache(QSize outer) const {
|
||||||
|
return prepareImageCacheWithLarge(
|
||||||
|
outer,
|
||||||
|
_dataMedia->image(PhotoSize::Large));
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Photo::prepareImageCacheWithLarge(QSize outer, Image *large) const {
|
||||||
using Size = PhotoSize;
|
using Size = PhotoSize;
|
||||||
const auto large = _dataMedia->image(Size::Large);
|
|
||||||
auto blurred = (Image*)nullptr;
|
auto blurred = (Image*)nullptr;
|
||||||
if (const auto embedded = _dataMedia->thumbnailInline()) {
|
if (const auto embedded = _dataMedia->thumbnailInline()) {
|
||||||
blurred = embedded;
|
blurred = embedded;
|
||||||
|
@ -536,15 +582,15 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
||||||
}
|
}
|
||||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
if (_data->uploading()) {
|
result.link = (_spoiler && !_spoiler->revealed)
|
||||||
result.link = _cancell;
|
? _spoiler->link
|
||||||
} else if (_dataMedia->loaded()) {
|
: _data->uploading()
|
||||||
result.link = _openl;
|
? _cancell
|
||||||
} else if (_data->loading()) {
|
: _dataMedia->loaded()
|
||||||
result.link = _cancell;
|
? _openl
|
||||||
} else {
|
: _data->loading()
|
||||||
result.link = _savel;
|
? _cancell
|
||||||
}
|
: _savel;
|
||||||
}
|
}
|
||||||
if (_caption.isEmpty() && _parent->media() == this) {
|
if (_caption.isEmpty() && _parent->media() == this) {
|
||||||
auto fullRight = paintx + paintw;
|
auto fullRight = paintx + paintw;
|
||||||
|
@ -593,8 +639,6 @@ void Photo::drawGrouped(
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
||||||
|
|
||||||
validateGroupedCache(geometry, rounding, cacheKey, cache);
|
|
||||||
|
|
||||||
const auto st = context.st;
|
const auto st = context.st;
|
||||||
const auto sti = context.imageStyle();
|
const auto sti = context.imageStyle();
|
||||||
const auto loaded = _dataMedia->loaded();
|
const auto loaded = _dataMedia->loaded();
|
||||||
|
@ -608,7 +652,22 @@ void Photo::drawGrouped(
|
||||||
}
|
}
|
||||||
const auto radial = isRadialAnimation();
|
const auto radial = isRadialAnimation();
|
||||||
|
|
||||||
p.drawPixmap(geometry.topLeft(), *cache);
|
const auto revealed = _spoiler
|
||||||
|
? _spoiler->revealAnimation.value(_spoiler->revealed ? 1. : 0.)
|
||||||
|
: 1.;
|
||||||
|
if (revealed < 1.) {
|
||||||
|
validateSpoilerImageCache(geometry.size(), rounding);
|
||||||
|
}
|
||||||
|
if (revealed > 0.) {
|
||||||
|
validateGroupedCache(geometry, rounding, cacheKey, cache);
|
||||||
|
p.drawPixmap(geometry.topLeft(), *cache);
|
||||||
|
}
|
||||||
|
if (revealed < 1.) {
|
||||||
|
p.setOpacity(1. - revealed);
|
||||||
|
p.drawImage(geometry.topLeft(), _spoiler->background);
|
||||||
|
fillImageSpoiler(p, _spoiler.get(), geometry, context);
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
|
||||||
const auto overlayOpacity = context.selected()
|
const auto overlayOpacity = context.selected()
|
||||||
? (1. - highlightOpacity)
|
? (1. - highlightOpacity)
|
||||||
|
@ -688,7 +747,9 @@ TextState Photo::getStateGrouped(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
return TextState(_parent, _data->uploading()
|
return TextState(_parent, (_spoiler && !_spoiler->revealed)
|
||||||
|
? _spoiler->link
|
||||||
|
: _data->uploading()
|
||||||
? _cancell
|
? _cancell
|
||||||
: _dataMedia->loaded()
|
: _dataMedia->loaded()
|
||||||
? _openl
|
? _openl
|
||||||
|
@ -904,6 +965,13 @@ TextForMimeData Photo::selectedText(TextSelection selection) const {
|
||||||
return _caption.toTextForMimeData(selection);
|
return _caption.toTextForMimeData(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Photo::hideSpoilers() {
|
||||||
|
_caption.setSpoilerRevealed(false, anim::type::instant);
|
||||||
|
if (_spoiler) {
|
||||||
|
_spoiler->revealed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Photo::needsBubble() const {
|
bool Photo::needsBubble() const {
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/view/media/history_view_file.h"
|
#include "history/view/media/history_view_file.h"
|
||||||
|
|
||||||
|
class Image;
|
||||||
enum class ImageRoundRadius;
|
enum class ImageRoundRadius;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -31,7 +32,8 @@ public:
|
||||||
Photo(
|
Photo(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<PhotoData*> photo);
|
not_null<PhotoData*> photo,
|
||||||
|
bool spoiler);
|
||||||
Photo(
|
Photo(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<PeerData*> chat,
|
not_null<PeerData*> chat,
|
||||||
|
@ -81,6 +83,7 @@ public:
|
||||||
TextWithEntities getCaption() const override {
|
TextWithEntities getCaption() const override {
|
||||||
return _caption.toTextWithEntities();
|
return _caption.toTextWithEntities();
|
||||||
}
|
}
|
||||||
|
void hideSpoilers() override;
|
||||||
bool needsBubble() const override;
|
bool needsBubble() const override;
|
||||||
bool customInfoLayout() const override {
|
bool customInfoLayout() const override {
|
||||||
return _caption.isEmpty();
|
return _caption.isEmpty();
|
||||||
|
@ -130,6 +133,12 @@ private:
|
||||||
std::optional<Ui::BubbleRounding> rounding) const;
|
std::optional<Ui::BubbleRounding> rounding) const;
|
||||||
void validateUserpicImageCache(QSize size, bool forum) const;
|
void validateUserpicImageCache(QSize size, bool forum) const;
|
||||||
[[nodiscard]] QImage prepareImageCache(QSize outer) const;
|
[[nodiscard]] QImage prepareImageCache(QSize outer) const;
|
||||||
|
void validateSpoilerImageCache(
|
||||||
|
QSize outer,
|
||||||
|
std::optional<Ui::BubbleRounding> rounding) const;
|
||||||
|
[[nodiscard]] QImage prepareImageCacheWithLarge(
|
||||||
|
QSize outer,
|
||||||
|
Image *large) const;
|
||||||
|
|
||||||
bool videoAutoplayEnabled() const;
|
bool videoAutoplayEnabled() const;
|
||||||
void setStreamed(std::unique_ptr<Streamed> value);
|
void setStreamed(std::unique_ptr<Streamed> value);
|
||||||
|
@ -148,6 +157,7 @@ private:
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||||
mutable std::unique_ptr<Streamed> _streamed;
|
mutable std::unique_ptr<Streamed> _streamed;
|
||||||
|
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||||
mutable QImage _imageCache;
|
mutable QImage _imageCache;
|
||||||
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
||||||
int _serviceWidth : 30 = 0;
|
int _serviceWidth : 30 = 0;
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace HistoryView {
|
||||||
|
|
||||||
class Message;
|
class Message;
|
||||||
|
|
||||||
class Poll : public Media, public base::has_weak_ptr {
|
class Poll final : public Media {
|
||||||
public:
|
public:
|
||||||
Poll(
|
Poll(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
|
|
|
@ -60,16 +60,19 @@ std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia(
|
||||||
auto result = std::vector<std::unique_ptr<Data::Media>>();
|
auto result = std::vector<std::unique_ptr<Data::Media>>();
|
||||||
result.reserve(data.items.size());
|
result.reserve(data.items.size());
|
||||||
for (const auto &item : data.items) {
|
for (const auto &item : data.items) {
|
||||||
|
const auto spoiler = false;
|
||||||
if (const auto document = std::get_if<DocumentData*>(&item)) {
|
if (const auto document = std::get_if<DocumentData*>(&item)) {
|
||||||
const auto skipPremiumEffect = false;
|
const auto skipPremiumEffect = false;
|
||||||
result.push_back(std::make_unique<Data::MediaFile>(
|
result.push_back(std::make_unique<Data::MediaFile>(
|
||||||
parent,
|
parent,
|
||||||
*document,
|
*document,
|
||||||
skipPremiumEffect));
|
skipPremiumEffect,
|
||||||
|
spoiler));
|
||||||
} else if (const auto photo = std::get_if<PhotoData*>(&item)) {
|
} else if (const auto photo = std::get_if<PhotoData*>(&item)) {
|
||||||
result.push_back(std::make_unique<Data::MediaPhoto>(
|
result.push_back(std::make_unique<Data::MediaPhoto>(
|
||||||
parent,
|
parent,
|
||||||
*photo));
|
*photo,
|
||||||
|
spoiler));
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f339dd377100beaef79af8512c67a3c16f950c3f
|
Subproject commit 64d277891403ba3275b47d6ccab849322670cf99
|
Loading…
Add table
Reference in a new issue