diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 1ae00575d..c27f3b7e9 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -319,6 +319,11 @@ InnerWidget::InnerWidget( } }, lifetime()); + controller->adaptive().chatWideValue( + ) | rpl::start_with_next([=](bool wide) { + _isChatWide = wide; + }, lifetime()); + updateEmptyText(); requestAdmins(); @@ -661,7 +666,7 @@ void InnerWidget::elementHandleViaClick(not_null bot) { } bool InnerWidget::elementIsChatWide() { - return _controller->adaptive().isChatWide(); + return _isChatWide; } not_null InnerWidget::elementPathShiftGradient() { @@ -1029,10 +1034,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) { p.setOpacity(opacity); const auto dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top()); const auto width = view->width(); - const auto chatWide = - _controller->adaptive().isChatWide(); if (const auto date = view->Get()) { - date->paint(p, context.st, dateY, width, chatWide); + date->paint(p, context.st, dateY, width, _isChatWide); } else { HistoryView::ServiceMessagePainter::PaintDate( p, @@ -1040,7 +1043,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { view->dateTime(), dateY, width, - chatWide); + _isChatWide); } } } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 04ecab2d8..bf43b683f 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -288,6 +288,7 @@ private: Element *_visibleTopItem = nullptr; int _visibleTopFromItem = 0; + bool _isChatWide = false; bool _scrollDateShown = false; Ui::Animations::Simple _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 6a3501264..9348c52d3 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -922,7 +922,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { const auto stm = &st->messageStyle(false, false); if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) { 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(); if (!_history->peer->isRepliesChat()) { diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index e5afecbb6..19c099145 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -761,17 +761,6 @@ void Message::draw(Painter &p, const PaintContext &context) const { && (_fromNameVersion < item->displayFrom()->nameVersion())) { 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( p, Ui::ComplexBubble{ @@ -783,7 +772,7 @@ void Message::draw(Painter &p, const PaintContext &context) const { .outerWidth = width(), .selected = context.selected(), .outbg = context.outbg, - .tailSide = displayTail, + .rounding = countBubbleRounding(), }, .selection = mediaSelectionIntervals, }); @@ -1408,7 +1397,8 @@ void Message::toggleCommentsButtonRipple(bool pressed) { const auto linkHeight = st::historyCommentsButtonHeight; if (!_comments->ripple) { const auto drawMask = [&](QPainter &p) { - const auto radius = st::historyMessageRadius; + // #TODO rounding + const auto radius = st::bubbleRadiusSmall; p.drawRoundedRect( 0, 0, @@ -2992,6 +2982,33 @@ QRect Message::countGeometry() const { 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) { if (isHidden()) { return marginTop() + marginBottom(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index c0d626974..28d11272a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -19,6 +19,10 @@ namespace Data { struct ReactionId; } // namespace Data +namespace Ui { +struct BubbleRounding; +} // namespace Ui + namespace HistoryView { class ViewButton; @@ -221,6 +225,7 @@ private: void updateMediaInBubbleState(); QRect countGeometry() const; + [[nodiscard]] Ui::BubbleRounding countBubbleRounding() const; int resizeContentGetHeight(int newWidth); QSize performCountOptimalSize() override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp index 25a103151..90f89fee3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp @@ -211,7 +211,8 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const { rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); } } 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 roundRadius = inWebPage diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 31038c96d..da77d8697 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -357,7 +357,8 @@ void Gif::draw(Painter &p, const PaintContext &context) const { } } } 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; diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.cpp b/Telegram/SourceFiles/history/view/media/history_view_location.cpp index 767339391..848e63e6b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_location.cpp @@ -186,7 +186,8 @@ void Location::draw(Painter &p, const PaintContext &context) const { } painth -= painty; } 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; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 3baa5aa76..130f4ed31 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -267,7 +267,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const { rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); } } 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 roundRadius = inWebPage diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index 64e51185b..ef91615d1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -1569,7 +1569,8 @@ void Poll::toggleLinkRipple(bool pressed) { const auto linkHeight = bottomButtonHeight(); if (!_linkRipple) { const auto drawMask = [&](QPainter &p) { - const auto radius = st::historyMessageRadius; + // #TODO rounding + const auto radius = st::bubbleRadiusSmall; p.drawRoundedRect( 0, 0, diff --git a/Telegram/SourceFiles/ui/cached_round_corners.cpp b/Telegram/SourceFiles/ui/cached_round_corners.cpp index bf7cc063f..81b10f412 100644 --- a/Telegram/SourceFiles/ui/cached_round_corners.cpp +++ b/Telegram/SourceFiles/ui/cached_round_corners.cpp @@ -66,7 +66,7 @@ void CreateMaskCorners() { CornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); 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) { CornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); CornersMaskLarge[i].setDevicePixelRatio(style::DevicePixelRatio()); @@ -79,7 +79,7 @@ void CreatePaletteCorners() { PrepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg); PrepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg); 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(StickerHoverCorners, st::roundRadiusSmall, st::emojiPanHover); PrepareCorners(BotKeyboardCorners, st::roundRadiusSmall, st::botKbBg); @@ -192,7 +192,7 @@ CornersPixmaps PrepareCornerPixmaps(ImageRoundRadius radius, style::color bg, co case ImageRoundRadius::Small: return PrepareCornerPixmaps(st::roundRadiusSmall, bg, sh); case ImageRoundRadius::Large: - return PrepareCornerPixmaps(st::historyMessageRadius, bg, sh); + return PrepareCornerPixmaps(st::roundRadiusLarge, bg, sh); } Unexpected("Image round radius in PrepareCornerPixmaps."); } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp index 9e2b18e3e..49be77289 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp @@ -29,7 +29,7 @@ AlbumThumbnail::AlbumThumbnail( Fn deleteCallback) : _layout(layout) , _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) , _isVideo(file.type == PreparedFile::Type::Video) { Expects(!_fullPreview.isNull()); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 21df4a0ca..4142c72d6 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -562,9 +562,11 @@ historyInfoToast: Toast(defaultToast) { iconPosition: point(13px, 13px); } +bubbleRadiusSmall: roundRadiusLarge; +bubbleRadiusLarge: 16px; + historyPhotoLeft: 14px; historyPhotoBubbleMinWidth: 200px; -historyMessageRadius: roundRadiusLarge; historyBubbleTailInLeft: icon {{ "bubble_tail", msgInBg }}; historyBubbleTailInLeftSelected: icon {{ "bubble_tail", msgInBgSelected }}; historyBubbleTailOutLeft: icon {{ "bubble_tail", msgOutBg }}; diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index dc179dd14..bdf8027e0 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -480,12 +480,14 @@ void ChatStyle::assignPalette(not_null palette) { *static_cast(this) = *palette; style::internal::resetIcons(); for (auto &style : _messageStyles) { - style.msgBgCorners = {}; + style.msgBgCornersSmall = {}; + style.msgBgCornersLarge = {}; } for (auto &style : _imageStyles) { style.msgDateImgBgCorners = {}; style.msgServiceBgCorners = {}; - style.msgShadowCorners = {}; + style.msgShadowCornersSmall = {}; + style.msgShadowCornersLarge = {}; } _serviceBgCornersNormal = {}; _serviceBgCornersInverted = {}; @@ -533,8 +535,13 @@ const CornersPixmaps &ChatStyle::serviceBgCornersInverted() const { const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const { auto &result = messageStyleRaw(outbg, selected); EnsureCorners( - result.msgBgCorners, - st::historyMessageRadius, + result.msgBgCornersSmall, + st::bubbleRadiusSmall, + result.msgBg, + &result.msgShadow); + EnsureCorners( + result.msgBgCornersLarge, + st::bubbleRadiusLarge, result.msgBg, &result.msgShadow); return result; @@ -551,8 +558,12 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const { st::dateRadius, result.msgServiceBg); EnsureCorners( - result.msgShadowCorners, - st::historyMessageRadius, + result.msgShadowCornersSmall, + st::bubbleRadiusSmall, + result.msgShadow); + EnsureCorners( + result.msgShadowCornersLarge, + st::bubbleRadiusLarge, result.msgShadow); return result; } @@ -576,7 +587,7 @@ const CornersPixmaps &ChatStyle::msgSelectOverlayCornersSmall() const { const CornersPixmaps &ChatStyle::msgSelectOverlayCornersLarge() const { EnsureCorners( _msgSelectOverlayCornersLarge, - st::historyMessageRadius, + st::roundRadiusLarge, msgSelectOverlay()); return _msgSelectOverlayCornersLarge; } @@ -707,7 +718,8 @@ void FillComplexLocationRect( ImageRoundRadius radius, RectParts roundCorners) { 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 diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 8acae297d..cc9b9cf8b 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -26,7 +26,8 @@ class ChatStyle; struct BubblePattern; struct MessageStyle { - CornersPixmaps msgBgCorners; + CornersPixmaps msgBgCornersSmall; + CornersPixmaps msgBgCornersLarge; style::color msgBg; style::color msgShadow; style::color msgServiceFg; @@ -79,7 +80,8 @@ struct MessageStyle { struct MessageImageStyle { CornersPixmaps msgDateImgBgCorners; CornersPixmaps msgServiceBgCorners; - CornersPixmaps msgShadowCorners; + CornersPixmaps msgShadowCornersSmall; + CornersPixmaps msgShadowCornersLarge; style::color msgServiceBg; style::color msgDateImgBg; style::color msgShadow; diff --git a/Telegram/SourceFiles/ui/chat/message_bubble.cpp b/Telegram/SourceFiles/ui/chat/message_bubble.cpp index 9996383e8..c02489086 100644 --- a/Telegram/SourceFiles/ui/chat/message_bubble.cpp +++ b/Telegram/SourceFiles/ui/chat/message_bubble.cpp @@ -15,79 +15,152 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { +using Corner = BubbleCornerRounding; + template < typename FillBg, // fillBg(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 void PaintBubbleGeneric( const SimpleBubble &args, FillBg &&fillBg, FillSh &&fillSh, - FillRounded &&fillRounded, + FillCorner &&fillCorner, PaintTail &&paintTail) { - auto parts = RectPart::None | RectPart::NoTopBottom; - auto rect = args.geometry; - if (args.skip & RectPart::Top) { - if (args.skip & RectPart::Bottom) { - fillBg(rect); - return; + const auto topLeft = args.rounding.topLeft; + const auto topRight = args.rounding.topRight; + const auto bottomWithTailLeft = args.rounding.bottomLeft; + const auto bottomWithTailRight = args.rounding.bottomRight; + if (topLeft == Corner::None + && 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 (skipBottom) { - rect.setHeight(rect.height() + st::historyMessageRadius); - } else { - parts |= RectPart::Bottom; + if (const auto h = rect.height() - top - bottom; h > 0) { + fillBg({ rect.left(), rect.top() + top, rect.width(), h }); } - if (!skipBottom && args.tailSide == RectPart::Right) { - parts |= RectPart::BottomLeft; - fillBg({ - rect.x() + rect.width() - st::historyMessageRadius, - rect.y() + rect.height() - st::historyMessageRadius, - st::historyMessageRadius, - st::historyMessageRadius }); - const auto tailWidth = paintTail({ - rect.x() + rect.width(), - rect.y() + rect.height() }); - fillSh({ - rect.x() + rect.width() - st::historyMessageRadius, - rect.y() + rect.height(), - st::historyMessageRadius + tailWidth, - st::msgShadow }); - } else if (!skipBottom && args.tailSide == RectPart::Left) { - parts |= RectPart::BottomRight; - fillBg({ - rect.x(), - rect.y() + rect.height() - st::historyMessageRadius, - st::historyMessageRadius, - st::historyMessageRadius }); - const auto tailWidth = paintTail({ - rect.x(), - rect.y() + rect.height() }); - fillSh({ - rect.x() - tailWidth, - rect.y() + rect.height(), - st::historyMessageRadius + tailWidth, - st::msgShadow }); - } else if (!skipBottom) { - parts |= RectPart::FullBottom; + if (bottom) { + const auto left = cornerSize(bottomLeft); + const auto right = cornerSize(bottomRight); + if (left) { + fillCorner( + rect.left(), + rect.top() + rect.height() - left, + 2, + bottomLeft); + if (const auto add = bottom - left) { + fillBg({ + rect.left(), + rect.top() + rect.height() - bottom, + left, + add, + }); + } + } + if (const auto fill = rect.width() - left - right; fill > 0) { + fillBg({ + rect.left() + left, + rect.top() + rect.height() - bottom, + fill, + bottom, + }); + } + if (right) { + fillCorner( + rect.left() + rect.width() - right, + rect.top() + rect.height() - right, + 3, + 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) { const auto opacity = args.st->msgOutBg()->c.alphaF(); const auto shadowOpacity = opacity * args.st->msgOutShadow()->c.alphaF(); const auto pattern = args.pattern; - const auto sh = !(args.skip & RectPart::Bottom); - const auto &tail = (args.tailSide == RectPart::Right) + const auto &tail = (args.rounding.bottomRight == Corner::Tail) ? pattern->tailRight : pattern->tailLeft; - const auto tailShift = (args.tailSide == RectPart::Right + const auto tailShift = (args.rounding.bottomRight == Corner::Tail ? QPoint(0, tail.height()) : QPoint(tail.width(), tail.height())) / int(tail.devicePixelRatio()); const auto fillBg = [&](const QRect &rect) { @@ -101,11 +174,9 @@ void PaintPatternBubble(QPainter &p, const SimpleBubble &args) { } }; const auto fillSh = [&](const QRect &rect) { - if (!(args.skip & RectPart::Bottom)) { - p.setOpacity(shadowOpacity); - fillBg(rect); - p.setOpacity(opacity); - } + p.setOpacity(shadowOpacity); + fillBg(rect); + p.setOpacity(opacity); }; const auto fillPattern = [&]( int x, @@ -120,121 +191,53 @@ void PaintPatternBubble(QPainter &p, const SimpleBubble &args) { mask, cache); }; - const auto fillCorner = [&](int x, int y, int index) { - fillPattern( - x, - y, - pattern->corners[index], - (index < 2) ? pattern->cornerTopCache : pattern->cornerBottomCache); - }; - const auto fillRounded = [&](const QRect &rect, RectParts parts) { - const auto x = rect.x(); - const auto y = rect.y(); - const auto w = rect.width(); - const auto h = rect.height(); - - 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 fillCorner = [&](int x, int y, int index, Corner size) { + auto &corner = (size == Corner::Large) + ? pattern->cornersLarge[index] + : pattern->cornersSmall[index]; + auto &cache = (size == Corner::Large) + ? (index < 2 + ? pattern->cornerTopLargeCache + : pattern->cornerBottomLargeCache) + : (index < 2 + ? pattern->cornerTopSmallCache + : pattern->cornerBottomSmallCache); + fillPattern(x, y, corner, cache); }; const auto paintTail = [&](QPoint bottomPosition) { const auto position = bottomPosition - tailShift; fillPattern(position.x(), position.y(), tail, pattern->tailCache); return tail.width() / int(tail.devicePixelRatio()); }; + p.setOpacity(opacity); - PaintBubbleGeneric(args, fillBg, fillSh, fillRounded, paintTail); + PaintBubbleGeneric(args, fillBg, fillSh, fillCorner, paintTail); p.setOpacity(1.); } void PaintSolidBubble(QPainter &p, const SimpleBubble &args) { const auto &st = args.st->messageStyle(args.outbg, args.selected); const auto &bg = st.msgBg; - const auto sh = (args.skip & RectPart::Bottom) + const auto sh = (args.rounding.bottomRight == Corner::None) ? nullptr : &st.msgShadow; - const auto &tail = (args.tailSide == RectPart::Right) + const auto &tail = (args.rounding.bottomRight == Corner::Tail) ? st.tailRight : st.tailLeft; - const auto tailShift = (args.tailSide == RectPart::Right) + const auto tailShift = (args.rounding.bottomRight == Corner::Tail) ? QPoint(0, tail.height()) : QPoint(tail.width(), tail.height()); + PaintBubbleGeneric(args, [&](const QRect &rect) { p.fillRect(rect, bg); }, [&](const QRect &rect) { p.fillRect(rect, *sh); - }, [&](const QRect &rect, RectParts parts) { - Ui::FillRoundRect(p, rect, bg, st.msgBgCorners, sh, parts); + }, [&](int x, int y, int index, Corner size) { + 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) { tail.paint(p, bottomPosition - tailShift, args.outerWidth); return tail.width(); @@ -246,7 +249,8 @@ void PaintSolidBubble(QPainter &p, const SimpleBubble &args) { std::unique_ptr PrepareBubblePattern( not_null st) { auto result = std::make_unique(); - result->corners = Images::CornersMask(st::historyMessageRadius); + result->cornersSmall = Images::CornersMask(st::bubbleRadiusSmall); + result->cornersLarge = Images::CornersMask(st::bubbleRadiusLarge); const auto addShadow = [&](QImage &bottomCorner) { auto result = QImage( bottomCorner.width(), @@ -264,13 +268,19 @@ std::unique_ptr PrepareBubblePattern( bottomCorner = std::move(result); }; - addShadow(result->corners[2]); - addShadow(result->corners[3]); - result->cornerTopCache = QImage( - result->corners[0].size(), + addShadow(result->cornersSmall[2]); + addShadow(result->cornersSmall[3]); + result->cornerTopSmallCache = QImage( + result->cornersSmall[0].size(), QImage::Format_ARGB32_Premultiplied); - result->cornerBottomCache = QImage( - result->corners[2].size(), + result->cornerTopLargeCache = QImage( + 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); return result; } @@ -305,40 +315,52 @@ void PaintBubble(QPainter &p, const ComplexBubble &args) { const auto width = rect.width(); const auto top = rect.y(); 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; simple.geometry = geometry; 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); }; auto from = top; for (const auto &selected : args.selection) { if (selected.top > from) { - const auto skip = RectPart::Bottom - | (from > top ? RectPart::Top : RectPart::None); + const auto fromTop = (from <= top); paintOne( QRect(left, from, width, selected.top - from), false, - skip); + (from <= top), + false); } - const auto skip = ((selected.top > top) - ? RectPart::Top - : RectPart::None) - | ((selected.top + selected.height < bottom) - ? RectPart::Bottom - : RectPart::None); + const auto fromTop = (selected.top <= top); + const auto tillBottom = (selected.top + selected.height >= bottom); paintOne( QRect(left, selected.top, width, selected.height), true, - skip); + (selected.top <= top), + (selected.top + selected.height >= bottom)); from = selected.top + selected.height; } if (from < bottom) { paintOne( QRect(left, from, width, bottom - from), false, - RectPart::Top); + false, + true); } } diff --git a/Telegram/SourceFiles/ui/chat/message_bubble.h b/Telegram/SourceFiles/ui/chat/message_bubble.h index f202f8bbf..e47bb66a3 100644 --- a/Telegram/SourceFiles/ui/chat/message_bubble.h +++ b/Telegram/SourceFiles/ui/chat/message_bubble.h @@ -16,6 +16,20 @@ namespace Ui { class ChatTheme; 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 { int top = 0; int height = 0; @@ -23,11 +37,14 @@ struct BubbleSelectionInterval { struct BubblePattern { QPixmap pixmap; - std::array corners; + std::array cornersSmall; + std::array cornersLarge; QImage tailLeft; QImage tailRight; - mutable QImage cornerTopCache; - mutable QImage cornerBottomCache; + mutable QImage cornerTopSmallCache; + mutable QImage cornerTopLargeCache; + mutable QImage cornerBottomSmallCache; + mutable QImage cornerBottomLargeCache; mutable QImage tailCache; }; @@ -42,9 +59,9 @@ struct SimpleBubble { QRect patternViewport; int outerWidth = 0; bool selected = false; + bool shadowed = true; bool outbg = false; - RectPart tailSide = RectPart::None; - RectParts skip = RectPart::None; + BubbleRounding rounding; }; struct ComplexBubble { diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index b5194748a..75dcd92eb 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -771,9 +771,9 @@ void Generator::paintBubble(const Bubble &bubble) { auto y = _historyBottom - st::msgMargin.bottom() - height; auto bubbleTop = y; auto bubbleHeight = height; - if (isPhoto) { - bubbleTop -= st::historyMessageRadius + 1; - bubbleHeight += st::historyMessageRadius + 1; + if (isPhoto) { // #TODO rounding + bubbleTop -= st::bubbleRadiusSmall + 1; + bubbleHeight += st::bubbleRadiusSmall + 1; } auto left = bubble.outbg ? st::msgMargin.right() : st::msgMargin.left(); @@ -783,20 +783,20 @@ void Generator::paintBubble(const Bubble &bubble) { x += left; _p->setPen(Qt::NoPen); - auto tailclip = st::historyMessageRadius + 1; + auto tailclip = st::bubbleRadiusSmall +1; if (bubble.tail) { 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 { - _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]; _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]; _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) { _p->setClipRect(_history); if (bubble.outbg) { diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp index 35414b75c..1e71863af 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp @@ -242,7 +242,7 @@ void CloudListCheck::paintNotSupported( const auto height = st::settingsThemePreviewSize.height(); const auto rect = QRect(0, 0, outerWidth, height); - const auto radius = st::historyMessageRadius; + const auto radius = st::roundRadiusLarge; p.drawRoundedRect(rect, radius, radius); st::settingsThemeNotSupportedIcon.paintInCenter(p, rect); } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index f1d20d33a..a69405816 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -83,7 +83,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_window.h" #include "styles/style_dialogs.h" #include "styles/style_layers.h" // st::boxLabel -#include "styles/style_chat.h" // st::historyMessageRadius namespace Window { namespace {