Use message text rendering for media captions.

This commit is contained in:
John Preston 2024-05-29 09:37:32 +04:00
parent d219bccf2b
commit 924d80ecba
18 changed files with 225 additions and 628 deletions

View file

@ -775,6 +775,10 @@ void Element::refreshMedia(Element *replacing) {
}
}
HistoryItem *Element::textItem() const {
return _textItem;
}
Ui::Text::IsolatedEmoji Element::isolatedEmoji() const {
return _text.toIsolatedEmoji();
}
@ -952,11 +956,11 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
void Element::validateText() {
const auto item = data();
const auto &text = item->_text;
const auto media = item->media();
const auto storyMention = media && media->storyMention();
if (media && media->storyExpired()) {
_media = nullptr;
_textItem = item;
if (!storyMention) {
if (_text.isEmpty()) {
setTextWithLinks(Ui::Text::Italic(
@ -965,6 +969,16 @@ void Element::validateText() {
return;
}
}
// Albums may show text of a different item than the parent one.
_textItem = _media ? _media->itemForText() : item.get();
if (!_textItem) {
if (!_text.isEmpty()) {
setTextWithLinks({});
}
return;
}
const auto &text = _textItem->_text;
if (_text.isEmpty() == text.empty()) {
} else if (_flags & Flag::ServiceMessage) {
const auto contextDependentText = contextDependentServiceText();
@ -972,11 +986,11 @@ void Element::validateText() {
? text
: contextDependentText.text;
const auto &customLinks = contextDependentText.text.empty()
? item->customTextLinks()
? _textItem->customTextLinks()
: contextDependentText.links;
setTextWithLinks(markedText, customLinks);
} else {
setTextWithLinks(item->translatedTextWithLocalEntities());
setTextWithLinks(_textItem->translatedTextWithLocalEntities());
}
}
@ -1407,6 +1421,12 @@ bool Element::hasVisibleText() const {
return false;
}
int Element::textualMaxWidth() const {
return st::msgPadding.left()
+ (hasVisibleText() ? text().maxWidth() : 0)
+ st::msgPadding.right();
}
auto Element::verticalRepaintRange() const -> VerticalRepaintRange {
return {
.top = 0,

View file

@ -373,6 +373,7 @@ public:
&& _text.isOnlyCustomEmoji();
}
[[nodiscard]] HistoryItem *textItem() const;
[[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const;
[[nodiscard]] Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const;
@ -476,6 +477,7 @@ public:
std::optional<QPoint> pressPoint) const;
[[nodiscard]] virtual TimeId displayedEditDate() const;
[[nodiscard]] virtual bool hasVisibleText() const;
[[nodiscard]] int textualMaxWidth() const;
virtual void applyGroupAdminChanges(
const base::flat_set<UserId> &changes) {
}
@ -633,6 +635,7 @@ private:
mutable ClickHandlerPtr _fromLink;
const QDateTime _dateTime;
HistoryItem *_textItem = nullptr;
mutable Ui::Text::String _text;
mutable int _textWidth = -1;
mutable int _textHeight = 0;

View file

@ -406,7 +406,6 @@ Message::Message(
not_null<HistoryItem*> data,
Element *replacing)
: Element(delegate, data, replacing, Flag(0))
, _invertMedia(data->invertMedia() && !data->emptyText())
, _hideReply(delegate->elementHideReply(this))
, _bottomInfo(
&data->history()->owner().reactions(),
@ -826,6 +825,15 @@ QSize Message::performCountOptimalSize() {
validateInlineKeyboard(markup);
updateViewButtonExistence();
refreshTopicButton();
const auto media = this->media();
const auto textItem = this->textItem();
const auto defaultInvert = media && media->aboveTextByDefault();
const auto invertDefault = textItem
&& textItem->invertMedia()
&& !textItem->emptyText();
_invertMedia = invertDefault ? !defaultInvert : defaultInvert;
updateMediaInBubbleState();
if (oldKey != reactionsKey()) {
refreshReactions();
@ -833,7 +841,6 @@ QSize Message::performCountOptimalSize() {
refreshRightBadge();
refreshInfoSkipBlock();
const auto media = this->media();
const auto botTop = item->isFakeAboutView()
? Get<FakeBotAboutTop>()
: nullptr;
@ -877,9 +884,10 @@ QSize Message::performCountOptimalSize() {
// Entry page is always a bubble bottom.
const auto withVisibleText = hasVisibleText();
const auto textualWidth = textualMaxWidth();
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
maxWidth = plainMaxWidth();
maxWidth = textualWidth;
if (context() == Context::Replies && item->isDiscussionPost()) {
maxWidth = std::max(maxWidth, st::msgMaxWidth);
}
@ -930,7 +938,7 @@ QSize Message::performCountOptimalSize() {
if (botTop) {
minHeight += botTop->height;
}
if (maxWidth < plainMaxWidth()) {
if (maxWidth < textualWidth) {
minHeight -= text().minHeight();
minHeight += text().countHeight(innerWidth);
}
@ -3432,12 +3440,6 @@ void Message::refreshDataIdHook() {
}
}
int Message::plainMaxWidth() const {
return st::msgPadding.left()
+ (hasVisibleText() ? text().maxWidth() : 0)
+ st::msgPadding.right();
}
int Message::monospaceMaxWidth() const {
return st::msgPadding.left()
+ (hasVisibleText() ? text().countMaxMonospaceWidth() : 0)
@ -4189,7 +4191,7 @@ QRect Message::countGeometry() const {
accumulate_min(contentWidth, maxWidth());
accumulate_min(contentWidth, int(_bubbleWidthLimit));
if (mediaWidth < contentWidth) {
const auto textualWidth = plainMaxWidth();
const auto textualWidth = textualMaxWidth();
if (mediaWidth < textualWidth
&& (!media || !media->enforceBubbleWidth())) {
accumulate_min(contentWidth, textualWidth);
@ -4300,7 +4302,7 @@ int Message::resizeContentGetHeight(int newWidth) {
if (mediaDisplayed) {
media->resizeGetHeight(contentWidth);
if (media->width() < contentWidth) {
const auto textualWidth = plainMaxWidth();
const auto textualWidth = textualMaxWidth();
if (media->width() < textualWidth
&& !media->enforceBubbleWidth()) {
accumulate_min(contentWidth, textualWidth);
@ -4474,8 +4476,11 @@ bool Message::invertMedia() const {
}
bool Message::hasVisibleText() const {
if (data()->emptyText()) {
if (const auto media = data()->media()) {
const auto textItem = this->textItem();
if (!textItem) {
return false;
} else if (textItem->emptyText()) {
if (const auto media = textItem->media()) {
return media->storyExpired();
}
return false;

View file

@ -287,7 +287,6 @@ private:
void ensureRightAction() const;
void refreshTopicButton();
void refreshInfoSkipBlock();
[[nodiscard]] int plainMaxWidth() const;
[[nodiscard]] int monospaceMaxWidth() const;
void validateInlineKeyboard(HistoryMessageReplyMarkup *markup);

View file

@ -300,9 +300,7 @@ Document::Document(
_transcribedRound = entry.shown;
}
auto caption = createCaption();
createComponents(!caption.isEmpty());
createComponents();
if (const auto named = Get<HistoryDocumentNamed>()) {
fillNamedFromData(named);
_tooltipFilename.setTooltipText(named->name);
@ -346,10 +344,6 @@ Document::Document(
}
setStatusSize(Ui::FileStatusSizeReady);
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->caption = std::move(caption);
}
}
Document::~Document() {
@ -374,7 +368,7 @@ bool Document::dataLoaded() const {
return _dataMedia->loaded();
}
void Document::createComponents(bool caption) {
void Document::createComponents() {
uint64 mask = 0;
if (_data->isVoiceMessage() || _transcribedRound) {
mask |= HistoryDocumentVoice::Bit();
@ -385,9 +379,6 @@ void Document::createComponents(bool caption) {
mask |= HistoryDocumentThumbed::Bit();
}
}
if (caption) {
mask |= HistoryDocumentCaptioned::Bit();
}
UpdateComponents(mask);
if (const auto thumbed = Get<HistoryDocumentThumbed>()) {
thumbed->linksavel = std::make_shared<DocumentSaveClickHandler>(
@ -421,18 +412,6 @@ void Document::fillNamedFromData(not_null<HistoryDocumentNamed*> named) {
}
QSize Document::countOptimalSize() {
auto captioned = Get<HistoryDocumentCaptioned>();
if (_parent->media() != this && !_realParent->groupId()) {
if (captioned) {
RemoveComponents(HistoryDocumentCaptioned::Bit());
captioned = nullptr;
}
} else if (captioned && captioned->caption.hasSkipBlock()) {
captioned->caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
auto hasTranscribe = false;
const auto voice = Get<HistoryDocumentVoice>();
if (voice) {
@ -481,7 +460,7 @@ QSize Document::countOptimalSize() {
st::messageTextStyle,
text);
hasTranscribe = true;
if (const auto skipBlockWidth = captioned
if (const auto skipBlockWidth = _parent->hasVisibleText()
? 0
: _parent->skipBlockWidth()) {
voice->transcribeText.updateSkipBlock(
@ -528,7 +507,7 @@ QSize Document::countOptimalSize() {
}
auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom();
if (!captioned && !hasTranscribe && _parent->bottomInfoIsWide()) {
if (isBubbleBottom() && !hasTranscribe && _parent->bottomInfoIsWide()) {
minHeight += st::msgDateFont->height - st::msgDateDelta.y();
}
if (!isBubbleTop()) {
@ -540,17 +519,6 @@ QSize Document::countOptimalSize() {
- st::msgPadding.left()
- st::msgPadding.right();
minHeight += voice->transcribeText.countHeight(captionw);
if (captioned) {
minHeight += st::mediaCaptionSkip;
} else if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
}
if (captioned) {
auto captionw = maxWidth
- st::msgPadding.left()
- st::msgPadding.right();
minHeight += captioned->caption.countHeight(captionw);
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
@ -1521,10 +1489,32 @@ QMargins Document::bubbleMargins() const {
return QMargins(padding.left(), padding.top(), padding.right(), padding.bottom());
}
QSize Document::sizeForGroupingOptimal(int maxWidth) const {
void Document::refreshCaption(bool last) {
auto caption = createCaption();
if (!caption.isEmpty()) {
AddComponents(HistoryDocumentCaptioned::Bit());
auto captioned = Get<HistoryDocumentCaptioned>();
captioned->caption = std::move(caption);
const auto skip = last ? _parent->skipBlockWidth() : 0;
if (skip) {
captioned->caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
} else {
captioned->caption.removeSkipBlock();
}
} else {
RemoveComponents(HistoryDocumentCaptioned::Bit());
}
}
QSize Document::sizeForGroupingOptimal(int maxWidth, bool last) const {
const auto thumbed = Get<HistoryDocumentThumbed>();
const auto &st = (thumbed ? st::msgFileThumbLayoutGrouped : st::msgFileLayoutGrouped);
auto height = st.padding.top() + st.thumbSize + st.padding.bottom();
const_cast<Document*>(this)->refreshCaption(last);
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
auto captionw = maxWidth
- st::msgPadding.left()
@ -1647,33 +1637,16 @@ void Document::refreshParentId(not_null<HistoryItem*> realParent) {
}
void Document::parentTextUpdated() {
auto caption = (_parent->media() == this || _realParent->groupId())
? createCaption()
: Ui::Text::String();
if (!caption.isEmpty()) {
AddComponents(HistoryDocumentCaptioned::Bit());
auto captioned = Get<HistoryDocumentCaptioned>();
captioned->caption = std::move(caption);
} else {
RemoveComponents(HistoryDocumentCaptioned::Bit());
}
history()->owner().requestViewResize(_parent);
}
TextWithEntities Document::getCaption() const {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->caption.toTextWithEntities();
}
return TextWithEntities();
}
void Document::hideSpoilers() {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->caption.setSpoilerRevealed(false, anim::type::instant);
}
}
Ui::Text::String Document::createCaption() {
Ui::Text::String Document::createCaption() const {
return File::createCaption(_realParent);
}

View file

@ -35,6 +35,10 @@ public:
not_null<DocumentData*> document);
~Document();
bool hideMessageText() const override {
return false;
}
void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) const override;
void updatePressed(QPoint point) override;
@ -56,7 +60,6 @@ public:
return _data;
}
TextWithEntities getCaption() const override;
void hideSpoilers() override;
bool needsBubble() const override {
return true;
@ -66,7 +69,7 @@ public:
}
QMargins bubbleMargins() const override;
QSize sizeForGroupingOptimal(int maxWidth) const override;
QSize sizeForGroupingOptimal(int maxWidth, bool last) const override;
QSize sizeForGrouping(int width) const override;
void drawGrouped(
Painter &p,
@ -117,12 +120,13 @@ private:
LayoutMode mode) const;
void ensureDataMediaCreated() const;
[[nodiscard]] Ui::Text::String createCaption();
[[nodiscard]] Ui::Text::String createCaption() const;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;
void createComponents(bool caption);
void refreshCaption(bool last);
void createComponents();
void fillNamedFromData(not_null<HistoryDocumentNamed*> named);
[[nodiscard]] Ui::BubbleRounding thumbRounding(

View file

@ -57,10 +57,8 @@ ExtendedPreview::ExtendedPreview(
not_null<Element*> parent,
not_null<Data::Invoice*> invoice)
: Media(parent)
, _invoice(invoice)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
, _invoice(invoice) {
const auto item = parent->data();
_caption = createCaption(item);
_spoiler.link = MakeInvoiceLink(item);
resolveButtonText();
}
@ -113,17 +111,9 @@ void ExtendedPreview::unloadHeavyPart() {
= _spoiler.cornerCache
= _buttonBackground = QImage();
_spoiler.animation = nullptr;
_caption.unloadPersistentAnimation();
}
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::min(
@ -141,15 +131,6 @@ QSize ExtendedPreview::countOptimalSize() {
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 };
}
@ -157,7 +138,7 @@ QSize ExtendedPreview::countCurrentSize(int newWidth) {
const auto &preview = _invoice->extendedPreview;
const auto dimensions = preview.dimensions;
const auto thumbMaxWidth = std::min(newWidth, st::maxMediaSize);
const auto minWidth = std::min(
const auto minWidth = std::min(
std::max({
_parent->minWidthForMedia(),
(_parent->hasBubble()
@ -176,20 +157,11 @@ QSize ExtendedPreview::countCurrentSize(int newWidth) {
maxWidth());
newWidth = qMax(scaled.width(), minWidth);
auto newHeight = qMax(scaled.height(), st::minPhotoSize);
if (_parent->hasBubble() && !_caption.isEmpty()) {
if (_parent->hasBubble()) {
const auto maxWithCaption = qMin(
st::msgMaxWidth,
(st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right()));
_parent->textualMaxWidth());
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
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 };
}
@ -210,16 +182,8 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
const auto inWebPage = (_parent->media() != this);
const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption);
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 {
: adjustedBubbleRounding();
if (!bubble) {
Assert(rounding.has_value());
fillImageShadow(p, rthumb, *rounding, context);
}
@ -232,27 +196,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
}
// date
if (!_caption.isEmpty()) {
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
auto highlightRequest = context.computeHighlightCache();
_caption.draw(p, {
.position = QPoint(
st::msgPadding.left(),
painty + painth + st::mediaCaptionSkip),
.availableWidth = captionw,
.palette = &stm->textPalette,
.pre = stm->preCache.get(),
.blockquote = context.quoteCache(parent()->contentColorIndex()),
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
.selection = context.selection,
.highlight = highlightRequest ? &*highlightRequest : nullptr,
});
} else if (!inWebPage) {
if (!inWebPage) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
if (needInfoDisplay()) {
@ -349,28 +293,10 @@ TextState ExtendedPreview::textState(QPoint point, StateRequest request) const {
}
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 = _spoiler.link;
}
if (_caption.isEmpty() && _parent->media() == this) {
if (!bubble && _parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -412,23 +338,13 @@ bool ExtendedPreview::needInfoDisplay() const {
|| _parent->isLastAndSelfMessage();
}
TextForMimeData ExtendedPreview::selectedText(TextSelection selection) const {
return _caption.toTextForMimeData(selection);
}
void ExtendedPreview::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
}
bool ExtendedPreview::needsBubble() const {
if (!_caption.isEmpty()) {
return true;
}
const auto item = _parent->data();
return !item->isService()
&& (item->repliesAreComments()
|| item->externalReply()
|| item->viaBot()
|| !item->emptyText()
|| _parent->displayReply()
|| _parent->displayForwardedFrom()
|| _parent->displayFromName()
@ -441,11 +357,4 @@ QPoint ExtendedPreview::resolveCustomInfoRightBottom() const {
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

View file

@ -31,6 +31,10 @@ public:
not_null<Data::Invoice*> invoice);
~ExtendedPreview();
bool hideMessageText() const override {
return false;
}
void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) const override;
@ -39,35 +43,15 @@ public:
[[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();
}
void hideSpoilers() override;
bool needsBubble() const override;
bool customInfoLayout() const override {
return _caption.isEmpty();
return true;
}
QPoint resolveCustomInfoRightBottom() const override;
bool skipBubbleTail() const override {
return isRoundedInBubbleBottom() && _caption.isEmpty();
return isRoundedInBubbleBottom();
}
void parentTextUpdated() override;
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
@ -90,7 +74,6 @@ private:
const PaintContext &context) const;
const not_null<Data::Invoice*> _invoice;
Ui::Text::String _caption;
mutable MediaSpoiler _spoiler;
mutable QImage _inlineThumbnail;
mutable QImage _buttonBackground;

View file

@ -135,8 +135,6 @@ Gif::Gif(
, _storyId(realParent->media()
? realParent->media()->storyId()
: FullStoryId())
, _caption(
st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _spoiler((spoiler || IsHiddenRoundMessage(_parent))
? std::make_unique<MediaSpoiler>()
: nullptr)
@ -184,7 +182,6 @@ Gif::Gif(
createSpoilerLink(_spoiler.get());
}
refreshCaption();
if ((_dataMedia = _data->activeMediaView())) {
dataMediaCreated();
} else {
@ -240,13 +237,6 @@ QSize Gif::countThumbSize(int &inOutWidthMax) const {
}
QSize Gif::countOptimalSize() {
if (_parent->media() != this) {
_caption = Ui::Text::String();
} else if (_caption.hasSkipBlock()) {
_caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
if (_data->isVideoMessage() && _transcribe) {
const auto &entry = _data->session().api().transcribes().entry(
_realParent);
@ -271,21 +261,16 @@ QSize Gif::countOptimalSize() {
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
}
if (_parent->hasBubble()) {
if (!_caption.isEmpty()) {
maxWidth = qMax(maxWidth, st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right());
minHeight = adjustHeightForLessCrop(
scaled,
{ maxWidth, minHeight });
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
minHeight += st::mediaCaptionSkip + _caption.minHeight();
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
maxWidth = qMax(maxWidth, _parent->textualMaxWidth());
minHeight = adjustHeightForLessCrop(
scaled,
{ maxWidth, minHeight });
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
} else if (isUnwrapped()) {
const auto item = _parent->data();
@ -318,29 +303,18 @@ QSize Gif::countCurrentSize(int newWidth) {
}
if (_parent->hasBubble()) {
accumulate_max(newWidth, _parent->minWidthForMedia());
if (!_caption.isEmpty()) {
auto captionMaxWidth = st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right();
const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth);
}
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
newHeight = adjustHeightForLessCrop(
scaled,
{ newWidth, newHeight });
const auto captionw = newWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (botTop) {
newHeight += botTop->height;
}
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
}
auto captionMaxWidth = _parent->textualMaxWidth();
const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth);
}
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
newHeight = adjustHeightForLessCrop(
scaled,
{ newWidth, newHeight });
if (botTop) {
newHeight += botTop->height;
}
} else if (isUnwrapped()) {
accumulate_max(newWidth, _parent->reactionsOptimalWidth());
@ -433,7 +407,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
const auto activeRoundPlaying = activeRoundStreamed();
auto paintx = 0, painty = 0, paintw = width(), painth = height();
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
const bool bubble = _parent->hasBubble();
const auto rightLayout = _parent->hasRightLayout();
const auto inWebPage = (_parent->media() != this);
@ -442,16 +415,10 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption);
: adjustedBubbleRounding();
if (bubble) {
if (!_caption.isEmpty()) {
if (botTop) {
painth -= botTop->height;
}
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
painth -= st::msgPadding.bottom();
}
if (botTop) {
painth -= botTop->height;
}
}
@ -792,11 +759,13 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
}
}
}
if (!unwrapped && !_caption.isEmpty()) {
if (!unwrapped && bubble) {
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
auto top = painty + painth + st::mediaCaptionSkip;
if (botTop) {
auto captionw = paintw
- st::msgPadding.left()
- st::msgPadding.right();
botTop->text.drawLeftElided(
p,
st::msgPadding.left(),
@ -805,21 +774,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
_parent->width());
top += botTop->height;
}
auto highlightRequest = context.computeHighlightCache();
_caption.draw(p, {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.pre = stm->preCache.get(),
.blockquote = context.quoteCache(parent()->contentColorIndex()),
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
.selection = context.selection,
.highlight = highlightRequest ? &*highlightRequest : nullptr,
});
} else if (!inWebPage && !skipDrawingSurrounding) {
auto fullRight = paintx + usex + usew;
auto fullBottom = painty + painth;
@ -1081,23 +1035,10 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
auto paintx = 0, painty = 0, paintw = width(), painth = height();
auto bubble = _parent->hasBubble();
if (bubble && !_caption.isEmpty()) {
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;
}
if (bubble) {
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
painth -= botTop->height;
}
painth -= st::mediaCaptionSkip;
}
const auto rightLayout = _parent->hasRightLayout();
const auto inWebPage = (_parent->media() != this);
@ -1212,7 +1153,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
? _cancell
: _savel;
}
if (unwrapped || _caption.isEmpty()) {
if (unwrapped || !bubble) {
auto fullRight = usex + paintx + usew;
auto fullBottom = painty + painth;
auto maxRight = _parent->width() - st::msgMargin.left();
@ -1289,23 +1230,11 @@ void Gif::clickHandlerPressedChanged(
}
}
TextForMimeData Gif::selectedText(TextSelection selection) const {
return _caption.toTextForMimeData(selection);
}
SelectedQuote Gif::selectedQuote(TextSelection selection) const {
return Element::FindSelectedQuote(_caption, selection, _realParent);
}
TextSelection Gif::selectionFromQuote(const SelectedQuote &quote) const {
return Element::FindSelectionFromQuote(_caption, quote);
}
bool Gif::fullFeaturedGrouped(RectParts sides) const {
return (sides & RectPart::Left) && (sides & RectPart::Right);
}
QSize Gif::sizeForGroupingOptimal(int maxWidth) const {
QSize Gif::sizeForGroupingOptimal(int maxWidth, bool last) const {
return sizeForAspectRatio();
}
@ -1588,7 +1517,6 @@ bool Gif::uploading() const {
}
void Gif::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
if (_spoiler) {
_spoiler->revealed = false;
}
@ -1599,13 +1527,12 @@ bool Gif::needsBubble() const {
return true;
} else if (_data->isVideoMessage()) {
return false;
} else if (!_caption.isEmpty()) {
return true;
}
const auto item = _parent->data();
return item->repliesAreComments()
|| item->externalReply()
|| item->viaBot()
|| !item->emptyText()
|| _parent->displayReply()
|| _parent->displayForwardedFrom()
|| _parent->displayFromName()
@ -1810,13 +1737,6 @@ bool Gif::isReadyForOpen() const {
return true;
}
void Gif::parentTextUpdated() {
if (_parent->media() == this) {
refreshCaption();
history()->owner().requestViewResize(_parent);
}
}
bool Gif::hasHeavyPart() const {
return (_spoiler && _spoiler->animation) || _streamed || _dataMedia;
}
@ -1830,19 +1750,11 @@ void Gif::unloadHeavyPart() {
}
_thumbCache = QImage();
_videoThumbnailFrame = nullptr;
_caption.unloadPersistentAnimation();
togglePollingStory(false);
}
void Gif::refreshParentId(not_null<HistoryItem*> realParent) {
File::refreshParentId(realParent);
if (_parent->media() == this) {
refreshCaption();
}
}
void Gif::refreshCaption() {
_caption = createCaption(_parent->data());
bool Gif::enforceBubbleWidth() const {
return true;
}
int Gif::additionalWidth(

View file

@ -54,6 +54,10 @@ public:
bool spoiler);
~Gif();
bool hideMessageText() const override {
return false;
}
void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) const override;
@ -61,23 +65,6 @@ public:
const ClickHandlerPtr &p,
bool pressed) 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;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
const SelectedQuote &quote) const override;
bool uploading() const override;
DocumentData *getDocument() const override {
@ -85,7 +72,7 @@ public:
}
bool fullFeaturedGrouped(RectParts sides) const;
QSize sizeForGroupingOptimal(int maxWidth) const override;
QSize sizeForGroupingOptimal(int maxWidth, bool last) const override;
QSize sizeForGrouping(int width) const override;
void drawGrouped(
Painter &p,
@ -105,14 +92,11 @@ public:
void stopAnimation() override;
void checkAnimation() override;
TextWithEntities getCaption() const override {
return _caption.toTextWithEntities();
}
void hideSpoilers() override;
bool needsBubble() const override;
bool unwrapped() const override;
bool customInfoLayout() const override {
return _caption.isEmpty();
return true;
}
QRect contentRectForReactions() const override;
std::optional<int> reactionButtonCenterOverride() const override;
@ -120,16 +104,13 @@ public:
QString additionalInfoString() const override;
bool skipBubbleTail() const override {
return isRoundedInBubbleBottom() && _caption.isEmpty();
return isRoundedInBubbleBottom();
}
bool isReadyForOpen() const override;
void parentTextUpdated() override;
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
void refreshParentId(not_null<HistoryItem*> realParent) override;
bool enforceBubbleWidth() const override;
[[nodiscard]] static bool CanPlayInline(not_null<DocumentData*> document);
@ -148,7 +129,6 @@ private:
void ensureDataMediaCreated() const;
void dataMediaCreated() const;
void refreshCaption();
[[nodiscard]] bool autoplayEnabled() const;
@ -223,7 +203,6 @@ private:
const not_null<DocumentData*> _data;
const FullStoryId _storyId;
Ui::Text::String _caption;
std::unique_ptr<Streamed> _streamed;
const std::unique_ptr<MediaSpoiler> _spoiler;
mutable std::unique_ptr<TranscribeButton> _transcribe;

View file

@ -30,6 +30,9 @@ public:
return _title.toString();
}
bool aboveTextByDefault() const override {
return false;
}
bool hideMessageText() const override {
return false;
}

View file

@ -391,10 +391,8 @@ Ui::BubbleRounding Media::adjustedBubbleRounding(RectParts square) const {
return result;
}
Ui::BubbleRounding Media::adjustedBubbleRoundingWithCaption(
const Ui::Text::String &caption) const {
return adjustedBubbleRounding(
caption.isEmpty() ? RectParts() : RectPart::FullBottom);
HistoryItem *Media::itemForText() const {
return _parent->data();
}
bool Media::isRoundedInBubbleBottom() const {

View file

@ -104,6 +104,10 @@ public:
[[nodiscard]] virtual bool hasTextForCopy() const {
return false;
}
[[nodiscard]] virtual bool aboveTextByDefault() const {
return true;
}
[[nodiscard]] virtual HistoryItem *itemForText() const;
[[nodiscard]] virtual bool hideMessageText() const {
return true;
}
@ -194,7 +198,9 @@ public:
virtual void checkAnimation() {
}
[[nodiscard]] virtual QSize sizeForGroupingOptimal(int maxWidth) const {
[[nodiscard]] virtual QSize sizeForGroupingOptimal(
int maxWidth,
bool last) const {
Unexpected("Grouping method call.");
}
[[nodiscard]] virtual QSize sizeForGrouping(int width) const {
@ -221,9 +227,6 @@ public:
return false;
}
[[nodiscard]] virtual TextWithEntities getCaption() const {
return TextWithEntities();
}
virtual void hideSpoilers() {
}
[[nodiscard]] virtual bool needsBubble() const = 0;
@ -273,8 +276,6 @@ public:
}
[[nodiscard]] Ui::BubbleRounding adjustedBubbleRounding(
RectParts square = {}) const;
[[nodiscard]] Ui::BubbleRounding adjustedBubbleRoundingWithCaption(
const Ui::Text::String &caption) const;
[[nodiscard]] bool isBubbleTop() const {
return (_inBubbleState == MediaInBubbleState::Top)
|| (_inBubbleState == MediaInBubbleState::None);

View file

@ -65,8 +65,7 @@ GroupedMedia::Part::Part(
GroupedMedia::GroupedMedia(
not_null<Element*> parent,
const std::vector<std::unique_ptr<Data::Media>> &medias)
: Media(parent)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
: Media(parent) {
const auto truncated = ranges::views::all(
medias
) | ranges::views::transform([](const std::unique_ptr<Data::Media> &v) {
@ -80,8 +79,7 @@ GroupedMedia::GroupedMedia(
GroupedMedia::GroupedMedia(
not_null<Element*> parent,
const std::vector<not_null<HistoryItem*>> &items)
: Media(parent)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
: Media(parent) {
const auto medias = ranges::views::all(
items
) | ranges::views::transform([](not_null<HistoryItem*> item) {
@ -97,6 +95,31 @@ GroupedMedia::~GroupedMedia() {
base::take(_parts);
}
HistoryItem *GroupedMedia::itemForText() const {
if (_mode == Mode::Column) {
return Media::itemForText();
} else if (!_captionItem) {
_captionItem = [&]() -> HistoryItem* {
auto result = (HistoryItem*)nullptr;
for (const auto &part : _parts) {
if (!part.item->emptyText()) {
if (result) {
return nullptr;
} else {
result = part.item;
}
}
}
return result;
}();
}
return *_captionItem;
}
bool GroupedMedia::hideMessageText() const {
return (_mode == Mode::Column);
}
GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) {
const auto document = media->document();
return (document && !document->isVideoFile())
@ -105,12 +128,6 @@ GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) {
}
QSize GroupedMedia::countOptimalSize() {
if (_caption.hasSkipBlock()) {
_caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
std::vector<QSize> sizes;
const auto partsCount = _parts.size();
sizes.reserve(partsCount);
@ -123,8 +140,11 @@ QSize GroupedMedia::countOptimalSize() {
accumulate_max(maxWidth, media->maxWidth());
}
}
auto index = 0;
for (const auto &part : _parts) {
sizes.push_back(part.content->sizeForGroupingOptimal(maxWidth));
const auto last = (++index == _parts.size());
sizes.push_back(
part.content->sizeForGroupingOptimal(maxWidth, last));
}
const auto layout = (_mode == Mode::Grid)
@ -145,13 +165,7 @@ QSize GroupedMedia::countOptimalSize() {
_parts[i].sides = item.sides;
}
if (!_caption.isEmpty()) {
auto captionw = maxWidth - st::msgPadding.left() - st::msgPadding.right();
minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
} else if (_mode == Mode::Column && _parts.back().item->emptyText()) {
if (_mode == Mode::Column && _parts.back().item->emptyText()) {
const auto item = _parent->data();
const auto msgsigned = item->Get<HistoryMessageSigned>();
const auto views = item->Get<HistoryMessageViews>();
@ -215,13 +229,7 @@ QSize GroupedMedia::countCurrentSize(int newWidth) {
accumulate_max(newHeight, top + height);
}
}
if (!_caption.isEmpty()) {
const auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right();
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
}
} else if (_mode == Mode::Column && _parts.back().item->emptyText()) {
if (_mode == Mode::Column && _parts.back().item->emptyText()) {
const auto item = _parent->data();
const auto msgsigned = item->Get<HistoryMessageSigned>();
const auto views = item->Get<HistoryMessageViews>();
@ -341,7 +349,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
constexpr auto kSmall = Ui::BubbleCornerRounding::Small;
const auto rounding = inWebPage
? Ui::BubbleRounding{ kSmall, kSmall, kSmall, kSmall }
: adjustedBubbleRoundingWithCaption(_caption);
: adjustedBubbleRounding();
auto highlight = context.highlight.range;
const auto subpartHighlight = IsSubGroupSelection(highlight);
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
@ -388,33 +396,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
}
// date
if (!_caption.isEmpty()) {
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
const auto captiony = height()
- groupPadding.bottom()
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw);
const auto stm = context.messageStyle();
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
auto highlightRequest = context.computeHighlightCache();
_caption.draw(p, {
.position = QPoint(
st::msgPadding.left(),
captiony),
.availableWidth = captionw,
.palette = &stm->textPalette,
.pre = stm->preCache.get(),
.blockquote = context.quoteCache(parent()->contentColorIndex()),
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
.selection = context.selection,
.highlight = highlightRequest ? &*highlightRequest : nullptr,
});
} else if (_parent->media() == this) {
if (_parent->media() == this) {
auto fullRight = width();
auto fullBottom = height();
if (needInfoDisplay()) {
@ -473,23 +455,7 @@ PointState GroupedMedia::pointState(QPoint point) const {
TextState GroupedMedia::textState(QPoint point, StateRequest request) const {
const auto groupPadding = groupedPadding();
auto result = getPartState(point - QPoint(0, groupPadding.top()), request);
if (!result.link && !_caption.isEmpty()) {
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
const auto captiony = height()
- groupPadding.bottom()
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw);
if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) {
return TextState(
_captionItem
? _captionItem
: _parent->data().get(),
_caption.getState(
point - QPoint(st::msgPadding.left(), captiony),
captionw,
request.forText()));
}
} else if (_parent->media() == this) {
if (_parent->media() == this) {
auto fullRight = width();
auto fullBottom = height();
const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -539,7 +505,7 @@ TextSelection GroupedMedia::adjustSelection(
TextSelection selection,
TextSelectType type) const {
if (_mode != Mode::Column) {
return _caption.adjustSelection(selection, type);
return {};
}
auto checked = 0;
for (const auto &part : _parts) {
@ -563,7 +529,7 @@ TextSelection GroupedMedia::adjustSelection(
uint16 GroupedMedia::fullSelectionLength() const {
if (_mode != Mode::Column) {
return _caption.length();
return {};
}
auto result = 0;
for (const auto &part : _parts) {
@ -574,7 +540,7 @@ uint16 GroupedMedia::fullSelectionLength() const {
bool GroupedMedia::hasTextForCopy() const {
if (_mode != Mode::Column) {
return !_caption.isEmpty();
return {};
}
for (const auto &part : _parts) {
if (part.content->hasTextForCopy()) {
@ -587,7 +553,7 @@ bool GroupedMedia::hasTextForCopy() const {
TextForMimeData GroupedMedia::selectedText(
TextSelection selection) const {
if (_mode != Mode::Column) {
return _caption.toTextForMimeData(selection);
return {};
}
auto result = TextForMimeData();
for (const auto &part : _parts) {
@ -606,9 +572,7 @@ TextForMimeData GroupedMedia::selectedText(
SelectedQuote GroupedMedia::selectedQuote(TextSelection selection) const {
if (_mode != Mode::Column) {
return _captionItem
? Element::FindSelectedQuote(_caption, selection, _captionItem)
: SelectedQuote();
return {};
}
for (const auto &part : _parts) {
const auto next = part.content->skipSelection(selection);
@ -630,9 +594,7 @@ TextSelection GroupedMedia::selectionFromQuote(
Expects(quote.item != nullptr);
if (_mode != Mode::Column) {
return (_captionItem == quote.item)
? Element::FindSelectionFromQuote(_caption, quote)
: TextSelection();
return {};
}
const auto i = ranges::find(_parts, not_null(quote.item), &Part::item);
if (i == end(_parts)) {
@ -730,7 +692,6 @@ bool GroupedMedia::applyGroup(const DataMediaRange &medias) {
if (_parts.empty()) {
return false;
}
refreshCaption();
Ensures(_parts.size() <= kMaxSize);
return true;
@ -750,43 +711,13 @@ bool GroupedMedia::validateGroupParts(
return (i == count);
}
void GroupedMedia::refreshCaption() {
const auto part = [&]() -> const Part* {
if (_mode == Mode::Column) {
return nullptr;
}
auto result = (const Part*)nullptr;
for (const auto &part : _parts) {
if (!part.item->emptyText()) {
if (result) {
return nullptr;
} else {
result = &part;
}
}
}
return result;
}();
if (part) {
_caption = createCaption(part->item);
_captionItem = part->item;
} else {
_captionItem = nullptr;
}
}
not_null<Media*> GroupedMedia::main() const {
Expects(!_parts.empty());
return _parts.back().content.get();
}
TextWithEntities GroupedMedia::getCaption() const {
return main()->getCaption();
}
void GroupedMedia::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
for (const auto &part : _parts) {
part.content->hideSpoilers();
}
@ -846,13 +777,11 @@ void GroupedMedia::unloadHeavyPart() {
part.cacheKey = 0;
part.cache = QPixmap();
}
_caption.unloadPersistentAnimation();
}
void GroupedMedia::parentTextUpdated() {
if (_parent->media() == this) {
refreshCaption();
history()->owner().requestViewResize(_parent);
_captionItem = std::nullopt;
}
}
@ -867,7 +796,9 @@ QPoint GroupedMedia::resolveCustomInfoRightBottom() const {
}
bool GroupedMedia::computeNeedBubble() const {
if (!_caption.isEmpty() || _mode == Mode::Column) {
Expects(_mode == Mode::Column || _captionItem.has_value());
if (_mode == Mode::Column || *_captionItem) {
return true;
}
if (const auto item = _parent->data()) {

View file

@ -31,6 +31,9 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override;
HistoryItem *itemForText() const override;
bool hideMessageText() const override;
void drawHighlight(
Painter &p,
const PaintContext &context,
@ -69,7 +72,6 @@ public:
const ClickHandlerPtr &p,
bool pressed) override;
TextWithEntities getCaption() const override;
void hideSpoilers() override;
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
@ -79,14 +81,12 @@ public:
HistoryMessageEdited *displayedEditBadge() const override;
bool skipBubbleTail() const override {
return (_mode == Mode::Grid)
&& isRoundedInBubbleBottom()
&& _caption.isEmpty();
return (_mode == Mode::Grid) && isRoundedInBubbleBottom();
}
void updateNeedBubbleState() override;
bool needsBubble() const override;
bool customInfoLayout() const override {
return _caption.isEmpty() && (_mode != Mode::Column);
return (_mode != Mode::Column);
}
QPoint resolveCustomInfoRightBottom() const override;
@ -143,15 +143,12 @@ private:
QPoint point,
StateRequest request) const;
void refreshCaption();
[[nodiscard]] Ui::BubbleRounding applyRoundingSides(
Ui::BubbleRounding already,
RectParts sides) const;
[[nodiscard]] QMargins groupedPadding() const;
Ui::Text::String _caption;
HistoryItem *_captionItem = nullptr;
mutable std::optional<HistoryItem*> _captionItem;
std::vector<Part> _parts;
Mode _mode = Mode::Grid;
bool _needBubble = false;

View file

@ -73,9 +73,7 @@ Photo::Photo(
, _storyId(realParent->media()
? realParent->media()->storyId()
: FullStoryId())
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
_caption = createCaption(realParent);
create(realParent->fullId());
}
@ -161,7 +159,6 @@ void Photo::unloadHeavyPart() {
_spoiler->animation = nullptr;
}
_imageCache = QImage();
_caption.unloadPersistentAnimation();
togglePollingStory(false);
}
@ -184,15 +181,6 @@ QSize Photo::countOptimalSize() {
if (_serviceWidth > 0) {
return { int(_serviceWidth), int(_serviceWidth) };
}
if (_parent->media() != this) {
_caption = Ui::Text::String();
} else if (_caption.hasSkipBlock()) {
_caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
const auto dimensions = photoSize();
const auto scaled = CountDesiredMediaSize(dimensions);
const auto minWidth = std::clamp(
@ -204,12 +192,7 @@ QSize Photo::countOptimalSize() {
const auto maxActualWidth = qMax(scaled.width(), minWidth);
auto maxWidth = qMax(maxActualWidth, scaled.height());
auto minHeight = qMax(scaled.height(), st::minPhotoSize);
if (_parent->hasBubble() && !_caption.isEmpty()) {
maxWidth = qMax(
maxWidth,
(st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right()));
if (_parent->hasBubble()) {
minHeight = adjustHeightForLessCrop(
dimensions,
{ maxWidth, minHeight });
@ -217,10 +200,6 @@ QSize Photo::countOptimalSize() {
accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height;
}
minHeight += st::mediaCaptionSkip + _caption.minHeight();
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
}
return { maxWidth, minHeight };
}
@ -244,10 +223,8 @@ QSize Photo::countCurrentSize(int newWidth) {
newWidth = qMax(pix.width(), minWidth);
auto newHeight = qMax(pix.height(), st::minPhotoSize);
auto imageHeight = newHeight;
if (_parent->hasBubble() && !_caption.isEmpty()) {
auto captionMaxWidth = st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right();
if (_parent->hasBubble()) {
auto captionMaxWidth = _parent->textualMaxWidth();
const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth);
@ -257,16 +234,9 @@ QSize Photo::countCurrentSize(int newWidth) {
imageHeight = newHeight = adjustHeightForLessCrop(
dimensions,
{ newWidth, newHeight });
const auto captionw = newWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (botTop) {
newHeight += botTop->height;
}
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
}
}
const auto enlargeInner = st::historyPageEnlargeSize;
const auto enlargeOuter = 2 * st::historyPageEnlargeSkip + enlargeInner;
@ -309,8 +279,6 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
auto paintx = 0, painty = 0, paintw = width(), painth = height();
auto bubble = _parent->hasBubble();
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
if (displayLoading) {
ensureAnimation();
if (!_animation->radial.animating()) {
@ -326,19 +294,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
} else {
const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption);
if (bubble) {
if (!_caption.isEmpty()) {
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (botTop) {
painth -= botTop->height;
}
if (isBubbleBottom()) {
painth -= st::msgPadding.bottom();
}
rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
}
} else {
: adjustedBubbleRounding();
if (!bubble) {
Assert(rounding.has_value());
fillImageShadow(p, rthumb, *rounding, context);
}
@ -414,35 +371,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
}
// date
if (!_caption.isEmpty()) {
p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, _caption);
auto top = painty + painth + st::mediaCaptionSkip;
if (botTop) {
botTop->text.drawLeftElided(
p,
st::msgPadding.left(),
top,
captionw,
_parent->width());
top += botTop->height;
}
auto highlightRequest = context.computeHighlightCache();
_caption.draw(p, {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.pre = stm->preCache.get(),
.blockquote = context.quoteCache(parent()->contentColorIndex()),
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
.selection = context.selection,
.highlight = highlightRequest ? &*highlightRequest : nullptr,
});
} else if (!inWebPage) {
if (isBubbleBottom() && !inWebPage) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
if (needInfoDisplay()) {
@ -682,21 +611,7 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
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;
}
if (bubble) {
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
painth -= botTop->height;
}
@ -719,7 +634,7 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
result.cursor = CursorState::Enlarge;
}
}
if (_caption.isEmpty() && _parent->media() == this) {
if (isBubbleBottom() && _parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = painty + painth;
const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -746,13 +661,13 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
return result;
}
QSize Photo::sizeForGroupingOptimal(int maxWidth) const {
QSize Photo::sizeForGroupingOptimal(int maxWidth, bool last) const {
const auto size = photoSize();
return { std::max(size.width(), 1), std::max(size.height(), 1)};
}
QSize Photo::sizeForGrouping(int width) const {
return sizeForGroupingOptimal(width);
return sizeForGroupingOptimal(width, false);
}
void Photo::drawGrouped(
@ -1094,27 +1009,14 @@ bool Photo::videoAutoplayEnabled() const {
_data);
}
TextForMimeData Photo::selectedText(TextSelection selection) const {
return _caption.toTextForMimeData(selection);
}
SelectedQuote Photo::selectedQuote(TextSelection selection) const {
return Element::FindSelectedQuote(_caption, selection, _realParent);
}
TextSelection Photo::selectionFromQuote(const SelectedQuote &quote) const {
return Element::FindSelectionFromQuote(_caption, quote);
}
void Photo::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
if (_spoiler) {
_spoiler->revealed = false;
}
}
bool Photo::needsBubble() const {
if (_storyId || !_caption.isEmpty()) {
if (_storyId) {
return true;
}
const auto item = _parent->data();
@ -1122,6 +1024,7 @@ bool Photo::needsBubble() const {
&& (item->repliesAreComments()
|| item->externalReply()
|| item->viaBot()
|| !item->emptyText()
|| _parent->displayReply()
|| _parent->displayForwardedFrom()
|| _parent->displayFromName()
@ -1139,13 +1042,6 @@ bool Photo::isReadyForOpen() const {
return _dataMedia->loaded();
}
void Photo::parentTextUpdated() {
_caption = (_parent->media() == this)
? createCaption(_parent->data())
: Ui::Text::String();
history()->owner().requestViewResize(_parent);
}
void Photo::showPhoto(FullMsgId id) {
_parent->delegate()->elementOpenPhoto(_data, id);
}

View file

@ -41,26 +41,13 @@ public:
int width);
~Photo();
bool hideMessageText() const override {
return false;
}
void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) 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;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
const SelectedQuote &quote) const override;
PhotoData *getPhoto() const override {
return _data;
}
@ -71,7 +58,7 @@ public:
QPoint photoPosition,
bool markFrameShown) const;
QSize sizeForGroupingOptimal(int maxWidth) const override;
QSize sizeForGroupingOptimal(int maxWidth, bool last) const override;
QSize sizeForGrouping(int width) const override;
void drawGrouped(
Painter &p,
@ -88,22 +75,17 @@ public:
QPoint point,
StateRequest request) const override;
TextWithEntities getCaption() const override {
return _caption.toTextWithEntities();
}
void hideSpoilers() override;
bool needsBubble() const override;
bool customInfoLayout() const override {
return _caption.isEmpty();
return true;
}
QPoint resolveCustomInfoRightBottom() const override;
bool skipBubbleTail() const override {
return isRoundedInBubbleBottom() && _caption.isEmpty();
return isRoundedInBubbleBottom();
}
bool isReadyForOpen() const override;
void parentTextUpdated() override;
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
@ -168,7 +150,6 @@ private:
const not_null<PhotoData*> _data;
const FullStoryId _storyId;
Ui::Text::String _caption;
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
mutable std::unique_ptr<Streamed> _streamed;
const std::unique_ptr<MediaSpoiler> _spoiler;

View file

@ -35,6 +35,9 @@ public:
void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) const override;
bool aboveTextByDefault() const override {
return false;
}
bool hideMessageText() const override {
return false;
}