From 784d57a2bc0be78765c828a07996de02ddc2be0b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 20 Aug 2021 13:12:07 +0300 Subject: [PATCH] Use QImage bubbles background for some Media parts. --- .../history/history_inner_widget.cpp | 12 +- .../history/view/history_view_element.h | 16 +++ .../history/view/history_view_message.cpp | 27 ++--- .../view/history_view_service_message.cpp | 4 +- .../history/view/media/history_view_call.cpp | 4 +- .../history/view/media/history_view_call.h | 2 +- .../view/media/history_view_contact.cpp | 4 +- .../history/view/media/history_view_contact.h | 2 +- .../view/media/history_view_document.cpp | 111 ++++++++++++------ .../view/media/history_view_document.h | 16 +-- .../history/view/media/history_view_game.cpp | 13 +- .../history/view/media/history_view_game.h | 2 +- .../history/view/media/history_view_gif.cpp | 12 +- .../history/view/media/history_view_gif.h | 6 +- .../view/media/history_view_invoice.cpp | 13 +- .../history/view/media/history_view_invoice.h | 2 +- .../view/media/history_view_location.cpp | 8 +- .../view/media/history_view_location.h | 2 +- .../history/view/media/history_view_media.cpp | 8 ++ .../history/view/media/history_view_media.h | 13 +- .../view/media/history_view_media_grouped.cpp | 14 +-- .../view/media/history_view_media_grouped.h | 6 +- .../media/history_view_media_unwrapped.cpp | 8 +- .../view/media/history_view_media_unwrapped.h | 2 +- .../history/view/media/history_view_photo.cpp | 12 +- .../history/view/media/history_view_photo.h | 6 +- .../history/view/media/history_view_poll.cpp | 49 +++++--- .../history/view/media/history_view_poll.h | 5 +- .../media/history_view_theme_document.cpp | 4 +- .../view/media/history_view_theme_document.h | 6 +- .../view/media/history_view_web_page.cpp | 17 +-- .../view/media/history_view_web_page.h | 2 +- .../SourceFiles/ui/chat/message_bubble.cpp | 104 ++++++++++++---- Telegram/SourceFiles/ui/chat/message_bubble.h | 22 ++++ .../window/window_session_controller.cpp | 4 +- .../window/window_session_controller.h | 1 - 36 files changed, 334 insertions(+), 205 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index e89dd88588..c1e719b09a 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -619,8 +619,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { .visibleAreaTop = _visibleAreaTop, .visibleAreaTopGlobal = visibleAreaTopGlobal, .clip = clip, - .initialShift = top, - }); + }).translated(0, -top); p.translate(0, top); if (context.clip.y() < view->height()) while (top < drawToY) { context.selection = itemRenderSelection( @@ -639,8 +638,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { const auto height = view->height(); top += height; - context.viewport.translate(0, -height); - context.clip.translate(0, -height); + context.translate(0, -height); p.translate(0, height); ++iItem; @@ -672,8 +670,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { .clip = clip.intersected( QRect(0, hdrawtop, width(), clip.top() + clip.height()) ), - .initialShift = top, - }); + }).translated(0, -top); p.translate(0, top); while (top < drawToY) { const auto height = view->height(); @@ -704,8 +701,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } } top += height; - context.viewport.translate(0, -height); - context.clip.translate(0, -height); + context.translate(0, -height); p.translate(0, height); ++iItem; diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 3b3ce6b043..5d332226dd 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -198,6 +198,22 @@ struct PaintContext { QRect clip; TextSelection selection; crl::time now = 0; + + void translate(int x, int y) { + viewport.translate(x, y); + clip.translate(x, y); + } + void translate(QPoint point) { + translate(point.x(), point.y()); + } + [[nodiscard]] PaintContext translated(int x, int y) const { + auto result = *this; + result.translate(x, y); + return result; + } + [[nodiscard]] PaintContext translated(QPoint point) const { + return translated(point.x(), point.y()); + } }; class Element diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index c046475067..42651ae65f 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -594,25 +594,22 @@ void Message::draw(Painter &p, const PaintContext &context) const { auto mediaTop = (trect.y() + trect.height() - mediaHeight); p.translate(mediaLeft, mediaTop); - media->draw( - p, - context.clip.translated(-mediaLeft, -mediaTop), - skipTextSelection(context.selection), context.now); + auto mediaContext = context.translated(-mediaLeft, -mediaTop); + mediaContext.selection = skipTextSelection(context.selection); + media->draw(p, mediaContext); p.translate(-mediaLeft, -mediaTop); } if (entry) { auto entryLeft = inner.left(); auto entryTop = trect.y() + trect.height(); p.translate(entryLeft, entryTop); - auto entrySelection = skipTextSelection(context.selection); + auto entryContext = context.translated(-entryLeft, -entryTop); + entryContext.selection = skipTextSelection(context.selection); if (mediaDisplayed) { - entrySelection = media->skipSelection(entrySelection); + entryContext.selection = media->skipSelection( + entryContext.selection); } - entry->draw( - p, - context.clip.translated(-entryLeft, -entryTop), - entrySelection, - context.now); + entry->draw(p, entryContext); p.translate(-entryLeft, -entryTop); } const auto needDrawInfo = entry @@ -652,11 +649,9 @@ void Message::draw(Painter &p, const PaintContext &context) const { } } else if (media && media->isDisplayed()) { p.translate(g.topLeft()); - media->draw( - p, - context.clip.translated(-g.topLeft()), - skipTextSelection(context.selection), - context.now); + auto mediaContext = context.translated(-g.topLeft()); + mediaContext.selection = skipTextSelection(context.selection); + media->draw(p, mediaContext); p.translate(-g.topLeft()); } diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index 961189674a..351a7a1e4e 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -561,7 +561,9 @@ void Service::draw(Painter &p, const PaintContext &context) const { height -= st::msgServiceMargin.top() + media->height(); auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top(); p.translate(left, top); - media->draw(p, clip.translated(-left, -top), TextSelection(), context.now); + auto mediaContext = context.translated(-left, -top); + mediaContext.selection = TextSelection(); + media->draw(p, mediaContext); p.translate(-left, -top); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_call.cpp b/Telegram/SourceFiles/history/view/media/history_view_call.cpp index 6b632bdf20..aefa5bcd7e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_call.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_call.cpp @@ -72,12 +72,12 @@ QSize Call::countOptimalSize() { return { maxWidth, minHeight }; } -void Call::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Call::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); auto outbg = _parent->hasOutLayout(); - auto selected = (selection == FullSelection); + auto selected = (context.selection == FullSelection); accumulate_min(paintw, maxWidth()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_call.h b/Telegram/SourceFiles/history/view/media/history_view_call.h index 92002696c0..8689b4ce96 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_call.h +++ b/Telegram/SourceFiles/history/view/media/history_view_call.h @@ -22,7 +22,7 @@ public: not_null parent, not_null call); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp index ece8e5ab30..03b9412240 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp @@ -153,12 +153,12 @@ QSize Contact::countOptimalSize() { return { maxWidth, minHeight }; } -void Contact::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Contact::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); auto outbg = _parent->hasOutLayout(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); accumulate_min(paintw, maxWidth()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.h b/Telegram/SourceFiles/history/view/media/history_view_contact.h index 291035b0de..1d7912eb94 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.h +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.h @@ -29,7 +29,7 @@ public: const QString &phone); ~Contact(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 839aa0d2c7..bf133b6ee7 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/format_values.h" #include "ui/text/format_song_document_name.h" +#include "ui/chat/message_bubble.h" #include "ui/cached_round_corners.h" #include "ui/ui_utility.h" #include "layout/layout_selection.h" // FullSelection @@ -325,19 +326,14 @@ QSize Document::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void Document::draw( - Painter &p, - const QRect &r, - TextSelection selection, - crl::time ms) const { - draw(p, width(), selection, ms, LayoutMode::Full); +void Document::draw(Painter &p, const PaintContext &context) const { + draw(p, width(), context, LayoutMode::Full); } void Document::draw( Painter &p, int width, - TextSelection selection, - crl::time ms, + const PaintContext &context, LayoutMode mode) const { if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return; @@ -349,7 +345,7 @@ void Document::draw( _dataMedia->automaticLoad(_realParent->fullId(), _realParent); } bool loaded = dataLoaded(), displayLoading = _data->displayLoading(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); int captionw = width - st::msgPadding.left() - st::msgPadding.right(); auto outbg = _parent->hasOutLayout(); @@ -507,19 +503,33 @@ void Document::draw( } return nullptr; }(); - if (previous && radialOpacity > 0. && radialOpacity < 1.) { - PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner); + + const auto paintContent = [&](Painter &q) { + if (previous && radialOpacity > 0. && radialOpacity < 1.) { + PaintInterpolatedIcon(q, *icon, *previous, radialOpacity, inner); + } else { + icon->paintInCenter(q, inner); + } + + if (radial && !cornerDownload) { + QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); + auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg); + _animation->radial.draw(q, rinner, st::msgFileRadialLine, fg); + } + }; + if (_data->isSongWithCover() || !usesBubblePattern(context)) { + paintContent(p); } else { - icon->paintInCenter(p, inner); + Ui::PaintPatternBubblePart( + p, + context.viewport, + context.bubblesPattern->pixmap, + inner, + paintContent, + _iconCache); } - if (radial && !cornerDownload) { - QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); - auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg); - _animation->radial.draw(p, rinner, st::msgFileRadialLine, fg); - } - - drawCornerDownload(p, selected, mode); + drawCornerDownload(p, context, mode); } auto namewidth = width - nameleft - nameright; auto statuswidth = namewidth; @@ -595,7 +605,7 @@ void Document::draw( if (auto captioned = Get()) { p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); - captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, selection); + captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, context.selection); } } @@ -625,7 +635,10 @@ bool Document::downloadInCorner() const { && IsServerMsgId(_realParent->id); } -void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) const { +void Document::drawCornerDownload( + Painter &p, + const PaintContext &context, + LayoutMode mode) const { if (dataLoaded() || _data->loadedInMediaCache() || !downloadInCorner()) { @@ -633,6 +646,7 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co } auto outbg = _parent->hasOutLayout(); auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; + const auto selected = (context.selection == FullSelection); const auto thumbed = false; const auto &st = (mode == LayoutMode::Full) ? (thumbed ? st::msgFileThumbLayout : st::msgFileLayout) @@ -640,11 +654,16 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co const auto shift = st::historyAudioDownloadShift; const auto size = st::historyAudioDownloadSize; const auto inner = style::rtlrect(st.padding.left() + shift, st.padding.top() - topMinus + shift, size, size, width()); - auto pen = (selected - ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) - : (outbg ? st::msgOutBg : st::msgInBg))->p; - pen.setWidth(st::lineWidth); - p.setPen(pen); + const auto bubblePattern = usesBubblePattern(context); + if (bubblePattern) { + p.setPen(Qt::NoPen); + } else { + auto pen = (selected + ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) + : (outbg ? st::msgOutBg : st::msgInBg))->p; + pen.setWidth(st::lineWidth); + p.setPen(pen); + } if (selected) { p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); } else { @@ -660,11 +679,34 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co } return &(outbg ? (selected ? st::historyAudioOutDownloadSelected : st::historyAudioOutDownload) : (selected ? st::historyAudioInDownloadSelected : st::historyAudioInDownload)); }(); - icon->paintInCenter(p, inner); - if (_animation && _animation->radial.animating()) { - const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine)); - auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg); - _animation->radial.draw(p, rinner, st::historyAudioRadialLine, fg); + const auto paintContent = [&](Painter &q) { + if (bubblePattern) { + auto hq = PainterHighQualityEnabler(q); + auto pen = st::msgOutBg->p; + pen.setWidth(st::lineWidth); + q.setPen(pen); + q.setBrush(Qt::NoBrush); + q.drawEllipse(inner); + } + icon->paintInCenter(q, inner); + if (_animation && _animation->radial.animating()) { + const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine)); + auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg); + _animation->radial.draw(q, rinner, st::historyAudioRadialLine, fg); + } + }; + if (bubblePattern) { + const auto add = st::lineWidth * 2; + const auto target = inner.marginsAdded({ add, add, add, add }); + Ui::PaintPatternBubblePart( + p, + context.viewport, + context.bubblesPattern->pixmap, + target, + paintContent, + _cornerDownloadCache); + } else { + paintContent(p); } } @@ -978,9 +1020,7 @@ QSize Document::sizeForGrouping(int width) const { void Document::drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, @@ -991,8 +1031,7 @@ void Document::drawGrouped( draw( p, geometry.width(), - selection, - ms, + context.translated(-geometry.topLeft()), LayoutMode::Grouped); p.translate(-geometry.topLeft()); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index 6cc148cb4b..420495104b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -34,7 +34,7 @@ public: not_null document); ~Document(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; void updatePressed(QPoint point) override; @@ -65,9 +65,7 @@ public: QSize sizeForGrouping(int width) const override; void drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, @@ -109,8 +107,7 @@ private: void draw( Painter &p, int width, - TextSelection selection, - crl::time ms, + const PaintContext &context, LayoutMode mode) const; [[nodiscard]] TextState textState( QPoint point, @@ -131,7 +128,10 @@ private: bool updateStatusText() const; // returns showPause [[nodiscard]] bool downloadInCorner() const; - void drawCornerDownload(Painter &p, bool selected, LayoutMode mode) const; + void drawCornerDownload( + Painter &p, + const PaintContext &context, + LayoutMode mode) const; [[nodiscard]] TextState cornerDownloadTextState( QPoint point, StateRequest request, @@ -139,6 +139,8 @@ private: not_null _data; mutable std::shared_ptr _dataMedia; + mutable QImage _iconCache; + mutable QImage _cornerDownloadCache; }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index 84cdfc312e..4702b0dd3d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -198,12 +198,12 @@ TextSelection Game::fromDescriptionSelection( return ShiftItemSelection(selection, _title); } -void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Game::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); auto outbg = _parent->hasOutLayout(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); auto &barfg = selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor); auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); @@ -227,7 +227,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m if (_title.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); } - _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, selection); + _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, context.selection); tshift += _titleLines * lineHeight; } if (_descriptionLines) { @@ -236,7 +236,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m if (_description.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); } - _description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection)); + _description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(context.selection)); tshift += _descriptionLines * lineHeight; } if (_attach) { @@ -247,10 +247,11 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m auto attachTop = tshift - bubble.top(); if (rtl()) attachLeft = width() - attachLeft - _attach->width(); - auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 }; + auto attachContext = context.translated(-attachLeft, -attachTop); + attachContext.selection = selected ? FullSelection : TextSelection { 0, 0 }; p.translate(attachLeft, attachTop); - _attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); + _attach->draw(p, attachContext); auto pixwidth = _attach->width(); auto pixheight = _attach->height(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.h b/Telegram/SourceFiles/history/view/media/history_view_game.h index ce7d525b76..8bc94238a4 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.h +++ b/Telegram/SourceFiles/history/view/media/history_view_game.h @@ -22,7 +22,7 @@ public: void refreshParentId(not_null realParent) override; - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; [[nodiscard]] TextSelection adjustSelection( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 94d756f178..6f45f3602e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -276,14 +276,14 @@ bool Gif::autoplayEnabled() const { _data); } -void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Gif::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; ensureDataMediaCreated(); const auto item = _parent->data(); const auto loaded = dataLoaded(); const auto displayLoading = item->isSending() || _data->displayLoading(); - const auto selected = (selection == FullSelection); + const auto selected = (context.selection == FullSelection); const auto autoPaused = _parent->delegate()->elementIsGifPaused(); const auto cornerDownload = downloadInCorner(); const auto canBePlayed = _dataMedia->canBePlayed(); @@ -613,7 +613,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } if (!isRound && !_caption.isEmpty()) { p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); - _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); + _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, context.selection); } else if (!inWebPage) { auto fullRight = paintx + usex + usew; auto fullBottom = painty + painth; @@ -896,9 +896,7 @@ QSize Gif::sizeForGrouping(int width) const { void Gif::drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, @@ -909,7 +907,7 @@ void Gif::drawGrouped( const auto item = _parent->data(); const auto loaded = dataLoaded(); const auto displayLoading = (item->id < 0) || _data->displayLoading(); - const auto selected = (selection == FullSelection); + const auto selected = (context.selection == FullSelection); const auto autoPaused = _parent->delegate()->elementIsGifPaused(); const auto fullFeatured = fullFeaturedGrouped(sides); const auto cornerDownload = fullFeatured && downloadInCorner(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index 1aa559b635..49fd40a020 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -45,7 +45,7 @@ public: not_null document); ~Gif(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; [[nodiscard]] TextSelection adjustSelection( @@ -73,9 +73,7 @@ public: QSize sizeForGrouping(int width) const override; void drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, diff --git a/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp b/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp index f01458629f..991c66409a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp @@ -199,12 +199,12 @@ void Invoice::refreshParentId(not_null realParent) { } } -void Invoice::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Invoice::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); auto outbg = _parent->hasOutLayout(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); @@ -226,14 +226,14 @@ void Invoice::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim if (_title.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); } - _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleHeight / lineHeight, style::al_left, 0, -1, endskip, false, selection); + _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleHeight / lineHeight, style::al_left, 0, -1, endskip, false, context.selection); tshift += _titleHeight; p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette)); } if (_descriptionHeight) { p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); - _description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(selection)); + _description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(context.selection)); tshift += _descriptionHeight; } if (_attach) { @@ -244,10 +244,11 @@ void Invoice::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim auto attachTop = tshift - bubble.top(); if (rtl()) attachLeft = width() - attachLeft - _attach->width(); - auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 }; + auto attachContext = context.translated(-attachLeft, -attachTop); + attachContext.selection = selected ? FullSelection : TextSelection { 0, 0 }; p.translate(attachLeft, attachTop); - _attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); + _attach->draw(p, attachContext); auto pixwidth = _attach->width(); auto available = _status.maxWidth(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_invoice.h b/Telegram/SourceFiles/history/view/media/history_view_invoice.h index 31c112da82..2b5d6b20e9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_invoice.h +++ b/Telegram/SourceFiles/history/view/media/history_view_invoice.h @@ -34,7 +34,7 @@ public: return false; } - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; [[nodiscard]] TextSelection adjustSelection( diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.cpp b/Telegram/SourceFiles/history/view/media/history_view_location.cpp index 07ad7741e6..fdf15b6e6d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_location.cpp @@ -148,12 +148,12 @@ TextSelection Location::fromDescriptionSelection( return ShiftItemSelection(selection, _title); } -void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Location::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintx = 0, painty = 0, paintw = width(), painth = height(); bool bubble = _parent->hasBubble(); auto outbg = _parent->hasOutLayout(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); if (bubble) { if (!_title.isEmpty() || !_description.isEmpty()) { @@ -166,12 +166,12 @@ void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti if (!_title.isEmpty()) { p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); - _title.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 2, style::al_left, 0, -1, 0, false, selection); + _title.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 2, style::al_left, 0, -1, 0, false, context.selection); painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); } if (!_description.isEmpty()) { p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); - _description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(selection)); + _description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(context.selection)); painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height); } if (!_title.isEmpty() || !_description.isEmpty()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.h b/Telegram/SourceFiles/history/view/media/history_view_location.h index eceb8dfb6c..7c182bb999 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.h +++ b/Telegram/SourceFiles/history/view/media/history_view_location.h @@ -27,7 +27,7 @@ public: const QString &description = QString()); ~Location(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; [[nodiscard]] TextSelection adjustSelection( diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index eeb3c8a3e4..5b27a2689b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "lottie/lottie_single_player.h" #include "storage/storage_shared_media.h" +#include "layout/layout_selection.h" #include "data/data_document.h" #include "ui/item_text_options.h" #include "ui/chat/message_bubble.h" @@ -173,6 +174,13 @@ auto Media::getBubbleSelectionIntervals( return {}; } +bool Media::usesBubblePattern(const PaintContext &context) const { + return (context.selection != FullSelection) + && _parent->hasOutLayout() + && context.bubblesPattern + && !context.viewport.isEmpty() + && !context.bubblesPattern->pixmap.size().isEmpty(); +} PointState Media::pointState(QPoint point) const { return QRect(0, 0, width(), height()).contains(point) diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index d910e00864..74454e752a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -40,6 +40,7 @@ enum class CursorState : char; enum class InfoDisplayType : char; struct TextState; struct StateRequest; +struct PaintContext; class Element; enum class MediaInBubbleState { @@ -85,11 +86,7 @@ public: } virtual void drawHighlight(Painter &p, int top) const { } - virtual void draw( - Painter &p, - const QRect &r, - TextSelection selection, - crl::time ms) const = 0; + virtual void draw(Painter &p, const PaintContext &context) const = 0; [[nodiscard]] virtual PointState pointState(QPoint point) const; [[nodiscard]] virtual TextState textState( QPoint point, @@ -170,9 +167,7 @@ public: } virtual void drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, @@ -299,6 +294,8 @@ protected: virtual void playAnimation(bool autoplay) { } + [[nodiscard]] bool usesBubblePattern(const PaintContext &context) const; + const not_null _parent; MediaInBubbleState _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 a0da973d4e..6f10b5448e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -281,21 +281,19 @@ void GroupedMedia::drawHighlight(Painter &p, int top) const { } } -void GroupedMedia::draw( - Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms) const { +void GroupedMedia::draw(Painter &p, const PaintContext &context) const { auto wasCache = false; auto nowCache = false; const auto groupPadding = groupedPadding(); + auto selection = context.selection; const auto fullSelection = (selection == FullSelection); const auto textSelection = (_mode == Mode::Column) && !fullSelection && !IsSubGroupSelection(selection); for (auto i = 0, count = int(_parts.size()); i != count; ++i) { const auto &part = _parts[i]; - const auto partSelection = fullSelection + auto partContext = context; + partContext.selection = fullSelection ? FullSelection : textSelection ? selection @@ -313,9 +311,7 @@ void GroupedMedia::draw( } part.content->drawGrouped( p, - clip, - partSelection, - ms, + partContext, part.geometry.translated(0, groupPadding.top()), part.sides, cornersFromSides(part.sides), 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 2d71c2c0da..48be7e2be8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -32,11 +32,7 @@ public: void refreshParentId(not_null realParent) override; void drawHighlight(Painter &p, int top) const override; - void draw( - Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; PointState pointState(QPoint point) const override; TextState textState( QPoint point, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index 393165fa74..f252d793df 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -105,15 +105,11 @@ QSize UnwrappedMedia::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void UnwrappedMedia::draw( - Painter &p, - const QRect &r, - TextSelection selection, - crl::time ms) const { +void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { return; } - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); const auto rightAligned = _parent->hasOutLayout() && !_parent->delegate()->elementIsChatWide(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h index cdfd6e3d4c..a28a8b8a93 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h @@ -57,7 +57,7 @@ public: not_null parent, std::unique_ptr content); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; PointState pointState(QPoint point) const override; TextState textState(QPoint point, StateRequest request) const override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index ca71ec0622..9862fa125d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -223,12 +223,12 @@ QSize Photo::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Photo::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; ensureDataMediaCreated(); _dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); - auto selected = (selection == FullSelection); + auto selected = (context.selection == FullSelection); auto loaded = _dataMedia->loaded(); auto displayLoading = _data->displayLoading(); @@ -330,7 +330,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time if (!_caption.isEmpty()) { auto outbg = _parent->hasOutLayout(); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); - _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); + _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, context.selection); } else if (!inWebPage) { auto fullRight = paintx + paintw; auto fullBottom = painty + painth; @@ -489,9 +489,7 @@ QSize Photo::sizeForGrouping(int width) const { void Photo::drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, @@ -503,7 +501,7 @@ void Photo::drawGrouped( validateGroupedCache(geometry, corners, cacheKey, cache); - const auto selected = (selection == FullSelection); + const auto selected = (context.selection == FullSelection); const auto loaded = _dataMedia->loaded(); const auto displayLoading = _data->displayLoading(); const auto bubble = _parent->hasBubble(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index 04dc9726ba..a265d8c5e1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -37,7 +37,7 @@ public: int width); ~Photo(); - void draw(Painter &p, const QRect &clip, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; [[nodiscard]] TextSelection adjustSelection( @@ -62,9 +62,7 @@ public: QSize sizeForGrouping(int width) const override; void drawGrouped( Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, + const PaintContext &context, const QRect &geometry, RectParts sides, RectParts corners, diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index 61192fc305..d9ad0d85f3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" #include "calls/calls_instance.h" +#include "ui/chat/message_bubble.h" #include "ui/text/text_options.h" #include "ui/text/text_utilities.h" #include "ui/text/format_values.h" @@ -715,15 +716,15 @@ void Poll::updateAnswerVotes() { } } -void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void Poll::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); checkSendingAnimation(); - _poll->checkResultsReload(_parent->data(), ms); + _poll->checkResultsReload(_parent->data(), context.now); const auto outbg = _parent->hasOutLayout(); - const auto selected = (selection == FullSelection); + const auto selected = (context.selection == FullSelection); const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); const auto padding = st::msgPadding; @@ -734,14 +735,14 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m paintw -= padding.left() + padding.right(); p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); - _question.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, selection); + _question.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, context.selection); tshift += _question.countHeight(paintw) + st::historyPollSubtitleSkip; p.setPen(regular); _subtitle.drawLeftElided(p, padding.left(), tshift, paintw, width()); - paintRecentVoters(p, padding.left() + _subtitle.maxWidth(), tshift, selection); - paintCloseByTimer(p, padding.left() + paintw, tshift, selection); - paintShowSolution(p, padding.left() + paintw, tshift, selection); + paintRecentVoters(p, padding.left() + _subtitle.maxWidth(), tshift, context); + paintCloseByTimer(p, padding.left() + paintw, tshift, context.selection); + paintShowSolution(p, padding.left() + paintw, tshift, context.selection); tshift += st::msgDateFont->height + st::historyPollAnswersSkip; const auto progress = _answersAnimation @@ -773,14 +774,14 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m tshift, paintw, width(), - selection); + context.selection); tshift += height; } if (!inlineFooter()) { - paintBottom(p, padding.left(), tshift, paintw, selection); + paintBottom(p, padding.left(), tshift, paintw, context.selection); } else if (!_totalVotesLabel.isEmpty()) { tshift += st::msgPadding.bottom(); - paintInlineFooter(p, padding.left(), tshift, paintw, selection); + paintInlineFooter(p, padding.left(), tshift, paintw, context.selection); } } @@ -864,7 +865,7 @@ void Poll::paintRecentVoters( Painter &p, int left, int top, - TextSelection selection) const { + const PaintContext &context) const { const auto count = int(_recentVoters.size()); if (!count) { return; @@ -875,7 +876,7 @@ void Poll::paintRecentVoters( auto y = top; const auto size = st::historyPollRecentVoterSize; const auto outbg = _parent->hasOutLayout(); - const auto selected = (selection == FullSelection); + const auto selected = (context.selection == FullSelection); auto pen = (selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg))->p; @@ -888,10 +889,26 @@ void Poll::paintRecentVoters( if (!was && recent.userpic) { created = true; } - p.setPen(pen); - p.setBrush(Qt::NoBrush); - PainterHighQualityEnabler hq(p); - p.drawEllipse(x, y, size, size); + const auto paintContent = [&](Painter &p) { + p.setPen(pen); + p.setBrush(Qt::NoBrush); + PainterHighQualityEnabler hq(p); + p.drawEllipse(x, y, size, size); + }; + if (usesBubblePattern(context)) { + const auto add = st::lineWidth * 2; + const auto target = QRect(x, y, size, size).marginsAdded( + { add, add, add, add }); + Ui::PaintPatternBubblePart( + p, + context.viewport, + context.bubblesPattern->pixmap, + target, + paintContent, + _userpicCircleCache); + } else { + paintContent(p); + } x -= st::historyPollRecentVoterSkip; } if (created) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index d9eea821a8..dfa9dcb4bd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -30,7 +30,7 @@ public: not_null poll); ~Poll(); - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { @@ -107,7 +107,7 @@ private: Painter &p, int left, int top, - TextSelection selection) const; + const PaintContext &context) const; void paintCloseByTimer( Painter &p, int right, @@ -213,6 +213,7 @@ private: mutable std::unique_ptr _fireworksAnimation; Ui::Animations::Simple _wrongAnswerAnimation; mutable QPoint _lastLinkPoint; + mutable QImage _userpicCircleCache; mutable std::unique_ptr _close; diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp index 76d6fd009b..67ae487bd1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -150,7 +150,7 @@ QSize ThemeDocument::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void ThemeDocument::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; ensureDataMediaCreated(); @@ -158,7 +158,7 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr if (_data) { _dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); } - auto selected = (selection == FullSelection); + auto selected = (context.selection == FullSelection); auto loaded = dataLoaded(); auto displayLoading = _data && _data->displayLoading(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h index a3b48cb6a9..66dc68ad59 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h @@ -27,11 +27,7 @@ public: const std::optional ¶ms); ~ThemeDocument(); - void draw( - Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; DocumentData *getDocument() const override { diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 7af5ac3b58..e96eb1264b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -448,12 +448,12 @@ void WebPage::unloadHeavyPart() { _photoMedia = nullptr; } -void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { +void WebPage::draw(Painter &p, const PaintContext &context) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintw = width(); auto outbg = _parent->hasOutLayout(); - bool selected = (selection == FullSelection); + bool selected = (context.selection == FullSelection); auto &barfg = selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor); auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); @@ -512,7 +512,7 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim if (_siteName.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); } - _siteName.drawLeftElided(p, padding.left(), tshift, paintw, width(), _siteNameLines, style::al_left, 0, -1, endskip, false, selection); + _siteName.drawLeftElided(p, padding.left(), tshift, paintw, width(), _siteNameLines, style::al_left, 0, -1, endskip, false, context.selection); tshift += lineHeight; } if (_titleLines) { @@ -521,7 +521,7 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim if (_title.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); } - _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, toTitleSelection(selection)); + _title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, toTitleSelection(context.selection)); tshift += _titleLines * lineHeight; } if (_descriptionLines) { @@ -531,10 +531,10 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim endskip = _parent->skipBlockWidth(); } if (_descriptionLines > 0) { - _description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection)); + _description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(context.selection)); tshift += _descriptionLines * lineHeight; } else { - _description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(selection)); + _description.drawLeft(p, padding.left(), tshift, paintw, width(), style::al_left, 0, -1, toDescriptionSelection(context.selection)); tshift += _description.countHeight(paintw); } } @@ -548,8 +548,9 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim p.translate(attachLeft, attachTop); - auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 }; - _attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); + auto attachContext = context.translated(-attachLeft, -attachTop); + attachContext.selection = selected ? FullSelection : TextSelection { 0, 0 }; + _attach->draw(p, attachContext); auto pixwidth = _attach->width(); auto pixheight = _attach->height(); 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 02d955573b..aabf099440 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.h +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.h @@ -24,7 +24,7 @@ public: void refreshParentId(not_null realParent) override; - void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override; + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; bool hideMessageText() const override { diff --git a/Telegram/SourceFiles/ui/chat/message_bubble.cpp b/Telegram/SourceFiles/ui/chat/message_bubble.cpp index ff7713eda0..4d334df44d 100644 --- a/Telegram/SourceFiles/ui/chat/message_bubble.cpp +++ b/Telegram/SourceFiles/ui/chat/message_bubble.cpp @@ -93,10 +93,11 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) { const auto fill = rect.intersected(args.patternViewport); if (!fill.isEmpty()) { p.setClipRect(fill); - // #TODO bubbles optimizes - const auto to = args.patternViewport; - const auto from = QRect(QPoint(), pattern->pixmap.size()); - p.drawPixmap(to, pattern->pixmap, from); + PaintPatternBubblePart( + p, + args.patternViewport, + pattern->pixmap, + fill); p.setClipping(false); } }; @@ -112,26 +113,13 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) { int y, const QImage &mask, QImage &cache) { - Expects(mask.bytesPerLine() == mask.width() * 4); - Expects(mask.format() == QImage::Format_ARGB32_Premultiplied); - - if (cache.size() != mask.size()) { - cache = QImage( - mask.size(), - QImage::Format_ARGB32_Premultiplied); - } - cache.setDevicePixelRatio(mask.devicePixelRatio()); - Assert(cache.bytesPerLine() == cache.width() * 4); - memcpy(cache.bits(), mask.constBits(), mask.sizeInBytes()); - - auto q = QPainter(&cache); - q.setCompositionMode(QPainter::CompositionMode_SourceIn); - const auto to = args.patternViewport.translated(-x, -y); - const auto from = QRect(QPoint(), pattern->pixmap.size()); - q.drawPixmap(to, pattern->pixmap, from); - q.end(); - - p.drawImage(x, y, cache); + PaintPatternBubblePart( + p, + args.patternViewport, + pattern->pixmap, + QRect(QPoint(x, y), mask.size() / int(mask.devicePixelRatio())), + mask, + cache); }; const auto fillCorner = [&](int x, int y, int index) { fillPattern( @@ -365,4 +353,72 @@ void PaintBubble(Painter &p, const ComplexBubble &args) { } } +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target) { + // #TODO bubbles optimizes + const auto to = viewport; + const auto from = QRect(QPoint(), pixmap.size()); + p.drawPixmap(to, pixmap, from); +} + +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target, + const QImage &mask, + QImage &cache) { + Expects(mask.bytesPerLine() == mask.width() * 4); + Expects(mask.format() == QImage::Format_ARGB32_Premultiplied); + + if (cache.size() != mask.size()) { + cache = QImage( + mask.size(), + QImage::Format_ARGB32_Premultiplied); + } + cache.setDevicePixelRatio(mask.devicePixelRatio()); + Assert(cache.bytesPerLine() == cache.width() * 4); + memcpy(cache.bits(), mask.constBits(), mask.sizeInBytes()); + + auto q = QPainter(&cache); + q.setCompositionMode(QPainter::CompositionMode_SourceIn); + PaintPatternBubblePart( + q, + viewport.translated(-target.topLeft()), + pixmap, + QRect(QPoint(), cache.size() / int(cache.devicePixelRatio()))); + q.end(); + + p.drawImage(target, cache); +} + +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target, + Fn paintContent, + QImage &cache) { + Expects(paintContent != nullptr); + + if (cache.size() != target.size() * style::DevicePixelRatio()) { + cache = QImage( + target.size() * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(style::DevicePixelRatio()); + } + cache.fill(Qt::transparent); + auto q = Painter(&cache); + q.translate(-target.topLeft()); + paintContent(q); + q.setCompositionMode(QPainter::CompositionMode_SourceIn); + PaintPatternBubblePart(q, viewport, pixmap, target); + q.end(); + + p.drawImage(target, cache); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/message_bubble.h b/Telegram/SourceFiles/ui/chat/message_bubble.h index f6bacaf071..9bb851ba1a 100644 --- a/Telegram/SourceFiles/ui/chat/message_bubble.h +++ b/Telegram/SourceFiles/ui/chat/message_bubble.h @@ -49,4 +49,26 @@ struct ComplexBubble { void PaintBubble(Painter &p, const SimpleBubble &args); void PaintBubble(Painter &p, const ComplexBubble &args); +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target); + +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target, + const QImage &mask, + QImage &cache); + +void PaintPatternBubblePart( + QPainter &p, + const QRect &viewport, + const QPixmap &pixmap, + const QRect &target, + Fn paintContent, + QImage &cache); + } // namespace Ui diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index a56ddd0fd4..12e195648b 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1484,8 +1484,8 @@ HistoryView::PaintContext SessionController::bubblesContext( //} return { .bubblesPattern = _bubblesBackgroundPattern.get(), - .viewport = viewport.translated(0, -args.initialShift), - .clip = args.clip.translated(0, -args.initialShift), + .viewport = viewport, + .clip = args.clip, .now = crl::now(), }; } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index eee38e5113..5abeaba17e 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -454,7 +454,6 @@ public: int visibleAreaTopGlobal = 0; int visibleAreaWidth = 0; QRect clip; - int initialShift = 0; }; [[nodiscard]] HistoryView::PaintContext bubblesContext( BubblesContextArgs &&args);