Use QImage bubbles background for some Media parts.

This commit is contained in:
John Preston 2021-08-20 13:12:07 +03:00
parent f4fdadd3b0
commit 784d57a2bc
36 changed files with 334 additions and 205 deletions

View File

@ -619,8 +619,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
.visibleAreaTop = _visibleAreaTop, .visibleAreaTop = _visibleAreaTop,
.visibleAreaTopGlobal = visibleAreaTopGlobal, .visibleAreaTopGlobal = visibleAreaTopGlobal,
.clip = clip, .clip = clip,
.initialShift = top, }).translated(0, -top);
});
p.translate(0, top); p.translate(0, top);
if (context.clip.y() < view->height()) while (top < drawToY) { if (context.clip.y() < view->height()) while (top < drawToY) {
context.selection = itemRenderSelection( context.selection = itemRenderSelection(
@ -639,8 +638,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
const auto height = view->height(); const auto height = view->height();
top += height; top += height;
context.viewport.translate(0, -height); context.translate(0, -height);
context.clip.translate(0, -height);
p.translate(0, height); p.translate(0, height);
++iItem; ++iItem;
@ -672,8 +670,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
.clip = clip.intersected( .clip = clip.intersected(
QRect(0, hdrawtop, width(), clip.top() + clip.height()) QRect(0, hdrawtop, width(), clip.top() + clip.height())
), ),
.initialShift = top, }).translated(0, -top);
});
p.translate(0, top); p.translate(0, top);
while (top < drawToY) { while (top < drawToY) {
const auto height = view->height(); const auto height = view->height();
@ -704,8 +701,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} }
} }
top += height; top += height;
context.viewport.translate(0, -height); context.translate(0, -height);
context.clip.translate(0, -height);
p.translate(0, height); p.translate(0, height);
++iItem; ++iItem;

View File

@ -198,6 +198,22 @@ struct PaintContext {
QRect clip; QRect clip;
TextSelection selection; TextSelection selection;
crl::time now = 0; 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 class Element

View File

@ -594,25 +594,22 @@ void Message::draw(Painter &p, const PaintContext &context) const {
auto mediaTop = (trect.y() + trect.height() - mediaHeight); auto mediaTop = (trect.y() + trect.height() - mediaHeight);
p.translate(mediaLeft, mediaTop); p.translate(mediaLeft, mediaTop);
media->draw( auto mediaContext = context.translated(-mediaLeft, -mediaTop);
p, mediaContext.selection = skipTextSelection(context.selection);
context.clip.translated(-mediaLeft, -mediaTop), media->draw(p, mediaContext);
skipTextSelection(context.selection), context.now);
p.translate(-mediaLeft, -mediaTop); p.translate(-mediaLeft, -mediaTop);
} }
if (entry) { if (entry) {
auto entryLeft = inner.left(); auto entryLeft = inner.left();
auto entryTop = trect.y() + trect.height(); auto entryTop = trect.y() + trect.height();
p.translate(entryLeft, entryTop); p.translate(entryLeft, entryTop);
auto entrySelection = skipTextSelection(context.selection); auto entryContext = context.translated(-entryLeft, -entryTop);
entryContext.selection = skipTextSelection(context.selection);
if (mediaDisplayed) { if (mediaDisplayed) {
entrySelection = media->skipSelection(entrySelection); entryContext.selection = media->skipSelection(
entryContext.selection);
} }
entry->draw( entry->draw(p, entryContext);
p,
context.clip.translated(-entryLeft, -entryTop),
entrySelection,
context.now);
p.translate(-entryLeft, -entryTop); p.translate(-entryLeft, -entryTop);
} }
const auto needDrawInfo = entry const auto needDrawInfo = entry
@ -652,11 +649,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
} }
} else if (media && media->isDisplayed()) { } else if (media && media->isDisplayed()) {
p.translate(g.topLeft()); p.translate(g.topLeft());
media->draw( auto mediaContext = context.translated(-g.topLeft());
p, mediaContext.selection = skipTextSelection(context.selection);
context.clip.translated(-g.topLeft()), media->draw(p, mediaContext);
skipTextSelection(context.selection),
context.now);
p.translate(-g.topLeft()); p.translate(-g.topLeft());
} }

View File

@ -561,7 +561,9 @@ void Service::draw(Painter &p, const PaintContext &context) const {
height -= st::msgServiceMargin.top() + media->height(); height -= st::msgServiceMargin.top() + media->height();
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top(); auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
p.translate(left, 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); p.translate(-left, -top);
} }

View File

@ -72,12 +72,12 @@ QSize Call::countOptimalSize() {
return { maxWidth, minHeight }; 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
auto selected = (selection == FullSelection); auto selected = (context.selection == FullSelection);
accumulate_min(paintw, maxWidth()); accumulate_min(paintw, maxWidth());

View File

@ -22,7 +22,7 @@ public:
not_null<Element*> parent, not_null<Element*> parent,
not_null<Data::Call*> call); not_null<Data::Call*> 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; TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {

View File

@ -153,12 +153,12 @@ QSize Contact::countOptimalSize() {
return { maxWidth, minHeight }; 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
bool selected = (selection == FullSelection); bool selected = (context.selection == FullSelection);
accumulate_min(paintw, maxWidth()); accumulate_min(paintw, maxWidth());

View File

@ -29,7 +29,7 @@ public:
const QString &phone); const QString &phone);
~Contact(); ~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; TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text/format_values.h" #include "ui/text/format_values.h"
#include "ui/text/format_song_document_name.h" #include "ui/text/format_song_document_name.h"
#include "ui/chat/message_bubble.h"
#include "ui/cached_round_corners.h" #include "ui/cached_round_corners.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "layout/layout_selection.h" // FullSelection #include "layout/layout_selection.h" // FullSelection
@ -325,19 +326,14 @@ QSize Document::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; return { newWidth, newHeight };
} }
void Document::draw( void Document::draw(Painter &p, const PaintContext &context) const {
Painter &p, draw(p, width(), context, LayoutMode::Full);
const QRect &r,
TextSelection selection,
crl::time ms) const {
draw(p, width(), selection, ms, LayoutMode::Full);
} }
void Document::draw( void Document::draw(
Painter &p, Painter &p,
int width, int width,
TextSelection selection, const PaintContext &context,
crl::time ms,
LayoutMode mode) const { LayoutMode mode) const {
if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return; if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
@ -349,7 +345,7 @@ void Document::draw(
_dataMedia->automaticLoad(_realParent->fullId(), _realParent); _dataMedia->automaticLoad(_realParent->fullId(), _realParent);
} }
bool loaded = dataLoaded(), displayLoading = _data->displayLoading(); bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
bool selected = (selection == FullSelection); bool selected = (context.selection == FullSelection);
int captionw = width - st::msgPadding.left() - st::msgPadding.right(); int captionw = width - st::msgPadding.left() - st::msgPadding.right();
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
@ -507,19 +503,33 @@ void Document::draw(
} }
return nullptr; 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 { } else {
icon->paintInCenter(p, inner); Ui::PaintPatternBubblePart(
p,
context.viewport,
context.bubblesPattern->pixmap,
inner,
paintContent,
_iconCache);
} }
if (radial && !cornerDownload) { drawCornerDownload(p, context, mode);
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);
} }
auto namewidth = width - nameleft - nameright; auto namewidth = width - nameleft - nameright;
auto statuswidth = namewidth; auto statuswidth = namewidth;
@ -595,7 +605,7 @@ void Document::draw(
if (auto captioned = Get<HistoryDocumentCaptioned>()) { if (auto captioned = Get<HistoryDocumentCaptioned>()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); 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); && 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() if (dataLoaded()
|| _data->loadedInMediaCache() || _data->loadedInMediaCache()
|| !downloadInCorner()) { || !downloadInCorner()) {
@ -633,6 +646,7 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co
} }
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
const auto selected = (context.selection == FullSelection);
const auto thumbed = false; const auto thumbed = false;
const auto &st = (mode == LayoutMode::Full) const auto &st = (mode == LayoutMode::Full)
? (thumbed ? st::msgFileThumbLayout : st::msgFileLayout) ? (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 shift = st::historyAudioDownloadShift;
const auto size = st::historyAudioDownloadSize; const auto size = st::historyAudioDownloadSize;
const auto inner = style::rtlrect(st.padding.left() + shift, st.padding.top() - topMinus + shift, size, size, width()); const auto inner = style::rtlrect(st.padding.left() + shift, st.padding.top() - topMinus + shift, size, size, width());
auto pen = (selected const auto bubblePattern = usesBubblePattern(context);
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) if (bubblePattern) {
: (outbg ? st::msgOutBg : st::msgInBg))->p; p.setPen(Qt::NoPen);
pen.setWidth(st::lineWidth); } else {
p.setPen(pen); auto pen = (selected
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected)
: (outbg ? st::msgOutBg : st::msgInBg))->p;
pen.setWidth(st::lineWidth);
p.setPen(pen);
}
if (selected) { if (selected) {
p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected);
} else { } 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)); return &(outbg ? (selected ? st::historyAudioOutDownloadSelected : st::historyAudioOutDownload) : (selected ? st::historyAudioInDownloadSelected : st::historyAudioInDownload));
}(); }();
icon->paintInCenter(p, inner); const auto paintContent = [&](Painter &q) {
if (_animation && _animation->radial.animating()) { if (bubblePattern) {
const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine)); auto hq = PainterHighQualityEnabler(q);
auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg); auto pen = st::msgOutBg->p;
_animation->radial.draw(p, rinner, st::historyAudioRadialLine, fg); 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( void Document::drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
@ -991,8 +1031,7 @@ void Document::drawGrouped(
draw( draw(
p, p,
geometry.width(), geometry.width(),
selection, context.translated(-geometry.topLeft()),
ms,
LayoutMode::Grouped); LayoutMode::Grouped);
p.translate(-geometry.topLeft()); p.translate(-geometry.topLeft());
} }

View File

@ -34,7 +34,7 @@ public:
not_null<DocumentData*> document); not_null<DocumentData*> document);
~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; TextState textState(QPoint point, StateRequest request) const override;
void updatePressed(QPoint point) override; void updatePressed(QPoint point) override;
@ -65,9 +65,7 @@ public:
QSize sizeForGrouping(int width) const override; QSize sizeForGrouping(int width) const override;
void drawGrouped( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
@ -109,8 +107,7 @@ private:
void draw( void draw(
Painter &p, Painter &p,
int width, int width,
TextSelection selection, const PaintContext &context,
crl::time ms,
LayoutMode mode) const; LayoutMode mode) const;
[[nodiscard]] TextState textState( [[nodiscard]] TextState textState(
QPoint point, QPoint point,
@ -131,7 +128,10 @@ private:
bool updateStatusText() const; // returns showPause bool updateStatusText() const; // returns showPause
[[nodiscard]] bool downloadInCorner() const; [[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( [[nodiscard]] TextState cornerDownloadTextState(
QPoint point, QPoint point,
StateRequest request, StateRequest request,
@ -139,6 +139,8 @@ private:
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
mutable QImage _iconCache;
mutable QImage _cornerDownloadCache;
}; };

View File

@ -198,12 +198,12 @@ TextSelection Game::fromDescriptionSelection(
return ShiftItemSelection(selection, _title); 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
auto outbg = _parent->hasOutLayout(); 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 &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); 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()) { if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth(); 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; tshift += _titleLines * lineHeight;
} }
if (_descriptionLines) { if (_descriptionLines) {
@ -236,7 +236,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
if (_description.hasSkipBlock()) { if (_description.hasSkipBlock()) {
endskip = _parent->skipBlockWidth(); 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; tshift += _descriptionLines * lineHeight;
} }
if (_attach) { if (_attach) {
@ -247,10 +247,11 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
auto attachTop = tshift - bubble.top(); auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = width() - attachLeft - _attach->width(); 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); p.translate(attachLeft, attachTop);
_attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); _attach->draw(p, attachContext);
auto pixwidth = _attach->width(); auto pixwidth = _attach->width();
auto pixheight = _attach->height(); auto pixheight = _attach->height();

View File

@ -22,7 +22,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> 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; TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection( [[nodiscard]] TextSelection adjustSelection(

View File

@ -276,14 +276,14 @@ bool Gif::autoplayEnabled() const {
_data); _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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
ensureDataMediaCreated(); ensureDataMediaCreated();
const auto item = _parent->data(); const auto item = _parent->data();
const auto loaded = dataLoaded(); const auto loaded = dataLoaded();
const auto displayLoading = item->isSending() || _data->displayLoading(); 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 autoPaused = _parent->delegate()->elementIsGifPaused();
const auto cornerDownload = downloadInCorner(); const auto cornerDownload = downloadInCorner();
const auto canBePlayed = _dataMedia->canBePlayed(); 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()) { if (!isRound && !_caption.isEmpty()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); 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) { } else if (!inWebPage) {
auto fullRight = paintx + usex + usew; auto fullRight = paintx + usex + usew;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
@ -896,9 +896,7 @@ QSize Gif::sizeForGrouping(int width) const {
void Gif::drawGrouped( void Gif::drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
@ -909,7 +907,7 @@ void Gif::drawGrouped(
const auto item = _parent->data(); const auto item = _parent->data();
const auto loaded = dataLoaded(); const auto loaded = dataLoaded();
const auto displayLoading = (item->id < 0) || _data->displayLoading(); 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 autoPaused = _parent->delegate()->elementIsGifPaused();
const auto fullFeatured = fullFeaturedGrouped(sides); const auto fullFeatured = fullFeaturedGrouped(sides);
const auto cornerDownload = fullFeatured && downloadInCorner(); const auto cornerDownload = fullFeatured && downloadInCorner();

View File

@ -45,7 +45,7 @@ public:
not_null<DocumentData*> document); not_null<DocumentData*> document);
~Gif(); ~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; TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection( [[nodiscard]] TextSelection adjustSelection(
@ -73,9 +73,7 @@ public:
QSize sizeForGrouping(int width) const override; QSize sizeForGrouping(int width) const override;
void drawGrouped( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,

View File

@ -199,12 +199,12 @@ void Invoice::refreshParentId(not_null<HistoryItem*> 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
auto outbg = _parent->hasOutLayout(); 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); 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()) { if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth(); 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; tshift += _titleHeight;
p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette)); p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette));
} }
if (_descriptionHeight) { if (_descriptionHeight) {
p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); 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; tshift += _descriptionHeight;
} }
if (_attach) { if (_attach) {
@ -244,10 +244,11 @@ void Invoice::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
auto attachTop = tshift - bubble.top(); auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = width() - attachLeft - _attach->width(); 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); p.translate(attachLeft, attachTop);
_attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); _attach->draw(p, attachContext);
auto pixwidth = _attach->width(); auto pixwidth = _attach->width();
auto available = _status.maxWidth(); auto available = _status.maxWidth();

View File

@ -34,7 +34,7 @@ public:
return false; 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; TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection( [[nodiscard]] TextSelection adjustSelection(

View File

@ -148,12 +148,12 @@ TextSelection Location::fromDescriptionSelection(
return ShiftItemSelection(selection, _title); 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintx = 0, painty = 0, paintw = width(), painth = height(); auto paintx = 0, painty = 0, paintw = width(), painth = height();
bool bubble = _parent->hasBubble(); bool bubble = _parent->hasBubble();
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
bool selected = (selection == FullSelection); bool selected = (context.selection == FullSelection);
if (bubble) { if (bubble) {
if (!_title.isEmpty() || !_description.isEmpty()) { if (!_title.isEmpty() || !_description.isEmpty()) {
@ -166,12 +166,12 @@ void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
if (!_title.isEmpty()) { if (!_title.isEmpty()) {
p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); 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); painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
} }
if (!_description.isEmpty()) { if (!_description.isEmpty()) {
p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); 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); painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
} }
if (!_title.isEmpty() || !_description.isEmpty()) { if (!_title.isEmpty() || !_description.isEmpty()) {

View File

@ -27,7 +27,7 @@ public:
const QString &description = QString()); const QString &description = QString());
~Location(); ~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; TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection( [[nodiscard]] TextSelection adjustSelection(

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "layout/layout_selection.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "ui/item_text_options.h" #include "ui/item_text_options.h"
#include "ui/chat/message_bubble.h" #include "ui/chat/message_bubble.h"
@ -173,6 +174,13 @@ auto Media::getBubbleSelectionIntervals(
return {}; 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 { PointState Media::pointState(QPoint point) const {
return QRect(0, 0, width(), height()).contains(point) return QRect(0, 0, width(), height()).contains(point)

View File

@ -40,6 +40,7 @@ enum class CursorState : char;
enum class InfoDisplayType : char; enum class InfoDisplayType : char;
struct TextState; struct TextState;
struct StateRequest; struct StateRequest;
struct PaintContext;
class Element; class Element;
enum class MediaInBubbleState { enum class MediaInBubbleState {
@ -85,11 +86,7 @@ public:
} }
virtual void drawHighlight(Painter &p, int top) const { virtual void drawHighlight(Painter &p, int top) const {
} }
virtual void draw( virtual void draw(Painter &p, const PaintContext &context) const = 0;
Painter &p,
const QRect &r,
TextSelection selection,
crl::time ms) const = 0;
[[nodiscard]] virtual PointState pointState(QPoint point) const; [[nodiscard]] virtual PointState pointState(QPoint point) const;
[[nodiscard]] virtual TextState textState( [[nodiscard]] virtual TextState textState(
QPoint point, QPoint point,
@ -170,9 +167,7 @@ public:
} }
virtual void drawGrouped( virtual void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
@ -299,6 +294,8 @@ protected:
virtual void playAnimation(bool autoplay) { virtual void playAnimation(bool autoplay) {
} }
[[nodiscard]] bool usesBubblePattern(const PaintContext &context) const;
const not_null<Element*> _parent; const not_null<Element*> _parent;
MediaInBubbleState _inBubbleState = MediaInBubbleState::None; MediaInBubbleState _inBubbleState = MediaInBubbleState::None;

View File

@ -281,21 +281,19 @@ void GroupedMedia::drawHighlight(Painter &p, int top) const {
} }
} }
void GroupedMedia::draw( void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
Painter &p,
const QRect &clip,
TextSelection selection,
crl::time ms) const {
auto wasCache = false; auto wasCache = false;
auto nowCache = false; auto nowCache = false;
const auto groupPadding = groupedPadding(); const auto groupPadding = groupedPadding();
auto selection = context.selection;
const auto fullSelection = (selection == FullSelection); const auto fullSelection = (selection == FullSelection);
const auto textSelection = (_mode == Mode::Column) const auto textSelection = (_mode == Mode::Column)
&& !fullSelection && !fullSelection
&& !IsSubGroupSelection(selection); && !IsSubGroupSelection(selection);
for (auto i = 0, count = int(_parts.size()); i != count; ++i) { for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
const auto &part = _parts[i]; const auto &part = _parts[i];
const auto partSelection = fullSelection auto partContext = context;
partContext.selection = fullSelection
? FullSelection ? FullSelection
: textSelection : textSelection
? selection ? selection
@ -313,9 +311,7 @@ void GroupedMedia::draw(
} }
part.content->drawGrouped( part.content->drawGrouped(
p, p,
clip, partContext,
partSelection,
ms,
part.geometry.translated(0, groupPadding.top()), part.geometry.translated(0, groupPadding.top()),
part.sides, part.sides,
cornersFromSides(part.sides), cornersFromSides(part.sides),

View File

@ -32,11 +32,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> realParent) override;
void drawHighlight(Painter &p, int top) const override; void drawHighlight(Painter &p, int top) const override;
void draw( void draw(Painter &p, const PaintContext &context) const override;
Painter &p,
const QRect &clip,
TextSelection selection,
crl::time ms) const override;
PointState pointState(QPoint point) const override; PointState pointState(QPoint point) const override;
TextState textState( TextState textState(
QPoint point, QPoint point,

View File

@ -105,15 +105,11 @@ QSize UnwrappedMedia::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; return { newWidth, newHeight };
} }
void UnwrappedMedia::draw( void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const {
Painter &p,
const QRect &r,
TextSelection selection,
crl::time ms) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return; return;
} }
bool selected = (selection == FullSelection); bool selected = (context.selection == FullSelection);
const auto rightAligned = _parent->hasOutLayout() const auto rightAligned = _parent->hasOutLayout()
&& !_parent->delegate()->elementIsChatWide(); && !_parent->delegate()->elementIsChatWide();

View File

@ -57,7 +57,7 @@ public:
not_null<Element*> parent, not_null<Element*> parent,
std::unique_ptr<Content> content); std::unique_ptr<Content> 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; PointState pointState(QPoint point) const override;
TextState textState(QPoint point, StateRequest request) const override; TextState textState(QPoint point, StateRequest request) const override;

View File

@ -223,12 +223,12 @@ QSize Photo::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
ensureDataMediaCreated(); ensureDataMediaCreated();
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); _dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
auto selected = (selection == FullSelection); auto selected = (context.selection == FullSelection);
auto loaded = _dataMedia->loaded(); auto loaded = _dataMedia->loaded();
auto displayLoading = _data->displayLoading(); auto displayLoading = _data->displayLoading();
@ -330,7 +330,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
auto outbg = _parent->hasOutLayout(); auto outbg = _parent->hasOutLayout();
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); 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) { } else if (!inWebPage) {
auto fullRight = paintx + paintw; auto fullRight = paintx + paintw;
auto fullBottom = painty + painth; auto fullBottom = painty + painth;
@ -489,9 +489,7 @@ QSize Photo::sizeForGrouping(int width) const {
void Photo::drawGrouped( void Photo::drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
@ -503,7 +501,7 @@ void Photo::drawGrouped(
validateGroupedCache(geometry, corners, cacheKey, cache); validateGroupedCache(geometry, corners, cacheKey, cache);
const auto selected = (selection == FullSelection); const auto selected = (context.selection == FullSelection);
const auto loaded = _dataMedia->loaded(); const auto loaded = _dataMedia->loaded();
const auto displayLoading = _data->displayLoading(); const auto displayLoading = _data->displayLoading();
const auto bubble = _parent->hasBubble(); const auto bubble = _parent->hasBubble();

View File

@ -37,7 +37,7 @@ public:
int width); int width);
~Photo(); ~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; TextState textState(QPoint point, StateRequest request) const override;
[[nodiscard]] TextSelection adjustSelection( [[nodiscard]] TextSelection adjustSelection(
@ -62,9 +62,7 @@ public:
QSize sizeForGrouping(int width) const override; QSize sizeForGrouping(int width) const override;
void drawGrouped( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const PaintContext &context,
TextSelection selection,
crl::time ms,
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "ui/chat/message_bubble.h"
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/text/format_values.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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
checkSendingAnimation(); checkSendingAnimation();
_poll->checkResultsReload(_parent->data(), ms); _poll->checkResultsReload(_parent->data(), context.now);
const auto outbg = _parent->hasOutLayout(); const auto outbg = _parent->hasOutLayout();
const auto selected = (selection == FullSelection); const auto selected = (context.selection == FullSelection);
const auto &regular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); const auto &regular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg);
const auto padding = st::msgPadding; 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(); paintw -= padding.left() + padding.right();
p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); 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; tshift += _question.countHeight(paintw) + st::historyPollSubtitleSkip;
p.setPen(regular); p.setPen(regular);
_subtitle.drawLeftElided(p, padding.left(), tshift, paintw, width()); _subtitle.drawLeftElided(p, padding.left(), tshift, paintw, width());
paintRecentVoters(p, padding.left() + _subtitle.maxWidth(), tshift, selection); paintRecentVoters(p, padding.left() + _subtitle.maxWidth(), tshift, context);
paintCloseByTimer(p, padding.left() + paintw, tshift, selection); paintCloseByTimer(p, padding.left() + paintw, tshift, context.selection);
paintShowSolution(p, padding.left() + paintw, tshift, selection); paintShowSolution(p, padding.left() + paintw, tshift, context.selection);
tshift += st::msgDateFont->height + st::historyPollAnswersSkip; tshift += st::msgDateFont->height + st::historyPollAnswersSkip;
const auto progress = _answersAnimation const auto progress = _answersAnimation
@ -773,14 +774,14 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
tshift, tshift,
paintw, paintw,
width(), width(),
selection); context.selection);
tshift += height; tshift += height;
} }
if (!inlineFooter()) { if (!inlineFooter()) {
paintBottom(p, padding.left(), tshift, paintw, selection); paintBottom(p, padding.left(), tshift, paintw, context.selection);
} else if (!_totalVotesLabel.isEmpty()) { } else if (!_totalVotesLabel.isEmpty()) {
tshift += st::msgPadding.bottom(); 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, Painter &p,
int left, int left,
int top, int top,
TextSelection selection) const { const PaintContext &context) const {
const auto count = int(_recentVoters.size()); const auto count = int(_recentVoters.size());
if (!count) { if (!count) {
return; return;
@ -875,7 +876,7 @@ void Poll::paintRecentVoters(
auto y = top; auto y = top;
const auto size = st::historyPollRecentVoterSize; const auto size = st::historyPollRecentVoterSize;
const auto outbg = _parent->hasOutLayout(); const auto outbg = _parent->hasOutLayout();
const auto selected = (selection == FullSelection); const auto selected = (context.selection == FullSelection);
auto pen = (selected auto pen = (selected
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected)
: (outbg ? st::msgOutBg : st::msgInBg))->p; : (outbg ? st::msgOutBg : st::msgInBg))->p;
@ -888,10 +889,26 @@ void Poll::paintRecentVoters(
if (!was && recent.userpic) { if (!was && recent.userpic) {
created = true; created = true;
} }
p.setPen(pen); const auto paintContent = [&](Painter &p) {
p.setBrush(Qt::NoBrush); p.setPen(pen);
PainterHighQualityEnabler hq(p); p.setBrush(Qt::NoBrush);
p.drawEllipse(x, y, size, size); 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; x -= st::historyPollRecentVoterSkip;
} }
if (created) { if (created) {

View File

@ -30,7 +30,7 @@ public:
not_null<PollData*> poll); not_null<PollData*> poll);
~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; TextState textState(QPoint point, StateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
@ -107,7 +107,7 @@ private:
Painter &p, Painter &p,
int left, int left,
int top, int top,
TextSelection selection) const; const PaintContext &context) const;
void paintCloseByTimer( void paintCloseByTimer(
Painter &p, Painter &p,
int right, int right,
@ -213,6 +213,7 @@ private:
mutable std::unique_ptr<Ui::FireworksAnimation> _fireworksAnimation; mutable std::unique_ptr<Ui::FireworksAnimation> _fireworksAnimation;
Ui::Animations::Simple _wrongAnswerAnimation; Ui::Animations::Simple _wrongAnswerAnimation;
mutable QPoint _lastLinkPoint; mutable QPoint _lastLinkPoint;
mutable QImage _userpicCircleCache;
mutable std::unique_ptr<CloseInformation> _close; mutable std::unique_ptr<CloseInformation> _close;

View File

@ -150,7 +150,7 @@ QSize ThemeDocument::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; 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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
ensureDataMediaCreated(); ensureDataMediaCreated();
@ -158,7 +158,7 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr
if (_data) { if (_data) {
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); _dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
} }
auto selected = (selection == FullSelection); auto selected = (context.selection == FullSelection);
auto loaded = dataLoaded(); auto loaded = dataLoaded();
auto displayLoading = _data && _data->displayLoading(); auto displayLoading = _data && _data->displayLoading();

View File

@ -27,11 +27,7 @@ public:
const std::optional<Data::WallPaper> &params); const std::optional<Data::WallPaper> &params);
~ThemeDocument(); ~ThemeDocument();
void draw( void draw(Painter &p, const PaintContext &context) const override;
Painter &p,
const QRect &clip,
TextSelection selection,
crl::time ms) const override;
TextState textState(QPoint point, StateRequest request) const override; TextState textState(QPoint point, StateRequest request) const override;
DocumentData *getDocument() const override { DocumentData *getDocument() const override {

View File

@ -448,12 +448,12 @@ void WebPage::unloadHeavyPart() {
_photoMedia = nullptr; _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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintw = width(); auto paintw = width();
auto outbg = _parent->hasOutLayout(); 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 &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); 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()) { if (_siteName.hasSkipBlock()) {
endskip = _parent->skipBlockWidth(); 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; tshift += lineHeight;
} }
if (_titleLines) { if (_titleLines) {
@ -521,7 +521,7 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
if (_title.hasSkipBlock()) { if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth(); 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; tshift += _titleLines * lineHeight;
} }
if (_descriptionLines) { if (_descriptionLines) {
@ -531,10 +531,10 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
endskip = _parent->skipBlockWidth(); endskip = _parent->skipBlockWidth();
} }
if (_descriptionLines > 0) { 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; tshift += _descriptionLines * lineHeight;
} else { } 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); tshift += _description.countHeight(paintw);
} }
} }
@ -548,8 +548,9 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
p.translate(attachLeft, attachTop); p.translate(attachLeft, attachTop);
auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 }; auto attachContext = context.translated(-attachLeft, -attachTop);
_attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); attachContext.selection = selected ? FullSelection : TextSelection { 0, 0 };
_attach->draw(p, attachContext);
auto pixwidth = _attach->width(); auto pixwidth = _attach->width();
auto pixheight = _attach->height(); auto pixheight = _attach->height();

View File

@ -24,7 +24,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> 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; TextState textState(QPoint point, StateRequest request) const override;
bool hideMessageText() const override { bool hideMessageText() const override {

View File

@ -93,10 +93,11 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) {
const auto fill = rect.intersected(args.patternViewport); const auto fill = rect.intersected(args.patternViewport);
if (!fill.isEmpty()) { if (!fill.isEmpty()) {
p.setClipRect(fill); p.setClipRect(fill);
// #TODO bubbles optimizes PaintPatternBubblePart(
const auto to = args.patternViewport; p,
const auto from = QRect(QPoint(), pattern->pixmap.size()); args.patternViewport,
p.drawPixmap(to, pattern->pixmap, from); pattern->pixmap,
fill);
p.setClipping(false); p.setClipping(false);
} }
}; };
@ -112,26 +113,13 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) {
int y, int y,
const QImage &mask, const QImage &mask,
QImage &cache) { QImage &cache) {
Expects(mask.bytesPerLine() == mask.width() * 4); PaintPatternBubblePart(
Expects(mask.format() == QImage::Format_ARGB32_Premultiplied); p,
args.patternViewport,
if (cache.size() != mask.size()) { pattern->pixmap,
cache = QImage( QRect(QPoint(x, y), mask.size() / int(mask.devicePixelRatio())),
mask.size(), mask,
QImage::Format_ARGB32_Premultiplied); cache);
}
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);
}; };
const auto fillCorner = [&](int x, int y, int index) { const auto fillCorner = [&](int x, int y, int index) {
fillPattern( 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<void(Painter&)> 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 } // namespace Ui

View File

@ -49,4 +49,26 @@ struct ComplexBubble {
void PaintBubble(Painter &p, const SimpleBubble &args); void PaintBubble(Painter &p, const SimpleBubble &args);
void PaintBubble(Painter &p, const ComplexBubble &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<void(Painter&)> paintContent,
QImage &cache);
} // namespace Ui } // namespace Ui

View File

@ -1484,8 +1484,8 @@ HistoryView::PaintContext SessionController::bubblesContext(
//} //}
return { return {
.bubblesPattern = _bubblesBackgroundPattern.get(), .bubblesPattern = _bubblesBackgroundPattern.get(),
.viewport = viewport.translated(0, -args.initialShift), .viewport = viewport,
.clip = args.clip.translated(0, -args.initialShift), .clip = args.clip,
.now = crl::now(), .now = crl::now(),
}; };
} }

View File

@ -454,7 +454,6 @@ public:
int visibleAreaTopGlobal = 0; int visibleAreaTopGlobal = 0;
int visibleAreaWidth = 0; int visibleAreaWidth = 0;
QRect clip; QRect clip;
int initialShift = 0;
}; };
[[nodiscard]] HistoryView::PaintContext bubblesContext( [[nodiscard]] HistoryView::PaintContext bubblesContext(
BubblesContextArgs &&args); BubblesContextArgs &&args);