mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 23:27:09 +02:00
Support correct rounding of file thumbnails.
This commit is contained in:
parent
dd52c53ec0
commit
f3662f4873
15 changed files with 479 additions and 152 deletions
|
@ -950,7 +950,7 @@ HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(
|
|||
HistoryMessageLogEntryOriginal::~HistoryMessageLogEntryOriginal() = default;
|
||||
|
||||
HistoryDocumentCaptioned::HistoryDocumentCaptioned()
|
||||
: _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
: caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
}
|
||||
|
||||
HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback(
|
||||
|
@ -964,14 +964,14 @@ HistoryDocumentVoicePlayback::HistoryDocumentVoicePlayback(
|
|||
|
||||
void HistoryDocumentVoice::ensurePlayback(
|
||||
const HistoryView::Document *that) const {
|
||||
if (!_playback) {
|
||||
_playback = std::make_unique<HistoryDocumentVoicePlayback>(that);
|
||||
if (!playback) {
|
||||
playback = std::make_unique<HistoryDocumentVoicePlayback>(that);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDocumentVoice::checkPlaybackFinished() const {
|
||||
if (_playback && !_playback->progressAnimation.animating()) {
|
||||
_playback.reset();
|
||||
if (playback && !playback->progressAnimation.animating()) {
|
||||
playback.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
|
||||
struct WebPageData;
|
||||
class VoiceSeekClickHandler;
|
||||
|
@ -441,24 +442,26 @@ struct HistoryMessageLogEntryOriginal
|
|||
|
||||
class FileClickHandler;
|
||||
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryView::Document> {
|
||||
std::shared_ptr<FileClickHandler> _linksavel;
|
||||
std::shared_ptr<FileClickHandler> _linkopenwithl;
|
||||
std::shared_ptr<FileClickHandler> _linkcancell;
|
||||
int _thumbw = 0;
|
||||
|
||||
mutable int _linkw = 0;
|
||||
mutable QString _link;
|
||||
std::shared_ptr<FileClickHandler> linksavel;
|
||||
std::shared_ptr<FileClickHandler> linkopenwithl;
|
||||
std::shared_ptr<FileClickHandler> linkcancell;
|
||||
mutable QImage thumbnail;
|
||||
mutable QString link;
|
||||
int thumbw = 0;
|
||||
mutable int linkw = 0;
|
||||
mutable Ui::BubbleRounding rounding;
|
||||
mutable bool blurred : 1 = false;
|
||||
};
|
||||
|
||||
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryView::Document> {
|
||||
HistoryDocumentCaptioned();
|
||||
|
||||
Ui::Text::String _caption;
|
||||
Ui::Text::String caption;
|
||||
};
|
||||
|
||||
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryView::Document> {
|
||||
QString _name;
|
||||
int _namew = 0;
|
||||
QString name;
|
||||
int namew = 0;
|
||||
};
|
||||
|
||||
struct HistoryDocumentVoicePlayback {
|
||||
|
@ -477,9 +480,9 @@ public:
|
|||
void ensurePlayback(const HistoryView::Document *interfaces) const;
|
||||
void checkPlaybackFinished() const;
|
||||
|
||||
mutable std::unique_ptr<HistoryDocumentVoicePlayback> _playback;
|
||||
std::shared_ptr<VoiceSeekClickHandler> _seekl;
|
||||
mutable int _lastDurationMs = 0;
|
||||
mutable std::unique_ptr<HistoryDocumentVoicePlayback> playback;
|
||||
std::shared_ptr<VoiceSeekClickHandler> seekl;
|
||||
mutable int lastDurationMs = 0;
|
||||
|
||||
[[nodiscard]] bool seeking() const;
|
||||
void startSeeking();
|
||||
|
|
|
@ -2813,11 +2813,17 @@ void Message::updateMediaInBubbleState() {
|
|||
? MediaInBubbleState::Bottom
|
||||
: MediaInBubbleState::None;
|
||||
entry->setInBubbleState(entryState);
|
||||
}
|
||||
if (!media) {
|
||||
if (!media) {
|
||||
entry->setBubbleRounding(countBubbleRounding());
|
||||
return;
|
||||
}
|
||||
} else if (!media) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto guard = gsl::finally([&] {
|
||||
media->setBubbleRounding(countBubbleRounding());
|
||||
});
|
||||
if (!drawBubble()) {
|
||||
media->setInBubbleState(MediaInBubbleState::None);
|
||||
return;
|
||||
|
@ -2985,7 +2991,7 @@ QRect Message::countGeometry() const {
|
|||
Ui::BubbleRounding Message::countBubbleRounding() const {
|
||||
const auto smallTop = isAttachedToPrevious();
|
||||
const auto smallBottom = isAttachedToNext();
|
||||
const auto media = this->media();
|
||||
const auto media = smallBottom ? nullptr : this->media();
|
||||
const auto keyboard = data()->inlineReplyKeyboard();
|
||||
const auto skipTail = smallBottom
|
||||
|| (media && media->skipBubbleTail())
|
||||
|
|
|
@ -185,7 +185,7 @@ Document::Document(
|
|||
setStatusSize(Ui::FileStatusSizeReady);
|
||||
|
||||
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);
|
||||
if (const auto thumbed = Get<HistoryDocumentThumbed>()) {
|
||||
thumbed->_linksavel = std::make_shared<DocumentSaveClickHandler>(
|
||||
thumbed->linksavel = std::make_shared<DocumentSaveClickHandler>(
|
||||
_data,
|
||||
_realParent->fullId());
|
||||
thumbed->_linkopenwithl = std::make_shared<DocumentOpenWithClickHandler>(
|
||||
thumbed->linkopenwithl = std::make_shared<DocumentOpenWithClickHandler>(
|
||||
_data,
|
||||
_realParent->fullId());
|
||||
thumbed->_linkcancell = std::make_shared<DocumentCancelClickHandler>(
|
||||
thumbed->linkcancell = std::make_shared<DocumentCancelClickHandler>(
|
||||
_data,
|
||||
crl::guard(this, [=](FullMsgId id) {
|
||||
_parent->delegate()->elementCancelUpload(id);
|
||||
|
@ -244,16 +244,16 @@ void Document::createComponents(bool caption) {
|
|||
_realParent->fullId());
|
||||
}
|
||||
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
||||
voice->_seekl = std::make_shared<VoiceSeekClickHandler>(
|
||||
voice->seekl = std::make_shared<VoiceSeekClickHandler>(
|
||||
_data,
|
||||
[](FullMsgId) {});
|
||||
}
|
||||
}
|
||||
|
||||
void Document::fillNamedFromData(HistoryDocumentNamed *named) {
|
||||
const auto nameString = named->_name = CleanTagSymbols(
|
||||
void Document::fillNamedFromData(not_null<HistoryDocumentNamed*> named) {
|
||||
const auto nameString = named->name = CleanTagSymbols(
|
||||
Ui::Text::FormatSongNameFor(_data).string());
|
||||
named->_namew = st::semiboldFont->width(nameString);
|
||||
named->namew = st::semiboldFont->width(nameString);
|
||||
}
|
||||
|
||||
QSize Document::countOptimalSize() {
|
||||
|
@ -263,8 +263,8 @@ QSize Document::countOptimalSize() {
|
|||
RemoveComponents(HistoryDocumentCaptioned::Bit());
|
||||
captioned = nullptr;
|
||||
}
|
||||
} else if (captioned && captioned->_caption.hasSkipBlock()) {
|
||||
captioned->_caption.updateSkipBlock(
|
||||
} else if (captioned && captioned->caption.hasSkipBlock()) {
|
||||
captioned->caption.updateSkipBlock(
|
||||
_parent->skipBlockWidth(),
|
||||
_parent->skipBlockHeight());
|
||||
}
|
||||
|
@ -329,9 +329,9 @@ QSize Document::countOptimalSize() {
|
|||
auto tw = style::ConvertScale(location.width());
|
||||
auto th = style::ConvertScale(location.height());
|
||||
if (tw > th) {
|
||||
thumbed->_thumbw = (tw * st.thumbSize) / th;
|
||||
thumbed->thumbw = (tw * st.thumbSize) / th;
|
||||
} else {
|
||||
thumbed->_thumbw = st.thumbSize;
|
||||
thumbed->thumbw = st.thumbSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,7 +347,7 @@ QSize Document::countOptimalSize() {
|
|||
}
|
||||
|
||||
if (auto named = Get<HistoryDocumentNamed>()) {
|
||||
accumulate_max(maxWidth, tleft + named->_namew + tright);
|
||||
accumulate_max(maxWidth, tleft + named->namew + tright);
|
||||
accumulate_min(maxWidth, st::msgMaxWidth);
|
||||
}
|
||||
if (voice && voice->transcribe) {
|
||||
|
@ -378,7 +378,7 @@ QSize Document::countOptimalSize() {
|
|||
auto captionw = maxWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
minHeight += captioned->_caption.countHeight(captionw);
|
||||
minHeight += captioned->caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
minHeight += st::msgPadding.bottom();
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ QSize Document::countCurrentSize(int newWidth) {
|
|||
}
|
||||
}
|
||||
if (captioned) {
|
||||
newHeight += captioned->_caption.countHeight(captionw);
|
||||
newHeight += captioned->caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
}
|
||||
|
@ -421,14 +421,21 @@ QSize Document::countCurrentSize(int newWidth) {
|
|||
}
|
||||
|
||||
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(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
int width,
|
||||
LayoutMode mode) const {
|
||||
LayoutMode mode,
|
||||
RectParts corners) const {
|
||||
if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
|
||||
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 radialOpacity = radial ? _animation->radial.opacity() : 1.;
|
||||
if (thumbed) {
|
||||
const auto inWebPage = (_parent->media() != this);
|
||||
const auto args = Images::PrepareArgs{
|
||||
.options = (inWebPage
|
||||
? 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);
|
||||
const auto rounding = thumbRounding(mode, corners);
|
||||
validateThumbnail(thumbed, st.thumbSize, rounding);
|
||||
p.drawImage(rthumb, thumbed->thumbnail);
|
||||
if (context.selected()) {
|
||||
const auto st = context.st;
|
||||
Ui::FillRoundRect(p, rthumb, st->msgSelectOverlay(), inWebPage
|
||||
? st->msgSelectOverlayCornersSmall()
|
||||
: st->msgSelectOverlayCornersLarge());
|
||||
fillThumbnailOverlay(p, rthumb, rounding, context);
|
||||
}
|
||||
|
||||
if (radial || (!loaded && !_data->loading()) || _data->waitingForAlbum()) {
|
||||
|
@ -524,14 +517,14 @@ void Document::draw(
|
|||
|
||||
if (_data->status != FileUploadFailed) {
|
||||
const auto &lnk = (_data->loading() || _data->uploading())
|
||||
? thumbed->_linkcancell
|
||||
? thumbed->linkcancell
|
||||
: dataLoaded()
|
||||
? thumbed->_linkopenwithl
|
||||
: thumbed->_linksavel;
|
||||
? thumbed->linkopenwithl
|
||||
: thumbed->linksavel;
|
||||
bool over = ClickHandler::showAsActive(lnk);
|
||||
p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont);
|
||||
p.setPen(stm->msgFileThumbLinkFg);
|
||||
p.drawTextLeft(nameleft, linktop, width, thumbed->_link, thumbed->_linkw);
|
||||
p.drawTextLeft(nameleft, linktop, width, thumbed->link, thumbed->linkw);
|
||||
}
|
||||
} else {
|
||||
p.setPen(Qt::NoPen);
|
||||
|
@ -625,21 +618,21 @@ void Document::draw(
|
|||
|
||||
const auto progress = [&] {
|
||||
if (!context.outbg
|
||||
&& !voice->_playback
|
||||
&& !voice->playback
|
||||
&& _realParent->hasUnreadMediaFlag()) {
|
||||
return 1.;
|
||||
}
|
||||
if (voice->seeking()) {
|
||||
return voice->seekingCurrent();
|
||||
} else if (voice->_playback) {
|
||||
return voice->_playback->progress.current();
|
||||
} else if (voice->playback) {
|
||||
return voice->playback->progress.current();
|
||||
}
|
||||
return 0.;
|
||||
}();
|
||||
if (voice->seeking()) {
|
||||
voiceStatusOverride = Ui::FormatPlayedText(
|
||||
base::SafeRound(progress * voice->_lastDurationMs) / 1000,
|
||||
voice->_lastDurationMs / 1000);
|
||||
base::SafeRound(progress * voice->lastDurationMs) / 1000,
|
||||
voice->lastDurationMs / 1000);
|
||||
}
|
||||
if (voice->transcribe) {
|
||||
const auto size = voice->transcribe->size();
|
||||
|
@ -660,10 +653,10 @@ void Document::draw(
|
|||
} else if (auto named = Get<HistoryDocumentNamed>()) {
|
||||
p.setFont(st::semiboldFont);
|
||||
p.setPen(stm->historyFileNameFg);
|
||||
if (namewidth < named->_namew) {
|
||||
p.drawTextLeft(nameleft, nametop, width, st::semiboldFont->elided(named->_name, namewidth, Qt::ElideMiddle));
|
||||
if (namewidth < named->namew) {
|
||||
p.drawTextLeft(nameleft, nametop, width, st::semiboldFont->elided(named->name, namewidth, Qt::ElideMiddle));
|
||||
} 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>()) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, captioned->_caption);
|
||||
captioned->_caption.draw(p, {
|
||||
_parent->prepareCustomEmojiPaint(p, context, captioned->caption);
|
||||
captioned->caption.draw(p, {
|
||||
.position = { st::msgPadding.left(), captiontop },
|
||||
.availableWidth = captionw,
|
||||
.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 {
|
||||
return (_dataMedia != nullptr);
|
||||
}
|
||||
|
@ -715,7 +793,7 @@ bool Document::hasHeavyPart() const {
|
|||
void Document::unloadHeavyPart() {
|
||||
_dataMedia = nullptr;
|
||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
captioned->_caption.unloadPersistentAnimation();
|
||||
captioned->caption.unloadPersistentAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -867,12 +945,12 @@ TextState Document::textState(
|
|||
}
|
||||
|
||||
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())
|
||||
? thumbed->_linkcancell
|
||||
? thumbed->linkcancell
|
||||
: dataLoaded()
|
||||
? thumbed->_linkopenwithl
|
||||
: thumbed->_linksavel;
|
||||
? thumbed->linkopenwithl
|
||||
: thumbed->linksavel;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -910,7 +988,7 @@ TextState Document::textState(
|
|||
if (!voice->seeking()) {
|
||||
voice->setSeekingStart((point.x() - nameleft) / float64(namewidth));
|
||||
}
|
||||
result.link = voice->_seekl;
|
||||
result.link = voice->seekl;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -939,7 +1017,7 @@ TextState Document::textState(
|
|||
bottom += st::mediaCaptionSkip;
|
||||
}
|
||||
if (point.y() >= bottom) {
|
||||
result = TextState(_parent, captioned->_caption.getState(
|
||||
result = TextState(_parent, captioned->caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), bottom),
|
||||
width - st::msgPadding.left() - st::msgPadding.right(),
|
||||
request.forText()));
|
||||
|
@ -947,7 +1025,7 @@ TextState Document::textState(
|
|||
return result;
|
||||
}
|
||||
auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
|
||||
painth -= captioned->_caption.countHeight(captionw);
|
||||
painth -= captioned->caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
painth -= st::msgPadding.bottom();
|
||||
}
|
||||
|
@ -996,7 +1074,7 @@ TextSelection Document::adjustSelection(
|
|||
transcribe = &voice->transcribeText;
|
||||
}
|
||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
caption = &captioned->_caption;
|
||||
caption = &captioned->caption;
|
||||
}
|
||||
const auto transcribeLength = transcribe ? transcribe->length() : 0;
|
||||
if (transcribe && selection.from < transcribeLength) {
|
||||
|
@ -1028,7 +1106,7 @@ uint16 Document::fullSelectionLength() const {
|
|||
result += voice->transcribeText.length();
|
||||
}
|
||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
result += captioned->_caption.length();
|
||||
result += captioned->caption.length();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1061,7 +1139,7 @@ TextForMimeData Document::selectedText(TextSelection selection) const {
|
|||
if (!result.empty()) {
|
||||
result.append("\n\n");
|
||||
}
|
||||
result.append(captioned->_caption.toTextForMimeData(selection));
|
||||
result.append(captioned->caption.toTextForMimeData(selection));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1079,17 +1157,17 @@ void Document::setStatusSize(int64 newSize, TimeId realDuration) const {
|
|||
File::setStatusSize(newSize, _data->size, duration, realDuration);
|
||||
if (auto thumbed = Get<HistoryDocumentThumbed>()) {
|
||||
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) {
|
||||
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) {
|
||||
thumbed->_link = tr::lng_media_download(tr::now).toUpper();
|
||||
thumbed->link = tr::lng_media_download(tr::now).toUpper();
|
||||
} else if (_statusSize >= 0) {
|
||||
thumbed->_link = tr::lng_media_cancel(tr::now).toUpper();
|
||||
thumbed->link = tr::lng_media_cancel(tr::now).toUpper();
|
||||
} 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())
|
||||
&& !::Media::Player::IsStoppedOrStopping(state.state)) {
|
||||
if (auto voice = Get<HistoryDocumentVoice>()) {
|
||||
bool was = (voice->_playback != nullptr);
|
||||
bool was = (voice->playback != nullptr);
|
||||
voice->ensurePlayback(this);
|
||||
if (!was || state.position != voice->_playback->position) {
|
||||
if (!was || state.position != voice->playback->position) {
|
||||
auto prg = state.length
|
||||
? std::clamp(
|
||||
float64(state.position) / state.length,
|
||||
0.,
|
||||
1.)
|
||||
: 0.;
|
||||
if (voice->_playback->position < state.position) {
|
||||
voice->_playback->progress.start(prg);
|
||||
if (voice->playback->position < state.position) {
|
||||
voice->playback->progress.start(prg);
|
||||
} else {
|
||||
voice->_playback->progress = anim::value(0., prg);
|
||||
voice->playback->progress = anim::value(0., prg);
|
||||
}
|
||||
voice->_playback->position = state.position;
|
||||
voice->_playback->progressAnimation.start();
|
||||
voice->playback->position = state.position;
|
||||
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);
|
||||
|
@ -1181,7 +1259,7 @@ QSize Document::sizeForGroupingOptimal(int maxWidth) const {
|
|||
auto captionw = maxWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
height += captioned->_caption.countHeight(captionw);
|
||||
height += captioned->caption.countHeight(captionw);
|
||||
}
|
||||
return { maxWidth, height };
|
||||
}
|
||||
|
@ -1194,7 +1272,7 @@ QSize Document::sizeForGrouping(int width) const {
|
|||
auto captionw = width
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
height += captioned->_caption.countHeight(captionw);
|
||||
height += captioned->caption.countHeight(captionw);
|
||||
}
|
||||
return { maxWidth(), height };
|
||||
}
|
||||
|
@ -1213,7 +1291,8 @@ void Document::drawGrouped(
|
|||
p,
|
||||
context.translated(-geometry.topLeft()),
|
||||
geometry.width(),
|
||||
LayoutMode::Grouped);
|
||||
LayoutMode::Grouped,
|
||||
corners);
|
||||
p.translate(-geometry.topLeft());
|
||||
}
|
||||
|
||||
|
@ -1235,14 +1314,14 @@ bool Document::voiceProgressAnimationCallback(crl::time now) {
|
|||
now += (2 * kAudioVoiceMsgUpdateView);
|
||||
}
|
||||
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
||||
if (voice->_playback) {
|
||||
const auto dt = (now - voice->_playback->progressAnimation.started())
|
||||
if (voice->playback) {
|
||||
const auto dt = (now - voice->playback->progressAnimation.started())
|
||||
/ float64(2 * kAudioVoiceMsgUpdateView);
|
||||
if (dt >= 1.) {
|
||||
voice->_playback->progressAnimation.stop();
|
||||
voice->_playback->progress.finish();
|
||||
voice->playback->progressAnimation.stop();
|
||||
voice->playback->progress.finish();
|
||||
} else {
|
||||
voice->_playback->progress.update(qMin(dt, 1.), anim::linear);
|
||||
voice->playback->progress.update(qMin(dt, 1.), anim::linear);
|
||||
}
|
||||
repaint();
|
||||
return (dt < 1.);
|
||||
|
@ -1253,7 +1332,7 @@ bool Document::voiceProgressAnimationCallback(crl::time now) {
|
|||
|
||||
void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
||||
if (auto voice = Get<HistoryDocumentVoice>()) {
|
||||
if (pressed && p == voice->_seekl && !voice->seeking()) {
|
||||
if (pressed && p == voice->seekl && !voice->seeking()) {
|
||||
voice->startSeeking();
|
||||
} else if (!pressed && voice->seeking()) {
|
||||
const auto type = AudioMsgId::Type::Voice;
|
||||
|
@ -1265,8 +1344,8 @@ void Document::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed
|
|||
currentProgress);
|
||||
|
||||
voice->ensurePlayback(this);
|
||||
voice->_playback->position = 0;
|
||||
voice->_playback->progress = anim::value(currentProgress, currentProgress);
|
||||
voice->playback->position = 0;
|
||||
voice->playback->progress = anim::value(currentProgress, currentProgress);
|
||||
}
|
||||
voice->stopSeeking();
|
||||
}
|
||||
|
@ -1279,14 +1358,14 @@ void Document::refreshParentId(not_null<HistoryItem*> realParent) {
|
|||
|
||||
const auto fullId = realParent->fullId();
|
||||
if (auto thumbed = Get<HistoryDocumentThumbed>()) {
|
||||
if (thumbed->_linksavel) {
|
||||
thumbed->_linksavel->setMessageId(fullId);
|
||||
thumbed->_linkcancell->setMessageId(fullId);
|
||||
if (thumbed->linksavel) {
|
||||
thumbed->linksavel->setMessageId(fullId);
|
||||
thumbed->linkcancell->setMessageId(fullId);
|
||||
}
|
||||
}
|
||||
if (auto voice = Get<HistoryDocumentVoice>()) {
|
||||
if (voice->_seekl) {
|
||||
voice->_seekl->setMessageId(fullId);
|
||||
if (voice->seekl) {
|
||||
voice->seekl->setMessageId(fullId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1298,7 +1377,7 @@ void Document::parentTextUpdated() {
|
|||
if (!caption.isEmpty()) {
|
||||
AddComponents(HistoryDocumentCaptioned::Bit());
|
||||
auto captioned = Get<HistoryDocumentCaptioned>();
|
||||
captioned->_caption = std::move(caption);
|
||||
captioned->caption = std::move(caption);
|
||||
} else {
|
||||
RemoveComponents(HistoryDocumentCaptioned::Bit());
|
||||
}
|
||||
|
@ -1307,7 +1386,7 @@ void Document::parentTextUpdated() {
|
|||
|
||||
TextWithEntities Document::getCaption() const {
|
||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
return captioned->_caption.toTextWithEntities();
|
||||
return captioned->caption.toTextWithEntities();
|
||||
}
|
||||
return TextWithEntities();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/runtime_composer.h"
|
||||
|
||||
struct HistoryDocumentNamed;
|
||||
struct HistoryDocumentThumbed;
|
||||
|
||||
namespace Data {
|
||||
class DocumentMedia;
|
||||
|
@ -108,7 +109,8 @@ private:
|
|||
Painter &p,
|
||||
const PaintContext &context,
|
||||
int width,
|
||||
LayoutMode mode) const;
|
||||
LayoutMode mode,
|
||||
RectParts corners) const;
|
||||
[[nodiscard]] TextState textState(
|
||||
QPoint point,
|
||||
QSize layout,
|
||||
|
@ -122,7 +124,20 @@ private:
|
|||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
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;
|
||||
bool updateStatusText() const; // returns showPause
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "history/view/history_view_object.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "ui/rect_part.h"
|
||||
|
||||
class History;
|
||||
|
@ -45,7 +46,7 @@ class Element;
|
|||
|
||||
using PaintContext = Ui::ChatPaintContext;
|
||||
|
||||
enum class MediaInBubbleState {
|
||||
enum class MediaInBubbleState : uchar {
|
||||
None,
|
||||
Top,
|
||||
Middle,
|
||||
|
@ -239,6 +240,12 @@ public:
|
|||
[[nodiscard]] MediaInBubbleState inBubbleState() const {
|
||||
return _inBubbleState;
|
||||
}
|
||||
void setBubbleRounding(Ui::BubbleRounding rounding) {
|
||||
_bubbleRounding = rounding;
|
||||
}
|
||||
[[nodiscard]] Ui::BubbleRounding bubbleRounding() const {
|
||||
return _bubbleRounding;
|
||||
}
|
||||
[[nodiscard]] bool isBubbleTop() const {
|
||||
return (_inBubbleState == MediaInBubbleState::Top)
|
||||
|| (_inBubbleState == MediaInBubbleState::None);
|
||||
|
@ -318,6 +325,7 @@ protected:
|
|||
|
||||
const not_null<Element*> _parent;
|
||||
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
||||
Ui::BubbleRounding _bubbleRounding;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ QSize GroupedMedia::countOptimalSize() {
|
|||
if (_mode == Mode::Column) {
|
||||
for (const auto &part : _parts) {
|
||||
const auto &media = part.content;
|
||||
media->setBubbleRounding(bubbleRounding());
|
||||
media->initDimensions();
|
||||
accumulate_max(maxWidth, media->maxWidth());
|
||||
}
|
||||
|
|
|
@ -181,7 +181,11 @@ void ThemeDocument::draw(Painter &p, const PaintContext &context) const {
|
|||
validateThumbnail();
|
||||
p.drawPixmap(rthumb.topLeft(), _thumbnail);
|
||||
if (context.selected()) {
|
||||
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners);
|
||||
Ui::FillComplexOverlayRect(
|
||||
p,
|
||||
rthumb,
|
||||
st->msgSelectOverlay(),
|
||||
st->msgSelectOverlayCornersSmall());
|
||||
}
|
||||
|
||||
if (_data) {
|
||||
|
|
|
@ -19,11 +19,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kCachedCornerRadiusCount = int(CachedCornerRadius::kCount);
|
||||
|
||||
std::vector<CornersPixmaps> Corners;
|
||||
base::flat_map<uint32, CornersPixmaps> CornersMap;
|
||||
QImage CornersMaskLarge[4], CornersMaskSmall[4];
|
||||
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) {
|
||||
int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio();
|
||||
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);
|
||||
}
|
||||
|
||||
[[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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
enum class CachedCornerRadius {
|
||||
Small,
|
||||
ThumbSmall,
|
||||
ThumbLarge,
|
||||
BubbleSmall,
|
||||
BubbleLarge,
|
||||
|
||||
kCount,
|
||||
};
|
||||
|
||||
[[nodiscard]] const std::array<QImage, 4> &CachedCornersMasks(
|
||||
CachedCornerRadius radius);
|
||||
|
||||
void StartCachedCorners();
|
||||
void FinishCachedCorners();
|
||||
|
||||
|
|
|
@ -771,31 +771,33 @@ HistoryFileLayout {
|
|||
thumbSkip: pixels;
|
||||
}
|
||||
|
||||
msgFileThumbRadiusSmall: 4px;
|
||||
msgFileThumbRadiusLarge: 12px;
|
||||
msgFileLayout: HistoryFileLayout {
|
||||
padding: margins(12px, 8px, 10px, 8px);
|
||||
nameTop: 16px;
|
||||
statusTop: 37px;
|
||||
nameTop: 12px;
|
||||
statusTop: 34px;
|
||||
thumbSize: 44px;
|
||||
thumbSkip: 11px;
|
||||
}
|
||||
msgFileThumbLayout: HistoryFileLayout {
|
||||
padding: margins(10px, 10px, 10px, 10px);
|
||||
nameTop: 12px;
|
||||
statusTop: 32px;
|
||||
linkTop: 60px;
|
||||
padding: margins(6px, 6px, 10px, 6px);
|
||||
nameTop: 11px;
|
||||
statusTop: 31px;
|
||||
linkTop: 55px;
|
||||
thumbSize: 72px;
|
||||
thumbSkip: 14px;
|
||||
}
|
||||
msgFileLayoutGrouped: HistoryFileLayout(msgFileLayout) {
|
||||
padding: margins(14px, 7px, 11px, 7px);
|
||||
nameTop: 13px;
|
||||
statusTop: 34px;
|
||||
padding: margins(12px, 5px, 10px, 5px);
|
||||
nameTop: 9px;
|
||||
statusTop: 31px;
|
||||
}
|
||||
msgFileThumbLayoutGrouped: HistoryFileLayout(msgFileThumbLayout) {
|
||||
padding: margins(10px, 7px, 14px, 7px);
|
||||
nameTop: 9px;
|
||||
statusTop: 29px;
|
||||
linkTop: 57px;
|
||||
padding: margins(6px, 3px, 10px, 3px);
|
||||
nameTop: 8px;
|
||||
statusTop: 28px;
|
||||
linkTop: 52px;
|
||||
}
|
||||
attachPreviewLayout: HistoryFileLayout {
|
||||
padding: margins(0px, 0px, 0px, 0px);
|
||||
|
|
|
@ -493,7 +493,6 @@ void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
|
|||
_serviceBgCornersInverted = {};
|
||||
_msgBotKbOverBgAddCorners = {};
|
||||
_msgSelectOverlayCornersSmall = {};
|
||||
_msgSelectOverlayCornersLarge = {};
|
||||
|
||||
for (auto &stm : _messageStyles) {
|
||||
const auto same = (stm.textPalette.linkFg->c == stm.historyTextFg->c);
|
||||
|
@ -584,12 +583,20 @@ const CornersPixmaps &ChatStyle::msgSelectOverlayCornersSmall() const {
|
|||
return _msgSelectOverlayCornersSmall;
|
||||
}
|
||||
|
||||
const CornersPixmaps &ChatStyle::msgSelectOverlayCornersLarge() const {
|
||||
const CornersPixmaps &ChatStyle::msgSelectOverlayCornersThumbSmall() const {
|
||||
EnsureCorners(
|
||||
_msgSelectOverlayCornersLarge,
|
||||
st::roundRadiusLarge,
|
||||
_msgSelectOverlayCornersThumbSmall,
|
||||
st::msgFileThumbRadiusSmall,
|
||||
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 {
|
||||
|
@ -691,6 +698,113 @@ void ChatStyle::make(
|
|||
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(
|
||||
QPainter &p,
|
||||
not_null<const ChatStyle*> st,
|
||||
|
@ -704,10 +818,11 @@ void FillComplexOverlayRect(
|
|||
p.setBrush(bg);
|
||||
p.drawEllipse(rect);
|
||||
} else {
|
||||
const auto &corners = (radius == ImageRoundRadius::Small)
|
||||
? st->msgSelectOverlayCornersSmall()
|
||||
: st->msgSelectOverlayCornersLarge();
|
||||
RectWithCorners(p, rect, bg, corners, roundCorners);
|
||||
// #TODO rounding
|
||||
//const auto &corners = (radius == ImageRoundRadius::Small)
|
||||
// ? st->msgSelectOverlayCornersSmall()
|
||||
// : st->msgSelectOverlayCornersLarge();
|
||||
//RectWithCorners(p, rect, bg, corners, roundCorners);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "ui/style/style_core_palette.h"
|
||||
#include "layout/layout_selection.h"
|
||||
#include "styles/style_basic.h"
|
||||
|
@ -192,7 +193,10 @@ public:
|
|||
|
||||
[[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCorners() 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 {
|
||||
return _historyPsaForwardPalette;
|
||||
|
@ -319,7 +323,8 @@ private:
|
|||
|
||||
mutable CornersPixmaps _msgBotKbOverBgAddCorners;
|
||||
mutable CornersPixmaps _msgSelectOverlayCornersSmall;
|
||||
mutable CornersPixmaps _msgSelectOverlayCornersLarge;
|
||||
mutable CornersPixmaps _msgSelectOverlayCornersThumbSmall;
|
||||
mutable CornersPixmaps _msgSelectOverlayCornersThumbLarge;
|
||||
|
||||
style::TextPalette _historyPsaForwardPalette;
|
||||
style::TextPalette _imgReplyTextPalette;
|
||||
|
@ -353,6 +358,12 @@ private:
|
|||
|
||||
};
|
||||
|
||||
void FillComplexOverlayRect(
|
||||
QPainter &p,
|
||||
QRect rect,
|
||||
const style::color &color,
|
||||
const CornersPixmaps &corners);
|
||||
|
||||
void FillComplexOverlayRect(
|
||||
QPainter &p,
|
||||
not_null<const ChatStyle*> st,
|
||||
|
|
|
@ -7,27 +7,74 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rect_part.h"
|
||||
|
||||
class Painter;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class ChatTheme;
|
||||
class ChatStyle;
|
||||
|
||||
enum class BubbleCornerRounding : uchar {
|
||||
Large,
|
||||
Small,
|
||||
None,
|
||||
Tail,
|
||||
Small,
|
||||
Large,
|
||||
};
|
||||
|
||||
struct BubbleRounding {
|
||||
BubbleCornerRounding topLeft = BubbleCornerRounding();
|
||||
BubbleCornerRounding topRight = BubbleCornerRounding();
|
||||
BubbleCornerRounding bottomLeft = BubbleCornerRounding();
|
||||
BubbleCornerRounding bottomRight = BubbleCornerRounding();
|
||||
BubbleCornerRounding topLeft : 2 = BubbleCornerRounding();
|
||||
BubbleCornerRounding topRight : 2 = BubbleCornerRounding();
|
||||
BubbleCornerRounding bottomLeft : 2 = 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 {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 80445f2bd3ebb377b990d2a471021e906e611240
|
||||
Subproject commit f49ec866c11fc887d9b16435f67f756d523a9b5b
|
Loading…
Add table
Reference in a new issue