Implement new bubble rounding.

This commit is contained in:
John Preston 2022-09-30 09:58:15 +04:00
parent 405d8c327d
commit ba2f92906b
20 changed files with 308 additions and 223 deletions

View file

@ -319,6 +319,11 @@ InnerWidget::InnerWidget(
} }
}, lifetime()); }, lifetime());
controller->adaptive().chatWideValue(
) | rpl::start_with_next([=](bool wide) {
_isChatWide = wide;
}, lifetime());
updateEmptyText(); updateEmptyText();
requestAdmins(); requestAdmins();
@ -661,7 +666,7 @@ void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) {
} }
bool InnerWidget::elementIsChatWide() { bool InnerWidget::elementIsChatWide() {
return _controller->adaptive().isChatWide(); return _isChatWide;
} }
not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() { not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
@ -1029,10 +1034,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
p.setOpacity(opacity); p.setOpacity(opacity);
const auto dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top()); const auto dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top());
const auto width = view->width(); const auto width = view->width();
const auto chatWide =
_controller->adaptive().isChatWide();
if (const auto date = view->Get<HistoryView::DateBadge>()) { if (const auto date = view->Get<HistoryView::DateBadge>()) {
date->paint(p, context.st, dateY, width, chatWide); date->paint(p, context.st, dateY, width, _isChatWide);
} else { } else {
HistoryView::ServiceMessagePainter::PaintDate( HistoryView::ServiceMessagePainter::PaintDate(
p, p,
@ -1040,7 +1043,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
view->dateTime(), view->dateTime(),
dateY, dateY,
width, width,
chatWide); _isChatWide);
} }
} }
} }

View file

@ -288,6 +288,7 @@ private:
Element *_visibleTopItem = nullptr; Element *_visibleTopItem = nullptr;
int _visibleTopFromItem = 0; int _visibleTopFromItem = 0;
bool _isChatWide = false;
bool _scrollDateShown = false; bool _scrollDateShown = false;
Ui::Animations::Simple _scrollDateOpacity; Ui::Animations::Simple _scrollDateOpacity;
SingleQueuedInvokation _scrollDateCheck; SingleQueuedInvokation _scrollDateCheck;

View file

@ -922,7 +922,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
const auto stm = &st->messageStyle(false, false); const auto stm = &st->messageStyle(false, false);
if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) { if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) {
p.setTextPalette(stm->textPalette); p.setTextPalette(stm->textPalette);
Ui::FillRoundRect(p, _botAbout->rect, stm->msgBg, stm->msgBgCorners, &stm->msgShadow); Ui::FillRoundRect(p, _botAbout->rect, stm->msgBg, stm->msgBgCornersLarge, &stm->msgShadow);
auto top = _botAbout->rect.top() + st::msgPadding.top(); auto top = _botAbout->rect.top() + st::msgPadding.top();
if (!_history->peer->isRepliesChat()) { if (!_history->peer->isRepliesChat()) {

View file

@ -761,17 +761,6 @@ void Message::draw(Painter &p, const PaintContext &context) const {
&& (_fromNameVersion < item->displayFrom()->nameVersion())) { && (_fromNameVersion < item->displayFrom()->nameVersion())) {
fromNameUpdated(g.width()); fromNameUpdated(g.width());
} }
const auto skipTail = isAttachedToNext()
|| (media && media->skipBubbleTail())
|| (keyboard != nullptr)
|| (this->context() == Context::Replies
&& data()->isDiscussionPost());
const auto displayTail = skipTail
? RectPart::None
: (context.outbg && !delegate()->elementIsChatWide())
? RectPart::Right
: RectPart::Left;
Ui::PaintBubble( Ui::PaintBubble(
p, p,
Ui::ComplexBubble{ Ui::ComplexBubble{
@ -783,7 +772,7 @@ void Message::draw(Painter &p, const PaintContext &context) const {
.outerWidth = width(), .outerWidth = width(),
.selected = context.selected(), .selected = context.selected(),
.outbg = context.outbg, .outbg = context.outbg,
.tailSide = displayTail, .rounding = countBubbleRounding(),
}, },
.selection = mediaSelectionIntervals, .selection = mediaSelectionIntervals,
}); });
@ -1408,7 +1397,8 @@ void Message::toggleCommentsButtonRipple(bool pressed) {
const auto linkHeight = st::historyCommentsButtonHeight; const auto linkHeight = st::historyCommentsButtonHeight;
if (!_comments->ripple) { if (!_comments->ripple) {
const auto drawMask = [&](QPainter &p) { const auto drawMask = [&](QPainter &p) {
const auto radius = st::historyMessageRadius; // #TODO rounding
const auto radius = st::bubbleRadiusSmall;
p.drawRoundedRect( p.drawRoundedRect(
0, 0,
0, 0,
@ -2992,6 +2982,33 @@ QRect Message::countGeometry() const {
height() - contentTop - marginBottom()); height() - contentTop - marginBottom());
} }
Ui::BubbleRounding Message::countBubbleRounding() const {
const auto smallTop = isAttachedToPrevious();
const auto smallBottom = isAttachedToNext();
const auto media = this->media();
const auto keyboard = data()->inlineReplyKeyboard();
const auto skipTail = smallBottom
|| (media && media->skipBubbleTail())
|| (keyboard != nullptr)
|| (context() == Context::Replies && data()->isDiscussionPost());
const auto right = !delegate()->elementIsChatWide() && hasOutLayout();
using Corner = Ui::BubbleCornerRounding;
return {
.topLeft = (smallTop && !right) ? Corner::Small : Corner::Large,
.topRight = (smallTop && right) ? Corner::Small : Corner::Large,
.bottomLeft = ((smallBottom && !right)
? Corner::Small
: (!skipTail && !right)
? Corner::Tail
: Corner::Large),
.bottomRight = ((smallBottom && right)
? Corner::Small
: (!skipTail && right)
? Corner::Tail
: Corner::Large),
};
}
int Message::resizeContentGetHeight(int newWidth) { int Message::resizeContentGetHeight(int newWidth) {
if (isHidden()) { if (isHidden()) {
return marginTop() + marginBottom(); return marginTop() + marginBottom();

View file

@ -19,6 +19,10 @@ namespace Data {
struct ReactionId; struct ReactionId;
} // namespace Data } // namespace Data
namespace Ui {
struct BubbleRounding;
} // namespace Ui
namespace HistoryView { namespace HistoryView {
class ViewButton; class ViewButton;
@ -221,6 +225,7 @@ private:
void updateMediaInBubbleState(); void updateMediaInBubbleState();
QRect countGeometry() const; QRect countGeometry() const;
[[nodiscard]] Ui::BubbleRounding countBubbleRounding() const;
int resizeContentGetHeight(int newWidth); int resizeContentGetHeight(int newWidth);
QSize performCountOptimalSize() override; QSize performCountOptimalSize() override;

View file

@ -211,7 +211,8 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
} }
} else { } else {
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCorners); // #TODO rounding
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCornersSmall);
} }
const auto inWebPage = (_parent->media() != this); const auto inWebPage = (_parent->media() != this);
const auto roundRadius = inWebPage const auto roundRadius = inWebPage

View file

@ -357,7 +357,8 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
} }
} }
} else if (!unwrapped) { } else if (!unwrapped) {
Ui::FillRoundShadow(p, 0, 0, paintw, height(), sti->msgShadow, sti->msgShadowCorners); // #TODO rounding
Ui::FillRoundShadow(p, 0, 0, paintw, height(), sti->msgShadow, sti->msgShadowCornersSmall);
} }
auto usex = 0, usew = paintw; auto usex = 0, usew = paintw;

View file

@ -186,7 +186,8 @@ void Location::draw(Painter &p, const PaintContext &context) const {
} }
painth -= painty; painth -= painty;
} else { } else {
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCorners); // #TODO rounding
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCornersSmall);
} }
auto roundRadius = ImageRoundRadius::Large; auto roundRadius = ImageRoundRadius::Large;

View file

@ -267,7 +267,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
} }
} else { } else {
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCorners); // #TODO rounding
Ui::FillRoundShadow(p, 0, 0, paintw, painth, sti->msgShadow, sti->msgShadowCornersSmall);
} }
const auto inWebPage = (_parent->media() != this); const auto inWebPage = (_parent->media() != this);
const auto roundRadius = inWebPage const auto roundRadius = inWebPage

View file

@ -1569,7 +1569,8 @@ void Poll::toggleLinkRipple(bool pressed) {
const auto linkHeight = bottomButtonHeight(); const auto linkHeight = bottomButtonHeight();
if (!_linkRipple) { if (!_linkRipple) {
const auto drawMask = [&](QPainter &p) { const auto drawMask = [&](QPainter &p) {
const auto radius = st::historyMessageRadius; // #TODO rounding
const auto radius = st::bubbleRadiusSmall;
p.drawRoundedRect( p.drawRoundedRect(
0, 0,
0, 0,

View file

@ -66,7 +66,7 @@ void CreateMaskCorners() {
CornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); CornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
CornersMaskSmall[i].setDevicePixelRatio(style::DevicePixelRatio()); CornersMaskSmall[i].setDevicePixelRatio(style::DevicePixelRatio());
} }
mask = PrepareCorners(st::historyMessageRadius, QColor(255, 255, 255), nullptr); mask = PrepareCorners(st::roundRadiusLarge, QColor(255, 255, 255), nullptr);
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
CornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); CornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
CornersMaskLarge[i].setDevicePixelRatio(style::DevicePixelRatio()); CornersMaskLarge[i].setDevicePixelRatio(style::DevicePixelRatio());
@ -79,7 +79,7 @@ void CreatePaletteCorners() {
PrepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg); PrepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg);
PrepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg); PrepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg);
PrepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected); PrepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected);
PrepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg); PrepareCorners(ForwardCorners, st::roundRadiusLarge, st::historyForwardChooseBg);
PrepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg); PrepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg);
PrepareCorners(StickerHoverCorners, st::roundRadiusSmall, st::emojiPanHover); PrepareCorners(StickerHoverCorners, st::roundRadiusSmall, st::emojiPanHover);
PrepareCorners(BotKeyboardCorners, st::roundRadiusSmall, st::botKbBg); PrepareCorners(BotKeyboardCorners, st::roundRadiusSmall, st::botKbBg);
@ -192,7 +192,7 @@ CornersPixmaps PrepareCornerPixmaps(ImageRoundRadius radius, style::color bg, co
case ImageRoundRadius::Small: case ImageRoundRadius::Small:
return PrepareCornerPixmaps(st::roundRadiusSmall, bg, sh); return PrepareCornerPixmaps(st::roundRadiusSmall, bg, sh);
case ImageRoundRadius::Large: case ImageRoundRadius::Large:
return PrepareCornerPixmaps(st::historyMessageRadius, bg, sh); return PrepareCornerPixmaps(st::roundRadiusLarge, bg, sh);
} }
Unexpected("Image round radius in PrepareCornerPixmaps."); Unexpected("Image round radius in PrepareCornerPixmaps.");
} }

View file

@ -29,7 +29,7 @@ AlbumThumbnail::AlbumThumbnail(
Fn<void()> deleteCallback) Fn<void()> deleteCallback)
: _layout(layout) : _layout(layout)
, _fullPreview(file.preview) , _fullPreview(file.preview)
, _shrinkSize(int(std::ceil(st::historyMessageRadius / 1.4))) , _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4)))
, _isPhoto(file.type == PreparedFile::Type::Photo) , _isPhoto(file.type == PreparedFile::Type::Photo)
, _isVideo(file.type == PreparedFile::Type::Video) { , _isVideo(file.type == PreparedFile::Type::Video) {
Expects(!_fullPreview.isNull()); Expects(!_fullPreview.isNull());

View file

@ -562,9 +562,11 @@ historyInfoToast: Toast(defaultToast) {
iconPosition: point(13px, 13px); iconPosition: point(13px, 13px);
} }
bubbleRadiusSmall: roundRadiusLarge;
bubbleRadiusLarge: 16px;
historyPhotoLeft: 14px; historyPhotoLeft: 14px;
historyPhotoBubbleMinWidth: 200px; historyPhotoBubbleMinWidth: 200px;
historyMessageRadius: roundRadiusLarge;
historyBubbleTailInLeft: icon {{ "bubble_tail", msgInBg }}; historyBubbleTailInLeft: icon {{ "bubble_tail", msgInBg }};
historyBubbleTailInLeftSelected: icon {{ "bubble_tail", msgInBgSelected }}; historyBubbleTailInLeftSelected: icon {{ "bubble_tail", msgInBgSelected }};
historyBubbleTailOutLeft: icon {{ "bubble_tail", msgOutBg }}; historyBubbleTailOutLeft: icon {{ "bubble_tail", msgOutBg }};

View file

@ -480,12 +480,14 @@ void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
*static_cast<style::palette*>(this) = *palette; *static_cast<style::palette*>(this) = *palette;
style::internal::resetIcons(); style::internal::resetIcons();
for (auto &style : _messageStyles) { for (auto &style : _messageStyles) {
style.msgBgCorners = {}; style.msgBgCornersSmall = {};
style.msgBgCornersLarge = {};
} }
for (auto &style : _imageStyles) { for (auto &style : _imageStyles) {
style.msgDateImgBgCorners = {}; style.msgDateImgBgCorners = {};
style.msgServiceBgCorners = {}; style.msgServiceBgCorners = {};
style.msgShadowCorners = {}; style.msgShadowCornersSmall = {};
style.msgShadowCornersLarge = {};
} }
_serviceBgCornersNormal = {}; _serviceBgCornersNormal = {};
_serviceBgCornersInverted = {}; _serviceBgCornersInverted = {};
@ -533,8 +535,13 @@ const CornersPixmaps &ChatStyle::serviceBgCornersInverted() const {
const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const { const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
auto &result = messageStyleRaw(outbg, selected); auto &result = messageStyleRaw(outbg, selected);
EnsureCorners( EnsureCorners(
result.msgBgCorners, result.msgBgCornersSmall,
st::historyMessageRadius, st::bubbleRadiusSmall,
result.msgBg,
&result.msgShadow);
EnsureCorners(
result.msgBgCornersLarge,
st::bubbleRadiusLarge,
result.msgBg, result.msgBg,
&result.msgShadow); &result.msgShadow);
return result; return result;
@ -551,8 +558,12 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
st::dateRadius, st::dateRadius,
result.msgServiceBg); result.msgServiceBg);
EnsureCorners( EnsureCorners(
result.msgShadowCorners, result.msgShadowCornersSmall,
st::historyMessageRadius, st::bubbleRadiusSmall,
result.msgShadow);
EnsureCorners(
result.msgShadowCornersLarge,
st::bubbleRadiusLarge,
result.msgShadow); result.msgShadow);
return result; return result;
} }
@ -576,7 +587,7 @@ const CornersPixmaps &ChatStyle::msgSelectOverlayCornersSmall() const {
const CornersPixmaps &ChatStyle::msgSelectOverlayCornersLarge() const { const CornersPixmaps &ChatStyle::msgSelectOverlayCornersLarge() const {
EnsureCorners( EnsureCorners(
_msgSelectOverlayCornersLarge, _msgSelectOverlayCornersLarge,
st::historyMessageRadius, st::roundRadiusLarge,
msgSelectOverlay()); msgSelectOverlay());
return _msgSelectOverlayCornersLarge; return _msgSelectOverlayCornersLarge;
} }
@ -707,7 +718,8 @@ void FillComplexLocationRect(
ImageRoundRadius radius, ImageRoundRadius radius,
RectParts roundCorners) { RectParts roundCorners) {
const auto stm = &st->messageStyle(false, false); const auto stm = &st->messageStyle(false, false);
RectWithCorners(p, rect, stm->msgBg, stm->msgBgCorners, roundCorners); // #TODO rounding
RectWithCorners(p, rect, stm->msgBg, stm->msgBgCornersSmall, roundCorners);
} }
} // namespace Ui } // namespace Ui

View file

@ -26,7 +26,8 @@ class ChatStyle;
struct BubblePattern; struct BubblePattern;
struct MessageStyle { struct MessageStyle {
CornersPixmaps msgBgCorners; CornersPixmaps msgBgCornersSmall;
CornersPixmaps msgBgCornersLarge;
style::color msgBg; style::color msgBg;
style::color msgShadow; style::color msgShadow;
style::color msgServiceFg; style::color msgServiceFg;
@ -79,7 +80,8 @@ struct MessageStyle {
struct MessageImageStyle { struct MessageImageStyle {
CornersPixmaps msgDateImgBgCorners; CornersPixmaps msgDateImgBgCorners;
CornersPixmaps msgServiceBgCorners; CornersPixmaps msgServiceBgCorners;
CornersPixmaps msgShadowCorners; CornersPixmaps msgShadowCornersSmall;
CornersPixmaps msgShadowCornersLarge;
style::color msgServiceBg; style::color msgServiceBg;
style::color msgDateImgBg; style::color msgDateImgBg;
style::color msgShadow; style::color msgShadow;

View file

@ -15,79 +15,152 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui { namespace Ui {
namespace { namespace {
using Corner = BubbleCornerRounding;
template < template <
typename FillBg, // fillBg(QRect rect) typename FillBg, // fillBg(QRect rect)
typename FillSh, // fillSh(QRect rect) typename FillSh, // fillSh(QRect rect)
typename FillRounded, // fillRounded(QRect rect, RectParts parts) typename FillCorner, // fillCorner(int x, int y, int index, Corner size)
typename PaintTail> // paintTail(QPoint bottomPosition) -> tailWidth typename PaintTail> // paintTail(QPoint bottomPosition) -> tailWidth
void PaintBubbleGeneric( void PaintBubbleGeneric(
const SimpleBubble &args, const SimpleBubble &args,
FillBg &&fillBg, FillBg &&fillBg,
FillSh &&fillSh, FillSh &&fillSh,
FillRounded &&fillRounded, FillCorner &&fillCorner,
PaintTail &&paintTail) { PaintTail &&paintTail) {
auto parts = RectPart::None | RectPart::NoTopBottom; const auto topLeft = args.rounding.topLeft;
auto rect = args.geometry; const auto topRight = args.rounding.topRight;
if (args.skip & RectPart::Top) { const auto bottomWithTailLeft = args.rounding.bottomLeft;
if (args.skip & RectPart::Bottom) { const auto bottomWithTailRight = args.rounding.bottomRight;
fillBg(rect); if (topLeft == Corner::None
return; && topRight == Corner::None
&& bottomWithTailLeft == Corner::None
&& bottomWithTailRight == Corner::None) {
fillBg(args.geometry);
return;
}
const auto bottomLeft = (bottomWithTailLeft == Corner::Tail)
? Corner::None
: bottomWithTailLeft;
const auto bottomRight = (bottomWithTailRight == Corner::Tail)
? Corner::None
: bottomWithTailRight;
const auto rect = args.geometry;
const auto small = st::bubbleRadiusSmall;
const auto large = st::bubbleRadiusLarge;
const auto cornerSize = [&](Corner corner) {
return (corner == Corner::Large)
? large
: (corner == Corner::Small)
? small
: 0;
};
const auto verticalSkip = [&](Corner left, Corner right) {
return std::max(cornerSize(left), cornerSize(right));
};
const auto top = verticalSkip(topLeft, topRight);
const auto bottom = verticalSkip(bottomLeft, bottomRight);
if (top) {
const auto left = cornerSize(topLeft);
const auto right = cornerSize(topRight);
if (left) {
fillCorner(rect.left(), rect.top(), 0, topLeft);
if (const auto add = top - left) {
fillBg({ rect.left(), rect.top() + left, left, add });
}
}
if (const auto fill = rect.width() - left - right; fill > 0) {
fillBg({ rect.left() + left, rect.top(), fill, top });
}
if (right) {
fillCorner(
rect.left() + rect.width() - right,
rect.top(),
1,
topRight);
if (const auto add = top - right) {
fillBg({
rect.left() + rect.width() - right,
rect.top() + right,
right,
add,
});
}
} }
rect.setTop(rect.y() - st::historyMessageRadius);
} else {
parts |= RectPart::FullTop;
} }
const auto skipBottom = (args.skip & RectPart::Bottom); if (const auto h = rect.height() - top - bottom; h > 0) {
if (skipBottom) { fillBg({ rect.left(), rect.top() + top, rect.width(), h });
rect.setHeight(rect.height() + st::historyMessageRadius);
} else {
parts |= RectPart::Bottom;
} }
if (!skipBottom && args.tailSide == RectPart::Right) { if (bottom) {
parts |= RectPart::BottomLeft; const auto left = cornerSize(bottomLeft);
fillBg({ const auto right = cornerSize(bottomRight);
rect.x() + rect.width() - st::historyMessageRadius, if (left) {
rect.y() + rect.height() - st::historyMessageRadius, fillCorner(
st::historyMessageRadius, rect.left(),
st::historyMessageRadius }); rect.top() + rect.height() - left,
const auto tailWidth = paintTail({ 2,
rect.x() + rect.width(), bottomLeft);
rect.y() + rect.height() }); if (const auto add = bottom - left) {
fillSh({ fillBg({
rect.x() + rect.width() - st::historyMessageRadius, rect.left(),
rect.y() + rect.height(), rect.top() + rect.height() - bottom,
st::historyMessageRadius + tailWidth, left,
st::msgShadow }); add,
} else if (!skipBottom && args.tailSide == RectPart::Left) { });
parts |= RectPart::BottomRight; }
fillBg({ }
rect.x(), if (const auto fill = rect.width() - left - right; fill > 0) {
rect.y() + rect.height() - st::historyMessageRadius, fillBg({
st::historyMessageRadius, rect.left() + left,
st::historyMessageRadius }); rect.top() + rect.height() - bottom,
const auto tailWidth = paintTail({ fill,
rect.x(), bottom,
rect.y() + rect.height() }); });
fillSh({ }
rect.x() - tailWidth, if (right) {
rect.y() + rect.height(), fillCorner(
st::historyMessageRadius + tailWidth, rect.left() + rect.width() - right,
st::msgShadow }); rect.top() + rect.height() - right,
} else if (!skipBottom) { 3,
parts |= RectPart::FullBottom; bottomRight);
if (const auto add = bottom - right) {
fillBg({
rect.left() + rect.width() - right,
rect.top() + rect.height() - bottom,
right,
add,
});
}
}
}
const auto leftTail = (bottomWithTailLeft == Corner::Tail)
? paintTail({ rect.x(), rect.y() + rect.height() })
: 0;
const auto rightTail = (bottomWithTailRight == Corner::Tail)
? paintTail({ rect.x() + rect.width(), rect.y() + rect.height() })
: 0;
if (!args.shadowed) {
return;
}
const auto shLeft = rect.x() + cornerSize(bottomLeft) - leftTail;
const auto shWidth = rect.x()
+ rect.width()
- cornerSize(bottomRight)
+ rightTail
- shLeft;
if (shWidth > 0) {
fillSh({ shLeft, rect.y() + rect.height(), shWidth, st::msgShadow });
} }
fillRounded(rect, parts);
} }
void PaintPatternBubble(QPainter &p, const SimpleBubble &args) { void PaintPatternBubble(QPainter &p, const SimpleBubble &args) {
const auto opacity = args.st->msgOutBg()->c.alphaF(); const auto opacity = args.st->msgOutBg()->c.alphaF();
const auto shadowOpacity = opacity * args.st->msgOutShadow()->c.alphaF(); const auto shadowOpacity = opacity * args.st->msgOutShadow()->c.alphaF();
const auto pattern = args.pattern; const auto pattern = args.pattern;
const auto sh = !(args.skip & RectPart::Bottom); const auto &tail = (args.rounding.bottomRight == Corner::Tail)
const auto &tail = (args.tailSide == RectPart::Right)
? pattern->tailRight ? pattern->tailRight
: pattern->tailLeft; : pattern->tailLeft;
const auto tailShift = (args.tailSide == RectPart::Right const auto tailShift = (args.rounding.bottomRight == Corner::Tail
? QPoint(0, tail.height()) ? QPoint(0, tail.height())
: QPoint(tail.width(), tail.height())) / int(tail.devicePixelRatio()); : QPoint(tail.width(), tail.height())) / int(tail.devicePixelRatio());
const auto fillBg = [&](const QRect &rect) { const auto fillBg = [&](const QRect &rect) {
@ -101,11 +174,9 @@ void PaintPatternBubble(QPainter &p, const SimpleBubble &args) {
} }
}; };
const auto fillSh = [&](const QRect &rect) { const auto fillSh = [&](const QRect &rect) {
if (!(args.skip & RectPart::Bottom)) { p.setOpacity(shadowOpacity);
p.setOpacity(shadowOpacity); fillBg(rect);
fillBg(rect); p.setOpacity(opacity);
p.setOpacity(opacity);
}
}; };
const auto fillPattern = [&]( const auto fillPattern = [&](
int x, int x,
@ -120,121 +191,53 @@ void PaintPatternBubble(QPainter &p, const SimpleBubble &args) {
mask, mask,
cache); cache);
}; };
const auto fillCorner = [&](int x, int y, int index) { const auto fillCorner = [&](int x, int y, int index, Corner size) {
fillPattern( auto &corner = (size == Corner::Large)
x, ? pattern->cornersLarge[index]
y, : pattern->cornersSmall[index];
pattern->corners[index], auto &cache = (size == Corner::Large)
(index < 2) ? pattern->cornerTopCache : pattern->cornerBottomCache); ? (index < 2
}; ? pattern->cornerTopLargeCache
const auto fillRounded = [&](const QRect &rect, RectParts parts) { : pattern->cornerBottomLargeCache)
const auto x = rect.x(); : (index < 2
const auto y = rect.y(); ? pattern->cornerTopSmallCache
const auto w = rect.width(); : pattern->cornerBottomSmallCache);
const auto h = rect.height(); fillPattern(x, y, corner, cache);
const auto cornerWidth = pattern->corners[0].width()
/ style::DevicePixelRatio();
const auto cornerHeight = pattern->corners[0].height()
/ style::DevicePixelRatio();
if (w < 2 * cornerWidth || h < 2 * cornerHeight) {
return;
}
if (w > 2 * cornerWidth) {
if (parts & RectPart::Top) {
fillBg({
x + cornerWidth,
y,
w - 2 * cornerWidth,
cornerHeight });
}
if (parts & RectPart::Bottom) {
fillBg({
x + cornerWidth,
y + h - cornerHeight,
w - 2 * cornerWidth,
cornerHeight });
if (sh) {
fillSh({
x + cornerWidth,
y + h,
w - 2 * cornerWidth,
st::msgShadow });
}
}
}
if (h > 2 * cornerHeight) {
if ((parts & RectPart::NoTopBottom) == RectPart::NoTopBottom) {
fillBg({
x,
y + cornerHeight,
w,
h - 2 * cornerHeight });
} else {
if (parts & RectPart::Left) {
fillBg({
x,
y + cornerHeight,
cornerWidth,
h - 2 * cornerHeight });
}
if ((parts & RectPart::Center) && w > 2 * cornerWidth) {
fillBg({
x + cornerWidth,
y + cornerHeight,
w - 2 * cornerWidth,
h - 2 * cornerHeight });
}
if (parts & RectPart::Right) {
fillBg({
x + w - cornerWidth,
y + cornerHeight,
cornerWidth,
h - 2 * cornerHeight });
}
}
}
if (parts & RectPart::TopLeft) {
fillCorner(x, y, 0);
}
if (parts & RectPart::TopRight) {
fillCorner(x + w - cornerWidth, y, 1);
}
if (parts & RectPart::BottomLeft) {
fillCorner(x, y + h - cornerHeight, 2);
}
if (parts & RectPart::BottomRight) {
fillCorner(x + w - cornerWidth, y + h - cornerHeight, 3);
}
}; };
const auto paintTail = [&](QPoint bottomPosition) { const auto paintTail = [&](QPoint bottomPosition) {
const auto position = bottomPosition - tailShift; const auto position = bottomPosition - tailShift;
fillPattern(position.x(), position.y(), tail, pattern->tailCache); fillPattern(position.x(), position.y(), tail, pattern->tailCache);
return tail.width() / int(tail.devicePixelRatio()); return tail.width() / int(tail.devicePixelRatio());
}; };
p.setOpacity(opacity); p.setOpacity(opacity);
PaintBubbleGeneric(args, fillBg, fillSh, fillRounded, paintTail); PaintBubbleGeneric(args, fillBg, fillSh, fillCorner, paintTail);
p.setOpacity(1.); p.setOpacity(1.);
} }
void PaintSolidBubble(QPainter &p, const SimpleBubble &args) { void PaintSolidBubble(QPainter &p, const SimpleBubble &args) {
const auto &st = args.st->messageStyle(args.outbg, args.selected); const auto &st = args.st->messageStyle(args.outbg, args.selected);
const auto &bg = st.msgBg; const auto &bg = st.msgBg;
const auto sh = (args.skip & RectPart::Bottom) const auto sh = (args.rounding.bottomRight == Corner::None)
? nullptr ? nullptr
: &st.msgShadow; : &st.msgShadow;
const auto &tail = (args.tailSide == RectPart::Right) const auto &tail = (args.rounding.bottomRight == Corner::Tail)
? st.tailRight ? st.tailRight
: st.tailLeft; : st.tailLeft;
const auto tailShift = (args.tailSide == RectPart::Right) const auto tailShift = (args.rounding.bottomRight == Corner::Tail)
? QPoint(0, tail.height()) ? QPoint(0, tail.height())
: QPoint(tail.width(), tail.height()); : QPoint(tail.width(), tail.height());
PaintBubbleGeneric(args, [&](const QRect &rect) { PaintBubbleGeneric(args, [&](const QRect &rect) {
p.fillRect(rect, bg); p.fillRect(rect, bg);
}, [&](const QRect &rect) { }, [&](const QRect &rect) {
p.fillRect(rect, *sh); p.fillRect(rect, *sh);
}, [&](const QRect &rect, RectParts parts) { }, [&](int x, int y, int index, Corner size) {
Ui::FillRoundRect(p, rect, bg, st.msgBgCorners, sh, parts); auto &corners = (size == Corner::Large)
? st.msgBgCornersLarge
: st.msgBgCornersSmall;
const auto &corner = corners.p[index];
p.drawPixmap(x, y, corners.p[index]);
}, [&](const QPoint &bottomPosition) { }, [&](const QPoint &bottomPosition) {
tail.paint(p, bottomPosition - tailShift, args.outerWidth); tail.paint(p, bottomPosition - tailShift, args.outerWidth);
return tail.width(); return tail.width();
@ -246,7 +249,8 @@ void PaintSolidBubble(QPainter &p, const SimpleBubble &args) {
std::unique_ptr<BubblePattern> PrepareBubblePattern( std::unique_ptr<BubblePattern> PrepareBubblePattern(
not_null<const style::palette*> st) { not_null<const style::palette*> st) {
auto result = std::make_unique<Ui::BubblePattern>(); auto result = std::make_unique<Ui::BubblePattern>();
result->corners = Images::CornersMask(st::historyMessageRadius); result->cornersSmall = Images::CornersMask(st::bubbleRadiusSmall);
result->cornersLarge = Images::CornersMask(st::bubbleRadiusLarge);
const auto addShadow = [&](QImage &bottomCorner) { const auto addShadow = [&](QImage &bottomCorner) {
auto result = QImage( auto result = QImage(
bottomCorner.width(), bottomCorner.width(),
@ -264,13 +268,19 @@ std::unique_ptr<BubblePattern> PrepareBubblePattern(
bottomCorner = std::move(result); bottomCorner = std::move(result);
}; };
addShadow(result->corners[2]); addShadow(result->cornersSmall[2]);
addShadow(result->corners[3]); addShadow(result->cornersSmall[3]);
result->cornerTopCache = QImage( result->cornerTopSmallCache = QImage(
result->corners[0].size(), result->cornersSmall[0].size(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
result->cornerBottomCache = QImage( result->cornerTopLargeCache = QImage(
result->corners[2].size(), result->cornersLarge[0].size(),
QImage::Format_ARGB32_Premultiplied);
result->cornerBottomSmallCache = QImage(
result->cornersSmall[2].size(),
QImage::Format_ARGB32_Premultiplied);
result->cornerBottomLargeCache = QImage(
result->cornersLarge[2].size(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
return result; return result;
} }
@ -305,40 +315,52 @@ void PaintBubble(QPainter &p, const ComplexBubble &args) {
const auto width = rect.width(); const auto width = rect.width();
const auto top = rect.y(); const auto top = rect.y();
const auto bottom = top + rect.height(); const auto bottom = top + rect.height();
const auto paintOne = [&](QRect geometry, bool selected, RectParts skip) { const auto paintOne = [&](
QRect geometry,
bool selected,
bool fromTop,
bool tillBottom) {
auto simple = args.simple; auto simple = args.simple;
simple.geometry = geometry; simple.geometry = geometry;
simple.selected = selected; simple.selected = selected;
simple.skip = skip; if (!fromTop) {
simple.rounding.topLeft
= simple.rounding.topRight
= Corner::None;
}
if (!tillBottom) {
simple.rounding.bottomLeft
= simple.rounding.bottomRight
= Corner::None;
simple.shadowed = false;
}
PaintBubble(p, simple); PaintBubble(p, simple);
}; };
auto from = top; auto from = top;
for (const auto &selected : args.selection) { for (const auto &selected : args.selection) {
if (selected.top > from) { if (selected.top > from) {
const auto skip = RectPart::Bottom const auto fromTop = (from <= top);
| (from > top ? RectPart::Top : RectPart::None);
paintOne( paintOne(
QRect(left, from, width, selected.top - from), QRect(left, from, width, selected.top - from),
false, false,
skip); (from <= top),
false);
} }
const auto skip = ((selected.top > top) const auto fromTop = (selected.top <= top);
? RectPart::Top const auto tillBottom = (selected.top + selected.height >= bottom);
: RectPart::None)
| ((selected.top + selected.height < bottom)
? RectPart::Bottom
: RectPart::None);
paintOne( paintOne(
QRect(left, selected.top, width, selected.height), QRect(left, selected.top, width, selected.height),
true, true,
skip); (selected.top <= top),
(selected.top + selected.height >= bottom));
from = selected.top + selected.height; from = selected.top + selected.height;
} }
if (from < bottom) { if (from < bottom) {
paintOne( paintOne(
QRect(left, from, width, bottom - from), QRect(left, from, width, bottom - from),
false, false,
RectPart::Top); false,
true);
} }
} }

View file

@ -16,6 +16,20 @@ namespace Ui {
class ChatTheme; class ChatTheme;
class ChatStyle; class ChatStyle;
enum class BubbleCornerRounding : uchar {
Large,
Small,
None,
Tail,
};
struct BubbleRounding {
BubbleCornerRounding topLeft = BubbleCornerRounding();
BubbleCornerRounding topRight = BubbleCornerRounding();
BubbleCornerRounding bottomLeft = BubbleCornerRounding();
BubbleCornerRounding bottomRight = BubbleCornerRounding();
};
struct BubbleSelectionInterval { struct BubbleSelectionInterval {
int top = 0; int top = 0;
int height = 0; int height = 0;
@ -23,11 +37,14 @@ struct BubbleSelectionInterval {
struct BubblePattern { struct BubblePattern {
QPixmap pixmap; QPixmap pixmap;
std::array<QImage, 4> corners; std::array<QImage, 4> cornersSmall;
std::array<QImage, 4> cornersLarge;
QImage tailLeft; QImage tailLeft;
QImage tailRight; QImage tailRight;
mutable QImage cornerTopCache; mutable QImage cornerTopSmallCache;
mutable QImage cornerBottomCache; mutable QImage cornerTopLargeCache;
mutable QImage cornerBottomSmallCache;
mutable QImage cornerBottomLargeCache;
mutable QImage tailCache; mutable QImage tailCache;
}; };
@ -42,9 +59,9 @@ struct SimpleBubble {
QRect patternViewport; QRect patternViewport;
int outerWidth = 0; int outerWidth = 0;
bool selected = false; bool selected = false;
bool shadowed = true;
bool outbg = false; bool outbg = false;
RectPart tailSide = RectPart::None; BubbleRounding rounding;
RectParts skip = RectPart::None;
}; };
struct ComplexBubble { struct ComplexBubble {

View file

@ -771,9 +771,9 @@ void Generator::paintBubble(const Bubble &bubble) {
auto y = _historyBottom - st::msgMargin.bottom() - height; auto y = _historyBottom - st::msgMargin.bottom() - height;
auto bubbleTop = y; auto bubbleTop = y;
auto bubbleHeight = height; auto bubbleHeight = height;
if (isPhoto) { if (isPhoto) { // #TODO rounding
bubbleTop -= st::historyMessageRadius + 1; bubbleTop -= st::bubbleRadiusSmall + 1;
bubbleHeight += st::historyMessageRadius + 1; bubbleHeight += st::bubbleRadiusSmall + 1;
} }
auto left = bubble.outbg ? st::msgMargin.right() : st::msgMargin.left(); auto left = bubble.outbg ? st::msgMargin.right() : st::msgMargin.left();
@ -783,20 +783,20 @@ void Generator::paintBubble(const Bubble &bubble) {
x += left; x += left;
_p->setPen(Qt::NoPen); _p->setPen(Qt::NoPen);
auto tailclip = st::historyMessageRadius + 1; auto tailclip = st::bubbleRadiusSmall +1;
if (bubble.tail) { if (bubble.tail) {
if (bubble.outbg) { if (bubble.outbg) {
_p->setClipRegion(QRegion(_history) - QRect(x + bubble.width - tailclip, bubbleTop + bubbleHeight - tailclip, tailclip + st::historyMessageRadius, tailclip + st::historyMessageRadius)); _p->setClipRegion(QRegion(_history) - QRect(x + bubble.width - tailclip, bubbleTop + bubbleHeight - tailclip, tailclip + st::bubbleRadiusSmall, tailclip + st::bubbleRadiusSmall));
} else { } else {
_p->setClipRegion(QRegion(_history) - QRect(x - st::historyMessageRadius, bubbleTop + bubbleHeight - tailclip, tailclip + st::historyMessageRadius, tailclip + st::historyMessageRadius)); _p->setClipRegion(QRegion(_history) - QRect(x - st::bubbleRadiusSmall, bubbleTop + bubbleHeight - tailclip, tailclip + st::bubbleRadiusSmall, tailclip + st::bubbleRadiusSmall));
} }
} }
auto sh = bubble.outbg ? st::msgOutShadow[_palette] : st::msgInShadow[_palette]; auto sh = bubble.outbg ? st::msgOutShadow[_palette] : st::msgInShadow[_palette];
_p->setBrush(sh); _p->setBrush(sh);
_p->drawRoundedRect(x, bubbleTop + st::msgShadow, bubble.width, bubbleHeight, st::historyMessageRadius, st::historyMessageRadius); _p->drawRoundedRect(x, bubbleTop + st::msgShadow, bubble.width, bubbleHeight, st::bubbleRadiusSmall, st::bubbleRadiusSmall);
auto bg = bubble.outbg ? st::msgOutBg[_palette] : st::msgInBg[_palette]; auto bg = bubble.outbg ? st::msgOutBg[_palette] : st::msgInBg[_palette];
_p->setBrush(bg); _p->setBrush(bg);
_p->drawRoundedRect(x, bubbleTop, bubble.width, bubbleHeight, st::historyMessageRadius, st::historyMessageRadius); _p->drawRoundedRect(x, bubbleTop, bubble.width, bubbleHeight, st::bubbleRadiusSmall, st::bubbleRadiusSmall);
if (bubble.tail) { if (bubble.tail) {
_p->setClipRect(_history); _p->setClipRect(_history);
if (bubble.outbg) { if (bubble.outbg) {

View file

@ -242,7 +242,7 @@ void CloudListCheck::paintNotSupported(
const auto height = st::settingsThemePreviewSize.height(); const auto height = st::settingsThemePreviewSize.height();
const auto rect = QRect(0, 0, outerWidth, height); const auto rect = QRect(0, 0, outerWidth, height);
const auto radius = st::historyMessageRadius; const auto radius = st::roundRadiusLarge;
p.drawRoundedRect(rect, radius, radius); p.drawRoundedRect(rect, radius, radius);
st::settingsThemeNotSupportedIcon.paintInCenter(p, rect); st::settingsThemeNotSupportedIcon.paintInCenter(p, rect);
} }

View file

@ -83,7 +83,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_window.h" #include "styles/style_window.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "styles/style_layers.h" // st::boxLabel #include "styles/style_layers.h" // st::boxLabel
#include "styles/style_chat.h" // st::historyMessageRadius
namespace Window { namespace Window {
namespace { namespace {