mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Initial implementation of ExtendedMedia.
This commit is contained in:
parent
2523d6e8d8
commit
20b5138e00
14 changed files with 659 additions and 77 deletions
|
@ -627,6 +627,8 @@ PRIVATE
|
||||||
history/view/media/history_view_dice.h
|
history/view/media/history_view_dice.h
|
||||||
history/view/media/history_view_document.cpp
|
history/view/media/history_view_document.cpp
|
||||||
history/view/media/history_view_document.h
|
history/view/media/history_view_document.h
|
||||||
|
history/view/media/history_view_extended_preview.cpp
|
||||||
|
history/view/media/history_view_extended_preview.h
|
||||||
history/view/media/history_view_file.cpp
|
history/view/media/history_view_file.cpp
|
||||||
history/view/media/history_view_file.h
|
history/view/media/history_view_file.h
|
||||||
history/view/media/history_view_game.cpp
|
history/view/media/history_view_game.cpp
|
||||||
|
|
|
@ -9,9 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "history/history_message.h" // CreateMedia.
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/view/history_view_item_preview.h"
|
#include "history/view/history_view_item_preview.h"
|
||||||
|
#include "history/view/media/history_view_extended_preview.h"
|
||||||
#include "history/view/media/history_view_photo.h"
|
#include "history/view/media/history_view_photo.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/media/history_view_gif.h"
|
#include "history/view/media/history_view_gif.h"
|
||||||
|
@ -245,10 +247,10 @@ TextForMimeData WithCaptionClipboardText(
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoice ComputeInvoiceData(
|
Invoice ComputeInvoiceData(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryMessage*> item,
|
||||||
const MTPDmessageMediaInvoice &data) {
|
const MTPDmessageMediaInvoice &data) {
|
||||||
auto description = qs(data.vdescription());
|
auto description = qs(data.vdescription());
|
||||||
return {
|
auto result = Invoice{
|
||||||
.receiptMsgId = data.vreceipt_msg_id().value_or_empty(),
|
.receiptMsgId = data.vreceipt_msg_id().value_or_empty(),
|
||||||
.amount = data.vtotal_amount().v,
|
.amount = data.vtotal_amount().v,
|
||||||
.currency = qs(data.vcurrency()),
|
.currency = qs(data.vcurrency()),
|
||||||
|
@ -263,6 +265,31 @@ Invoice ComputeInvoiceData(
|
||||||
: nullptr),
|
: nullptr),
|
||||||
.isTest = data.is_test(),
|
.isTest = data.is_test(),
|
||||||
};
|
};
|
||||||
|
if (const auto &media = data.vextended_media()) {
|
||||||
|
media->match([&](const MTPDmessageExtendedMediaPreview &data) {
|
||||||
|
auto &preview = result.extendedPreview;
|
||||||
|
if (const auto &w = data.vw()) {
|
||||||
|
const auto &h = data.vh();
|
||||||
|
Assert(h.has_value());
|
||||||
|
preview.dimensions = QSize(w->v, h->v);
|
||||||
|
}
|
||||||
|
if (const auto &thumb = data.vthumb()) {
|
||||||
|
if (thumb->type() == mtpc_photoStrippedSize) {
|
||||||
|
preview.inlineThumbnailBytes
|
||||||
|
= thumb->c_photoStrippedSize().vbytes().v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto &duration = data.vvideo_duration()) {
|
||||||
|
preview.videoDuration = duration->v;
|
||||||
|
}
|
||||||
|
}, [&](const MTPDmessageExtendedMedia &data) {
|
||||||
|
result.extendedMedia = HistoryMessage::CreateMedia(
|
||||||
|
item,
|
||||||
|
data.vmedia());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
|
Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
|
||||||
|
@ -1479,7 +1506,19 @@ MediaInvoice::MediaInvoice(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
const Invoice &data)
|
const Invoice &data)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _invoice(data) {
|
, _invoice{
|
||||||
|
.receiptMsgId = data.receiptMsgId,
|
||||||
|
.amount = data.amount,
|
||||||
|
.currency = data.currency,
|
||||||
|
.title = data.title,
|
||||||
|
.description = data.description,
|
||||||
|
.extendedPreview = data.extendedPreview,
|
||||||
|
.extendedMedia = (data.extendedMedia
|
||||||
|
? data.extendedMedia->clone(parent)
|
||||||
|
: nullptr),
|
||||||
|
.photo = data.photo,
|
||||||
|
.isTest = data.isTest,
|
||||||
|
} {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Media> MediaInvoice::clone(not_null<HistoryItem*> parent) {
|
std::unique_ptr<Media> MediaInvoice::clone(not_null<HistoryItem*> parent) {
|
||||||
|
@ -1537,6 +1576,16 @@ std::unique_ptr<HistoryView::Media> MediaInvoice::createView(
|
||||||
not_null<HistoryView::Element*> message,
|
not_null<HistoryView::Element*> message,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
HistoryView::Element *replacing) {
|
HistoryView::Element *replacing) {
|
||||||
|
if (_invoice.extendedMedia) {
|
||||||
|
return _invoice.extendedMedia->createView(
|
||||||
|
message,
|
||||||
|
realParent,
|
||||||
|
replacing);
|
||||||
|
} else if (!_invoice.extendedPreview.dimensions.isEmpty()) {
|
||||||
|
return std::make_unique<HistoryView::ExtendedPreview>(
|
||||||
|
message,
|
||||||
|
&_invoice);
|
||||||
|
}
|
||||||
return std::make_unique<HistoryView::Invoice>(message, &_invoice);
|
return std::make_unique<HistoryView::Invoice>(message, &_invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
class Image;
|
class Image;
|
||||||
class History;
|
class History;
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
|
class HistoryMessage;
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
template <typename Enum>
|
template <typename Enum>
|
||||||
|
@ -58,12 +59,22 @@ struct Call {
|
||||||
bool video = false;
|
bool video = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExtendedPreview {
|
||||||
|
QByteArray inlineThumbnailBytes;
|
||||||
|
QSize dimensions;
|
||||||
|
TimeId videoDuration = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Media;
|
||||||
|
|
||||||
struct Invoice {
|
struct Invoice {
|
||||||
MsgId receiptMsgId = 0;
|
MsgId receiptMsgId = 0;
|
||||||
uint64 amount = 0;
|
uint64 amount = 0;
|
||||||
QString currency;
|
QString currency;
|
||||||
QString title;
|
QString title;
|
||||||
TextWithEntities description;
|
TextWithEntities description;
|
||||||
|
ExtendedPreview extendedPreview;
|
||||||
|
std::unique_ptr<Media> extendedMedia;
|
||||||
PhotoData *photo = nullptr;
|
PhotoData *photo = nullptr;
|
||||||
bool isTest = false;
|
bool isTest = false;
|
||||||
};
|
};
|
||||||
|
@ -515,7 +526,7 @@ private:
|
||||||
TextForMimeData &&caption);
|
TextForMimeData &&caption);
|
||||||
|
|
||||||
[[nodiscard]] Invoice ComputeInvoiceData(
|
[[nodiscard]] Invoice ComputeInvoiceData(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryMessage*> item,
|
||||||
const MTPDmessageMediaInvoice &data);
|
const MTPDmessageMediaInvoice &data);
|
||||||
|
|
||||||
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call);
|
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call);
|
||||||
|
|
|
@ -1063,6 +1063,11 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) {
|
||||||
|
|
||||||
void HistoryMessage::refreshMedia(const MTPMessageMedia *media) {
|
void HistoryMessage::refreshMedia(const MTPMessageMedia *media) {
|
||||||
const auto was = (_media != nullptr);
|
const auto was = (_media != nullptr);
|
||||||
|
if (const auto invoice = was ? _media->invoice() : nullptr) {
|
||||||
|
if (invoice->extendedMedia) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
_media = nullptr;
|
_media = nullptr;
|
||||||
if (media) {
|
if (media) {
|
||||||
setMedia(*media);
|
setMedia(*media);
|
||||||
|
|
|
@ -229,10 +229,6 @@ struct Message::CommentsButton {
|
||||||
QImage cachedUserpics;
|
QImage cachedUserpics;
|
||||||
ClickHandlerPtr link;
|
ClickHandlerPtr link;
|
||||||
QPoint lastPoint;
|
QPoint lastPoint;
|
||||||
|
|
||||||
QString rightActionCountString;
|
|
||||||
int rightActionCount = 0;
|
|
||||||
int rightActionCountWidth = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Message::FromNameStatus {
|
struct Message::FromNameStatus {
|
||||||
|
|
|
@ -0,0 +1,403 @@
|
||||||
|
/*
|
||||||
|
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_extended_preview.h"
|
||||||
|
|
||||||
|
//#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#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 "main/main_session.h"
|
||||||
|
//#include "main/main_session_settings.h"
|
||||||
|
#include "media/streaming/media_streaming_utility.h"
|
||||||
|
#include "ui/effects/spoiler_mess.h"
|
||||||
|
#include "ui/image/image.h"
|
||||||
|
#include "ui/chat/chat_style.h"
|
||||||
|
//#include "ui/cached_round_corners.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
//#include "data/data_streaming.h"
|
||||||
|
//#include "data/data_photo.h"
|
||||||
|
//#include "data/data_photo_media.h"
|
||||||
|
//#include "data/data_file_click_handler.h"
|
||||||
|
//#include "data/data_file_origin.h"
|
||||||
|
//#include "data/data_auto_download.h"
|
||||||
|
//#include "core/application.h"
|
||||||
|
#include "payments/payments_checkout_process.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] ClickHandlerPtr MakeInvoiceLink(not_null<HistoryItem*> item) {
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
const auto controller = my.sessionWindow.get();
|
||||||
|
Payments::CheckoutProcess::Start(
|
||||||
|
item,
|
||||||
|
Payments::Mode::Payment,
|
||||||
|
(controller
|
||||||
|
? crl::guard(
|
||||||
|
controller,
|
||||||
|
[=](auto) { controller->widget()->activate(); })
|
||||||
|
: Fn<void(Payments::CheckoutResult)>()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ExtendedPreview::ExtendedPreview(
|
||||||
|
not_null<Element*> parent,
|
||||||
|
not_null<Data::Invoice*> invoice)
|
||||||
|
: Media(parent)
|
||||||
|
, _invoice(invoice)
|
||||||
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||||
|
const auto item = parent->data();
|
||||||
|
_caption = createCaption(item);
|
||||||
|
_link = MakeInvoiceLink(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedPreview::~ExtendedPreview() = default;
|
||||||
|
|
||||||
|
void ExtendedPreview::ensureThumbnailRead() const {
|
||||||
|
if (!_inlineThumbnail.isNull() || _imageCacheInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &bytes = _invoice->extendedPreview.inlineThumbnailBytes;
|
||||||
|
if (bytes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_inlineThumbnail = Images::FromInlineBytes(bytes);
|
||||||
|
if (_inlineThumbnail.isNull()) {
|
||||||
|
_imageCacheInvalid = 1;
|
||||||
|
} else {
|
||||||
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtendedPreview::hasHeavyPart() const {
|
||||||
|
return !_inlineThumbnail.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::unloadHeavyPart() {
|
||||||
|
_inlineThumbnail = _imageCache = QImage();
|
||||||
|
_caption.unloadCustomEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ExtendedPreview::countOptimalSize() {
|
||||||
|
if (_parent->media() != this) {
|
||||||
|
_caption = Ui::Text::String();
|
||||||
|
} else if (_caption.hasSkipBlock()) {
|
||||||
|
_caption.updateSkipBlock(
|
||||||
|
_parent->skipBlockWidth(),
|
||||||
|
_parent->skipBlockHeight());
|
||||||
|
}
|
||||||
|
const auto &preview = _invoice->extendedPreview;
|
||||||
|
const auto dimensions = preview.dimensions;
|
||||||
|
const auto minWidth = std::clamp(
|
||||||
|
_parent->minWidthForMedia(),
|
||||||
|
(_parent->hasBubble()
|
||||||
|
? st::historyPhotoBubbleMinWidth
|
||||||
|
: st::minPhotoSize),
|
||||||
|
st::maxMediaSize);
|
||||||
|
const auto scaled = CountDesiredMediaSize(dimensions);
|
||||||
|
auto maxWidth = qMax(scaled.width(), minWidth);
|
||||||
|
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||||
|
if (preview.videoDuration < 0) {
|
||||||
|
accumulate_max(maxWidth, scaled.height());
|
||||||
|
}
|
||||||
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||||
|
maxWidth = qMax(maxWidth, st::msgPadding.left()
|
||||||
|
+ _caption.maxWidth()
|
||||||
|
+ st::msgPadding.right());
|
||||||
|
minHeight += st::mediaCaptionSkip + _caption.minHeight();
|
||||||
|
if (isBubbleBottom()) {
|
||||||
|
minHeight += st::msgPadding.bottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { maxWidth, minHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ExtendedPreview::countCurrentSize(int newWidth) {
|
||||||
|
const auto &preview = _invoice->extendedPreview;
|
||||||
|
const auto dimensions = preview.dimensions;
|
||||||
|
const auto minWidth = std::clamp(
|
||||||
|
_parent->minWidthForMedia(),
|
||||||
|
(_parent->hasBubble()
|
||||||
|
? st::historyPhotoBubbleMinWidth
|
||||||
|
: st::minPhotoSize),
|
||||||
|
std::min(newWidth, st::maxMediaSize));
|
||||||
|
const auto scaled = (preview.videoDuration >= 0)
|
||||||
|
? CountMediaSize(
|
||||||
|
CountDesiredMediaSize(dimensions),
|
||||||
|
newWidth)
|
||||||
|
: CountPhotoMediaSize(
|
||||||
|
CountDesiredMediaSize(dimensions),
|
||||||
|
newWidth,
|
||||||
|
maxWidth());
|
||||||
|
newWidth = qMax(scaled.width(), minWidth);
|
||||||
|
auto newHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||||
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||||
|
const auto maxWithCaption = qMin(
|
||||||
|
st::msgMaxWidth,
|
||||||
|
(st::msgPadding.left()
|
||||||
|
+ _caption.maxWidth()
|
||||||
|
+ st::msgPadding.right()));
|
||||||
|
newWidth = qMax(newWidth, maxWithCaption);
|
||||||
|
const auto captionw = newWidth
|
||||||
|
- st::msgPadding.left()
|
||||||
|
- st::msgPadding.right();
|
||||||
|
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||||
|
if (isBubbleBottom()) {
|
||||||
|
newHeight += st::msgPadding.bottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { newWidth, newHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
|
||||||
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||||
|
|
||||||
|
const auto st = context.st;
|
||||||
|
const auto sti = context.imageStyle();
|
||||||
|
const auto stm = context.messageStyle();
|
||||||
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||||
|
auto bubble = _parent->hasBubble();
|
||||||
|
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
|
||||||
|
auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
|
||||||
|
if (bubble) {
|
||||||
|
if (!_caption.isEmpty()) {
|
||||||
|
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||||
|
if (isBubbleBottom()) {
|
||||||
|
painth -= st::msgPadding.bottom();
|
||||||
|
}
|
||||||
|
rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCorners);
|
||||||
|
}
|
||||||
|
const auto inWebPage = (_parent->media() != this);
|
||||||
|
const auto roundRadius = inWebPage
|
||||||
|
? ImageRoundRadius::Small
|
||||||
|
: ImageRoundRadius::Large;
|
||||||
|
const auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
|
||||||
|
| ((isRoundedInBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
|
||||||
|
validateImageCache(rthumb.size(), roundRadius, roundCorners);
|
||||||
|
p.drawImage(rthumb.topLeft(), _imageCache);
|
||||||
|
fillSpoilerMess(p, rthumb, roundRadius, roundCorners);
|
||||||
|
if (context.selected()) {
|
||||||
|
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto innerSize = st::msgFileLayout.thumbSize;
|
||||||
|
QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
if (context.selected()) {
|
||||||
|
p.setBrush(st->msgDateImgBgSelected());
|
||||||
|
} else {
|
||||||
|
const auto over = ClickHandler::showAsActive(_link);
|
||||||
|
p.setBrush(over ? st->msgDateImgBgOver() : st->msgDateImgBg());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PainterHighQualityEnabler hq(p);
|
||||||
|
p.drawEllipse(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// date
|
||||||
|
if (!_caption.isEmpty()) {
|
||||||
|
p.setPen(stm->historyTextFg);
|
||||||
|
_parent->prepareCustomEmojiPaint(p, _caption);
|
||||||
|
_caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, context.selection);
|
||||||
|
} else if (!inWebPage) {
|
||||||
|
auto fullRight = paintx + paintw;
|
||||||
|
auto fullBottom = painty + painth;
|
||||||
|
if (needInfoDisplay()) {
|
||||||
|
_parent->drawInfo(
|
||||||
|
p,
|
||||||
|
context,
|
||||||
|
fullRight,
|
||||||
|
fullBottom,
|
||||||
|
2 * paintx + paintw,
|
||||||
|
InfoDisplayType::Image);
|
||||||
|
}
|
||||||
|
if (const auto size = bubble ? std::nullopt : _parent->rightActionSize()) {
|
||||||
|
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||||
|
auto fastShareTop = (fullBottom - st::historyFastShareBottom - size->height());
|
||||||
|
_parent->drawRightAction(p, context, fastShareLeft, fastShareTop, 2 * paintx + paintw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::validateImageCache(
|
||||||
|
QSize outer,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const {
|
||||||
|
const auto intRadius = static_cast<int>(radius);
|
||||||
|
const auto intCorners = static_cast<int>(corners);
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
if (_imageCache.size() == (outer * ratio)
|
||||||
|
&& _imageCacheRoundRadius == intRadius
|
||||||
|
&& _imageCacheRoundCorners == intCorners) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_imageCache = prepareImageCache(outer, radius, corners);
|
||||||
|
_imageCacheRoundRadius = intRadius;
|
||||||
|
_imageCacheRoundCorners = intCorners;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage ExtendedPreview::prepareImageCache(
|
||||||
|
QSize outer,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const {
|
||||||
|
return Images::Round(prepareImageCache(outer), radius, corners);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage ExtendedPreview::prepareImageCache(QSize outer) const {
|
||||||
|
ensureThumbnailRead();
|
||||||
|
return PrepareWithBlurredBackground(outer, {}, {}, _inlineThumbnail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::fillSpoilerMess(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const {
|
||||||
|
const auto size = style::ConvertScale(100);
|
||||||
|
static const auto test = [&] {
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
return Ui::GenerateSpoilerMess({
|
||||||
|
.particleFadeInDuration = 200,
|
||||||
|
.particleFadeOutDuration = 200,
|
||||||
|
.particleSizeMin = style::ConvertScaleExact(1.5) * ratio,
|
||||||
|
.particleSizeMax = style::ConvertScaleExact(2.) * ratio,
|
||||||
|
.particleSpritesCount = 5,
|
||||||
|
.particlesCount = 2000,
|
||||||
|
.canvasSize = size * ratio,
|
||||||
|
.framesCount = 60,
|
||||||
|
.frameDuration = 33,
|
||||||
|
});
|
||||||
|
}();
|
||||||
|
const auto frame = test.frame();
|
||||||
|
const auto columns = (rect.width() + size - 1) / size;
|
||||||
|
const auto rows = (rect.height() + size - 1) / size;
|
||||||
|
p.setClipRect(rect);
|
||||||
|
p.translate(rect.topLeft());
|
||||||
|
for (auto y = 0; y != rows; ++y) {
|
||||||
|
for (auto x = 0; x != columns; ++x) {
|
||||||
|
p.drawImage(
|
||||||
|
QRect(x * size, y * size, size, size),
|
||||||
|
*frame.image,
|
||||||
|
frame.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.translate(-rect.topLeft());
|
||||||
|
p.setClipping(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextState ExtendedPreview::textState(QPoint point, StateRequest request) const {
|
||||||
|
auto result = TextState(_parent);
|
||||||
|
|
||||||
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||||
|
auto bubble = _parent->hasBubble();
|
||||||
|
|
||||||
|
if (bubble && !_caption.isEmpty()) {
|
||||||
|
const auto captionw = paintw
|
||||||
|
- st::msgPadding.left()
|
||||||
|
- st::msgPadding.right();
|
||||||
|
painth -= _caption.countHeight(captionw);
|
||||||
|
if (isBubbleBottom()) {
|
||||||
|
painth -= st::msgPadding.bottom();
|
||||||
|
}
|
||||||
|
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
|
||||||
|
result = TextState(_parent, _caption.getState(
|
||||||
|
point - QPoint(st::msgPadding.left(), painth),
|
||||||
|
captionw,
|
||||||
|
request.forText()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
painth -= st::mediaCaptionSkip;
|
||||||
|
}
|
||||||
|
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||||
|
result.link = _link;
|
||||||
|
}
|
||||||
|
if (_caption.isEmpty() && _parent->media() == this) {
|
||||||
|
auto fullRight = paintx + paintw;
|
||||||
|
auto fullBottom = painty + painth;
|
||||||
|
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||||
|
fullRight,
|
||||||
|
fullBottom,
|
||||||
|
point,
|
||||||
|
InfoDisplayType::Image);
|
||||||
|
if (bottomInfoResult.link
|
||||||
|
|| bottomInfoResult.cursor != CursorState::None) {
|
||||||
|
return bottomInfoResult;
|
||||||
|
}
|
||||||
|
if (const auto size = bubble ? std::nullopt : _parent->rightActionSize()) {
|
||||||
|
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||||
|
auto fastShareTop = (fullBottom - st::historyFastShareBottom - size->height());
|
||||||
|
if (QRect(fastShareLeft, fastShareTop, size->width(), size->height()).contains(point)) {
|
||||||
|
result.link = _parent->rightActionLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtendedPreview::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
|
||||||
|
return p == _link;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtendedPreview::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||||
|
return p == _link;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtendedPreview::needInfoDisplay() const {
|
||||||
|
return _parent->data()->isSending()
|
||||||
|
|| _parent->data()->hasFailed()
|
||||||
|
|| _parent->isUnderCursor()
|
||||||
|
|| _parent->isLastAndSelfMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextForMimeData ExtendedPreview::selectedText(TextSelection selection) const {
|
||||||
|
return _caption.toTextForMimeData(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtendedPreview::needsBubble() const {
|
||||||
|
if (!_caption.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto item = _parent->data();
|
||||||
|
return !item->isService()
|
||||||
|
&& (item->repliesAreComments()
|
||||||
|
|| item->externalReply()
|
||||||
|
|| item->viaBot()
|
||||||
|
|| _parent->displayedReply()
|
||||||
|
|| _parent->displayForwardedFrom()
|
||||||
|
|| _parent->displayFromName());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint ExtendedPreview::resolveCustomInfoRightBottom() const {
|
||||||
|
const auto skipx = (st::msgDateImgDelta + st::msgDateImgPadding.x());
|
||||||
|
const auto skipy = (st::msgDateImgDelta + st::msgDateImgPadding.y());
|
||||||
|
return QPoint(width() - skipx, height() - skipy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtendedPreview::parentTextUpdated() {
|
||||||
|
_caption = (_parent->media() == this)
|
||||||
|
? createCaption(_parent->data())
|
||||||
|
: Ui::Text::String();
|
||||||
|
history()->owner().requestViewResize(_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
enum class ImageRoundRadius;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct Invoice;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
|
||||||
|
class ExtendedPreview final : public Media {
|
||||||
|
public:
|
||||||
|
ExtendedPreview(
|
||||||
|
not_null<Element*> parent,
|
||||||
|
not_null<Data::Invoice*> invoice);
|
||||||
|
~ExtendedPreview();
|
||||||
|
|
||||||
|
void draw(Painter &p, const PaintContext &context) const override;
|
||||||
|
TextState textState(QPoint point, StateRequest request) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool toggleSelectionByHandlerClick(
|
||||||
|
const ClickHandlerPtr &p) const override;
|
||||||
|
[[nodiscard]] bool dragItemByHandler(
|
||||||
|
const ClickHandlerPtr &p) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] TextSelection adjustSelection(
|
||||||
|
TextSelection selection,
|
||||||
|
TextSelectType type) const override {
|
||||||
|
return _caption.adjustSelection(selection, type);
|
||||||
|
}
|
||||||
|
uint16 fullSelectionLength() const override {
|
||||||
|
return _caption.length();
|
||||||
|
}
|
||||||
|
bool hasTextForCopy() const override {
|
||||||
|
return !_caption.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextForMimeData selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
|
TextWithEntities getCaption() const override {
|
||||||
|
return _caption.toTextWithEntities();
|
||||||
|
}
|
||||||
|
bool needsBubble() const override;
|
||||||
|
bool customInfoLayout() const override {
|
||||||
|
return _caption.isEmpty();
|
||||||
|
}
|
||||||
|
QPoint resolveCustomInfoRightBottom() const override;
|
||||||
|
bool skipBubbleTail() const override {
|
||||||
|
return isRoundedInBubbleBottom() && _caption.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parentTextUpdated() override;
|
||||||
|
|
||||||
|
bool hasHeavyPart() const override;
|
||||||
|
void unloadHeavyPart() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ensureThumbnailRead() const;
|
||||||
|
|
||||||
|
QSize countOptimalSize() override;
|
||||||
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
bool needInfoDisplay() const;
|
||||||
|
void validateImageCache(
|
||||||
|
QSize outer,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const;
|
||||||
|
[[nodiscard]] QImage prepareImageCache(
|
||||||
|
QSize outer,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const;
|
||||||
|
[[nodiscard]] QImage prepareImageCache(QSize outer) const;
|
||||||
|
|
||||||
|
void fillSpoilerMess(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners) const;
|
||||||
|
|
||||||
|
const not_null<Data::Invoice*> _invoice;
|
||||||
|
ClickHandlerPtr _link;
|
||||||
|
Ui::Text::String _caption;
|
||||||
|
mutable QImage _inlineThumbnail;
|
||||||
|
mutable QImage _imageCache;
|
||||||
|
mutable int _imageCacheRoundRadius : 4 = 0;
|
||||||
|
mutable int _imageCacheRoundCorners : 12 = 0;
|
||||||
|
mutable int _imageCacheInvalid : 1 = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -22,6 +22,7 @@ namespace HistoryView {
|
||||||
bool File::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
|
bool File::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
|
||||||
return p == _openl || p == _savel || p == _cancell;
|
return p == _openl || p == _savel || p == _cancell;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool File::dragItemByHandler(const ClickHandlerPtr &p) const {
|
bool File::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||||
return p == _openl || p == _savel || p == _cancell;
|
return p == _openl || p == _savel || p == _cancell;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,10 +131,9 @@ QSize Gif::countThumbSize(int &inOutWidthMax) const {
|
||||||
: _data->isVideoMessage()
|
: _data->isVideoMessage()
|
||||||
? st::maxVideoMessageSize
|
? st::maxVideoMessageSize
|
||||||
: st::maxGifSize;
|
: st::maxGifSize;
|
||||||
const auto useMaxSize = std::max(maxSize, st::minPhotoSize);
|
|
||||||
const auto size = style::ConvertScale(videoSize());
|
const auto size = style::ConvertScale(videoSize());
|
||||||
accumulate_min(inOutWidthMax, useMaxSize);
|
accumulate_min(inOutWidthMax, maxSize);
|
||||||
return DownscaledSize(size, { inOutWidthMax, useMaxSize });
|
return DownscaledSize(size, { inOutWidthMax, maxSize });
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Gif::countOptimalSize() {
|
QSize Gif::countOptimalSize() {
|
||||||
|
@ -146,20 +145,22 @@ QSize Gif::countOptimalSize() {
|
||||||
_parent->skipBlockHeight());
|
_parent->skipBlockHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto minWidth = std::clamp(
|
||||||
|
_parent->minWidthForMedia(),
|
||||||
|
(_parent->hasBubble()
|
||||||
|
? st::historyPhotoBubbleMinWidth
|
||||||
|
: st::minPhotoSize),
|
||||||
|
st::maxMediaSize);
|
||||||
auto thumbMaxWidth = st::msgMaxWidth;
|
auto thumbMaxWidth = st::msgMaxWidth;
|
||||||
const auto scaled = countThumbSize(thumbMaxWidth);
|
const auto scaled = countThumbSize(thumbMaxWidth);
|
||||||
const auto minWidthByInfo = _parent->infoWidth()
|
auto maxWidth = std::min(
|
||||||
+ 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x());
|
std::max(scaled.width(), minWidth),
|
||||||
auto maxWidth = std::clamp(
|
|
||||||
std::max(scaled.width(), minWidthByInfo),
|
|
||||||
st::minPhotoSize,
|
|
||||||
thumbMaxWidth);
|
thumbMaxWidth);
|
||||||
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
|
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||||
if (!activeCurrentStreamed()) {
|
if (!activeCurrentStreamed()) {
|
||||||
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||||
}
|
}
|
||||||
if (_parent->hasBubble()) {
|
if (_parent->hasBubble()) {
|
||||||
accumulate_max(maxWidth, _parent->minWidthForMedia());
|
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
maxWidth = qMax(maxWidth, st::msgPadding.left()
|
maxWidth = qMax(maxWidth, st::msgPadding.left()
|
||||||
+ _caption.maxWidth()
|
+ _caption.maxWidth()
|
||||||
|
|
|
@ -98,9 +98,21 @@ QImage PrepareWithBlurredBackground(
|
||||||
::Media::Streaming::ExpandDecision resize,
|
::Media::Streaming::ExpandDecision resize,
|
||||||
Image *large,
|
Image *large,
|
||||||
Image *blurred) {
|
Image *blurred) {
|
||||||
|
return PrepareWithBlurredBackground(
|
||||||
|
outer,
|
||||||
|
resize,
|
||||||
|
large ? large->original() : QImage(),
|
||||||
|
blurred ? blurred->original() : QImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage PrepareWithBlurredBackground(
|
||||||
|
QSize outer,
|
||||||
|
::Media::Streaming::ExpandDecision resize,
|
||||||
|
QImage large,
|
||||||
|
QImage blurred) {
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
if (resize.expanding) {
|
if (resize.expanding) {
|
||||||
return Images::Prepare(large->original(), resize.result * ratio, {
|
return Images::Prepare(std::move(large), resize.result * ratio, {
|
||||||
.outer = outer,
|
.outer = outer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -108,19 +120,19 @@ QImage PrepareWithBlurredBackground(
|
||||||
outer * ratio,
|
outer * ratio,
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
background.setDevicePixelRatio(ratio);
|
background.setDevicePixelRatio(ratio);
|
||||||
if (!blurred) {
|
if (blurred.isNull()) {
|
||||||
background.fill(Qt::black);
|
background.fill(Qt::black);
|
||||||
if (!large) {
|
if (large.isNull()) {
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto p = QPainter(&background);
|
auto p = QPainter(&background);
|
||||||
if (blurred) {
|
if (!blurred.isNull()) {
|
||||||
using namespace ::Media::Streaming;
|
using namespace ::Media::Streaming;
|
||||||
FillBlurredBackground(p, outer, blurred->original());
|
FillBlurredBackground(p, outer, std::move(blurred));
|
||||||
}
|
}
|
||||||
if (large) {
|
if (!large.isNull()) {
|
||||||
auto image = large->original().scaled(
|
auto image = large.scaled(
|
||||||
resize.result * ratio,
|
resize.result * ratio,
|
||||||
Qt::IgnoreAspectRatio,
|
Qt::IgnoreAspectRatio,
|
||||||
Qt::SmoothTransformation);
|
Qt::SmoothTransformation);
|
||||||
|
@ -134,4 +146,30 @@ QImage PrepareWithBlurredBackground(
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize CountDesiredMediaSize(QSize original) {
|
||||||
|
return DownscaledSize(
|
||||||
|
style::ConvertScale(original),
|
||||||
|
{ st::maxMediaSize, st::maxMediaSize });
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize CountMediaSize(QSize desired, int newWidth) {
|
||||||
|
Expects(!desired.isEmpty());
|
||||||
|
|
||||||
|
return (desired.width() <= newWidth)
|
||||||
|
? desired
|
||||||
|
: NonEmptySize(
|
||||||
|
desired.scaled(newWidth, desired.height(), Qt::KeepAspectRatio));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize CountPhotoMediaSize(
|
||||||
|
QSize desired,
|
||||||
|
int newWidth,
|
||||||
|
int maxWidth) {
|
||||||
|
const auto media = CountMediaSize(desired, qMin(newWidth, maxWidth));
|
||||||
|
return (media.height() <= newWidth)
|
||||||
|
? media
|
||||||
|
: NonEmptySize(
|
||||||
|
media.scaled(media.width(), newWidth, Qt::KeepAspectRatio));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -62,5 +62,17 @@ void PaintInterpolatedIcon(
|
||||||
::Media::Streaming::ExpandDecision resize,
|
::Media::Streaming::ExpandDecision resize,
|
||||||
Image *large,
|
Image *large,
|
||||||
Image *blurred);
|
Image *blurred);
|
||||||
|
[[nodiscard]] QImage PrepareWithBlurredBackground(
|
||||||
|
QSize outer,
|
||||||
|
::Media::Streaming::ExpandDecision resize,
|
||||||
|
QImage large,
|
||||||
|
QImage blurred);
|
||||||
|
|
||||||
|
[[nodiscard]] QSize CountDesiredMediaSize(QSize original);
|
||||||
|
[[nodiscard]] QSize CountMediaSize(QSize desired, int newWidth);
|
||||||
|
[[nodiscard]] QSize CountPhotoMediaSize(
|
||||||
|
QSize desired,
|
||||||
|
int newWidth,
|
||||||
|
int maxWidth);
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -139,6 +139,10 @@ void Photo::unloadHeavyPart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Photo::countOptimalSize() {
|
QSize Photo::countOptimalSize() {
|
||||||
|
if (_serviceWidth > 0) {
|
||||||
|
return { _serviceWidth, _serviceWidth };
|
||||||
|
}
|
||||||
|
|
||||||
if (_parent->media() != this) {
|
if (_parent->media() != this) {
|
||||||
_caption = Ui::Text::String();
|
_caption = Ui::Text::String();
|
||||||
} else if (_caption.hasSkipBlock()) {
|
} else if (_caption.hasSkipBlock()) {
|
||||||
|
@ -147,33 +151,15 @@ QSize Photo::countOptimalSize() {
|
||||||
_parent->skipBlockHeight());
|
_parent->skipBlockHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto maxWidth = 0;
|
const auto scaled = CountDesiredMediaSize(
|
||||||
auto minHeight = 0;
|
{ _data->width(), _data->height() });
|
||||||
|
|
||||||
auto tw = style::ConvertScale(_data->width());
|
|
||||||
auto th = style::ConvertScale(_data->height());
|
|
||||||
if (!tw || !th) {
|
|
||||||
tw = th = 1;
|
|
||||||
}
|
|
||||||
if (tw > st::maxMediaSize) {
|
|
||||||
th = (st::maxMediaSize * th) / tw;
|
|
||||||
tw = st::maxMediaSize;
|
|
||||||
}
|
|
||||||
if (th > st::maxMediaSize) {
|
|
||||||
tw = (st::maxMediaSize * tw) / th;
|
|
||||||
th = st::maxMediaSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_serviceWidth > 0) {
|
|
||||||
return { _serviceWidth, _serviceWidth };
|
|
||||||
}
|
|
||||||
const auto minWidth = std::clamp(
|
const auto minWidth = std::clamp(
|
||||||
_parent->minWidthForMedia(),
|
_parent->minWidthForMedia(),
|
||||||
(_parent->hasBubble() ? st::historyPhotoBubbleMinWidth : st::minPhotoSize),
|
(_parent->hasBubble() ? st::historyPhotoBubbleMinWidth : st::minPhotoSize),
|
||||||
st::maxMediaSize);
|
st::maxMediaSize);
|
||||||
const auto maxActualWidth = qMax(tw, minWidth);
|
const auto maxActualWidth = qMax(scaled.width(), minWidth);
|
||||||
maxWidth = qMax(maxActualWidth, th);
|
auto maxWidth = qMax(maxActualWidth, scaled.height());
|
||||||
minHeight = qMax(th, st::minPhotoSize);
|
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
|
||||||
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||||
maxWidth = qMax(maxWidth, st::msgPadding.left()
|
maxWidth = qMax(maxWidth, st::msgPadding.left()
|
||||||
+ _caption.maxWidth()
|
+ _caption.maxWidth()
|
||||||
|
@ -186,32 +172,6 @@ QSize Photo::countOptimalSize() {
|
||||||
return { maxWidth, minHeight };
|
return { maxWidth, minHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Photo::pixmapSizeFromData(int newWidth) const {
|
|
||||||
auto tw = style::ConvertScale(_data->width());
|
|
||||||
auto th = style::ConvertScale(_data->height());
|
|
||||||
if (tw > st::maxMediaSize) {
|
|
||||||
th = (st::maxMediaSize * th) / tw;
|
|
||||||
tw = st::maxMediaSize;
|
|
||||||
}
|
|
||||||
if (th > st::maxMediaSize) {
|
|
||||||
tw = (st::maxMediaSize * tw) / th;
|
|
||||||
th = st::maxMediaSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pixw = qMin(newWidth, maxWidth());
|
|
||||||
auto pixh = th;
|
|
||||||
if (tw > pixw) {
|
|
||||||
pixh = (pixw * pixh / tw);
|
|
||||||
} else {
|
|
||||||
pixw = tw;
|
|
||||||
}
|
|
||||||
if (pixh > newWidth) {
|
|
||||||
pixw = (pixw * newWidth) / pixh;
|
|
||||||
pixh = newWidth;
|
|
||||||
}
|
|
||||||
return { pixw, pixh };
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize Photo::countCurrentSize(int newWidth) {
|
QSize Photo::countCurrentSize(int newWidth) {
|
||||||
if (_serviceWidth) {
|
if (_serviceWidth) {
|
||||||
return { _serviceWidth, _serviceWidth };
|
return { _serviceWidth, _serviceWidth };
|
||||||
|
@ -220,7 +180,10 @@ QSize Photo::countCurrentSize(int newWidth) {
|
||||||
_parent->minWidthForMedia(),
|
_parent->minWidthForMedia(),
|
||||||
(_parent->hasBubble() ? st::historyPhotoBubbleMinWidth : st::minPhotoSize),
|
(_parent->hasBubble() ? st::historyPhotoBubbleMinWidth : st::minPhotoSize),
|
||||||
std::min(newWidth, st::maxMediaSize));
|
std::min(newWidth, st::maxMediaSize));
|
||||||
auto pix = pixmapSizeFromData(newWidth);
|
auto pix = CountPhotoMediaSize(
|
||||||
|
CountDesiredMediaSize({ _data->width(), _data->height() }),
|
||||||
|
newWidth,
|
||||||
|
maxWidth());
|
||||||
newWidth = qMax(pix.width(), minWidth);
|
newWidth = qMax(pix.width(), minWidth);
|
||||||
auto newHeight = qMax(pix.height(), st::minPhotoSize);
|
auto newHeight = qMax(pix.height(), st::minPhotoSize);
|
||||||
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||||
|
|
|
@ -116,7 +116,6 @@ private:
|
||||||
|
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
[[nodiscard]] QSize pixmapSizeFromData(int newWidth) const;
|
|
||||||
|
|
||||||
bool needInfoDisplay() const;
|
bool needInfoDisplay() const;
|
||||||
void validateGroupedCache(
|
void validateGroupedCache(
|
||||||
|
|
|
@ -590,8 +590,8 @@ bool FFMpegLoader::seekTo(crl::time positionMs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioPlayerLoader::ReadResult FFMpegLoader::readMore(
|
AudioPlayerLoader::ReadResult FFMpegLoader::readMore(
|
||||||
QByteArray & result,
|
QByteArray &result,
|
||||||
int64 & samplesAdded) {
|
int64 &samplesAdded) {
|
||||||
const auto readResult = readFromReadyContext(
|
const auto readResult = readFromReadyContext(
|
||||||
_codecContext,
|
_codecContext,
|
||||||
result,
|
result,
|
||||||
|
|
Loading…
Add table
Reference in a new issue