diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index dc72577172..7b546c7e3a 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -769,7 +769,11 @@ std::unique_ptr MediaFile::createView( not_null message, not_null realParent) { if (_document->sticker()) { - return std::make_unique(message, _document); + return std::make_unique( + message, + std::make_unique( + message, + _document)); } else if (_document->isAnimation()) { return std::make_unique(message, _document); } else if (_document->isVideoFile()) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 260987b1aa..78927694bf 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1087,7 +1087,8 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but if (uponSelected) { _mouseAction = MouseAction::PrepareDrag; // start text drag } else if (!_pressWasInactive) { - if (dynamic_cast(App::pressedItem()->media()) + const auto media = App::pressedItem()->media(); + if ((media && media->dragItem()) || _mouseCursorState == CursorState::Date) { _mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag } else { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 693b177747..57ae48d5c5 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -345,7 +345,9 @@ void Element::refreshMedia() { if (_data->media()) { _media = _data->media()->createView(this); } else if (const auto document = emojiStickers->stickerForEmoji(_data)) { - _media = std::make_unique(this, document); + _media = std::make_unique( + this, + std::make_unique(this, document)); } else { _media = nullptr; } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index d01c95e28b..409b2d41f2 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -888,8 +888,10 @@ bool ListWidget::requiredToStartDragging( not_null view) const { if (_mouseCursorState == CursorState::Date) { return true; - } else if (dynamic_cast(view->media()) != nullptr) { - return true; + } else if (const auto media = view->media()) { + if (media->dragItem()) { + return true; + } } return false; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp index 3fb03eb646..f21d576aad 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp @@ -70,7 +70,9 @@ std::unique_ptr CreateAttach( return std::make_unique(parent, collage); } else if (document) { if (document->sticker()) { - return std::make_unique(parent, document); + return std::make_unique( + parent, + std::make_unique(parent, document)); } else if (document->isAnimation()) { return std::make_unique(parent, document); } else if (document->isVideoFile()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_common.h b/Telegram/SourceFiles/history/view/media/history_view_media_common.h index a8a153734f..22b0507e48 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_common.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_common.h @@ -39,4 +39,14 @@ std::unique_ptr CreateAttach( const QString &webpageUrl = QString()); int unitedLineHeight(); +[[nodiscard]] inline QSize NonEmptySize(QSize size) { + return QSize(std::max(size.width(), 1), std::max(size.height(), 1)); +} + +[[nodiscard]] inline QSize DownscaledSize(QSize size, QSize box) { + return (size.width() > box.width() || size.height() > box.height()) + ? size.scaled(box, Qt::IgnoreAspectRatio) + : size; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp new file mode 100644 index 0000000000..e6bfe61f13 --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -0,0 +1,252 @@ +/* +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_unwrapped.h" + +#include "history/view/media/history_view_media_common.h" +#include "history/view/history_view_element.h" +#include "history/view/history_view_cursor_state.h" +#include "history/history_item.h" +#include "history/history_item_components.h" +#include "layout.h" +#include "styles/style_history.h" + +namespace HistoryView { + +UnwrappedMedia::Content::~Content() = default; + +UnwrappedMedia::UnwrappedMedia( + not_null parent, + std::unique_ptr content) +: Media(parent) +, _content(std::move(content)) { +} + +QSize UnwrappedMedia::countOptimalSize() { + _content->refreshLink(); + _contentSize = NonEmptySize(DownscaledSize( + _content->size(), + { st::maxStickerSize, st::maxStickerSize })); + auto maxWidth = std::max(_contentSize.width(), st::minPhotoSize); + auto minHeight = std::max(_contentSize.height(), st::minPhotoSize); + accumulate_max( + maxWidth, + _parent->infoWidth() + 2 * st::msgDateImgPadding.x()); + if (_parent->media() == this) { + maxWidth += additionalWidth(); + } + return { maxWidth, minHeight }; +} + +QSize UnwrappedMedia::countCurrentSize(int newWidth) { + const auto item = _parent->data(); + accumulate_min(newWidth, maxWidth()); + if (_parent->media() == this) { + auto via = item->Get(); + auto reply = item->Get(); + if (via || reply) { + int usew = maxWidth() - additionalWidth(via, reply); + int availw = newWidth - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.left(); + if (via) { + via->resize(availw); + } + if (reply) { + reply->resize(availw); + } + } + } + return { newWidth, minHeight() }; +} + +void UnwrappedMedia::draw( + Painter &p, + const QRect &r, + TextSelection selection, + crl::time ms) const { + if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { + return; + } + bool selected = (selection == FullSelection); + + const auto outbg = _parent->hasOutLayout(); + const auto inWebPage = (_parent->media() != this); + + const auto item = _parent->data(); + int usew = maxWidth(), usex = 0; + auto via = inWebPage ? nullptr : item->Get(); + auto reply = inWebPage ? nullptr : item->Get(); + if (via || reply) { + usew -= additionalWidth(via, reply); + if (outbg) { + usex = width() - usew; + } + } + if (rtl()) usex = width() - usex - usew; + + const auto inner = QRect(usex, 0, usew, height()); + _content->draw(p, inner, selected); + + if (!inWebPage) { + drawSurrounding(p, inner, selected, via, reply); + } +} + +void UnwrappedMedia::drawSurrounding( + Painter &p, + const QRect &inner, + bool selected, + const HistoryMessageVia *via, + const HistoryMessageReply *reply) const { + auto fullRight = inner.x() + inner.width(); + auto fullBottom = inner.y() + inner.height(); + if (needInfoDisplay()) { + _parent->drawInfo( + p, + fullRight, + fullBottom, + inner.x() * 2 + inner.width(), + selected, + InfoDisplayType::Background); + } + if (via || reply) { + int rectw = width() - inner.width() - st::msgReplyPadding.left(); + int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom(); + if (via) { + recth += st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); + } + if (reply) { + recth += st::msgReplyBarSize.height(); + } + const auto outbg = _parent->hasOutLayout(); + int rectx = outbg ? 0 : (inner.width() + st::msgReplyPadding.left()); + int recty = st::msgDateImgDelta; + if (rtl()) rectx = width() - rectx - rectw; + + App::roundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + p.setPen(st::msgServiceFg); + rectx += st::msgReplyPadding.left(); + rectw -= st::msgReplyPadding.left() + st::msgReplyPadding.right(); + if (via) { + p.setFont(st::msgDateFont); + p.drawTextLeft(rectx, recty + st::msgReplyPadding.top(), 2 * rectx + rectw, via->text); + int skip = st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); + recty += skip; + } + if (reply) { + HistoryMessageReply::PaintFlags flags = 0; + if (selected) { + flags |= HistoryMessageReply::PaintFlag::Selected; + } + reply->paint(p, _parent, rectx, recty, rectw, flags); + } + } + if (_parent->displayRightAction()) { + auto fastShareLeft = (fullRight + st::historyFastShareLeft); + auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); + _parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * inner.x() + inner.width()); + } + +} + +TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const { + auto result = TextState(_parent); + if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { + return result; + } + + auto outbg = _parent->hasOutLayout(); + auto inWebPage = (_parent->media() != this); + + const auto item = _parent->data(); + int usew = maxWidth(), usex = 0; + auto via = inWebPage ? nullptr : item->Get(); + auto reply = inWebPage ? nullptr : item->Get(); + if (via || reply) { + usew -= additionalWidth(via, reply); + if (outbg) { + usex = width() - usew; + } + } + if (rtl()) usex = width() - usex - usew; + + if (via || reply) { + int rectw = width() - usew - st::msgReplyPadding.left(); + int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom(); + if (via) { + recth += st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); + } + if (reply) { + recth += st::msgReplyBarSize.height(); + } + int rectx = outbg ? 0 : (usew + st::msgReplyPadding.left()); + int recty = st::msgDateImgDelta; + if (rtl()) rectx = width() - rectx - rectw; + + if (via) { + int viah = st::msgReplyPadding.top() + st::msgServiceNameFont->height + (reply ? 0 : st::msgReplyPadding.bottom()); + if (QRect(rectx, recty, rectw, viah).contains(point)) { + result.link = via->link; + return result; + } + int skip = st::msgServiceNameFont->height + (reply ? 2 * st::msgReplyPadding.top() : 0); + recty += skip; + recth -= skip; + } + if (reply) { + if (QRect(rectx, recty, rectw, recth).contains(point)) { + result.link = reply->replyToLink(); + return result; + } + } + } + if (_parent->media() == this) { + auto fullRight = usex + usew; + auto fullBottom = height(); + if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) { + result.cursor = CursorState::Date; + } + if (_parent->displayRightAction()) { + auto fastShareLeft = (fullRight + st::historyFastShareLeft); + auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); + if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) { + result.link = _parent->rightActionLink(); + } + } + } + + auto pixLeft = usex + (usew - _contentSize.width()) / 2; + auto pixTop = (minHeight() - _contentSize.height()) / 2; + if (QRect({ pixLeft, pixTop }, _contentSize).contains(point)) { + result.link = _content->link(); + return result; + } + return result; +} + +bool UnwrappedMedia::needInfoDisplay() const { + return (_parent->data()->id < 0 || _parent->isUnderCursor()); +} + +int UnwrappedMedia::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const { + int result = 0; + if (via) { + accumulate_max(result, st::msgReplyPadding.left() + st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.left()); + } + if (reply) { + accumulate_max(result, st::msgReplyPadding.left() + reply->replyToWidth()); + } + return result; +} + +int UnwrappedMedia::additionalWidth() const { + const auto item = _parent->data(); + return additionalWidth( + item->Get(), + item->Get()); +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h new file mode 100644 index 0000000000..8b0cd24d03 --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h @@ -0,0 +1,100 @@ +/* +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 "history/view/media/history_view_media.h" +#include "base/weak_ptr.h" +#include "base/timer.h" + +struct HistoryMessageVia; +struct HistoryMessageReply; +struct HistoryMessageForwarded; + +namespace HistoryView { + +class UnwrappedMedia final : public Media { +public: + class Content { + public: + [[nodiscard]] virtual QSize size() = 0; + virtual void draw(Painter &p, const QRect &r, bool selected) = 0; + [[nodiscard]] virtual ClickHandlerPtr link() = 0; + + [[nodiscard]] virtual DocumentData *document() { + return nullptr; + } + virtual void clearStickerLoopPlayed() { + } + virtual void unloadHeavyPart() { + } + virtual void refreshLink() { + } + virtual ~Content() = 0; + }; + + UnwrappedMedia( + not_null parent, + std::unique_ptr content); + + void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + TextState textState(QPoint point, StateRequest request) const override; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return true; + } + bool dragItem() const override { + return true; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return true; + } + + DocumentData *getDocument() const override { + return _content->document(); + } + + bool needsBubble() const override { + return false; + } + bool customInfoLayout() const override { + return true; + } + bool hidesForwardedInfo() const override { + return true; + } + void clearStickerLoopPlayed() override { + _content->clearStickerLoopPlayed(); + } + + void unloadHeavyPart() override { + _content->unloadHeavyPart(); + } + +private: + void drawSurrounding( + Painter &p, + const QRect &inner, + bool selected, + const HistoryMessageVia *via, + const HistoryMessageReply *reply) const; + + QSize countOptimalSize() override; + QSize countCurrentSize(int newWidth) override; + + bool needInfoDisplay() const; + int additionalWidth( + const HistoryMessageVia *via, + const HistoryMessageReply *reply) const; + int additionalWidth() const; + + std::unique_ptr _content; + QSize _contentSize; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 0f37063826..80ad17ceb2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -34,99 +34,155 @@ double GetEmojiStickerZoom(not_null session) { } // namespace -Sticker::Sticker( +StickerContent::StickerContent( not_null parent, not_null document) -: Media(parent) -, _data(document) { - _data->loadThumbnail(parent->data()->fullId()); +: _parent(parent) +, _document(document) { + _document->loadThumbnail(parent->data()->fullId()); } -Sticker::~Sticker() { +StickerContent::~StickerContent() { unloadLottie(); } -bool Sticker::isEmojiSticker() const { +bool StickerContent::isEmojiSticker() const { return (_parent->data()->media() == nullptr); } -QSize Sticker::countOptimalSize() { - auto sticker = _data->sticker(); - - if (!_packLink) { - if (isEmojiSticker()) { - const auto weak = base::make_weak(this); - _packLink = std::make_shared([weak] { - const auto that = weak.get(); - if (!that) { - return; - } - that->_lottieOncePlayed = false; - that->_parent->data()->history()->owner().requestViewRepaint( - that->_parent); - }); - } else if (sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) { - _packLink = std::make_shared([document = _data] { - StickerSetBox::Show(App::wnd()->sessionController(), document); - }); - } - } - _pixw = _data->dimensions.width(); - _pixh = _data->dimensions.height(); +QSize StickerContent::size() { + _size = _document->dimensions; if (isEmojiSticker()) { constexpr auto kIdealStickerSize = 512; - const auto zoom = GetEmojiStickerZoom(&history()->session()); + const auto zoom = GetEmojiStickerZoom(&_document->session()); const auto convert = [&](int size) { return int(size * st::maxStickerSize * zoom / kIdealStickerSize); }; - _pixw = convert(_pixw); - _pixh = convert(_pixh); + _size = QSize(convert(_size.width()), convert(_size.height())); + } + return _size; +} + +void StickerContent::draw( + Painter &p, + const QRect &r, + bool selected) { + const auto sticker = _document->sticker(); + if (!sticker) { + return; + } + + _document->checkStickerLarge(); + const auto loaded = _document->loaded(); + if (sticker->animated && !_lottie && loaded) { + setupLottie(); + } + + if (_lottie && _lottie->ready()) { + paintLottie(p, r, selected); } else { - if (_pixw > st::maxStickerSize) { - _pixh = (st::maxStickerSize * _pixh) / _pixw; - _pixw = st::maxStickerSize; - } - if (_pixh > st::maxStickerSize) { - _pixw = (st::maxStickerSize * _pixw) / _pixh; - _pixh = st::maxStickerSize; - } + paintPixmap(p, r, selected); } - if (_pixw < 1) _pixw = 1; - if (_pixh < 1) _pixh = 1; - auto maxWidth = qMax(_pixw, st::minPhotoSize); - auto minHeight = qMax(_pixh, st::minPhotoSize); - accumulate_max( - maxWidth, - _parent->infoWidth() + 2 * st::msgDateImgPadding.x()); - if (_parent->media() == this) { - maxWidth += additionalWidth(); - } - return { maxWidth, minHeight }; } -QSize Sticker::countCurrentSize(int newWidth) { - const auto item = _parent->data(); - accumulate_min(newWidth, maxWidth()); - if (_parent->media() == this) { - auto via = item->Get(); - auto reply = item->Get(); - if (via || reply) { - int usew = maxWidth() - additionalWidth(via, reply); - int availw = newWidth - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.left(); - if (via) { - via->resize(availw); - } - if (reply) { - reply->resize(availw); - } - } +void StickerContent::paintLottie(Painter &p, const QRect &r, bool selected) { + auto request = Lottie::FrameRequest(); + request.box = _size * cIntRetinaFactor(); + if (selected) { + request.colored = st::msgStickerOverlay->c; + } + const auto frame = _lottie->frameInfo(request); + const auto size = frame.image.size() / cIntRetinaFactor(); + p.drawImage( + QRect( + QPoint( + r.x() + (r.width() - size.width()) / 2, + r.y() + (r.height() - size.height()) / 2), + size), + frame.image); + + const auto paused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); + const auto playOnce = isEmojiSticker() + || !_document->session().settings().loopAnimatedStickers(); + if (!paused + && (!playOnce || frame.index != 0 || !_lottieOncePlayed) + && _lottie->markFrameShown() + && playOnce + && !_lottieOncePlayed) { + _lottieOncePlayed = true; + _parent->delegate()->elementStartStickerLoop(_parent); } - return { newWidth, minHeight() }; } -void Sticker::setupLottie() { +void StickerContent::paintPixmap(Painter &p, const QRect &r, bool selected) { + const auto pixmap = paintedPixmap(selected); + if (!pixmap.isNull()) { + p.drawPixmap( + QPoint( + r.x() + (r.width() - _size.width()) / 2, + r.y() + (r.height() - _size.height()) / 2), + pixmap); + } +} + +QPixmap StickerContent::paintedPixmap(bool selected) const { + const auto o = _parent->data()->fullId(); + const auto w = _size.width(); + const auto h = _size.height(); + const auto &c = st::msgStickerOverlay; + const auto good = _document->goodThumbnail(); + if (good && !good->loaded()) { + good->load({}); + } + if (const auto image = _document->getStickerLarge()) { + return selected + ? image->pixColored(o, c, w, h) + : image->pix(o, w, h); + // + // Inline thumbnails can't have alpha channel. + // + //} else if (const auto blurred = _document->thumbnailInline()) { + // return selected + // ? blurred->pixBlurredColored(o, c, w, h) + // : blurred->pixBlurred(o, w, h); + } else if (good && good->loaded()) { + return selected + ? good->pixColored(o, c, w, h) + : good->pix(o, w, h); + } else if (const auto thumbnail = _document->thumbnail()) { + return selected + ? thumbnail->pixBlurredColored(o, c, w, h) + : thumbnail->pixBlurred(o, w, h); + } + return QPixmap(); +} + +void StickerContent::refreshLink() { + if (_link) { + return; + } + const auto sticker = _document->sticker(); + if (isEmojiSticker()) { + const auto weak = base::make_weak(this); + _link = std::make_shared([weak] { + const auto that = weak.get(); + if (!that) { + return; + } + that->_lottieOncePlayed = false; + that->_parent->data()->history()->owner().requestViewRepaint( + that->_parent); + }); + } else if (sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) { + _link = std::make_shared([document = _document] { + StickerSetBox::Show(App::wnd()->sessionController(), document); + }); + } +} + +void StickerContent::setupLottie() { _lottie = Stickers::LottiePlayerFromDocument( - _data, + _document, Stickers::LottieSize::MessageHistory, QSize(st::maxStickerSize, st::maxStickerSize) * cIntRetinaFactor(), Lottie::Quality::High); @@ -142,7 +198,7 @@ void Sticker::setupLottie() { }, _lifetime); } -void Sticker::unloadLottie() { +void StickerContent::unloadLottie() { if (!_lottie) { return; } @@ -150,247 +206,4 @@ void Sticker::unloadLottie() { _parent->data()->history()->owner().unregisterHeavyViewPart(_parent); } -void Sticker::draw( - Painter &p, - const QRect &r, - TextSelection selection, - crl::time ms) const { - auto sticker = _data->sticker(); - if (!sticker) return; - - if (sticker->animated && !_lottie && _data->loaded()) { - const_cast(this)->setupLottie(); - } - - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - - _data->checkStickerLarge(); - bool loaded = _data->loaded(); - bool selected = (selection == FullSelection); - - auto outbg = _parent->hasOutLayout(); - auto inWebPage = (_parent->media() != this); - - const auto item = _parent->data(); - int usew = maxWidth(), usex = 0; - auto via = inWebPage ? nullptr : item->Get(); - auto reply = inWebPage ? nullptr : item->Get(); - if (via || reply) { - usew -= additionalWidth(via, reply); - if (outbg) { - usex = width() - usew; - } - } - if (rtl()) usex = width() - usex - usew; - - const auto lottieReady = (_lottie && _lottie->ready()); - const auto &pixmap = [&]() -> const QPixmap & { - const auto o = item->fullId(); - const auto w = _pixw; - const auto h = _pixh; - const auto &c = st::msgStickerOverlay; - const auto good = _data->goodThumbnail(); - if (!lottieReady && good && !good->loaded()) { - good->load({}); - } - static QPixmap empty; - if (lottieReady) { - return empty; - } else if (const auto image = _data->getStickerLarge()) { - return selected - ? image->pixColored(o, c, w, h) - : image->pix(o, w, h); - // - // Inline thumbnails can't have alpha channel. - // - //} else if (const auto blurred = _data->thumbnailInline()) { - // return selected - // ? blurred->pixBlurredColored(o, c, w, h) - // : blurred->pixBlurred(o, w, h); - } else if (good && good->loaded()) { - return selected - ? good->pixColored(o, c, w, h) - : good->pix(o, w, h); - } else if (const auto thumbnail = _data->thumbnail()) { - return selected - ? thumbnail->pixBlurredColored(o, c, w, h) - : thumbnail->pixBlurred(o, w, h); - } else { - return empty; - } - }(); - if (!pixmap.isNull()) { - p.drawPixmap( - QPoint{ usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2 }, - pixmap); - } else if (lottieReady) { - auto request = Lottie::FrameRequest(); - request.box = QSize(_pixw, _pixh) * cIntRetinaFactor(); - if (selected) { - request.colored = st::msgStickerOverlay->c; - } - const auto frame = _lottie->frameInfo(request); - const auto size = frame.image.size() / cIntRetinaFactor(); - p.drawImage( - QRect( - QPoint( - usex + (usew - size.width()) / 2, - (minHeight() - size.height()) / 2), - size), - frame.image); - - const auto paused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); - const auto playOnce = isEmojiSticker() - || !_data->session().settings().loopAnimatedStickers(); - if (!paused - && (!playOnce || frame.index != 0 || !_lottieOncePlayed) - && _lottie->markFrameShown() - && playOnce - && !_lottieOncePlayed) { - _lottieOncePlayed = true; - _parent->delegate()->elementStartStickerLoop(_parent); - } - } - if (!inWebPage) { - auto fullRight = usex + usew; - auto fullBottom = height(); - if (needInfoDisplay()) { - _parent->drawInfo(p, fullRight, fullBottom, usex * 2 + usew, selected, InfoDisplayType::Background); - } - if (via || reply) { - int rectw = width() - usew - st::msgReplyPadding.left(); - int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom(); - if (via) { - recth += st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); - } - if (reply) { - recth += st::msgReplyBarSize.height(); - } - int rectx = outbg ? 0 : (usew + st::msgReplyPadding.left()); - int recty = st::msgDateImgDelta; - if (rtl()) rectx = width() - rectx - rectw; - - App::roundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); - p.setPen(st::msgServiceFg); - rectx += st::msgReplyPadding.left(); - rectw -= st::msgReplyPadding.left() + st::msgReplyPadding.right(); - if (via) { - p.setFont(st::msgDateFont); - p.drawTextLeft(rectx, recty + st::msgReplyPadding.top(), 2 * rectx + rectw, via->text); - int skip = st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); - recty += skip; - } - if (reply) { - HistoryMessageReply::PaintFlags flags = 0; - if (selected) { - flags |= HistoryMessageReply::PaintFlag::Selected; - } - reply->paint(p, _parent, rectx, recty, rectw, flags); - } - } - if (_parent->displayRightAction()) { - auto fastShareLeft = (fullRight + st::historyFastShareLeft); - auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); - _parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * usex + usew); - } - } -} - -TextState Sticker::textState(QPoint point, StateRequest request) const { - auto result = TextState(_parent); - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { - return result; - } - - auto outbg = _parent->hasOutLayout(); - auto inWebPage = (_parent->media() != this); - - const auto item = _parent->data(); - int usew = maxWidth(), usex = 0; - auto via = inWebPage ? nullptr : item->Get(); - auto reply = inWebPage ? nullptr : item->Get(); - if (via || reply) { - usew -= additionalWidth(via, reply); - if (outbg) { - usex = width() - usew; - } - } - if (rtl()) usex = width() - usex - usew; - - if (via || reply) { - int rectw = width() - usew - st::msgReplyPadding.left(); - int recth = st::msgReplyPadding.top() + st::msgReplyPadding.bottom(); - if (via) { - recth += st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); - } - if (reply) { - recth += st::msgReplyBarSize.height(); - } - int rectx = outbg ? 0 : (usew + st::msgReplyPadding.left()); - int recty = st::msgDateImgDelta; - if (rtl()) rectx = width() - rectx - rectw; - - if (via) { - int viah = st::msgReplyPadding.top() + st::msgServiceNameFont->height + (reply ? 0 : st::msgReplyPadding.bottom()); - if (QRect(rectx, recty, rectw, viah).contains(point)) { - result.link = via->link; - return result; - } - int skip = st::msgServiceNameFont->height + (reply ? 2 * st::msgReplyPadding.top() : 0); - recty += skip; - recth -= skip; - } - if (reply) { - if (QRect(rectx, recty, rectw, recth).contains(point)) { - result.link = reply->replyToLink(); - return result; - } - } - } - if (_parent->media() == this) { - auto fullRight = usex + usew; - auto fullBottom = height(); - if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) { - result.cursor = CursorState::Date; - } - if (_parent->displayRightAction()) { - auto fastShareLeft = (fullRight + st::historyFastShareLeft); - auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); - if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) { - result.link = _parent->rightActionLink(); - } - } - } - - auto pixLeft = usex + (usew - _pixw) / 2; - auto pixTop = (minHeight() - _pixh) / 2; - if (QRect(pixLeft, pixTop, _pixw, _pixh).contains(point)) { - result.link = _packLink; - return result; - } - return result; -} - -bool Sticker::needInfoDisplay() const { - return (_parent->data()->id < 0 || _parent->isUnderCursor()); -} - -int Sticker::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const { - int result = 0; - if (via) { - accumulate_max(result, st::msgReplyPadding.left() + st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.left()); - } - if (reply) { - accumulate_max(result, st::msgReplyPadding.left() + reply->replyToWidth()); - } - return result; -} - -int Sticker::additionalWidth() const { - const auto item = _parent->data(); - return additionalWidth( - item->Get(), - item->Get()); -} - } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index d791b3c4a1..d2dddfd2bb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -7,13 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "history/view/media/history_view_media.h" +#include "history/view/media/history_view_media_unwrapped.h" #include "base/weak_ptr.h" #include "base/timer.h" -struct HistoryMessageVia; -struct HistoryMessageReply; -struct HistoryMessageForwarded; +namespace Data { +struct FileOrigin; +} // namespace Data namespace Lottie { class SinglePlayer; @@ -21,65 +21,46 @@ class SinglePlayer; namespace HistoryView { -class Sticker : public Media, public base::has_weak_ptr { +class StickerContent final + : public UnwrappedMedia::Content + , public base::has_weak_ptr { public: - Sticker( + StickerContent( not_null parent, not_null document); - ~Sticker(); + ~StickerContent(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; - TextState textState(QPoint point, StateRequest request) const override; - - bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { - return true; - } - bool dragItem() const override { - return true; - } - bool dragItemByHandler(const ClickHandlerPtr &p) const override { - return true; + QSize size() override; + void draw(Painter &p, const QRect &r, bool selected) override; + ClickHandlerPtr link() override { + return _link; } - DocumentData *getDocument() const override { - return _data; - } - - bool needsBubble() const override { - return false; - } - bool customInfoLayout() const override { - return true; - } - bool hidesForwardedInfo() const override { - return true; + DocumentData *document() override { + return _document; } void clearStickerLoopPlayed() override { _lottieOncePlayed = false; } - void unloadHeavyPart() override { unloadLottie(); } + void refreshLink() override; private: [[nodiscard]] bool isEmojiSticker() const; - - QSize countOptimalSize() override; - QSize countCurrentSize(int newWidth) override; - - bool needInfoDisplay() const; - int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const; - int additionalWidth() const; + void paintLottie(Painter &p, const QRect &r, bool selected); + void paintPixmap(Painter &p, const QRect &r, bool selected); + [[nodiscard]] QPixmap paintedPixmap(bool selected) const; void setupLottie(); void unloadLottie(); - int _pixw = 1; - int _pixh = 1; - ClickHandlerPtr _packLink; - DocumentData *_data = nullptr; + const not_null _parent; + const not_null _document; std::unique_ptr _lottie; + ClickHandlerPtr _link; + QSize _size; mutable bool _lottieOncePlayed = false; rpl::lifetime _lifetime; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index ff1729eadb..444b191c46 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -296,6 +296,8 @@ <(src_loc)/history/view/media/history_view_media_common.cpp <(src_loc)/history/view/media/history_view_media_grouped.h <(src_loc)/history/view/media/history_view_media_grouped.cpp +<(src_loc)/history/view/media/history_view_media_unwrapped.h +<(src_loc)/history/view/media/history_view_media_unwrapped.cpp <(src_loc)/history/view/media/history_view_photo.h <(src_loc)/history/view/media/history_view_photo.cpp <(src_loc)/history/view/media/history_view_poll.h