diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 82688196a..3cdb44ed0 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -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, diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index ab458b1a5..fdac9a583 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -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 pressPoint) const; [[nodiscard]] virtual TimeId displayedEditDate() const; [[nodiscard]] virtual bool hasVisibleText() const; + [[nodiscard]] int textualMaxWidth() const; virtual void applyGroupAdminChanges( const base::flat_set &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; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index ddcd5e279..133a19973 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -406,7 +406,6 @@ Message::Message( not_null 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() : 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; diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 240e1a47a..5c61fdaaa 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -287,7 +287,6 @@ private: void ensureRightAction() const; void refreshTopicButton(); void refreshInfoSkipBlock(); - [[nodiscard]] int plainMaxWidth() const; [[nodiscard]] int monospaceMaxWidth() const; void validateInlineKeyboard(HistoryMessageReplyMarkup *markup); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index bb64302c7..e415264b3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -300,9 +300,7 @@ Document::Document( _transcribedRound = entry.shown; } - auto caption = createCaption(); - - createComponents(!caption.isEmpty()); + createComponents(); if (const auto named = Get()) { fillNamedFromData(named); _tooltipFilename.setTooltipText(named->name); @@ -346,10 +344,6 @@ Document::Document( } setStatusSize(Ui::FileStatusSizeReady); - - if (const auto captioned = Get()) { - 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()) { thumbed->linksavel = std::make_shared( @@ -421,18 +412,6 @@ void Document::fillNamedFromData(not_null named) { } QSize Document::countOptimalSize() { - auto captioned = Get(); - 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(); 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(); + 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(); const auto &st = (thumbed ? st::msgFileThumbLayoutGrouped : st::msgFileLayoutGrouped); auto height = st.padding.top() + st.thumbSize + st.padding.bottom(); + + const_cast(this)->refreshCaption(last); + if (const auto captioned = Get()) { auto captionw = maxWidth - st::msgPadding.left() @@ -1647,33 +1637,16 @@ void Document::refreshParentId(not_null realParent) { } void Document::parentTextUpdated() { - auto caption = (_parent->media() == this || _realParent->groupId()) - ? createCaption() - : Ui::Text::String(); - if (!caption.isEmpty()) { - AddComponents(HistoryDocumentCaptioned::Bit()); - auto captioned = Get(); - captioned->caption = std::move(caption); - } else { - RemoveComponents(HistoryDocumentCaptioned::Bit()); - } history()->owner().requestViewResize(_parent); } -TextWithEntities Document::getCaption() const { - if (const auto captioned = Get()) { - return captioned->caption.toTextWithEntities(); - } - return TextWithEntities(); -} - void Document::hideSpoilers() { if (const auto captioned = Get()) { captioned->caption.setSpoilerRevealed(false, anim::type::instant); } } -Ui::Text::String Document::createCaption() { +Ui::Text::String Document::createCaption() const { return File::createCaption(_realParent); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index 3d29651c4..2f8ec9ac2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -35,6 +35,10 @@ public: not_null 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 named); [[nodiscard]] Ui::BubbleRounding thumbRounding( diff --git a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp index 9db9acc07..7d5b9f00a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp @@ -57,10 +57,8 @@ ExtendedPreview::ExtendedPreview( not_null parent, not_null 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() - : 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 diff --git a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.h b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.h index 8bec02154..0e645456e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.h +++ b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.h @@ -31,6 +31,10 @@ public: not_null 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 _invoice; - Ui::Text::String _caption; mutable MediaSpoiler _spoiler; mutable QImage _inlineThumbnail; mutable QImage _buttonBackground; diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 7dbee0416..000cacbfa 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -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() : 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()) { - 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()) { + 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(); - 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(); + 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() - : 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()) { 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 "e) 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 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( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index dad49b351..840b12587 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -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 "e) 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 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 realParent) override; + bool enforceBubbleWidth() const override; [[nodiscard]] static bool CanPlayInline(not_null 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 _data; const FullStoryId _storyId; - Ui::Text::String _caption; std::unique_ptr _streamed; const std::unique_ptr _spoiler; mutable std::unique_ptr _transcribe; diff --git a/Telegram/SourceFiles/history/view/media/history_view_invoice.h b/Telegram/SourceFiles/history/view/media/history_view_invoice.h index a95c7d5dc..b9f5203ab 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_invoice.h +++ b/Telegram/SourceFiles/history/view/media/history_view_invoice.h @@ -30,6 +30,9 @@ public: return _title.toString(); } + bool aboveTextByDefault() const override { + return false; + } bool hideMessageText() const override { return false; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index 56720e83f..65c209153 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -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 { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index f9ad5c987..4926ca7c8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -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); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index 038192cf1..7d11579e9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -65,8 +65,7 @@ GroupedMedia::Part::Part( GroupedMedia::GroupedMedia( not_null parent, const std::vector> &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 &v) { @@ -80,8 +79,7 @@ GroupedMedia::GroupedMedia( GroupedMedia::GroupedMedia( not_null parent, const std::vector> &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 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 media) { const auto document = media->document(); return (document && !document->isVideoFile()) @@ -105,12 +128,6 @@ GroupedMedia::Mode GroupedMedia::DetectMode(not_null media) { } QSize GroupedMedia::countOptimalSize() { - if (_caption.hasSkipBlock()) { - _caption.updateSkipBlock( - _parent->skipBlockWidth(), - _parent->skipBlockHeight()); - } - std::vector 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(); const auto views = item->Get(); @@ -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(); const auto views = item->Get(); @@ -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 = ∂ - } - } - } - return result; - }(); - if (part) { - _caption = createCaption(part->item); - _captionItem = part->item; - } else { - _captionItem = nullptr; - } -} - not_null 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()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h index f8baabe8a..5637f65ad 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -31,6 +31,9 @@ public: void refreshParentId(not_null 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 _captionItem; std::vector _parts; Mode _mode = Mode::Grid; bool _needBubble = false; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 2532509a7..869527ece 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -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() : 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(); 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() - : 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()) { 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 "e) 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); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index 1c99839ce..8b50f52ac 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -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 "e) 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 _data; const FullStoryId _storyId; - Ui::Text::String _caption; mutable std::shared_ptr _dataMedia; mutable std::unique_ptr _streamed; const std::unique_ptr _spoiler; diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.h b/Telegram/SourceFiles/history/view/media/history_view_web_page.h index 454af2bbf..31bc076b0 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.h +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.h @@ -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; }