From 4709e11e465b75ce866719650b06e14a9e239020 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 23 Oct 2023 14:58:40 +0400 Subject: [PATCH] Support two-color quote outlines. --- .../history/history_item_components.cpp | 22 +- .../history/view/history_view_element.cpp | 23 +- .../history/view/history_view_element.h | 28 ++- .../history/view/history_view_message.cpp | 34 +-- .../history/view/history_view_message.h | 4 - .../history/view/history_view_view_button.cpp | 7 +- .../view/media/history_view_document.cpp | 2 +- .../media/history_view_extended_preview.cpp | 2 +- .../history/view/media/history_view_game.cpp | 5 +- .../history/view/media/history_view_gif.cpp | 2 +- .../view/media/history_view_giveaway.cpp | 5 +- .../view/media/history_view_media_grouped.cpp | 2 +- .../history/view/media/history_view_photo.cpp | 2 +- .../view/media/history_view_web_page.cpp | 9 +- Telegram/SourceFiles/ui/chat/chat_style.cpp | 222 +++++++++++++----- Telegram/SourceFiles/ui/chat/chat_style.h | 59 ++++- Telegram/SourceFiles/ui/chat/chat_theme.cpp | 2 +- Telegram/lib_ui | 2 +- 18 files changed, 290 insertions(+), 142 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 9968347b4..a5efe9ba8 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -682,15 +682,19 @@ void HistoryMessageReply::paint( const auto hasQuote = !_fields.quote.empty(); const auto selected = context.selected(); const auto colorIndexPlusOne = context.outbg ? 0 : _colorIndexPlusOne; + const auto twoColored = colorIndexPlusOne + && Ui::ColorIndexTwoColored(colorIndexPlusOne - 1); const auto cache = !inBubble ? (hasQuote - ? st->serviceQuoteCache() - : st->serviceReplyCache()).get() + ? st->serviceQuoteCache(twoColored) + : st->serviceReplyCache(twoColored)).get() : colorIndexPlusOne ? (hasQuote ? st->coloredQuoteCache(selected, colorIndexPlusOne - 1) : st->coloredReplyCache(selected, colorIndexPlusOne - 1)).get() - : (hasQuote ? stm->quoteCache : stm->replyCache).get(); + : (hasQuote + ? (twoColored ? stm->quoteCacheTwo : stm->quoteCache) + : (twoColored ? stm->replyCacheTwo : stm->replyCache)).get(); const auto "eSt = hasQuote ? st::messageTextStyle.blockquote : st::messageQuoteStyle; @@ -763,12 +767,10 @@ void HistoryMessageReply::paint( if (w > textLeft + st::historyReplyPadding.right()) { w -= textLeft + st::historyReplyPadding.right(); p.setPen(!inBubble - ? st->msgImgReplyBarColor() + ? st->msgImgReplyBarColor()->c : colorIndexPlusOne - ? HistoryView::FromNameFg( - context, - colorIndexPlusOne - 1) - : stm->msgServiceFg); + ? FromNameFg(context, colorIndexPlusOne - 1) + : stm->msgServiceFg->c); _name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft); if (originalVia && w > _name.maxWidth() + st::msgServiceFont->spacew) { p.setFont(st::msgServiceFont); @@ -802,7 +804,7 @@ void HistoryMessageReply::paint( auto copy = std::optional(); if (inBubble && _colorIndexPlusOne) { copy.emplace(*replyToTextPalette); - owned.emplace(cache->outline); + owned.emplace(cache->icon); copy->linkFg = owned->color(); replyToTextPalette = &*copy; } @@ -821,7 +823,7 @@ void HistoryMessageReply::paint( } } else { p.setFont(st::msgDateFont); - p.setPen(cache->outline); + p.setPen(cache->icon); p.drawTextLeft( x + textLeft, (y + (_height - st::msgDateFont->height) / 2), diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 58e1676b0..e490fc0ff 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -467,6 +467,7 @@ Element::Element( | (IsItemScheduledUntilOnline(data) ? Flag::ScheduledUntilOnline : Flag())) +, _colorIndex(data->computeColorIndex()) , _context(delegate->elementContext()) { history()->owner().registerItemView(this); refreshMedia(replacing); @@ -490,6 +491,10 @@ not_null Element::history() const { return _data->history(); } +uint8 Element::colorIndex() const { + return _colorIndex; +} + QDateTime Element::dateTime() const { return _dateTime; } @@ -557,8 +562,8 @@ void Element::prepareCustomEmojiPaint( } clearCustomEmojiRepaint(); p.setInactive(context.paused); - if (!_heavyCustomEmoji) { - _heavyCustomEmoji = true; + if (!(_flags & Flag::HeavyCustomEmoji)) { + _flags |= Flag::HeavyCustomEmoji; history()->owner().registerHeavyViewPart(const_cast(this)); } } @@ -572,8 +577,8 @@ void Element::prepareCustomEmojiPaint( } clearCustomEmojiRepaint(); p.setInactive(context.paused); - if (!_heavyCustomEmoji) { - _heavyCustomEmoji = true; + if (!(_flags & Flag::HeavyCustomEmoji)) { + _flags |= Flag::HeavyCustomEmoji; history()->owner().registerHeavyViewPart(const_cast(this)); } } @@ -1411,7 +1416,7 @@ auto Element::verticalRepaintRange() const -> VerticalRepaintRange { } bool Element::hasHeavyPart() const { - return _heavyCustomEmoji; + return (_flags & Flag::HeavyCustomEmoji); } void Element::checkHeavyPart() { @@ -1445,8 +1450,8 @@ void Element::unloadHeavyPart() { if (_media) { _media->unloadHeavyPart(); } - if (_heavyCustomEmoji) { - _heavyCustomEmoji = false; + if (_flags & Flag::HeavyCustomEmoji) { + _flags &= ~Flag::HeavyCustomEmoji; _text.unloadPersistentAnimation(); if (const auto reply = data()->Get()) { reply->unloadPersistentAnimation(); @@ -1623,8 +1628,8 @@ Element::~Element() { // Delete media while owner still exists. clearSpecialOnlyEmoji(); base::take(_media); - if (_heavyCustomEmoji) { - _heavyCustomEmoji = false; + if (_flags & Flag::HeavyCustomEmoji) { + _flags &= ~Flag::HeavyCustomEmoji; _text.unloadPersistentAnimation(); checkHeavyPart(); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 6c16cef30..d570093c1 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -277,18 +277,19 @@ class Element , public base::has_weak_ptr { public: enum class Flag : uint16 { - ServiceMessage = 0x0001, - NeedsResize = 0x0002, - AttachedToPrevious = 0x0004, - AttachedToNext = 0x0008, + ServiceMessage = 0x0001, + NeedsResize = 0x0002, + AttachedToPrevious = 0x0004, + AttachedToNext = 0x0008, BubbleAttachedToPrevious = 0x0010, - BubbleAttachedToNext = 0x0020, - HiddenByGroup = 0x0040, - SpecialOnlyEmoji = 0x0080, - CustomEmojiRepainting = 0x0100, - ScheduledUntilOnline = 0x0200, - TopicRootReply = 0x0400, - MediaOverriden = 0x0800, + BubbleAttachedToNext = 0x0020, + HiddenByGroup = 0x0040, + SpecialOnlyEmoji = 0x0080, + CustomEmojiRepainting = 0x0100, + ScheduledUntilOnline = 0x0200, + TopicRootReply = 0x0400, + MediaOverriden = 0x0800, + HeavyCustomEmoji = 0x1000, }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } @@ -306,6 +307,7 @@ public: [[nodiscard]] Context context() const; void refreshDataId(); + [[nodiscard]] uint8 colorIndex() const; [[nodiscard]] QDateTime dateTime() const; [[nodiscard]] int y() const; @@ -592,9 +594,11 @@ private: int _indexInBlock = -1; mutable Flags _flags = Flag(0); - mutable bool _heavyCustomEmoji = false; + uint8 _colorIndex = 0; Context _context = Context(); }; +constexpr auto size = sizeof(Element); + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 2c6a081eb..b89a6385a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -366,12 +366,6 @@ QString FastReplyText() { } // namespace -style::color FromNameFg( - const Ui::ChatPaintContext &context, - uint8 colorIndex) { - return Ui::FromNameFg(context.st, context.selected(), colorIndex); -} - struct Message::CommentsButton { std::unique_ptr ripple; std::vector userpics; @@ -1358,8 +1352,8 @@ void Message::paintFromName( Assert(from || info); const auto st = context.st; const auto nameFg = !context.outbg - ? FromNameFg(context, from ? from->colorIndex() : info->colorIndex) - : stm->msgServiceFg; + ? FromNameFg(context, colorIndex()) + : stm->msgServiceFg->c; const auto nameText = [&] { if (from) { validateFromNameText(from); @@ -1374,11 +1368,8 @@ void Message::paintFromName( const auto x = availableLeft + std::min(availableWidth - statusWidth, nameText->maxWidth()); const auto y = trect.top(); - const auto color = QColor( - nameFg->c.red(), - nameFg->c.green(), - nameFg->c.blue(), - nameFg->c.alpha() * 115 / 255); + auto color = nameFg; + color.setAlpha(115); const auto user = from->asUser(); const auto id = user ? user->emojiStatusId() : 0; if (_fromNameStatus->id != id) { @@ -1630,7 +1621,7 @@ void Message::paintText( .availableWidth = trect.width(), .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, @@ -3064,14 +3055,13 @@ void Message::updateViewButtonExistence() { } else if (_viewButton) { return; } - auto repainter = [=] { repaint(); }; - const auto index = item->computeColorIndex(); - _viewButton = sponsored - ? std::make_unique( - sponsored, - index, - std::move(repainter)) - : std::make_unique(media, index, std::move(repainter)); + auto make = [=](auto &&from) { + return std::make_unique( + std::forward(from), + colorIndex(), + [=] { repaint(); }); + }; + _viewButton = sponsored ? make(sponsored) : make(media); } void Message::initLogEntryOriginal() { diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 555666007..33c1b6607 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -56,10 +56,6 @@ struct BottomRippleMask { int shift = 0; }; -[[nodiscard]] style::color FromNameFg( - const Ui::ChatPaintContext &context, - uint8 colorIndex); - class Message final : public Element { public: Message( diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.cpp b/Telegram/SourceFiles/history/view/history_view_view_button.cpp index 448ec0624..afffc0b2b 100644 --- a/Telegram/SourceFiles/history/view/history_view_view_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_view_button.cpp @@ -235,8 +235,9 @@ void ViewButton::draw( const auto stm = context.messageStyle(); const auto selected = context.selected(); + const auto twoColored = Ui::ColorIndexTwoColored(_inner->colorIndex); const auto cache = context.outbg - ? stm->replyCache.get() + ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get() : context.st->coloredReplyCache(selected, _inner->colorIndex).get(); const auto radius = st::historyPagePreview.radius; @@ -249,7 +250,7 @@ void ViewButton::draw( p.setBrush(cache->bg); p.drawRoundedRect(r, radius, radius); - p.setPen(cache->outline); + p.setPen(cache->icon); _inner->text.drawElided( p, r.left(), @@ -266,7 +267,7 @@ void ViewButton::draw( r.left() + r.width() - icon.width() - padding, r.top() + padding, r.width(), - cache->outline); + cache->icon); } if (_inner->lastWidth != r.width()) { _inner->lastWidth = r.width(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 7184a7fc0..071b6bb3d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -749,7 +749,7 @@ void Document::draw( .availableWidth = captionw, .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(parent()->colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, 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 a4695e4a1..6c259748d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp @@ -236,7 +236,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const { .availableWidth = captionw, .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(parent()->colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index 80204eb5d..70e92a6e9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -220,15 +220,16 @@ void Game::draw(Painter &p, const PaintContext &context) const { auto paintw = inner.width(); const auto selected = context.selected(); + const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex); const auto cache = context.outbg - ? stm->replyCache.get() + ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get() : st->coloredReplyCache(selected, _colorIndex).get(); Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st); auto lineHeight = UnitedLineHeight(); if (_titleLines) { - p.setPen(cache->outline); + p.setPen(cache->icon); p.setTextPalette(context.outbg ? stm->semiboldPalette : st->coloredTextPalette(selected, _colorIndex)); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index f30a8d550..b30035efa 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -721,7 +721,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { .availableWidth = captionw, .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(parent()->colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp index 47ecb030d..07a96f934 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp @@ -347,8 +347,9 @@ void Giveaway::paintChannels( const auto ratio = style::DevicePixelRatio(); const auto stm = context.messageStyle(); const auto selected = context.selected(); + const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex); const auto cache = context.outbg - ? stm->replyCache.get() + ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get() : context.st->coloredReplyCache(selected, _colorIndex).get(); if (_channelCorners[0].isNull() || _channelBg != cache->bg) { _channelBg = cache->bg; @@ -357,7 +358,7 @@ void Giveaway::paintChannels( style::colorizeImage(image, cache->bg, &image); } } - p.setPen(cache->outline); + p.setPen(cache->icon); const auto padding = st::chatGiveawayChannelPadding; for (const auto &channel : _channels) { const auto &thumbnail = channel.thumbnail; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index fc7263386..79cbcf869 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -368,7 +368,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const { .availableWidth = captionw, .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(parent()->colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index a83991bf6..9d09faf17 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -406,7 +406,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const { .availableWidth = captionw, .palette = &stm->textPalette, .pre = stm->preCache.get(), - .blockquote = stm->quoteCache.get(), + .blockquote = context.quoteCache(parent()->colorIndex()), .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index fd9a5b089..2bce7b939 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -546,8 +546,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { auto attachAdditionalInfoText = _attach ? _attach->additionalInfoString() : QString(); const auto selected = context.selected(); + const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex); const auto cache = context.outbg - ? stm->replyCache.get() + ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get() : st->coloredReplyCache(selected, _colorIndex).get(); Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st); @@ -601,7 +602,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { paintw -= pw + st::webPagePhotoDelta; } if (_siteNameLines) { - p.setPen(cache->outline); + p.setPen(cache->icon); p.setTextPalette(context.outbg ? stm->semiboldPalette : st->coloredTextPalette(selected, _colorIndex)); @@ -703,10 +704,10 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { if (_openButtonWidth) { p.setFont(st::semiboldFont); - p.setPen(cache->outline); + p.setPen(cache->icon); const auto end = inner.y() + inner.height() + _st.padding.bottom(); const auto line = st::historyPageButtonLine; - auto color = cache->outline; + auto color = cache->icon; color.setAlphaF(color.alphaF() * 0.3); p.fillRect(inner.x(), end, inner.width(), line, color); const auto top = end + st::historyPageButtonPadding.top(); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 8e241d12f..5f817b44c 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -31,16 +31,16 @@ void EnsureCorners( void EnsureBlockquoteCache( std::unique_ptr &cache, - const style::color &color) { + Fn values) { if (cache) { return; } cache = std::make_unique(); - cache->bg = color->c; - cache->bg.setAlphaF(0.12); - cache->outline = color->c; - cache->outline.setAlphaF(0.9); - cache->icon = cache->outline; + const auto &colors = values(); + cache->bg = colors.bg; + cache->outline1 = colors.outline1; + cache->outline2 = colors.outline2; + cache->icon = colors.name; } void EnsurePreCache( @@ -56,11 +56,12 @@ void EnsurePreCache( if (!bg) { cache->bg.setAlphaF(0.12); } - cache->outline = color->c; - cache->outline.setAlphaF(0.9); + cache->outline1 = color->c; + cache->outline1.setAlphaF(0.9); + cache->outline2 = cache->outline1; cache->header = color->c; cache->header.setAlphaF(0.25); - cache->icon = cache->outline; + cache->icon = cache->outline1; cache->icon.setAlphaF(0.6); } @@ -74,6 +75,15 @@ not_null ChatPaintContext::imageStyle() const { return &st->imageStyle(selected()); } +not_null ChatPaintContext::quoteCache( + uint8 colorIndex) const { + return !outbg + ? st->coloredQuoteCache(selected(), colorIndex).get() + : ColorIndexTwoColored(colorIndex) + ? messageStyle()->quoteCacheTwo.get() + : messageStyle()->quoteCache.get(); +} + int HistoryServiceMsgRadius() { static const auto result = [] { const auto minMessageHeight = st::msgServicePadding.top() @@ -99,6 +109,87 @@ int HistoryServiceMsgInvertedShrink() { return result; } +ColorIndexValues ComputeColorIndexValues( + not_null st, + bool selected, + uint8 colorIndex) { + if (colorIndex < kSimpleColorIndexCount) { + const style::color list[] = { + st->historyPeer1NameFg(), + st->historyPeer2NameFg(), + st->historyPeer3NameFg(), + st->historyPeer4NameFg(), + st->historyPeer5NameFg(), + st->historyPeer6NameFg(), + st->historyPeer7NameFg(), + st->historyPeer8NameFg(), + }; + const style::color listSelected[] = { + st->historyPeer1NameFgSelected(), + st->historyPeer2NameFgSelected(), + st->historyPeer3NameFgSelected(), + st->historyPeer4NameFgSelected(), + st->historyPeer5NameFgSelected(), + st->historyPeer6NameFgSelected(), + st->historyPeer7NameFgSelected(), + st->historyPeer8NameFgSelected(), + }; + const auto paletteIndex = ColorIndexToPaletteIndex(colorIndex); + auto result = ColorIndexValues{ + .name = (selected ? listSelected : list)[paletteIndex]->c, + }; + result.bg = result.name; + result.bg.setAlphaF(0.12); + result.outline1 = result.name; + result.outline1.setAlphaF(0.9); + result.outline2 = result.outline1; + return result; + } + struct Pair { + QColor outline1; + QColor outline2; + }; + const Pair list[] = { + { QColor(0xE1, 0x50, 0x52), QColor(0xF9, 0xAE, 0x63) }, // Red + { QColor(0xE0, 0x80, 0x2B), QColor(0xFA, 0xC5, 0x34) }, // Orange + { QColor(0xA0, 0x5F, 0xF3), QColor(0xF4, 0x8F, 0xFF) }, // Violet + { QColor(0x27, 0xA9, 0x10), QColor(0xA7, 0xDC, 0x57) }, // Green + { QColor(0x27, 0xAC, 0xCE), QColor(0x82, 0xE8, 0xD6) }, // Cyan + { QColor(0x33, 0x91, 0xD4), QColor(0x7D, 0xD3, 0xF0) }, // Blue + { QColor(0xD1, 0x48, 0x72), QColor(0xFF, 0xBE, 0xA0) }, // Pink + }; + const auto &pair = list[colorIndex - kSimpleColorIndexCount]; + auto bg = pair.outline1; + bg.setAlphaF(0.12); + return { + .name = st->dark() ? pair.outline2 : pair.outline1, + .bg = bg, + .outline1 = pair.outline1, + .outline2 = pair.outline2, + }; +} + +bool ColorIndexTwoColored(uint8 colorIndex) { + return (colorIndex >= kSimpleColorIndexCount); +} + +ColorIndexValues SimpleColorIndexValues(QColor color, bool twoColored) { + auto bg = color; + bg.setAlphaF(0.12); + auto outline1 = color; + outline1.setAlphaF(0.9); + auto outline2 = outline1; + if (twoColored) { + outline2.setAlphaF(0.3); + } + return { + .name = color, + .bg = bg, + .outline1 = outline1, + .outline2 = outline2, + }; +} + ChatStyle::ChatStyle() { finalize(); make(_historyPsaForwardPalette, st::historyPsaForwardPalette); @@ -453,6 +544,8 @@ ChatStyle::ChatStyle() { &MessageImageStyle::historyVideoMessageMute, st::historyVideoMessageMute, st::historyVideoMessageMuteSelected); + + updateDarkValue(); } ChatStyle::ChatStyle(not_null isolated) @@ -464,6 +557,13 @@ void ChatStyle::apply(not_null theme) { applyCustomPalette(theme->palette()); } +void ChatStyle::updateDarkValue() { + const auto withBg = [&](const QColor &color) { + return CountContrast(windowBg()->c, color); + }; + _dark = (withBg({ 0, 0, 0 }) < withBg({ 255, 255, 255 })); +} + void ChatStyle::applyCustomPalette(const style::palette *palette) { assignPalette(palette ? palette : style::main_palette::get().get()); if (palette) { @@ -547,10 +647,13 @@ void ChatStyle::assignPalette(not_null palette) { = stm.semiboldPalette.linkAlwaysActive = (stm.textPalette.linkFg->c == stm.historyTextFg->c); } - - for (auto &palette : _coloredTextPalettes) { - palette.inited = false; + for (auto &values : _coloredValues) { + values.reset(); } + for (auto &palette : _coloredTextPalettes) { + palette.linkFg.reset(); + } + updateDarkValue(); _paletteChanged.fire({}); } @@ -584,20 +687,24 @@ const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const { BubbleRadiusLarge(), result.msgBg, &result.msgShadow); + const auto &replyBar = result.msgReplyBarColor->c; EnsureBlockquoteCache( result.replyCache, - result.msgReplyBarColor); + [&] { return SimpleColorIndexValues(replyBar, false); }); + EnsureBlockquoteCache( + result.replyCacheTwo, + [&] { return SimpleColorIndexValues(replyBar, true); }); if (!result.quoteCache) { result.quoteCache = std::make_unique( *result.replyCache); } + if (!result.quoteCacheTwo) { + result.quoteCacheTwo = std::make_unique( + *result.replyCacheTwo); + } const auto preBgOverride = [&] { - const auto withBg = [&](const QColor &color) { - return CountContrast(windowBg()->c, color); - }; - const auto dark = (withBg({ 0, 0, 0 }) < withBg({ 255, 255, 255 })); - return dark ? QColor(0, 0, 0, 192) : std::optional(); + return _dark ? QColor(0, 0, 0, 192) : std::optional(); }; EnsurePreCache( result.preCache, @@ -634,14 +741,37 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const { return result; } -not_null ChatStyle::serviceQuoteCache() const { - EnsureBlockquoteCache(_serviceQuoteCache, msgServiceFg()); - return _serviceQuoteCache.get(); +not_null ChatStyle::serviceQuoteCache( + bool twoColored) const { + const auto index = (twoColored ? 1 : 0); + const auto &service = msgServiceFg()->c; + EnsureBlockquoteCache( + _serviceQuoteCache[index], + [&] { return SimpleColorIndexValues(service, twoColored); }); + return _serviceQuoteCache[index].get(); } -not_null ChatStyle::serviceReplyCache() const { - EnsureBlockquoteCache(_serviceReplyCache, msgServiceFg()); - return _serviceReplyCache.get(); +not_null ChatStyle::serviceReplyCache( + bool twoColored) const { + const auto index = (twoColored ? 1 : 0); + const auto &service = msgServiceFg()->c; + EnsureBlockquoteCache( + _serviceReplyCache[index], + [&] { return SimpleColorIndexValues(service, twoColored); }); + return _serviceReplyCache[index].get(); +} + +const ColorIndexValues &ChatStyle::coloredValues( + bool selected, + uint8 colorIndex) const { + Expects(colorIndex >= 0 && colorIndex < kColorIndexCount); + + const auto shift = (selected ? kColorIndexCount : 0); + auto &result = _coloredValues[shift + colorIndex]; + if (!result) { + result.emplace(ComputeColorIndexValues(this, selected, colorIndex)); + } + return *result; } const style::TextPalette &ChatStyle::coloredTextPalette( @@ -651,14 +781,14 @@ const style::TextPalette &ChatStyle::coloredTextPalette( const auto shift = (selected ? kColorIndexCount : 0); auto &result = _coloredTextPalettes[shift + colorIndex]; - if (!result.inited) { - result.inited = true; + if (!result.linkFg) { + result.linkFg.emplace(FromNameFg(this, selected, colorIndex)); make( result.data, (selected ? st::inReplyTextPaletteSelected : st::inReplyTextPalette)); - result.data.linkFg = FromNameFg(this, selected, colorIndex); + result.data.linkFg = result.linkFg->color(); result.data.selectLinkFg = result.data.linkFg; } return result.data; @@ -684,7 +814,9 @@ not_null ChatStyle::coloredCache( const auto shift = (selected ? kColorIndexCount : 0); auto &cache = caches[shift + colorIndex]; - EnsureBlockquoteCache(cache, FromNameFg(this, selected, colorIndex)); + EnsureBlockquoteCache(cache, [&] { + return coloredValues(selected, colorIndex); + }); return cache.get(); } @@ -816,47 +948,21 @@ void ChatStyle::make( } uint8 DecideColorIndex(uint64 id) { - return id % kColorIndexCount; + return id % kSimpleColorIndexCount; } uint8 ColorIndexToPaletteIndex(uint8 colorIndex) { Expects(colorIndex >= 0 && colorIndex < kColorIndexCount); const int8 map[] = { 0, 7, 4, 1, 6, 3, 5 }; - return map[colorIndex]; + return map[colorIndex % kSimpleColorIndexCount]; } -style::color FromNameFg( +QColor FromNameFg( not_null st, bool selected, uint8 colorIndex) { - Expects(colorIndex >= 0 && colorIndex < kColorIndexCount); - - if (selected) { - const style::color colors[] = { - st->historyPeer1NameFgSelected(), - st->historyPeer2NameFgSelected(), - st->historyPeer3NameFgSelected(), - st->historyPeer4NameFgSelected(), - st->historyPeer5NameFgSelected(), - st->historyPeer6NameFgSelected(), - st->historyPeer7NameFgSelected(), - st->historyPeer8NameFgSelected(), - }; - return colors[ColorIndexToPaletteIndex(colorIndex)]; - } else { - const style::color colors[] = { - st->historyPeer1NameFg(), - st->historyPeer2NameFg(), - st->historyPeer3NameFg(), - st->historyPeer4NameFg(), - st->historyPeer5NameFg(), - st->historyPeer6NameFg(), - st->historyPeer7NameFg(), - st->historyPeer8NameFg(), - }; - return colors[ColorIndexToPaletteIndex(colorIndex)]; - } + return st->coloredValues(selected, colorIndex).name; } void FillComplexOverlayRect( diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 8482d5936..6d7367b44 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -27,7 +27,8 @@ class ChatTheme; class ChatStyle; struct BubblePattern; -inline constexpr auto kColorIndexCount = uint8(7); +inline constexpr auto kColorIndexCount = uint8(14); +inline constexpr auto kSimpleColorIndexCount = uint8(7); struct MessageStyle { CornersPixmaps msgBgCornersSmall; @@ -79,7 +80,9 @@ struct MessageStyle { style::icon historyTranscribeIcon = { Qt::Uninitialized }; style::icon historyTranscribeHide = { Qt::Uninitialized }; std::unique_ptr quoteCache; + std::unique_ptr quoteCacheTwo; std::unique_ptr replyCache; + std::unique_ptr replyCacheTwo; std::unique_ptr preCache; }; @@ -116,8 +119,6 @@ struct ChatPaintContext { QRect viewport; QRect clip; TextSelection selection; - bool outbg = false; - bool paused = false; crl::time now = 0; void translate(int x, int y) { @@ -133,6 +134,8 @@ struct ChatPaintContext { } [[nodiscard]] not_null messageStyle() const; [[nodiscard]] not_null imageStyle() const; + [[nodiscard]] not_null quoteCache( + uint8 colorIndex) const; [[nodiscard]] ChatPaintContext translated(int x, int y) const { auto result = *this; @@ -157,12 +160,27 @@ struct ChatPaintContext { }; SkipDrawingParts skipDrawingParts = SkipDrawingParts::None; + bool outbg = false; + bool paused = false; + }; [[nodiscard]] int HistoryServiceMsgRadius(); [[nodiscard]] int HistoryServiceMsgInvertedRadius(); [[nodiscard]] int HistoryServiceMsgInvertedShrink(); +struct ColorIndexValues { + QColor name; + QColor bg; + QColor outline1; + QColor outline2; +}; +[[nodiscard]] ColorIndexValues ComputeColorIndexValues( + not_null st, + bool selected, + uint8 colorIndex); +[[nodiscard]] bool ColorIndexTwoColored(uint8 colorIndex); + class ChatStyle final : public style::palette { public: ChatStyle(); @@ -172,6 +190,10 @@ public: void applyCustomPalette(const style::palette *palette); void applyAdjustedServiceBg(QColor serviceBg); + [[nodiscard]] bool dark() const { + return _dark; + } + [[nodiscard]] std::span highlightColors() const; [[nodiscard]] rpl::producer<> paletteChanged() const { @@ -202,10 +224,13 @@ public: bool selected) const; [[nodiscard]] const MessageImageStyle &imageStyle(bool selected) const; - [[nodiscard]] auto serviceQuoteCache() const + [[nodiscard]] auto serviceQuoteCache(bool twoColored) const -> not_null; - [[nodiscard]] auto serviceReplyCache() const + [[nodiscard]] auto serviceReplyCache(bool twoColored) const -> not_null; + [[nodiscard]] const ColorIndexValues &coloredValues( + bool selected, + uint8 colorIndex) const; [[nodiscard]] not_null coloredQuoteCache( bool selected, uint8 colorIndex) const; @@ -307,11 +332,12 @@ private: kColorIndexCount * 2>; struct ColoredPalette { + std::optional linkFg; style::TextPalette data; - bool inited = false; }; void assignPalette(not_null palette); + void updateDarkValue(); [[nodiscard]] not_null coloredCache( ColoredQuotePaintCaches &caches, @@ -368,8 +394,15 @@ private: int(CachedCornerRadius::kCount)]; mutable std::vector _highlightColors; - mutable std::unique_ptr _serviceQuoteCache; - mutable std::unique_ptr _serviceReplyCache; + mutable std::array< + std::unique_ptr, + 2> _serviceQuoteCache; + mutable std::array< + std::unique_ptr, + 2> _serviceReplyCache; + mutable std::array< + std::optional, + 2 * kColorIndexCount> _coloredValues; mutable ColoredQuotePaintCaches _coloredQuoteCaches; mutable ColoredQuotePaintCaches _coloredReplyCaches; mutable std::array< @@ -403,6 +436,8 @@ private: style::icon _historyPollChoiceRight = { Qt::Uninitialized }; style::icon _historyPollChoiceWrong = { Qt::Uninitialized }; + bool _dark = false; + rpl::event_stream<> _paletteChanged; rpl::lifetime _defaultPaletteChangeLifetime; @@ -412,11 +447,17 @@ private: [[nodiscard]] uint8 DecideColorIndex(uint64 id); [[nodiscard]] uint8 ColorIndexToPaletteIndex(uint8 colorIndex); -[[nodiscard]] style::color FromNameFg( +[[nodiscard]] QColor FromNameFg( not_null st, bool selected, uint8 colorIndex); +[[nodiscard]] inline QColor FromNameFg( + const ChatPaintContext &context, + uint8 colorIndex) { + return FromNameFg(context.st, context.selected(), colorIndex); +} + void FillComplexOverlayRect( QPainter &p, QRect rect, diff --git a/Telegram/SourceFiles/ui/chat/chat_theme.cpp b/Telegram/SourceFiles/ui/chat/chat_theme.cpp index 856b75b4a..1022f8e56 100644 --- a/Telegram/SourceFiles/ui/chat/chat_theme.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_theme.cpp @@ -489,8 +489,8 @@ ChatPaintContext ChatTheme::preparePaintContext( .bubblesPattern = _bubblesBackgroundPattern.get(), .viewport = viewport, .clip = clip, - .paused = paused, .now = now, + .paused = paused, }; } diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 4d1a5686a..71d24af3a 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 4d1a5686a7620b163639492e1b81101b677d2472 +Subproject commit 71d24af3a840d15a6cc0a3a8a1e9cbe77a604739