mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue