Support Webm sticker display in chat history.

This commit is contained in:
John Preston 2022-01-20 15:58:32 +03:00
parent 2f01efdd64
commit 63d15e4479
9 changed files with 138 additions and 149 deletions

View file

@ -302,7 +302,7 @@ bool ShouldAutoPlay(
const Full &data,
not_null<PeerData*> peer,
not_null<DocumentData*> document) {
return data.shouldDownload(
return document->sticker() || data.shouldDownload(
SourceFromPeer(peer),
AutoPlayTypeFromDocument(document),
document->size);

View file

@ -1187,6 +1187,9 @@ bool DocumentData::hasRemoteLocation() const {
}
bool DocumentData::useStreamingLoader() const {
if (const auto info = sticker()) {
return info->isWebm();
}
return isAnimation()
|| isVideoFile()
|| isAudioFile()

View file

@ -947,14 +947,16 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent,
HistoryView::Element *replacing) {
if (_document->sticker()) {
if (const auto info = _document->sticker(); info && !info->isWebm()) {
return std::make_unique<HistoryView::UnwrappedMedia>(
message,
std::make_unique<HistoryView::Sticker>(
message,
_document,
replacing));
} else if (_document->isAnimation() || _document->isVideoFile()) {
} else if (_document->isAnimation()
|| _document->isVideoFile()
|| (info && info->isWebm())) {
return std::make_unique<HistoryView::Gif>(
message,
realParent,

View file

@ -2010,7 +2010,6 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (document->isGifv()) {
const auto notAutoplayedGif = [&] {
return item
&& document->isGifv()
&& !Data::AutoDownload::ShouldAutoPlay(
session->settings().autoDownload(),
item->history()->peer,

View file

@ -244,20 +244,17 @@ void AddDocumentActions(
}
const auto contextId = item ? item->fullId() : FullMsgId();
const auto session = &document->session();
if (item) {
const auto notAutoplayedGif = [&] {
return document->isGifv()
&& !Data::AutoDownload::ShouldAutoPlay(
document->session().settings().autoDownload(),
item->history()->peer,
document);
}();
if (item && document->isGifv()) {
const auto notAutoplayedGif = !Data::AutoDownload::ShouldAutoPlay(
document->session().settings().autoDownload(),
item->history()->peer,
document);
if (notAutoplayedGif) {
menu->addAction(tr::lng_context_open_gif(tr::now), [=] {
OpenGif(list->controller(), contextId);
}, &st::menuIconShowInChat);
}
if (document->isGifv() && !list->hasCopyRestriction(item)) {
if (!list->hasCopyRestriction(item)) {
menu->addAction(tr::lng_context_save_gif(tr::now), [=] {
SaveGif(list->controller(), contextId);
}, &st::menuIconGif);

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_sticker.h"
#include "window/window_session_controller.h"
#include "core/application.h" // Application::showDocument.
#include "ui/chat/chat_style.h"
@ -124,6 +125,22 @@ QSize Gif::sizeForAspectRatio() const {
return { 1, 1 };
}
QSize Gif::countThumbSize(int &inOutWidthMax) const {
const auto maxSize = _data->sticker()
? Sticker::Size().width()
: _data->isVideoFile()
? st::maxMediaSize
: _data->isVideoMessage()
? st::maxVideoMessageSize
: st::maxGifSize;
const auto useMaxSize = std::max(maxSize, st::minPhotoSize);
const auto size = _data->sticker()
? videoSize()
: style::ConvertScale(videoSize());
accumulate_min(inOutWidthMax, useMaxSize);
return DownscaledSize(size, { inOutWidthMax, useMaxSize });
}
QSize Gif::countOptimalSize() {
if (_parent->media() != this) {
_caption = Ui::Text::String();
@ -133,32 +150,15 @@ QSize Gif::countOptimalSize() {
_parent->skipBlockHeight());
}
const auto maxSize = _data->isVideoFile()
? st::maxMediaSize
: _data->isVideoMessage()
? st::maxVideoMessageSize
: st::maxGifSize;
const auto size = style::ConvertScale(videoSize());
auto tw = size.width();
auto th = size.height();
if (tw > maxSize) {
th = (maxSize * th) / tw;
tw = maxSize;
}
if (th > maxSize) {
tw = (maxSize * tw) / th;
th = maxSize;
}
if (!tw || !th) {
tw = th = 1;
}
_thumbw = tw;
_thumbh = th;
auto thumbMaxWidth = st::msgMaxWidth;
const auto scaled = countThumbSize(thumbMaxWidth);
_thumbw = scaled.width();
_thumbh = scaled.height();
auto maxWidth = std::clamp(
std::max(tw, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())),
std::max(_thumbw, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())),
st::minPhotoSize,
maxSize);
auto minHeight = qMax(th, st::minPhotoSize);
thumbMaxWidth);
auto minHeight = qMax(_thumbh, st::minPhotoSize);
if (!activeCurrentStreamed()) {
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
}
@ -171,7 +171,7 @@ QSize Gif::countOptimalSize() {
minHeight += st::msgPadding.bottom();
}
}
} else if (isSeparateRoundVideo()) {
} else if (isUnwrapped()) {
const auto item = _parent->data();
auto via = item->Get<HistoryMessageVia>();
auto reply = _parent->displayedReply();
@ -188,38 +188,16 @@ QSize Gif::countOptimalSize() {
QSize Gif::countCurrentSize(int newWidth) {
auto availableWidth = newWidth;
const auto maxSize = _data->isVideoFile()
? st::maxMediaSize
: _data->isVideoMessage()
? st::maxVideoMessageSize
: st::maxGifSize;
const auto size = style::ConvertScale(videoSize());
auto tw = size.width();
auto th = size.height();
if (tw > maxSize) {
th = (maxSize * th) / tw;
tw = maxSize;
}
if (th > maxSize) {
tw = (maxSize * tw) / th;
th = maxSize;
}
if (!tw || !th) {
tw = th = 1;
}
if (newWidth < tw) {
th = qRound((newWidth / float64(tw)) * th);
tw = newWidth;
}
_thumbw = tw;
_thumbh = th;
auto thumbMaxWidth = newWidth;
const auto scaled = countThumbSize(thumbMaxWidth);
_thumbw = scaled.width();
_thumbh = scaled.height();
newWidth = std::clamp(
std::max(tw, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())),
std::max(_thumbw, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())),
st::minPhotoSize,
std::min(newWidth, maxSize));
auto newHeight = qMax(th, st::minPhotoSize);
thumbMaxWidth);
auto newHeight = qMax(_thumbh, st::minPhotoSize);
if (!activeCurrentStreamed()) {
accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
}
@ -232,7 +210,7 @@ QSize Gif::countCurrentSize(int newWidth) {
newHeight += st::msgPadding.bottom();
}
}
} else if (isSeparateRoundVideo()) {
} else if (isUnwrapped()) {
accumulate_max(newWidth, _parent->reactionsOptimalWidth());
const auto item = _parent->data();
@ -314,13 +292,12 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
const auto activeOwnPlaying = activeOwnStreamed();
auto paintx = 0, painty = 0, paintw = width(), painth = height();
bool bubble = _parent->hasBubble();
const auto outbg = context.outbg;
auto inWebPage = (_parent->media() != this);
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
const bool bubble = _parent->hasBubble();
const auto outbg = context.outbg;
const auto inWebPage = (_parent->media() != this);
const auto isRound = _data->isVideoMessage();
const auto unwrapped = isUnwrapped();
auto displayMute = false;
const auto streamed = activeRoundPlaying
? activeRoundPlaying
@ -354,16 +331,15 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
painth -= st::msgPadding.bottom();
}
}
} else if (!isRound) {
} else if (!unwrapped) {
Ui::FillRoundShadow(p, 0, 0, paintw, height(), sti->msgShadow, sti->msgShadowCorners);
}
auto usex = 0, usew = paintw;
auto separateRoundVideo = isSeparateRoundVideo();
auto via = separateRoundVideo ? item->Get<HistoryMessageVia>() : nullptr;
auto reply = separateRoundVideo ? _parent->displayedReply() : nullptr;
auto forwarded = separateRoundVideo ? item->Get<HistoryMessageForwarded>() : nullptr;
const auto rightAligned = separateRoundVideo
const auto via = unwrapped ? item->Get<HistoryMessageVia>() : nullptr;
const auto reply = unwrapped ? _parent->displayedReply() : nullptr;
const auto forwarded = unwrapped ? item->Get<HistoryMessageForwarded>() : nullptr;
const auto rightAligned = unwrapped
&& outbg
&& !_parent->delegate()->elementIsChatWide();
if (via || reply || forwarded) {
@ -376,9 +352,25 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
QRect rthumb(style::rtlrect(usex + paintx, painty, usew, painth, width()));
auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
| ((isRoundedInBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
const auto roundRadius = isRound
? ImageRoundRadius::Ellipse
: unwrapped
? ImageRoundRadius::None
: inWebPage
? ImageRoundRadius::Small
: ImageRoundRadius::Large;
const auto roundCorners = isRound
? RectPart::AllCorners
: unwrapped
? RectPart::None
: inWebPage
? RectPart::AllCorners
: ((isBubbleTop()
? (RectPart::TopLeft | RectPart::TopRight)
: RectPart::None)
| ((isRoundedInBubbleBottom() && _caption.isEmpty())
? (RectPart::BottomLeft | RectPart::BottomRight)
: RectPart::None));
if (streamed) {
auto paused = autoPaused;
if (isRound) {
@ -391,7 +383,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
auto request = ::Media::Streaming::FrameRequest();
request.outer = QSize(usew, painth) * cIntRetinaFactor();
request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor();
request.keepAlpha = true; AssertIsDebug();
request.keepAlpha = !isRound && unwrapped;
request.corners = roundCorners;
request.radius = roundRadius;
if (!activeRoundPlaying && activeOwnPlaying->instance.playerLocked()) {
@ -462,7 +454,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
p.drawPixmap(rthumb.topLeft(), _videoThumbnailFrame->pixSingle(size, args));
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
p.drawPixmap(rthumb.topLeft(), blurred->pixSingle(size, args.blurred()));
} else if (!isRound) {
} else if (!unwrapped) {
const auto roundTop = (roundCorners & RectPart::TopLeft);
const auto roundBottom = (roundCorners & RectPart::BottomLeft);
const auto margin = inWebPage
@ -479,7 +471,11 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
}
if (context.selected()) {
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners);
if (unwrapped && !isRound) {
// #TODO stickers
} else {
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners);
}
}
if (radial
@ -559,30 +555,32 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
sti->historyVideoMessageMute.paintInCenter(p, muteRect);
}
if (!isRound) {
if (!unwrapped) {
drawCornerStatus(p, context, QPoint());
}
if (!inWebPage && isRound) {
auto mediaUnread = item->hasUnreadMediaFlag();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
auto statusX = usex + paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + painth - st::msgDateImgDelta - statusH + st::msgDateImgPadding.y();
if (mediaUnread) {
statusW += st::mediaUnreadSkip + st::mediaUnreadSize;
}
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), sti->msgServiceBg, sti->msgServiceBgCorners);
p.setFont(st::normalFont);
p.setPen(st->msgServiceFg());
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (mediaUnread) {
p.setPen(Qt::NoPen);
p.setBrush(st->msgServiceFg());
if (unwrapped) {
if (isRound) {
const auto mediaUnread = item->hasUnreadMediaFlag();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
auto statusX = usex + paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + painth - st::msgDateImgDelta - statusH + st::msgDateImgPadding.y();
if (mediaUnread) {
statusW += st::mediaUnreadSkip + st::mediaUnreadSize;
}
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), sti->msgServiceBg, sti->msgServiceBgCorners);
p.setFont(st::normalFont);
p.setPen(st->msgServiceFg());
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (mediaUnread) {
p.setPen(Qt::NoPen);
p.setBrush(st->msgServiceFg());
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(style::rtlrect(statusX - st::msgDateImgPadding.x() + statusW - st::msgDateImgPadding.x() - st::mediaUnreadSize, statusY + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, width()));
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(style::rtlrect(statusX - st::msgDateImgPadding.x() + statusW - st::msgDateImgPadding.x() - st::mediaUnreadSize, statusY + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, width()));
}
}
}
if (via || reply || forwarded) {
@ -613,7 +611,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
forwarded->text.drawElided(p, rectx, recty + st::msgReplyPadding.top(), rectw, kMaxGifForwardedBarLines, style::al_left, 0, -1, 0, breakEverywhere);
p.restoreTextPalette();
} else if (via) {
p.setFont(st::msgDateFont);
p.setFont(st::msgServiceNameFont);
p.drawTextLeft(rectx, recty + st::msgReplyPadding.top(), 2 * rectx + rectw, via->text);
int skip = st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0);
recty += skip;
@ -623,7 +621,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
}
}
}
if (!isRound && !_caption.isEmpty()) {
if (!unwrapped && !_caption.isEmpty()) {
p.setPen(stm->historyTextFg);
_caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, context.selection);
} else if (!inWebPage) {
@ -635,7 +633,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
} else {
maxRight -= st::msgMargin.left();
}
if (isRound && !rightAligned) {
if (unwrapped && !rightAligned) {
auto infoWidth = _parent->infoWidth();
// This is just some arbitrary point,
@ -652,7 +650,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
fullRight,
fullBottom,
2 * paintx + paintw,
(isRound
(unwrapped
? InfoDisplayType::Background
: InfoDisplayType::Image));
}
@ -769,15 +767,15 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
painth -= st::mediaCaptionSkip;
}
const auto outbg = _parent->hasOutLayout();
auto inWebPage = (_parent->media() != this);
auto isRound = _data->isVideoMessage();
auto usew = paintw, usex = 0;
auto separateRoundVideo = isSeparateRoundVideo();
const auto inWebPage = (_parent->media() != this);
const auto isRound = _data->isVideoMessage();
const auto unwrapped = isUnwrapped();
const auto item = _parent->data();
auto via = separateRoundVideo ? item->Get<HistoryMessageVia>() : nullptr;
auto reply = separateRoundVideo ? _parent->displayedReply() : nullptr;
auto forwarded = separateRoundVideo ? item->Get<HistoryMessageForwarded>() : nullptr;
const auto rightAligned = separateRoundVideo
auto usew = paintw, usex = 0;
const auto via = unwrapped ? item->Get<HistoryMessageVia>() : nullptr;
const auto reply = unwrapped ? _parent->displayedReply() : nullptr;
const auto forwarded = unwrapped ? item->Get<HistoryMessageForwarded>() : nullptr;
const auto rightAligned = unwrapped
&& outbg
&& !_parent->delegate()->elementIsChatWide();
if (via || reply || forwarded) {
@ -845,7 +843,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
}
}
}
if (!isRound) {
if (!unwrapped) {
if (const auto state = cornerStatusTextState(point, request, QPoint()); state.link) {
return state;
}
@ -862,7 +860,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
? _cancell
: _savel;
}
if (isRound || _caption.isEmpty()) {
if (unwrapped || _caption.isEmpty()) {
auto fullRight = usex + paintx + usew;
auto fullBottom = painty + painth;
auto maxRight = _parent->width() - st::msgMargin.left();
@ -871,7 +869,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
} else {
maxRight -= st::msgMargin.left();
}
if (isRound && !rightAligned) {
if (unwrapped && !rightAligned) {
auto infoWidth = _parent->infoWidth();
// This is just some arbitrary point,
@ -886,7 +884,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
fullRight,
fullBottom,
point,
(isRound
(unwrapped
? InfoDisplayType::Background
: InfoDisplayType::Image));
if (bottomInfoResult.link
@ -993,7 +991,6 @@ void Gif::drawGrouped(
{ geometry.width(), geometry.height() });
request.outer = geometry.size() * cIntRetinaFactor();
request.resize = pixSize * cIntRetinaFactor();
request.keepAlpha = true; AssertIsDebug();
request.corners = corners;
request.radius = roundRadius;
if (activeOwnPlaying->instance.playerLocked()) {
@ -1169,10 +1166,9 @@ bool Gif::uploading() const {
}
bool Gif::needsBubble() const {
if (_data->isVideoMessage()) {
if (_data->sticker() || _data->isVideoMessage()) {
return false;
}
if (!_caption.isEmpty()) {
} else if (!_caption.isEmpty()) {
return true;
}
const auto item = _parent->data();
@ -1186,7 +1182,7 @@ bool Gif::needsBubble() const {
}
QRect Gif::contentRectForReactions() const {
if (!isSeparateRoundVideo()) {
if (!isUnwrapped()) {
return QRect(0, 0, width(), height());
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@ -1210,7 +1206,7 @@ QRect Gif::contentRectForReactions() const {
}
std::optional<int> Gif::reactionButtonCenterOverride() const {
if (!isSeparateRoundVideo()) {
if (!isUnwrapped()) {
return std::nullopt;
}
const auto right = resolveCustomInfoRightBottom().x()
@ -1223,8 +1219,8 @@ QPoint Gif::resolveCustomInfoRightBottom() const {
const auto inner = contentRectForReactions();
auto fullBottom = inner.y() + inner.height();
auto fullRight = inner.x() + inner.width();
const auto isRound = isSeparateRoundVideo();
if (isRound) {
const auto unwrapped = isUnwrapped();
if (unwrapped) {
auto maxRight = _parent->width() - st::msgMargin.left();
if (_parent->hasFromPhoto()) {
maxRight -= st::msgMargin.right();
@ -1244,10 +1240,10 @@ QPoint Gif::resolveCustomInfoRightBottom() const {
}
}
}
const auto skipx = isRound
const auto skipx = unwrapped
? st::msgDateImgPadding.x()
: (st::msgDateImgDelta + st::msgDateImgPadding.x());
const auto skipy = isRound
const auto skipy = unwrapped
? st::msgDateImgPadding.y()
: (st::msgDateImgDelta + st::msgDateImgPadding.y());
return QPoint(fullRight - skipx, fullBottom - skipy);
@ -1261,16 +1257,9 @@ int Gif::additionalWidth() const {
item->Get<HistoryMessageForwarded>());
}
QString Gif::mediaTypeString() const {
return _data->isVideoMessage()
? tr::lng_in_dlg_video_message(tr::now)
: qsl("GIF");
}
bool Gif::isSeparateRoundVideo() const {
return _data->isVideoMessage()
&& (_parent->media() == this)
&& !_parent->hasBubble();
bool Gif::isUnwrapped() const {
return (_data->sticker() || _data->isVideoMessage())
&& (_parent->media() == this);
}
void Gif::validateGroupedCache(

View file

@ -119,6 +119,7 @@ private:
struct Streamed;
void validateVideoThumbnail() const;
[[nodiscard]] QSize countThumbSize(int &inOutWidthMax) const;
float64 dataProgress() const override;
bool dataFinished() const override;
@ -155,8 +156,7 @@ private:
const HistoryMessageReply *reply,
const HistoryMessageForwarded *forwarded) const;
[[nodiscard]] int additionalWidth() const;
[[nodiscard]] QString mediaTypeString() const;
[[nodiscard]] bool isSeparateRoundVideo() const;
[[nodiscard]] bool isUnwrapped() const;
void validateGroupedCache(
const QRect &geometry,

View file

@ -44,9 +44,10 @@ int unitedLineHeight();
}
[[nodiscard]] inline QSize DownscaledSize(QSize size, QSize box) {
return (size.width() > box.width() || size.height() > box.height())
? size.scaled(box, Qt::KeepAspectRatio)
: size;
return NonEmptySize(
((size.width() > box.width() || size.height() > box.height())
? size.scaled(box, Qt::KeepAspectRatio)
: size));
}
} // namespace HistoryView

View file

@ -41,9 +41,7 @@ UnwrappedMedia::UnwrappedMedia(
QSize UnwrappedMedia::countOptimalSize() {
_content->refreshLink();
_contentSize = NonEmptySize(DownscaledSize(
_content->size(),
Sticker::Size()));
_contentSize = DownscaledSize(_content->size(), Sticker::Size());
auto maxWidth = _contentSize.width();
const auto minimal = st::largeEmojiSize + 2 * st::largeEmojiOutline;
auto minHeight = std::max(_contentSize.height(), minimal);