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 { Ui::Text::IsolatedEmoji Element::isolatedEmoji() const {
return _text.toIsolatedEmoji(); return _text.toIsolatedEmoji();
} }
@ -952,11 +956,11 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
void Element::validateText() { void Element::validateText() {
const auto item = data(); const auto item = data();
const auto &text = item->_text;
const auto media = item->media(); const auto media = item->media();
const auto storyMention = media && media->storyMention(); const auto storyMention = media && media->storyMention();
if (media && media->storyExpired()) { if (media && media->storyExpired()) {
_media = nullptr; _media = nullptr;
_textItem = item;
if (!storyMention) { if (!storyMention) {
if (_text.isEmpty()) { if (_text.isEmpty()) {
setTextWithLinks(Ui::Text::Italic( setTextWithLinks(Ui::Text::Italic(
@ -965,6 +969,16 @@ void Element::validateText() {
return; 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()) { if (_text.isEmpty() == text.empty()) {
} else if (_flags & Flag::ServiceMessage) { } else if (_flags & Flag::ServiceMessage) {
const auto contextDependentText = contextDependentServiceText(); const auto contextDependentText = contextDependentServiceText();
@ -972,11 +986,11 @@ void Element::validateText() {
? text ? text
: contextDependentText.text; : contextDependentText.text;
const auto &customLinks = contextDependentText.text.empty() const auto &customLinks = contextDependentText.text.empty()
? item->customTextLinks() ? _textItem->customTextLinks()
: contextDependentText.links; : contextDependentText.links;
setTextWithLinks(markedText, customLinks); setTextWithLinks(markedText, customLinks);
} else { } else {
setTextWithLinks(item->translatedTextWithLocalEntities()); setTextWithLinks(_textItem->translatedTextWithLocalEntities());
} }
} }
@ -1407,6 +1421,12 @@ bool Element::hasVisibleText() const {
return false; return false;
} }
int Element::textualMaxWidth() const {
return st::msgPadding.left()
+ (hasVisibleText() ? text().maxWidth() : 0)
+ st::msgPadding.right();
}
auto Element::verticalRepaintRange() const -> VerticalRepaintRange { auto Element::verticalRepaintRange() const -> VerticalRepaintRange {
return { return {
.top = 0, .top = 0,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -57,10 +57,8 @@ ExtendedPreview::ExtendedPreview(
not_null<Element*> parent, not_null<Element*> parent,
not_null<Data::Invoice*> invoice) not_null<Data::Invoice*> invoice)
: Media(parent) : Media(parent)
, _invoice(invoice) , _invoice(invoice) {
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto item = parent->data(); const auto item = parent->data();
_caption = createCaption(item);
_spoiler.link = MakeInvoiceLink(item); _spoiler.link = MakeInvoiceLink(item);
resolveButtonText(); resolveButtonText();
} }
@ -113,17 +111,9 @@ void ExtendedPreview::unloadHeavyPart() {
= _spoiler.cornerCache = _spoiler.cornerCache
= _buttonBackground = QImage(); = _buttonBackground = QImage();
_spoiler.animation = nullptr; _spoiler.animation = nullptr;
_caption.unloadPersistentAnimation();
} }
QSize ExtendedPreview::countOptimalSize() { 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 &preview = _invoice->extendedPreview;
const auto dimensions = preview.dimensions; const auto dimensions = preview.dimensions;
const auto minWidth = std::min( const auto minWidth = std::min(
@ -141,15 +131,6 @@ QSize ExtendedPreview::countOptimalSize() {
if (preview.videoDuration < 0) { if (preview.videoDuration < 0) {
accumulate_max(maxWidth, scaled.height()); 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 }; return { maxWidth, minHeight };
} }
@ -157,7 +138,7 @@ QSize ExtendedPreview::countCurrentSize(int newWidth) {
const auto &preview = _invoice->extendedPreview; const auto &preview = _invoice->extendedPreview;
const auto dimensions = preview.dimensions; const auto dimensions = preview.dimensions;
const auto thumbMaxWidth = std::min(newWidth, st::maxMediaSize); const auto thumbMaxWidth = std::min(newWidth, st::maxMediaSize);
const auto minWidth = std::min( const auto minWidth = std::min(
std::max({ std::max({
_parent->minWidthForMedia(), _parent->minWidthForMedia(),
(_parent->hasBubble() (_parent->hasBubble()
@ -176,20 +157,11 @@ QSize ExtendedPreview::countCurrentSize(int newWidth) {
maxWidth()); maxWidth());
newWidth = qMax(scaled.width(), minWidth); newWidth = qMax(scaled.width(), minWidth);
auto newHeight = qMax(scaled.height(), st::minPhotoSize); auto newHeight = qMax(scaled.height(), st::minPhotoSize);
if (_parent->hasBubble() && !_caption.isEmpty()) { if (_parent->hasBubble()) {
const auto maxWithCaption = qMin( const auto maxWithCaption = qMin(
st::msgMaxWidth, st::msgMaxWidth,
(st::msgPadding.left() _parent->textualMaxWidth());
+ _caption.maxWidth()
+ st::msgPadding.right()));
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth); 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 }; return { newWidth, newHeight };
} }
@ -210,16 +182,8 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
const auto inWebPage = (_parent->media() != this); const auto inWebPage = (_parent->media() != this);
const auto rounding = inWebPage const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>() ? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption); : adjustedBubbleRounding();
if (bubble) { 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 {
Assert(rounding.has_value()); Assert(rounding.has_value());
fillImageShadow(p, rthumb, *rounding, context); fillImageShadow(p, rthumb, *rounding, context);
} }
@ -232,27 +196,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
} }
// date // date
if (!_caption.isEmpty()) { if (!inWebPage) {
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) {
auto fullRight = paintx + paintw; auto fullRight = paintx + paintw;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
if (needInfoDisplay()) { if (needInfoDisplay()) {
@ -349,28 +293,10 @@ TextState ExtendedPreview::textState(QPoint point, StateRequest request) const {
} }
auto paintx = 0, painty = 0, paintw = width(), painth = height(); auto paintx = 0, painty = 0, paintw = width(), painth = height();
auto bubble = _parent->hasBubble(); 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)) { if (QRect(paintx, painty, paintw, painth).contains(point)) {
result.link = _spoiler.link; result.link = _spoiler.link;
} }
if (_caption.isEmpty() && _parent->media() == this) { if (!bubble && _parent->media() == this) {
auto fullRight = paintx + paintw; auto fullRight = paintx + paintw;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
const auto bottomInfoResult = _parent->bottomInfoTextState( const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -412,23 +338,13 @@ bool ExtendedPreview::needInfoDisplay() const {
|| _parent->isLastAndSelfMessage(); || _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 { bool ExtendedPreview::needsBubble() const {
if (!_caption.isEmpty()) {
return true;
}
const auto item = _parent->data(); const auto item = _parent->data();
return !item->isService() return !item->isService()
&& (item->repliesAreComments() && (item->repliesAreComments()
|| item->externalReply() || item->externalReply()
|| item->viaBot() || item->viaBot()
|| !item->emptyText()
|| _parent->displayReply() || _parent->displayReply()
|| _parent->displayForwardedFrom() || _parent->displayForwardedFrom()
|| _parent->displayFromName() || _parent->displayFromName()
@ -441,11 +357,4 @@ QPoint ExtendedPreview::resolveCustomInfoRightBottom() const {
return QPoint(width() - skipx, height() - skipy); 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 } // namespace HistoryView

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -65,8 +65,7 @@ GroupedMedia::Part::Part(
GroupedMedia::GroupedMedia( GroupedMedia::GroupedMedia(
not_null<Element*> parent, not_null<Element*> parent,
const std::vector<std::unique_ptr<Data::Media>> &medias) const std::vector<std::unique_ptr<Data::Media>> &medias)
: Media(parent) : Media(parent) {
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto truncated = ranges::views::all( const auto truncated = ranges::views::all(
medias medias
) | ranges::views::transform([](const std::unique_ptr<Data::Media> &v) { ) | ranges::views::transform([](const std::unique_ptr<Data::Media> &v) {
@ -80,8 +79,7 @@ GroupedMedia::GroupedMedia(
GroupedMedia::GroupedMedia( GroupedMedia::GroupedMedia(
not_null<Element*> parent, not_null<Element*> parent,
const std::vector<not_null<HistoryItem*>> &items) const std::vector<not_null<HistoryItem*>> &items)
: Media(parent) : Media(parent) {
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto medias = ranges::views::all( const auto medias = ranges::views::all(
items items
) | ranges::views::transform([](not_null<HistoryItem*> item) { ) | ranges::views::transform([](not_null<HistoryItem*> item) {
@ -97,6 +95,31 @@ GroupedMedia::~GroupedMedia() {
base::take(_parts); 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) { GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) {
const auto document = media->document(); const auto document = media->document();
return (document && !document->isVideoFile()) return (document && !document->isVideoFile())
@ -105,12 +128,6 @@ GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) {
} }
QSize GroupedMedia::countOptimalSize() { QSize GroupedMedia::countOptimalSize() {
if (_caption.hasSkipBlock()) {
_caption.updateSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
std::vector<QSize> sizes; std::vector<QSize> sizes;
const auto partsCount = _parts.size(); const auto partsCount = _parts.size();
sizes.reserve(partsCount); sizes.reserve(partsCount);
@ -123,8 +140,11 @@ QSize GroupedMedia::countOptimalSize() {
accumulate_max(maxWidth, media->maxWidth()); accumulate_max(maxWidth, media->maxWidth());
} }
} }
auto index = 0;
for (const auto &part : _parts) { 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) const auto layout = (_mode == Mode::Grid)
@ -145,13 +165,7 @@ QSize GroupedMedia::countOptimalSize() {
_parts[i].sides = item.sides; _parts[i].sides = item.sides;
} }
if (!_caption.isEmpty()) { if (_mode == Mode::Column && _parts.back().item->emptyText()) {
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()) {
const auto item = _parent->data(); const auto item = _parent->data();
const auto msgsigned = item->Get<HistoryMessageSigned>(); const auto msgsigned = item->Get<HistoryMessageSigned>();
const auto views = item->Get<HistoryMessageViews>(); const auto views = item->Get<HistoryMessageViews>();
@ -215,13 +229,7 @@ QSize GroupedMedia::countCurrentSize(int newWidth) {
accumulate_max(newHeight, top + height); accumulate_max(newHeight, top + height);
} }
} }
if (!_caption.isEmpty()) { if (_mode == Mode::Column && _parts.back().item->emptyText()) {
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()) {
const auto item = _parent->data(); const auto item = _parent->data();
const auto msgsigned = item->Get<HistoryMessageSigned>(); const auto msgsigned = item->Get<HistoryMessageSigned>();
const auto views = item->Get<HistoryMessageViews>(); 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; constexpr auto kSmall = Ui::BubbleCornerRounding::Small;
const auto rounding = inWebPage const auto rounding = inWebPage
? Ui::BubbleRounding{ kSmall, kSmall, kSmall, kSmall } ? Ui::BubbleRounding{ kSmall, kSmall, kSmall, kSmall }
: adjustedBubbleRoundingWithCaption(_caption); : adjustedBubbleRounding();
auto highlight = context.highlight.range; auto highlight = context.highlight.range;
const auto subpartHighlight = IsSubGroupSelection(highlight); const auto subpartHighlight = IsSubGroupSelection(highlight);
for (auto i = 0, count = int(_parts.size()); i != count; ++i) { 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 // date
if (!_caption.isEmpty()) { if (_parent->media() == this) {
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) {
auto fullRight = width(); auto fullRight = width();
auto fullBottom = height(); auto fullBottom = height();
if (needInfoDisplay()) { if (needInfoDisplay()) {
@ -473,23 +455,7 @@ PointState GroupedMedia::pointState(QPoint point) const {
TextState GroupedMedia::textState(QPoint point, StateRequest request) const { TextState GroupedMedia::textState(QPoint point, StateRequest request) const {
const auto groupPadding = groupedPadding(); const auto groupPadding = groupedPadding();
auto result = getPartState(point - QPoint(0, groupPadding.top()), request); auto result = getPartState(point - QPoint(0, groupPadding.top()), request);
if (!result.link && !_caption.isEmpty()) { if (_parent->media() == this) {
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) {
auto fullRight = width(); auto fullRight = width();
auto fullBottom = height(); auto fullBottom = height();
const auto bottomInfoResult = _parent->bottomInfoTextState( const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -539,7 +505,7 @@ TextSelection GroupedMedia::adjustSelection(
TextSelection selection, TextSelection selection,
TextSelectType type) const { TextSelectType type) const {
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return _caption.adjustSelection(selection, type); return {};
} }
auto checked = 0; auto checked = 0;
for (const auto &part : _parts) { for (const auto &part : _parts) {
@ -563,7 +529,7 @@ TextSelection GroupedMedia::adjustSelection(
uint16 GroupedMedia::fullSelectionLength() const { uint16 GroupedMedia::fullSelectionLength() const {
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return _caption.length(); return {};
} }
auto result = 0; auto result = 0;
for (const auto &part : _parts) { for (const auto &part : _parts) {
@ -574,7 +540,7 @@ uint16 GroupedMedia::fullSelectionLength() const {
bool GroupedMedia::hasTextForCopy() const { bool GroupedMedia::hasTextForCopy() const {
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return !_caption.isEmpty(); return {};
} }
for (const auto &part : _parts) { for (const auto &part : _parts) {
if (part.content->hasTextForCopy()) { if (part.content->hasTextForCopy()) {
@ -587,7 +553,7 @@ bool GroupedMedia::hasTextForCopy() const {
TextForMimeData GroupedMedia::selectedText( TextForMimeData GroupedMedia::selectedText(
TextSelection selection) const { TextSelection selection) const {
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return _caption.toTextForMimeData(selection); return {};
} }
auto result = TextForMimeData(); auto result = TextForMimeData();
for (const auto &part : _parts) { for (const auto &part : _parts) {
@ -606,9 +572,7 @@ TextForMimeData GroupedMedia::selectedText(
SelectedQuote GroupedMedia::selectedQuote(TextSelection selection) const { SelectedQuote GroupedMedia::selectedQuote(TextSelection selection) const {
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return _captionItem return {};
? Element::FindSelectedQuote(_caption, selection, _captionItem)
: SelectedQuote();
} }
for (const auto &part : _parts) { for (const auto &part : _parts) {
const auto next = part.content->skipSelection(selection); const auto next = part.content->skipSelection(selection);
@ -630,9 +594,7 @@ TextSelection GroupedMedia::selectionFromQuote(
Expects(quote.item != nullptr); Expects(quote.item != nullptr);
if (_mode != Mode::Column) { if (_mode != Mode::Column) {
return (_captionItem == quote.item) return {};
? Element::FindSelectionFromQuote(_caption, quote)
: TextSelection();
} }
const auto i = ranges::find(_parts, not_null(quote.item), &Part::item); const auto i = ranges::find(_parts, not_null(quote.item), &Part::item);
if (i == end(_parts)) { if (i == end(_parts)) {
@ -730,7 +692,6 @@ bool GroupedMedia::applyGroup(const DataMediaRange &medias) {
if (_parts.empty()) { if (_parts.empty()) {
return false; return false;
} }
refreshCaption();
Ensures(_parts.size() <= kMaxSize); Ensures(_parts.size() <= kMaxSize);
return true; return true;
@ -750,43 +711,13 @@ bool GroupedMedia::validateGroupParts(
return (i == count); 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 { not_null<Media*> GroupedMedia::main() const {
Expects(!_parts.empty()); Expects(!_parts.empty());
return _parts.back().content.get(); return _parts.back().content.get();
} }
TextWithEntities GroupedMedia::getCaption() const {
return main()->getCaption();
}
void GroupedMedia::hideSpoilers() { void GroupedMedia::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
for (const auto &part : _parts) { for (const auto &part : _parts) {
part.content->hideSpoilers(); part.content->hideSpoilers();
} }
@ -846,13 +777,11 @@ void GroupedMedia::unloadHeavyPart() {
part.cacheKey = 0; part.cacheKey = 0;
part.cache = QPixmap(); part.cache = QPixmap();
} }
_caption.unloadPersistentAnimation();
} }
void GroupedMedia::parentTextUpdated() { void GroupedMedia::parentTextUpdated() {
if (_parent->media() == this) { if (_parent->media() == this) {
refreshCaption(); _captionItem = std::nullopt;
history()->owner().requestViewResize(_parent);
} }
} }
@ -867,7 +796,9 @@ QPoint GroupedMedia::resolveCustomInfoRightBottom() const {
} }
bool GroupedMedia::computeNeedBubble() 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; return true;
} }
if (const auto item = _parent->data()) { if (const auto item = _parent->data()) {

View file

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

View file

@ -73,9 +73,7 @@ Photo::Photo(
, _storyId(realParent->media() , _storyId(realParent->media()
? realParent->media()->storyId() ? realParent->media()->storyId()
: FullStoryId()) : FullStoryId())
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) { , _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
_caption = createCaption(realParent);
create(realParent->fullId()); create(realParent->fullId());
} }
@ -161,7 +159,6 @@ void Photo::unloadHeavyPart() {
_spoiler->animation = nullptr; _spoiler->animation = nullptr;
} }
_imageCache = QImage(); _imageCache = QImage();
_caption.unloadPersistentAnimation();
togglePollingStory(false); togglePollingStory(false);
} }
@ -184,15 +181,6 @@ QSize Photo::countOptimalSize() {
if (_serviceWidth > 0) { if (_serviceWidth > 0) {
return { int(_serviceWidth), int(_serviceWidth) }; 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 dimensions = photoSize();
const auto scaled = CountDesiredMediaSize(dimensions); const auto scaled = CountDesiredMediaSize(dimensions);
const auto minWidth = std::clamp( const auto minWidth = std::clamp(
@ -204,12 +192,7 @@ QSize Photo::countOptimalSize() {
const auto maxActualWidth = qMax(scaled.width(), minWidth); const auto maxActualWidth = qMax(scaled.width(), minWidth);
auto maxWidth = qMax(maxActualWidth, scaled.height()); auto maxWidth = qMax(maxActualWidth, scaled.height());
auto minHeight = qMax(scaled.height(), st::minPhotoSize); auto minHeight = qMax(scaled.height(), st::minPhotoSize);
if (_parent->hasBubble() && !_caption.isEmpty()) { if (_parent->hasBubble()) {
maxWidth = qMax(
maxWidth,
(st::msgPadding.left()
+ _caption.maxWidth()
+ st::msgPadding.right()));
minHeight = adjustHeightForLessCrop( minHeight = adjustHeightForLessCrop(
dimensions, dimensions,
{ maxWidth, minHeight }); { maxWidth, minHeight });
@ -217,10 +200,6 @@ QSize Photo::countOptimalSize() {
accumulate_max(maxWidth, botTop->maxWidth); accumulate_max(maxWidth, botTop->maxWidth);
minHeight += botTop->height; minHeight += botTop->height;
} }
minHeight += st::mediaCaptionSkip + _caption.minHeight();
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
} }
return { maxWidth, minHeight }; return { maxWidth, minHeight };
} }
@ -244,10 +223,8 @@ QSize Photo::countCurrentSize(int newWidth) {
newWidth = qMax(pix.width(), minWidth); newWidth = qMax(pix.width(), minWidth);
auto newHeight = qMax(pix.height(), st::minPhotoSize); auto newHeight = qMax(pix.height(), st::minPhotoSize);
auto imageHeight = newHeight; auto imageHeight = newHeight;
if (_parent->hasBubble() && !_caption.isEmpty()) { if (_parent->hasBubble()) {
auto captionMaxWidth = st::msgPadding.left() auto captionMaxWidth = _parent->textualMaxWidth();
+ _caption.maxWidth()
+ st::msgPadding.right();
const auto botTop = _parent->Get<FakeBotAboutTop>(); const auto botTop = _parent->Get<FakeBotAboutTop>();
if (botTop) { if (botTop) {
accumulate_max(captionMaxWidth, botTop->maxWidth); accumulate_max(captionMaxWidth, botTop->maxWidth);
@ -257,16 +234,9 @@ QSize Photo::countCurrentSize(int newWidth) {
imageHeight = newHeight = adjustHeightForLessCrop( imageHeight = newHeight = adjustHeightForLessCrop(
dimensions, dimensions,
{ newWidth, newHeight }); { newWidth, newHeight });
const auto captionw = newWidth
- st::msgPadding.left()
- st::msgPadding.right();
if (botTop) { if (botTop) {
newHeight += botTop->height; newHeight += botTop->height;
} }
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
}
} }
const auto enlargeInner = st::historyPageEnlargeSize; const auto enlargeInner = st::historyPageEnlargeSize;
const auto enlargeOuter = 2 * st::historyPageEnlargeSkip + enlargeInner; 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 paintx = 0, painty = 0, paintw = width(), painth = height();
auto bubble = _parent->hasBubble(); auto bubble = _parent->hasBubble();
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
if (displayLoading) { if (displayLoading) {
ensureAnimation(); ensureAnimation();
if (!_animation->radial.animating()) { if (!_animation->radial.animating()) {
@ -326,19 +294,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
} else { } else {
const auto rounding = inWebPage const auto rounding = inWebPage
? std::optional<Ui::BubbleRounding>() ? std::optional<Ui::BubbleRounding>()
: adjustedBubbleRoundingWithCaption(_caption); : adjustedBubbleRounding();
if (bubble) { 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 {
Assert(rounding.has_value()); Assert(rounding.has_value());
fillImageShadow(p, rthumb, *rounding, context); fillImageShadow(p, rthumb, *rounding, context);
} }
@ -414,35 +371,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
} }
// date // date
if (!_caption.isEmpty()) { if (isBubbleBottom() && !inWebPage) {
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) {
auto fullRight = paintx + paintw; auto fullRight = paintx + paintw;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
if (needInfoDisplay()) { if (needInfoDisplay()) {
@ -682,21 +611,7 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
auto paintx = 0, painty = 0, paintw = width(), painth = height(); auto paintx = 0, painty = 0, paintw = width(), painth = height();
auto bubble = _parent->hasBubble(); auto bubble = _parent->hasBubble();
if (bubble && !_caption.isEmpty()) { if (bubble) {
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 (const auto botTop = _parent->Get<FakeBotAboutTop>()) { if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
painth -= botTop->height; painth -= botTop->height;
} }
@ -719,7 +634,7 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
result.cursor = CursorState::Enlarge; result.cursor = CursorState::Enlarge;
} }
} }
if (_caption.isEmpty() && _parent->media() == this) { if (isBubbleBottom() && _parent->media() == this) {
auto fullRight = paintx + paintw; auto fullRight = paintx + paintw;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
const auto bottomInfoResult = _parent->bottomInfoTextState( const auto bottomInfoResult = _parent->bottomInfoTextState(
@ -746,13 +661,13 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
return result; return result;
} }
QSize Photo::sizeForGroupingOptimal(int maxWidth) const { QSize Photo::sizeForGroupingOptimal(int maxWidth, bool last) const {
const auto size = photoSize(); const auto size = photoSize();
return { std::max(size.width(), 1), std::max(size.height(), 1)}; return { std::max(size.width(), 1), std::max(size.height(), 1)};
} }
QSize Photo::sizeForGrouping(int width) const { QSize Photo::sizeForGrouping(int width) const {
return sizeForGroupingOptimal(width); return sizeForGroupingOptimal(width, false);
} }
void Photo::drawGrouped( void Photo::drawGrouped(
@ -1094,27 +1009,14 @@ bool Photo::videoAutoplayEnabled() const {
_data); _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() { void Photo::hideSpoilers() {
_caption.setSpoilerRevealed(false, anim::type::instant);
if (_spoiler) { if (_spoiler) {
_spoiler->revealed = false; _spoiler->revealed = false;
} }
} }
bool Photo::needsBubble() const { bool Photo::needsBubble() const {
if (_storyId || !_caption.isEmpty()) { if (_storyId) {
return true; return true;
} }
const auto item = _parent->data(); const auto item = _parent->data();
@ -1122,6 +1024,7 @@ bool Photo::needsBubble() const {
&& (item->repliesAreComments() && (item->repliesAreComments()
|| item->externalReply() || item->externalReply()
|| item->viaBot() || item->viaBot()
|| !item->emptyText()
|| _parent->displayReply() || _parent->displayReply()
|| _parent->displayForwardedFrom() || _parent->displayForwardedFrom()
|| _parent->displayFromName() || _parent->displayFromName()
@ -1139,13 +1042,6 @@ bool Photo::isReadyForOpen() const {
return _dataMedia->loaded(); 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) { void Photo::showPhoto(FullMsgId id) {
_parent->delegate()->elementOpenPhoto(_data, id); _parent->delegate()->elementOpenPhoto(_data, id);
} }

View file

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

View file

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