Support correct rounding of file thumbnails.

This commit is contained in:
John Preston 2022-09-30 18:49:48 +04:00
parent dd52c53ec0
commit f3662f4873
15 changed files with 479 additions and 152 deletions

View file

@ -950,7 +950,7 @@ HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(
HistoryMessageLogEntryOriginal::~HistoryMessageLogEntryOriginal() = default; HistoryMessageLogEntryOriginal::~HistoryMessageLogEntryOriginal() = default;
HistoryDocumentCaptioned::HistoryDocumentCaptioned() HistoryDocumentCaptioned::HistoryDocumentCaptioned()
: _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) { : caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) {
} }
HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback( HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback(
@ -964,14 +964,14 @@ HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback(
void HistoryDocumentVoice::ensurePlayback( void HistoryDocumentVoice::ensurePlayback(
const HistoryView::Document *that) const { const HistoryView::Document *that) const {
if (!_playback) { if (!playback) {
_playback = std::make_unique<HistoryDocumentVoicePlayback>(that); playback = std::make_unique<HistoryDocumentVoicePlayback>(that);
} }
} }
void HistoryDocumentVoice::checkPlaybackFinished() const { void HistoryDocumentVoice::checkPlaybackFinished() const {
if (_playback && !_playback->progressAnimation.animating()) { if (playback && !playback->progressAnimation.animating()) {
_playback.reset(); playback.reset();
} }
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h" #include "history/history_item.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/chat/message_bubble.h"
struct WebPageData; struct WebPageData;
class VoiceSeekClickHandler; class VoiceSeekClickHandler;
@ -441,24 +442,26 @@ struct HistoryMessageLogEntryOriginal
class FileClickHandler; class FileClickHandler;
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryView::Document> { struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryView::Document> {
std::shared_ptr<FileClickHandler> _linksavel; std::shared_ptr<FileClickHandler> linksavel;
std::shared_ptr<FileClickHandler> _linkopenwithl; std::shared_ptr<FileClickHandler> linkopenwithl;
std::shared_ptr<FileClickHandler> _linkcancell; std::shared_ptr<FileClickHandler> linkcancell;
int _thumbw = 0; mutable QImage thumbnail;
mutable QString link;
mutable int _linkw = 0; int thumbw = 0;
mutable QString _link; mutable int linkw = 0;
mutable Ui::BubbleRounding rounding;
mutable bool blurred : 1 = false;
}; };
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryView::Document> { struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryView::Document> {
HistoryDocumentCaptioned(); HistoryDocumentCaptioned();
Ui::Text::String _caption; Ui::Text::String caption;
}; };
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryView::Document> { struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryView::Document> {
QString _name; QString name;
int _namew = 0; int namew = 0;
}; };
struct HistoryDocumentVoicePlayback { struct HistoryDocumentVoicePlayback {
@ -477,9 +480,9 @@ public:
void ensurePlayback(const HistoryView::Document *interfaces) const; void ensurePlayback(const HistoryView::Document *interfaces) const;
void checkPlaybackFinished() const; void checkPlaybackFinished() const;
mutable std::unique_ptr<HistoryDocumentVoicePlayback> _playback; mutable std::unique_ptr<HistoryDocumentVoicePlayback> playback;
std::shared_ptr<VoiceSeekClickHandler> _seekl; std::shared_ptr<VoiceSeekClickHandler> seekl;
mutable int _lastDurationMs = 0; mutable int lastDurationMs = 0;
[[nodiscard]] bool seeking() const; [[nodiscard]] bool seeking() const;
void startSeeking(); void startSeeking();

View file

@ -2813,11 +2813,17 @@ void Message::updateMediaInBubbleState() {
? MediaInBubbleState::Bottom ? MediaInBubbleState::Bottom
: MediaInBubbleState::None; : MediaInBubbleState::None;
entry->setInBubbleState(entryState); entry->setInBubbleState(entryState);
} if (!media) {
if (!media) { entry->setBubbleRounding(countBubbleRounding());
return;
}
} else if (!media) {
return; return;
} }
const auto guard = gsl::finally([&] {
media->setBubbleRounding(countBubbleRounding());
});
if (!drawBubble()) { if (!drawBubble()) {
media->setInBubbleState(MediaInBubbleState::None); media->setInBubbleState(MediaInBubbleState::None);
return; return;
@ -2985,7 +2991,7 @@ QRect Message::countGeometry() const {
Ui::BubbleRounding Message::countBubbleRounding() const { Ui::BubbleRounding Message::countBubbleRounding() const {
const auto smallTop = isAttachedToPrevious(); const auto smallTop = isAttachedToPrevious();
const auto smallBottom = isAttachedToNext(); const auto smallBottom = isAttachedToNext();
const auto media = this->media(); const auto media = smallBottom ? nullptr : this->media();
const auto keyboard = data()->inlineReplyKeyboard(); const auto keyboard = data()->inlineReplyKeyboard();
const auto skipTail = smallBottom const auto skipTail = smallBottom
|| (media && media->skipBubbleTail()) || (media && media->skipBubbleTail())

View file

@ -185,7 +185,7 @@ Document::Document(
setStatusSize(Ui::FileStatusSizeReady); setStatusSize(Ui::FileStatusSizeReady);
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->_caption = std::move(caption); captioned->caption = std::move(caption);
} }
} }
@ -230,13 +230,13 @@ void Document::createComponents(bool caption) {
} }
UpdateComponents(mask); UpdateComponents(mask);
if (const auto thumbed = Get<HistoryDocumentThumbed>()) { if (const auto thumbed = Get<HistoryDocumentThumbed>()) {
thumbed->_linksavel = std::make_shared<DocumentSaveClickHandler>( thumbed->linksavel = std::make_shared<DocumentSaveClickHandler>(
_data, _data,
_realParent->fullId()); _realParent->fullId());
thumbed->_linkopenwithl = std::make_shared<DocumentOpenWithClickHandler>( thumbed->linkopenwithl = std::make_shared<DocumentOpenWithClickHandler>(
_data, _data,
_realParent->fullId()); _realParent->fullId());
thumbed->_linkcancell = std::make_shared<DocumentCancelClickHandler>( thumbed->linkcancell = std::make_shared<DocumentCancelClickHandler>(
_data, _data,
crl::guard(this, [=](FullMsgId id) { crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementCancelUpload(id); _parent->delegate()->elementCancelUpload(id);
@ -244,16 +244,16 @@ void Document::createComponents(bool caption) {
_realParent->fullId()); _realParent->fullId());
} }
if (const auto voice = Get<HistoryDocumentVoice>()) { if (const auto voice = Get<HistoryDocumentVoice>()) {
voice->_seekl = std::make_shared<VoiceSeekClickHandler>( voice->seekl = std::make_shared<VoiceSeekClickHandler>(
_data, _data,
[](FullMsgId) {}); [](FullMsgId) {});
} }
} }
void Document::fillNamedFromData(HistoryDocumentNamed *named) { void Document::fillNamedFromData(not_null<HistoryDocumentNamed*> named) {
const auto nameString = named->_name = CleanTagSymbols( const auto nameString = named->name = CleanTagSymbols(
Ui::Text::FormatSongNameFor(_data).string()); Ui::Text::FormatSongNameFor(_data).string());
named->_namew = st::semiboldFont->width(nameString); named->namew = st::semiboldFont->width(nameString);
} }
QSize Document::countOptimalSize() { QSize Document::countOptimalSize() {
@ -263,8 +263,8 @@ QSize Document::countOptimalSize() {
RemoveComponents(HistoryDocumentCaptioned::Bit()); RemoveComponents(HistoryDocumentCaptioned::Bit());
captioned = nullptr; captioned = nullptr;
} }
} else if (captioned && captioned->_caption.hasSkipBlock()) { } else if (captioned && captioned->caption.hasSkipBlock()) {
captioned->_caption.updateSkipBlock( captioned->caption.updateSkipBlock(
_parent->skipBlockWidth(), _parent->skipBlockWidth(),
_parent->skipBlockHeight()); _parent->skipBlockHeight());
} }
@ -329,9 +329,9 @@ QSize Document::countOptimalSize() {
auto tw = style::ConvertScale(location.width()); auto tw = style::ConvertScale(location.width());
auto th = style::ConvertScale(location.height()); auto th = style::ConvertScale(location.height());
if (tw > th) { if (tw > th) {
thumbed->_thumbw = (tw * st.thumbSize) / th; thumbed->thumbw = (tw * st.thumbSize) / th;
} else { } else {
thumbed->_thumbw = st.thumbSize; thumbed->thumbw = st.thumbSize;
} }
} }
@ -347,7 +347,7 @@ QSize Document::countOptimalSize() {
} }
if (auto named = Get<HistoryDocumentNamed>()) { if (auto named = Get<HistoryDocumentNamed>()) {
accumulate_max(maxWidth, tleft + named->_namew + tright); accumulate_max(maxWidth, tleft + named->namew + tright);
accumulate_min(maxWidth, st::msgMaxWidth); accumulate_min(maxWidth, st::msgMaxWidth);
} }
if (voice && voice->transcribe) { if (voice && voice->transcribe) {
@ -378,7 +378,7 @@ QSize Document::countOptimalSize() {
auto captionw = maxWidth auto captionw = maxWidth
- st::msgPadding.left() - st::msgPadding.left()
- st::msgPadding.right(); - st::msgPadding.right();
minHeight += captioned->_caption.countHeight(captionw); minHeight += captioned->caption.countHeight(captionw);
if (isBubbleBottom()) { if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom(); minHeight += st::msgPadding.bottom();
} }
@ -411,7 +411,7 @@ QSize Document::countCurrentSize(int newWidth) {
} }
} }
if (captioned) { if (captioned) {
newHeight += captioned->_caption.countHeight(captionw); newHeight += captioned->caption.countHeight(captionw);
if (isBubbleBottom()) { if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom(); newHeight += st::msgPadding.bottom();
} }
@ -421,14 +421,21 @@ QSize Document::countCurrentSize(int newWidth) {
} }
void Document::draw(Painter &p, const PaintContext &context) const { void Document::draw(Painter &p, const PaintContext &context) const {
draw(p, context, width(), LayoutMode::Full); const auto corners = (isBubbleTop()
? (RectPart::TopLeft | RectPart::TopRight)
: RectParts())
| ((isBubbleBottom() && !Has<HistoryDocumentCaptioned>())
? (RectPart::BottomLeft | RectPart::BottomRight)
: RectParts());
draw(p, context, width(), LayoutMode::Full, corners);
} }
void Document::draw( void Document::draw(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
int width, int width,
LayoutMode mode) const { LayoutMode mode,
RectParts corners) const {
if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return; if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
ensureDataMediaCreated(); ensureDataMediaCreated();
@ -469,25 +476,11 @@ void Document::draw(
const auto inner = QRect(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize); const auto inner = QRect(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
const auto radialOpacity = radial ? _animation->radial.opacity() : 1.; const auto radialOpacity = radial ? _animation->radial.opacity() : 1.;
if (thumbed) { if (thumbed) {
const auto inWebPage = (_parent->media() != this); const auto rounding = thumbRounding(mode, corners);
const auto args = Images::PrepareArgs{ validateThumbnail(thumbed, st.thumbSize, rounding);
.options = (inWebPage p.drawImage(rthumb, thumbed->thumbnail);
? Images::Option::RoundSmall
: Images::Option::RoundLarge),
.outer = QSize(st.thumbSize, st.thumbSize),
};
QPixmap thumb;
if (const auto normal = _dataMedia->thumbnail()) {
thumb = normal->pixSingle(thumbed->_thumbw, args);
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
thumb = blurred->pixSingle(thumbed->_thumbw, args.blurred());
}
p.drawPixmap(rthumb.topLeft(), thumb);
if (context.selected()) { if (context.selected()) {
const auto st = context.st; fillThumbnailOverlay(p, rthumb, rounding, context);
Ui::FillRoundRect(p, rthumb, st->msgSelectOverlay(), inWebPage
? st->msgSelectOverlayCornersSmall()
: st->msgSelectOverlayCornersLarge());
} }
if (radial || (!loaded && !_data->loading()) || _data->waitingForAlbum()) { if (radial || (!loaded && !_data->loading()) || _data->waitingForAlbum()) {
@ -524,14 +517,14 @@ void Document::draw(
if (_data->status != FileUploadFailed) { if (_data->status != FileUploadFailed) {
const auto &lnk = (_data->loading() || _data->uploading()) const auto &lnk = (_data->loading() || _data->uploading())
? thumbed->_linkcancell ? thumbed->linkcancell
: dataLoaded() : dataLoaded()
? thumbed->_linkopenwithl ? thumbed->linkopenwithl
: thumbed->_linksavel; : thumbed->linksavel;
bool over = ClickHandler::showAsActive(lnk); bool over = ClickHandler::showAsActive(lnk);
p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont);
p.setPen(stm->msgFileThumbLinkFg); p.setPen(stm->msgFileThumbLinkFg);
p.drawTextLeft(nameleft, linktop, width, thumbed->_link, thumbed->_linkw); p.drawTextLeft(nameleft, linktop, width, thumbed->link, thumbed->linkw);
} }
} else { } else {
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
@ -625,21 +618,21 @@ void Document::draw(
const auto progress = [&] { const auto progress = [&] {
if (!context.outbg if (!context.outbg
&& !voice->_playback && !voice->playback
&& _realParent->hasUnreadMediaFlag()) { && _realParent->hasUnreadMediaFlag()) {
return 1.; return 1.;
} }
if (voice->seeking()) { if (voice->seeking()) {
return voice->seekingCurrent(); return voice->seekingCurrent();
} else if (voice->_playback) { } else if (voice->playback) {
return voice->_playback->progress.current(); return voice->playback->progress.current();
} }
return 0.; return 0.;
}(); }();
if (voice->seeking()) { if (voice->seeking()) {
voiceStatusOverride = Ui::FormatPlayedText( voiceStatusOverride = Ui::FormatPlayedText(
base::SafeRound(progress * voice->_lastDurationMs) / 1000, base::SafeRound(progress * voice->lastDurationMs) / 1000,
voice->_lastDurationMs / 1000); voice->lastDurationMs / 1000);
} }
if (voice->transcribe) { if (voice->transcribe) {
const auto size = voice->transcribe->size(); const auto size = voice->transcribe->size();
@ -660,10 +653,10 @@ void Document::draw(
} else if (auto named = Get<HistoryDocumentNamed>()) { } else if (auto named = Get<HistoryDocumentNamed>()) {
p.setFont(st::semiboldFont); p.setFont(st::semiboldFont);
p.setPen(stm->historyFileNameFg); p.setPen(stm->historyFileNameFg);
if (namewidth < named->_namew) { if (namewidth < named->namew) {
p.drawTextLeft(nameleft, nametop, width, st::semiboldFont->elided(named->_name, namewidth, Qt::ElideMiddle)); p.drawTextLeft(nameleft, nametop, width, st::semiboldFont->elided(named->name, namewidth, Qt::ElideMiddle));
} else { } else {
p.drawTextLeft(nameleft, nametop, width, named->_name, named->_namew); p.drawTextLeft(nameleft, nametop, width, named->name, named->namew);
} }
} }
@ -695,8 +688,8 @@ void Document::draw(
} }
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
p.setPen(stm->historyTextFg); p.setPen(stm->historyTextFg);
_parent->prepareCustomEmojiPaint(p, context, captioned->_caption); _parent->prepareCustomEmojiPaint(p, context, captioned->caption);
captioned->_caption.draw(p, { captioned->caption.draw(p, {
.position = { st::msgPadding.left(), captiontop }, .position = { st::msgPadding.left(), captiontop },
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
@ -708,6 +701,91 @@ void Document::draw(
} }
} }
Ui::BubbleRounding Document::thumbRounding(
LayoutMode mode,
RectParts corners) const {
auto result = bubbleRounding();
using Corner = Ui::BubbleCornerRounding;
if (mode != LayoutMode::Grouped && _parent->media() != this) {
return {}; // In a WebPage preview.
}
const auto adjust = [&](RectPart corner, Corner already) {
return (already == Corner::Large && (corners & corner))
? Corner::Large
: Corner::Small;
};
result.topLeft = adjust(RectPart::TopLeft, result.topLeft);
result.bottomLeft = adjust(RectPart::BottomLeft, result.bottomLeft);
result.topRight = result.bottomRight = Corner::Small;
return result;
}
void Document::validateThumbnail(
not_null<const HistoryDocumentThumbed*> thumbed,
int size,
Ui::BubbleRounding rounding) const {
const auto normal = _dataMedia->thumbnail();
const auto blurred = _dataMedia->thumbnailInline();
if (!normal && !blurred) {
return;
}
const auto outer = QSize(size, size);
if ((thumbed->thumbnail.size() == outer * style::DevicePixelRatio())
&& (thumbed->blurred == !normal)
&& (thumbed->rounding == rounding)) {
return;
}
const auto small = (rounding == Ui::BubbleRounding());
auto image = normal ? normal : blurred;
auto thumbnail = Images::Prepare(image->original(), thumbed->thumbw, {
.options = (normal ? Images::Option() : Images::Option::Blur)
| (small ? Images::Option::RoundSmall : Images::Option()),
.outer = outer,
});
if (!small) {
using Corner = Ui::BubbleCornerRounding;
using Radius = Ui::CachedCornerRadius;
auto corners = std::array<QImage, 4>();
const auto &small = Ui::CachedCornersMasks(Radius::ThumbSmall);
const auto &large = Ui::CachedCornersMasks(Radius::ThumbLarge);
for (auto i = 0; i != 4; ++i) {
switch (rounding[i]) {
case Corner::Small: corners[i] = small[i]; break;
case Corner::Large: corners[i] = large[i]; break;
}
}
thumbnail = Images::Round(std::move(thumbnail), corners);
}
thumbed->thumbnail = std::move(thumbnail);
thumbed->blurred = !normal;
thumbed->rounding = rounding;
}
void Document::fillThumbnailOverlay(
QPainter &p,
QRect rect,
Ui::BubbleRounding rounding,
const PaintContext &context) const {
using Corner = Ui::BubbleCornerRounding;
auto corners = Ui::CornersPixmaps();
const auto &st = context.st;
const auto set = [&](int index, const Ui::CornersPixmaps &from) {
corners.p[index] = from.p[index];
};
const auto lookup = [&](Corner corner) -> const Ui::CornersPixmaps & {
switch (corner) {
case Corner::None: return st->msgSelectOverlayCornersSmall();
case Corner::Small: return st->msgSelectOverlayCornersThumbSmall();
case Corner::Large: return st->msgSelectOverlayCornersThumbLarge();
}
Unexpected("Corner value in Document::fillThumbnailOverlay.");
};
for (auto i = 0; i != 4; ++i) {
corners.p[i] = lookup(rounding[i]).p[i];
}
Ui::FillComplexOverlayRect(p, rect, st->msgSelectOverlay(), corners);
}
bool Document::hasHeavyPart() const { bool Document::hasHeavyPart() const {
return (_dataMedia != nullptr); return (_dataMedia != nullptr);
} }
@ -715,7 +793,7 @@ bool Document::hasHeavyPart() const {
void Document::unloadHeavyPart() { void Document::unloadHeavyPart() {
_dataMedia = nullptr; _dataMedia = nullptr;
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
captioned->_caption.unloadPersistentAnimation(); captioned->caption.unloadPersistentAnimation();
} }
} }
@ -867,12 +945,12 @@ TextState Document::textState(
} }
if (_data->status != FileUploadFailed) { if (_data->status != FileUploadFailed) {
if (style::rtlrect(nameleft, linktop, thumbed->_linkw, st::semiboldFont->height, width).contains(point)) { if (style::rtlrect(nameleft, linktop, thumbed->linkw, st::semiboldFont->height, width).contains(point)) {
result.link = (_data->loading() || _data->uploading()) result.link = (_data->loading() || _data->uploading())
? thumbed->_linkcancell ? thumbed->linkcancell
: dataLoaded() : dataLoaded()
? thumbed->_linkopenwithl ? thumbed->linkopenwithl
: thumbed->_linksavel; : thumbed->linksavel;
return result; return result;
} }
} }
@ -910,7 +988,7 @@ TextState Document::textState(
if (!voice->seeking()) { if (!voice->seeking()) {
voice->setSeekingStart((point.x() - nameleft) / float64(namewidth)); voice->setSeekingStart((point.x() - nameleft) / float64(namewidth));
} }
result.link = voice->_seekl; result.link = voice->seekl;
return result; return result;
} }
} }
@ -939,7 +1017,7 @@ TextState Document::textState(
bottom += st::mediaCaptionSkip; bottom += st::mediaCaptionSkip;
} }
if (point.y() >= bottom) { if (point.y() >= bottom) {
result = TextState(_parent, captioned->_caption.getState( result = TextState(_parent, captioned->caption.getState(
point - QPoint(st::msgPadding.left(), bottom), point - QPoint(st::msgPadding.left(), bottom),
width - st::msgPadding.left() - st::msgPadding.right(), width - st::msgPadding.left() - st::msgPadding.right(),
request.forText())); request.forText()));
@ -947,7 +1025,7 @@ TextState Document::textState(
return result; return result;
} }
auto captionw = width - st::msgPadding.left() - st::msgPadding.right(); auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
painth -= captioned->_caption.countHeight(captionw); painth -= captioned->caption.countHeight(captionw);
if (isBubbleBottom()) { if (isBubbleBottom()) {
painth -= st::msgPadding.bottom(); painth -= st::msgPadding.bottom();
} }
@ -996,7 +1074,7 @@ TextSelection Document::adjustSelection(
transcribe = &voice->transcribeText; transcribe = &voice->transcribeText;
} }
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
caption = &captioned->_caption; caption = &captioned->caption;
} }
const auto transcribeLength = transcribe ? transcribe->length() : 0; const auto transcribeLength = transcribe ? transcribe->length() : 0;
if (transcribe && selection.from < transcribeLength) { if (transcribe && selection.from < transcribeLength) {
@ -1028,7 +1106,7 @@ uint16 Document::fullSelectionLength() const {
result += voice->transcribeText.length(); result += voice->transcribeText.length();
} }
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
result += captioned->_caption.length(); result += captioned->caption.length();
} }
return result; return result;
} }
@ -1061,7 +1139,7 @@ TextForMimeData Document::selectedText(TextSelection selection) const {
if (!result.empty()) { if (!result.empty()) {
result.append("\n\n"); result.append("\n\n");
} }
result.append(captioned->_caption.toTextForMimeData(selection)); result.append(captioned->caption.toTextForMimeData(selection));
} }
return result; return result;
} }
@ -1079,17 +1157,17 @@ void Document::setStatusSize(int64 newSize, TimeId realDuration) const {
File::setStatusSize(newSize, _data->size, duration, realDuration); File::setStatusSize(newSize, _data->size, duration, realDuration);
if (auto thumbed = Get<HistoryDocumentThumbed>()) { if (auto thumbed = Get<HistoryDocumentThumbed>()) {
if (_statusSize == Ui::FileStatusSizeReady) { if (_statusSize == Ui::FileStatusSizeReady) {
thumbed->_link = tr::lng_media_download(tr::now).toUpper(); thumbed->link = tr::lng_media_download(tr::now).toUpper();
} else if (_statusSize == Ui::FileStatusSizeLoaded) { } else if (_statusSize == Ui::FileStatusSizeLoaded) {
thumbed->_link = tr::lng_media_open_with(tr::now).toUpper(); thumbed->link = tr::lng_media_open_with(tr::now).toUpper();
} else if (_statusSize == Ui::FileStatusSizeFailed) { } else if (_statusSize == Ui::FileStatusSizeFailed) {
thumbed->_link = tr::lng_media_download(tr::now).toUpper(); thumbed->link = tr::lng_media_download(tr::now).toUpper();
} else if (_statusSize >= 0) { } else if (_statusSize >= 0) {
thumbed->_link = tr::lng_media_cancel(tr::now).toUpper(); thumbed->link = tr::lng_media_cancel(tr::now).toUpper();
} else { } else {
thumbed->_link = tr::lng_media_open_with(tr::now).toUpper(); thumbed->link = tr::lng_media_open_with(tr::now).toUpper();
} }
thumbed->_linkw = st::semiboldFont->width(thumbed->_link); thumbed->linkw = st::semiboldFont->width(thumbed->link);
} }
} }
@ -1114,24 +1192,24 @@ bool Document::updateStatusText() const {
if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()) if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId())
&& !::Media::Player::IsStoppedOrStopping(state.state)) { && !::Media::Player::IsStoppedOrStopping(state.state)) {
if (auto voice = Get<HistoryDocumentVoice>()) { if (auto voice = Get<HistoryDocumentVoice>()) {
bool was = (voice->_playback != nullptr); bool was = (voice->playback != nullptr);
voice->ensurePlayback(this); voice->ensurePlayback(this);
if (!was || state.position != voice->_playback->position) { if (!was || state.position != voice->playback->position) {
auto prg = state.length auto prg = state.length
? std::clamp( ? std::clamp(
float64(state.position) / state.length, float64(state.position) / state.length,
0., 0.,
1.) 1.)
: 0.; : 0.;
if (voice->_playback->position < state.position) { if (voice->playback->position < state.position) {
voice->_playback->progress.start(prg); voice->playback->progress.start(prg);
} else { } else {
voice->_playback->progress = anim::value(0., prg); voice->playback->progress = anim::value(0., prg);
} }
voice->_playback->position = state.position; voice->playback->position = state.position;
voice->_playback->progressAnimation.start(); voice->playback->progressAnimation.start();
} }
voice->_lastDurationMs = static_cast<int>((state.length * 1000LL) / state.frequency); // Bad :( voice->lastDurationMs = static_cast<int>((state.length * 1000LL) / state.frequency); // Bad :(
} }
statusSize = -1 - (state.position / state.frequency); statusSize = -1 - (state.position / state.frequency);
@ -1181,7 +1259,7 @@ QSize Document::sizeForGroupingOptimal(int maxWidth) const {
auto captionw = maxWidth auto captionw = maxWidth
- st::msgPadding.left() - st::msgPadding.left()
- st::msgPadding.right(); - st::msgPadding.right();
height += captioned->_caption.countHeight(captionw); height += captioned->caption.countHeight(captionw);
} }
return { maxWidth, height }; return { maxWidth, height };
} }
@ -1194,7 +1272,7 @@ QSize Document::sizeForGrouping(int width) const {
auto captionw = width auto captionw = width
- st::msgPadding.left() - st::msgPadding.left()
- st::msgPadding.right(); - st::msgPadding.right();
height += captioned->_caption.countHeight(captionw); height += captioned->caption.countHeight(captionw);
} }
return { maxWidth(), height }; return { maxWidth(), height };
} }
@ -1213,7 +1291,8 @@ void Document::drawGrouped(
p, p,
context.translated(-geometry.topLeft()), context.translated(-geometry.topLeft()),
geometry.width(), geometry.width(),
LayoutMode::Grouped); LayoutMode::Grouped,
corners);
p.translate(-geometry.topLeft()); p.translate(-geometry.topLeft());
} }
@ -1235,14 +1314,14 @@ bool Document::voiceProgressAnimationCallback(crl::time now) {
now += (2 * kAudioVoiceMsgUpdateView); now += (2 * kAudioVoiceMsgUpdateView);
} }
if (const auto voice = Get<HistoryDocumentVoice>()) { if (const auto voice = Get<HistoryDocumentVoice>()) {
if (voice->_playback) { if (voice->playback) {
const auto dt = (now - voice->_playback->progressAnimation.started()) const auto dt = (now - voice->playback->progressAnimation.started())
/ float64(2 * kAudioVoiceMsgUpdateView); / float64(2 * kAudioVoiceMsgUpdateView);
if (dt >= 1.) { if (dt >= 1.) {
voice->_playback->progressAnimation.stop(); voice->playback->progressAnimation.stop();
voice->_playback->progress.finish(); voice->playback->progress.finish();
} else { } else {
voice->_playback->progress.update(qMin(dt, 1.), anim::linear); voice->playback->progress.update(qMin(dt, 1.), anim::linear);
} }
repaint(); repaint();
return (dt < 1.); return (dt < 1.);
@ -1253,7 +1332,7 @@ bool Document::voiceProgressAnimationCallback(crl::time now) {
void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
if (auto voice = Get<HistoryDocumentVoice>()) { if (auto voice = Get<HistoryDocumentVoice>()) {
if (pressed && p == voice->_seekl && !voice->seeking()) { if (pressed && p == voice->seekl && !voice->seeking()) {
voice->startSeeking(); voice->startSeeking();
} else if (!pressed && voice->seeking()) { } else if (!pressed && voice->seeking()) {
const auto type = AudioMsgId::Type::Voice; const auto type = AudioMsgId::Type::Voice;
@ -1265,8 +1344,8 @@ void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed
currentProgress); currentProgress);
voice->ensurePlayback(this); voice->ensurePlayback(this);
voice->_playback->position = 0; voice->playback->position = 0;
voice->_playback->progress = anim::value(currentProgress, currentProgress); voice->playback->progress = anim::value(currentProgress, currentProgress);
} }
voice->stopSeeking(); voice->stopSeeking();
} }
@ -1279,14 +1358,14 @@ void Document::refreshParentId(not_null<HistoryItem*> realParent) {
const auto fullId = realParent->fullId(); const auto fullId = realParent->fullId();
if (auto thumbed = Get<HistoryDocumentThumbed>()) { if (auto thumbed = Get<HistoryDocumentThumbed>()) {
if (thumbed->_linksavel) { if (thumbed->linksavel) {
thumbed->_linksavel->setMessageId(fullId); thumbed->linksavel->setMessageId(fullId);
thumbed->_linkcancell->setMessageId(fullId); thumbed->linkcancell->setMessageId(fullId);
} }
} }
if (auto voice = Get<HistoryDocumentVoice>()) { if (auto voice = Get<HistoryDocumentVoice>()) {
if (voice->_seekl) { if (voice->seekl) {
voice->_seekl->setMessageId(fullId); voice->seekl->setMessageId(fullId);
} }
} }
} }
@ -1298,7 +1377,7 @@ void Document::parentTextUpdated() {
if (!caption.isEmpty()) { if (!caption.isEmpty()) {
AddComponents(HistoryDocumentCaptioned::Bit()); AddComponents(HistoryDocumentCaptioned::Bit());
auto captioned = Get<HistoryDocumentCaptioned>(); auto captioned = Get<HistoryDocumentCaptioned>();
captioned->_caption = std::move(caption); captioned->caption = std::move(caption);
} else { } else {
RemoveComponents(HistoryDocumentCaptioned::Bit()); RemoveComponents(HistoryDocumentCaptioned::Bit());
} }
@ -1307,7 +1386,7 @@ void Document::parentTextUpdated() {
TextWithEntities Document::getCaption() const { TextWithEntities Document::getCaption() const {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.toTextWithEntities(); return captioned->caption.toTextWithEntities();
} }
return TextWithEntities(); return TextWithEntities();
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/runtime_composer.h" #include "base/runtime_composer.h"
struct HistoryDocumentNamed; struct HistoryDocumentNamed;
struct HistoryDocumentThumbed;
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
@ -108,7 +109,8 @@ private:
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
int width, int width,
LayoutMode mode) const; LayoutMode mode,
RectParts corners) const;
[[nodiscard]] TextState textState( [[nodiscard]] TextState textState(
QPoint point, QPoint point,
QSize layout, QSize layout,
@ -122,7 +124,20 @@ private:
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
void createComponents(bool caption); void createComponents(bool caption);
void fillNamedFromData(HistoryDocumentNamed *named); void fillNamedFromData(not_null<HistoryDocumentNamed*> named);
[[nodiscard]] Ui::BubbleRounding thumbRounding(
LayoutMode mode,
RectParts corners) const;
void validateThumbnail(
not_null<const HistoryDocumentThumbed*> thumbed,
int size,
Ui::BubbleRounding rounding) const;
void fillThumbnailOverlay(
QPainter &p,
QRect rect,
Ui::BubbleRounding rounding,
const PaintContext &context) const;
void setStatusSize(int64 newSize, TimeId realDuration = 0) const; void setStatusSize(int64 newSize, TimeId realDuration = 0) const;
bool updateStatusText() const; // returns showPause bool updateStatusText() const; // returns showPause

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "history/view/history_view_object.h" #include "history/view/history_view_object.h"
#include "ui/chat/message_bubble.h"
#include "ui/rect_part.h" #include "ui/rect_part.h"
class History; class History;
@ -45,7 +46,7 @@ class Element;
using PaintContext = Ui::ChatPaintContext; using PaintContext = Ui::ChatPaintContext;
enum class MediaInBubbleState { enum class MediaInBubbleState : uchar {
None, None,
Top, Top,
Middle, Middle,
@ -239,6 +240,12 @@ public:
[[nodiscard]] MediaInBubbleState inBubbleState() const { [[nodiscard]] MediaInBubbleState inBubbleState() const {
return _inBubbleState; return _inBubbleState;
} }
void setBubbleRounding(Ui::BubbleRounding rounding) {
_bubbleRounding = rounding;
}
[[nodiscard]] Ui::BubbleRounding bubbleRounding() const {
return _bubbleRounding;
}
[[nodiscard]] bool isBubbleTop() const { [[nodiscard]] bool isBubbleTop() const {
return (_inBubbleState == MediaInBubbleState::Top) return (_inBubbleState == MediaInBubbleState::Top)
|| (_inBubbleState == MediaInBubbleState::None); || (_inBubbleState == MediaInBubbleState::None);
@ -318,6 +325,7 @@ protected:
const not_null<Element*> _parent; const not_null<Element*> _parent;
MediaInBubbleState _inBubbleState = MediaInBubbleState::None; MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
Ui::BubbleRounding _bubbleRounding;
}; };

View file

@ -117,6 +117,7 @@ QSize GroupedMedia::countOptimalSize() {
if (_mode == Mode::Column) { if (_mode == Mode::Column) {
for (const auto &part : _parts) { for (const auto &part : _parts) {
const auto &media = part.content; const auto &media = part.content;
media->setBubbleRounding(bubbleRounding());
media->initDimensions(); media->initDimensions();
accumulate_max(maxWidth, media->maxWidth()); accumulate_max(maxWidth, media->maxWidth());
} }

View file

@ -181,7 +181,11 @@ void ThemeDocument::draw(Painter &p, const PaintContext &context) const {
validateThumbnail(); validateThumbnail();
p.drawPixmap(rthumb.topLeft(), _thumbnail); p.drawPixmap(rthumb.topLeft(), _thumbnail);
if (context.selected()) { if (context.selected()) {
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners); Ui::FillComplexOverlayRect(
p,
rthumb,
st->msgSelectOverlay(),
st->msgSelectOverlayCornersSmall());
} }
if (_data) { if (_data) {

View file

@ -19,11 +19,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui { namespace Ui {
namespace { namespace {
constexpr auto kCachedCornerRadiusCount = int(CachedCornerRadius::kCount);
std::vector<CornersPixmaps> Corners; std::vector<CornersPixmaps> Corners;
base::flat_map<uint32, CornersPixmaps> CornersMap; base::flat_map<uint32, CornersPixmaps> CornersMap;
QImage CornersMaskLarge[4], CornersMaskSmall[4]; QImage CornersMaskLarge[4], CornersMaskSmall[4];
rpl::lifetime PaletteChangedLifetime; rpl::lifetime PaletteChangedLifetime;
std::array<std::array<QImage, 4>, kCachedCornerRadiusCount> CachedMasks;
[[nodiscard]] std::array<QImage, 4> PrepareCorners(int32 radius, const QBrush &brush, const style::color *shadow = nullptr) { [[nodiscard]] std::array<QImage, 4> PrepareCorners(int32 radius, const QBrush &brush, const style::color *shadow = nullptr) {
int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio(); int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio();
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied); QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied);
@ -215,4 +219,23 @@ void FillRoundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, style::color
FillRoundRect(p, x, y, w, h, bg, i->second, nullptr, parts); FillRoundRect(p, x, y, w, h, bg, i->second, nullptr, parts);
} }
[[nodiscard]] const std::array<QImage, 4> &CachedCornersMasks(
CachedCornerRadius radius) {
const auto index = static_cast<int>(radius);
Assert(index >= 0 && index < kCachedCornerRadiusCount);
if (CachedMasks[index][0].isNull()) {
using Radius = CachedCornerRadius;
const auto set = [](Radius key, int radius) {
CachedMasks[static_cast<int>(key)] = Images::CornersMask(radius);
};
set(Radius::Small, st::roundRadiusSmall);
set(Radius::ThumbSmall, st::msgFileThumbRadiusSmall);
set(Radius::ThumbLarge, st::msgFileThumbRadiusLarge);
set(Radius::BubbleSmall, st::bubbleRadiusSmall);
set(Radius::BubbleLarge, st::bubbleRadiusLarge);
}
return CachedMasks[index];
}
} // namespace Ui } // namespace Ui

View file

@ -66,6 +66,19 @@ inline void FillRoundShadow(QPainter &p, const QRect &rect, style::color shadow,
FillRoundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, corner, parts); FillRoundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, corner, parts);
} }
enum class CachedCornerRadius {
Small,
ThumbSmall,
ThumbLarge,
BubbleSmall,
BubbleLarge,
kCount,
};
[[nodiscard]] const std::array<QImage, 4> &CachedCornersMasks(
CachedCornerRadius radius);
void StartCachedCorners(); void StartCachedCorners();
void FinishCachedCorners(); void FinishCachedCorners();

View file

@ -771,31 +771,33 @@ HistoryFileLayout {
thumbSkip: pixels; thumbSkip: pixels;
} }
msgFileThumbRadiusSmall: 4px;
msgFileThumbRadiusLarge: 12px;
msgFileLayout: HistoryFileLayout { msgFileLayout: HistoryFileLayout {
padding: margins(12px, 8px, 10px, 8px); padding: margins(12px, 8px, 10px, 8px);
nameTop: 16px; nameTop: 12px;
statusTop: 37px; statusTop: 34px;
thumbSize: 44px; thumbSize: 44px;
thumbSkip: 11px; thumbSkip: 11px;
} }
msgFileThumbLayout: HistoryFileLayout { msgFileThumbLayout: HistoryFileLayout {
padding: margins(10px, 10px, 10px, 10px); padding: margins(6px, 6px, 10px, 6px);
nameTop: 12px; nameTop: 11px;
statusTop: 32px; statusTop: 31px;
linkTop: 60px; linkTop: 55px;
thumbSize: 72px; thumbSize: 72px;
thumbSkip: 14px; thumbSkip: 14px;
} }
msgFileLayoutGrouped: HistoryFileLayout(msgFileLayout) { msgFileLayoutGrouped: HistoryFileLayout(msgFileLayout) {
padding: margins(14px, 7px, 11px, 7px); padding: margins(12px, 5px, 10px, 5px);
nameTop: 13px; nameTop: 9px;
statusTop: 34px; statusTop: 31px;
} }
msgFileThumbLayoutGrouped: HistoryFileLayout(msgFileThumbLayout) { msgFileThumbLayoutGrouped: HistoryFileLayout(msgFileThumbLayout) {
padding: margins(10px, 7px, 14px, 7px); padding: margins(6px, 3px, 10px, 3px);
nameTop: 9px; nameTop: 8px;
statusTop: 29px; statusTop: 28px;
linkTop: 57px; linkTop: 52px;
} }
attachPreviewLayout: HistoryFileLayout { attachPreviewLayout: HistoryFileLayout {
padding: margins(0px, 0px, 0px, 0px); padding: margins(0px, 0px, 0px, 0px);

View file

@ -493,7 +493,6 @@ void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
_serviceBgCornersInverted = {}; _serviceBgCornersInverted = {};
_msgBotKbOverBgAddCorners = {}; _msgBotKbOverBgAddCorners = {};
_msgSelectOverlayCornersSmall = {}; _msgSelectOverlayCornersSmall = {};
_msgSelectOverlayCornersLarge = {};
for (auto &stm : _messageStyles) { for (auto &stm : _messageStyles) {
const auto same = (stm.textPalette.linkFg->c == stm.historyTextFg->c); const auto same = (stm.textPalette.linkFg->c == stm.historyTextFg->c);
@ -584,12 +583,20 @@ const CornersPixmaps &ChatStyle::msgSelectOverlayCornersSmall() const {
return _msgSelectOverlayCornersSmall; return _msgSelectOverlayCornersSmall;
} }
const CornersPixmaps &ChatStyle::msgSelectOverlayCornersLarge() const { const CornersPixmaps &ChatStyle::msgSelectOverlayCornersThumbSmall() const {
EnsureCorners( EnsureCorners(
_msgSelectOverlayCornersLarge, _msgSelectOverlayCornersThumbSmall,
st::roundRadiusLarge, st::msgFileThumbRadiusSmall,
msgSelectOverlay()); msgSelectOverlay());
return _msgSelectOverlayCornersLarge; return _msgSelectOverlayCornersThumbSmall;
}
const CornersPixmaps &ChatStyle::msgSelectOverlayCornersThumbLarge() const {
EnsureCorners(
_msgSelectOverlayCornersThumbLarge,
st::msgFileThumbRadiusLarge,
msgSelectOverlay());
return _msgSelectOverlayCornersThumbLarge;
} }
MessageStyle &ChatStyle::messageStyleRaw(bool outbg, bool selected) const { MessageStyle &ChatStyle::messageStyleRaw(bool outbg, bool selected) const {
@ -691,6 +698,113 @@ void ChatStyle::make(
make(imageSelected().*my, originalSelected); make(imageSelected().*my, originalSelected);
} }
void FillComplexOverlayRect(
QPainter &p,
QRect rect,
const style::color &color,
const CornersPixmaps &corners) {
constexpr auto kTopLeft = 0;
constexpr auto kTopRight = 1;
constexpr auto kBottomLeft = 2;
constexpr auto kBottomRight = 3;
const auto pix = corners.p;
const auto fillRect = [&](QRect rect) {
p.fillRect(rect, color);
};
if (pix[kTopLeft].isNull()
&& pix[kTopRight].isNull()
&& pix[kBottomLeft].isNull()
&& pix[kBottomRight].isNull()) {
fillRect(rect);
return;
}
const auto ratio = style::DevicePixelRatio();
const auto fillCorner = [&](int left, int top, int index) {
p.drawPixmap(left, top, pix[index]);
};
const auto cornerSize = [&](int index) {
const auto &p = pix[index];
return p.isNull() ? 0 : p.width() / ratio;
};
const auto verticalSkip = [&](int left, int right) {
return std::max(cornerSize(left), cornerSize(right));
};
const auto top = verticalSkip(kTopLeft, kTopRight);
const auto bottom = verticalSkip(kBottomLeft, kBottomRight);
if (top) {
const auto left = cornerSize(kTopLeft);
const auto right = cornerSize(kTopRight);
if (left) {
fillCorner(rect.left(), rect.top(), kTopLeft);
if (const auto add = top - left) {
fillRect({ rect.left(), rect.top() + left, left, add });
}
}
if (const auto fill = rect.width() - left - right; fill > 0) {
fillRect({ rect.left() + left, rect.top(), fill, top });
}
if (right) {
fillCorner(
rect.left() + rect.width() - right,
rect.top(),
kTopRight);
if (const auto add = top - right) {
fillRect({
rect.left() + rect.width() - right,
rect.top() + right,
right,
add,
});
}
}
}
if (const auto h = rect.height() - top - bottom; h > 0) {
fillRect({ rect.left(), rect.top() + top, rect.width(), h });
}
if (bottom) {
const auto left = cornerSize(kBottomLeft);
const auto right = cornerSize(kBottomRight);
if (left) {
fillCorner(
rect.left(),
rect.top() + rect.height() - left,
kBottomLeft);
if (const auto add = bottom - left) {
fillRect({
rect.left(),
rect.top() + rect.height() - bottom,
left,
add,
});
}
}
if (const auto fill = rect.width() - left - right; fill > 0) {
fillRect({
rect.left() + left,
rect.top() + rect.height() - bottom,
fill,
bottom,
});
}
if (right) {
fillCorner(
rect.left() + rect.width() - right,
rect.top() + rect.height() - right,
kBottomRight);
if (const auto add = bottom - right) {
fillRect({
rect.left() + rect.width() - right,
rect.top() + rect.height() - bottom,
right,
add,
});
}
}
}
}
void FillComplexOverlayRect( void FillComplexOverlayRect(
QPainter &p, QPainter &p,
not_null<const ChatStyle*> st, not_null<const ChatStyle*> st,
@ -704,10 +818,11 @@ void FillComplexOverlayRect(
p.setBrush(bg); p.setBrush(bg);
p.drawEllipse(rect); p.drawEllipse(rect);
} else { } else {
const auto &corners = (radius == ImageRoundRadius::Small) // #TODO rounding
? st->msgSelectOverlayCornersSmall() //const auto &corners = (radius == ImageRoundRadius::Small)
: st->msgSelectOverlayCornersLarge(); // ? st->msgSelectOverlayCornersSmall()
RectWithCorners(p, rect, bg, corners, roundCorners); // : st->msgSelectOverlayCornersLarge();
//RectWithCorners(p, rect, bg, corners, roundCorners);
} }
} }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/cached_round_corners.h" #include "ui/cached_round_corners.h"
#include "ui/chat/message_bubble.h"
#include "ui/style/style_core_palette.h" #include "ui/style/style_core_palette.h"
#include "layout/layout_selection.h" #include "layout/layout_selection.h"
#include "styles/style_basic.h" #include "styles/style_basic.h"
@ -192,7 +193,10 @@ public:
[[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCorners() const; [[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCorners() const;
[[nodiscard]] const CornersPixmaps &msgSelectOverlayCornersSmall() const; [[nodiscard]] const CornersPixmaps &msgSelectOverlayCornersSmall() const;
[[nodiscard]] const CornersPixmaps &msgSelectOverlayCornersLarge() const; [[nodiscard]] auto msgSelectOverlayCornersThumbSmall() const
-> const CornersPixmaps &;
[[nodiscard]] auto msgSelectOverlayCornersThumbLarge() const
-> const CornersPixmaps &;
[[nodiscard]] const style::TextPalette &historyPsaForwardPalette() const { [[nodiscard]] const style::TextPalette &historyPsaForwardPalette() const {
return _historyPsaForwardPalette; return _historyPsaForwardPalette;
@ -319,7 +323,8 @@ private:
mutable CornersPixmaps _msgBotKbOverBgAddCorners; mutable CornersPixmaps _msgBotKbOverBgAddCorners;
mutable CornersPixmaps _msgSelectOverlayCornersSmall; mutable CornersPixmaps _msgSelectOverlayCornersSmall;
mutable CornersPixmaps _msgSelectOverlayCornersLarge; mutable CornersPixmaps _msgSelectOverlayCornersThumbSmall;
mutable CornersPixmaps _msgSelectOverlayCornersThumbLarge;
style::TextPalette _historyPsaForwardPalette; style::TextPalette _historyPsaForwardPalette;
style::TextPalette _imgReplyTextPalette; style::TextPalette _imgReplyTextPalette;
@ -353,6 +358,12 @@ private:
}; };
void FillComplexOverlayRect(
QPainter &p,
QRect rect,
const style::color &color,
const CornersPixmaps &corners);
void FillComplexOverlayRect( void FillComplexOverlayRect(
QPainter &p, QPainter &p,
not_null<const ChatStyle*> st, not_null<const ChatStyle*> st,

View file

@ -7,27 +7,74 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "ui/rect_part.h"
class Painter;
namespace Ui { namespace Ui {
class ChatTheme; class ChatTheme;
class ChatStyle; class ChatStyle;
enum class BubbleCornerRounding : uchar { enum class BubbleCornerRounding : uchar {
Large,
Small,
None, None,
Tail, Tail,
Small,
Large,
}; };
struct BubbleRounding { struct BubbleRounding {
BubbleCornerRounding topLeft = BubbleCornerRounding(); BubbleCornerRounding topLeft : 2 = BubbleCornerRounding();
BubbleCornerRounding topRight = BubbleCornerRounding(); BubbleCornerRounding topRight : 2 = BubbleCornerRounding();
BubbleCornerRounding bottomLeft = BubbleCornerRounding(); BubbleCornerRounding bottomLeft : 2 = BubbleCornerRounding();
BubbleCornerRounding bottomRight = BubbleCornerRounding(); BubbleCornerRounding bottomRight : 2 = BubbleCornerRounding();
struct ConstProxy {
constexpr ConstProxy(
not_null<const BubbleRounding*> that,
int index) noexcept
: that(that)
, index(index) {
Expects(index >= 0 && index < 4);
}
constexpr operator BubbleCornerRounding() const noexcept {
switch (index) {
case 0: return that->topLeft;
case 1: return that->topRight;
case 2: return that->bottomLeft;
case 3: return that->bottomRight;
}
Unexpected("Index value in BubbleRounding::ConstProxy.");
}
not_null<const BubbleRounding*> that;
int index = 0;
};
struct Proxy : ConstProxy {
constexpr Proxy(not_null<BubbleRounding*> that, int index) noexcept
: ConstProxy(that, index) {
}
using ConstProxy::operator BubbleCornerRounding;
constexpr Proxy &operator=(BubbleCornerRounding value) noexcept {
const auto nonconst = const_cast<BubbleRounding*>(that.get());
switch (index) {
case 0: nonconst->topLeft = value; break;
case 1: nonconst->topRight = value; break;
case 2: nonconst->bottomLeft = value; break;
case 3: nonconst->bottomRight = value; break;
}
return *this;
}
};
[[nodiscard]] constexpr ConstProxy operator[](int index) const {
return { this, index };
}
[[nodiscard]] constexpr Proxy operator[](int index) {
return { this, index };
}
inline friend constexpr auto operator<=>(
BubbleRounding,
BubbleRounding) = default;
}; };
struct BubbleSelectionInterval { struct BubbleSelectionInterval {

@ -1 +1 @@
Subproject commit 80445f2bd3ebb377b990d2a471021e906e611240 Subproject commit f49ec866c11fc887d9b16435f67f756d523a9b5b