diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index b03564ecba..625b86f75b 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -88,6 +88,28 @@ using UpdateFlag = Data::PeerUpdate::Flag; return (month > 0 && month <= 12) ? month : 0; } +[[nodiscard]] Ui::ColorCollectible ParseColorCollectible( + const MTPDpeerColorCollectible &data) { + return { + .giftEmojiId = data.vgift_emoji_id().v, + .accentColor = Ui::ColorFromSerialized(data.vaccent_color()), + .strip = ranges::views::all( + data.vcolors().v + ) | ranges::views::transform( + &Ui::ColorFromSerialized + ) | ranges::to_vector, + .darkAccentColor = Ui::MaybeColorFromSerialized( + data.vdark_accent_color()).value_or(QColor(0, 0, 0, 0)), + .darkStrip = (data.vdark_colors() + ? ranges::views::all( + data.vdark_colors()->v + ) | ranges::views::transform( + &Ui::ColorFromSerialized + ) | ranges::to_vector + : std::vector()), + }; +} + } // namespace namespace Data { @@ -998,13 +1020,6 @@ TimeId PeerData::photoChangeDate() const { return _barDetails ? _barDetails->photoChangeDate : 0; } -bool PeerData::changeColorIndex( - const tl::conditional &cloudColorIndex) { - return cloudColorIndex - ? changeColorIndex(cloudColorIndex->v) - : clearColorIndex(); -} - bool PeerData::changeBackgroundEmojiId( const tl::conditional &cloudBackgroundEmoji) { return changeBackgroundEmojiId(cloudBackgroundEmoji @@ -1012,6 +1027,36 @@ bool PeerData::changeBackgroundEmojiId( : DocumentId()); } +bool PeerData::changeColorCollectible( + const tl::conditional &cloudColor) { + const auto clear = [&] { + if (_colorCollectible) { + _colorCollectible = nullptr; + return true; + } + return false; + }; + if (!cloudColor) { + return clear(); + } + return cloudColor->match([&](const MTPDpeerColorCollectible &data) { + auto parsed = ParseColorCollectible(data); + if (!_colorCollectible) { + _colorCollectible = std::make_shared( + std::move(parsed)); + return true; + } else if (*_colorCollectible != parsed) { + *_colorCollectible = std::move(parsed); + return true; + } + return false; + }, [&](const MTPDpeerColor &) { + return clear(); + }, [&](const MTPDinputPeerColorCollectible &) { + return clear(); + }); +} + bool PeerData::changeColor( const tl::conditional &cloudColor) { const auto changed1 = cloudColor @@ -1019,7 +1064,8 @@ bool PeerData::changeColor( : clearColorIndex(); const auto changed2 = changeBackgroundEmojiId( Data::BackgroundEmojiIdFromColor(cloudColor)); - return changed1 || changed2; + const auto changed3 = changeColorCollectible(cloudColor); + return changed1 || changed2 || changed3; } void PeerData::fillNames() { @@ -1344,6 +1390,7 @@ bool PeerData::clearColorIndex() { } _colorIndexCloud = 0; _colorIndex = Data::DecideColorIndex(id); + _colorCollectible = nullptr; return true; } @@ -2036,7 +2083,7 @@ uint8 ColorIndexFromColor(const MTPPeerColor *color) { return color->match([](const MTPDpeerColor &data) -> uint8 { return data.vcolor().value_or_empty(); }, [](const MTPDpeerColorCollectible &data) -> uint8 { - return 0; // themes + return 0; }, [](const MTPDinputPeerColorCollectible &) -> uint8 { return 0; }); diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 71bd92c9d0..720b5cdc49 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -25,6 +25,7 @@ enum class ChatRestriction; namespace Ui { class EmptyUserpic; struct BotVerifyDetails; +struct ColorCollectible; } // namespace Ui namespace Main { @@ -214,6 +215,10 @@ public: [[nodiscard]] uint8 colorIndex() const { return _colorIndex; } + [[nodiscard]] auto colorCollectible() const + -> const std::shared_ptr & { + return _colorCollectible; + } bool changeColorIndex(uint8 index); bool clearColorIndex(); [[nodiscard]] DocumentId backgroundEmojiId() const; @@ -489,9 +494,10 @@ public: void saveTranslationDisabled(bool disabled); void setBarSettings(const MTPPeerSettings &data); - bool changeColorIndex(const tl::conditional &cloudColorIndex); bool changeBackgroundEmojiId( const tl::conditional &cloudBackgroundEmoji); + bool changeColorCollectible( + const tl::conditional &cloudColor); bool changeColor(const tl::conditional &cloudColor); enum class BlockStatus : char { @@ -609,6 +615,7 @@ private: BarSettings _barSettings = PeerBarSettings(PeerBarSetting::Unknown); std::unique_ptr _barDetails; + std::shared_ptr _colorCollectible; BlockStatus _blockStatus = BlockStatus::Unknown; LoadedStatus _loadedStatus = LoadedStatus::Not; diff --git a/Telegram/SourceFiles/history/view/history_view_reply.cpp b/Telegram/SourceFiles/history/view/history_view_reply.cpp index fc18f3dcdf..0427c74839 100644 --- a/Telegram/SourceFiles/history/view/history_view_reply.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reply.cpp @@ -699,14 +699,24 @@ void Reply::paint( const auto colorIndexPlusOne = _colorPeer ? (_colorPeer->colorIndex() + 1) : _hiddenSenderColorIndexPlusOne; + const auto &colorCollectible = _colorPeer + ? _colorPeer->colorCollectible() + : nullptr; + const auto useColorCollectible = colorCollectible && !context.outbg; const auto useColorIndex = colorIndexPlusOne && !context.outbg; - const auto colorPattern = colorIndexPlusOne + const auto colorPattern = colorCollectible + ? 2 + : colorIndexPlusOne ? st->colorPatternIndex(colorIndexPlusOne - 1) : 0; const auto cache = !inBubble ? (_hasQuoteIcon ? st->serviceQuoteCache(colorPattern) : st->serviceReplyCache(colorPattern)).get() + : useColorCollectible + ? (_hasQuoteIcon + ? st->collectibleQuoteCache(selected, colorCollectible) + : st->collectibleReplyCache(selected, colorCollectible)).get() : useColorIndex ? (_hasQuoteIcon ? st->coloredQuoteCache(selected, colorIndexPlusOne - 1) @@ -832,6 +842,8 @@ void Reply::paint( if (namew > 0) { p.setPen(!inBubble ? st->msgImgReplyBarColor()->c + : useColorCollectible + ? cache->icon : useColorIndex ? FromNameFg(context, colorIndexPlusOne - 1) : stm->msgServiceFg->c); @@ -849,6 +861,8 @@ void Reply::paint( view->prepareCustomEmojiPaint(p, context, _text); auto replyToTextPalette = &(!inBubble ? st->imgReplyTextPalette() + : useColorCollectible + ? st->collectibleTextPalette(selected, colorCollectible) : useColorIndex ? st->coloredTextPalette(selected, colorIndexPlusOne - 1) : stm->replyTextPalette); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 7929c9ce0d..cb70fc0572 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -871,6 +871,13 @@ const ColorIndexValues &ChatStyle::coloredValues( return *result; } +QColor ChatStyle::collectibleNameColor( + const std::shared_ptr &collectible) const { + return (_dark && collectible->darkAccentColor.alpha() > 0) + ? collectible->darkAccentColor + : collectible->accentColor; +} + const style::TextPalette &ChatStyle::coloredTextPalette( bool selected, uint8 colorIndex) const { @@ -891,6 +898,24 @@ const style::TextPalette &ChatStyle::coloredTextPalette( return result.data; } +const style::TextPalette &ChatStyle::collectibleTextPalette( + bool selected, + const std::shared_ptr &collectible) const { + auto &entry = resolveCollectibleCaches(collectible); + auto &result = selected ? entry.paletteSelected : entry.palette; + if (!result.linkFg) { + result.linkFg.emplace(collectibleNameColor(collectible)); + make( + result.data, + (selected + ? st::inReplyTextPaletteSelected + : st::inReplyTextPalette)); + result.data.linkFg = result.linkFg->color(); + result.data.selectLinkFg = result.data.linkFg; + } + return result.data; +} + not_null ChatStyle::backgroundEmojiData( uint64 id) const { return &_backgroundEmojis[id]; @@ -908,6 +933,24 @@ not_null ChatStyle::coloredReplyCache( return coloredCache(_coloredReplyCaches, selected, colorIndex); } +not_null ChatStyle::collectibleQuoteCache( + bool selected, + const std::shared_ptr &collectible) const { + auto &entry = resolveCollectibleCaches(collectible); + return collectibleCache( + selected ? entry.quoteSelected : entry.quote, + collectible); +} + +not_null ChatStyle::collectibleReplyCache( + bool selected, + const std::shared_ptr &collectible) const { + auto &entry = resolveCollectibleCaches(collectible); + return collectibleCache( + selected ? entry.replySelected : entry.reply, + collectible); +} + not_null ChatStyle::coloredCache( ColoredQuotePaintCaches &caches, bool selected, @@ -922,6 +965,46 @@ not_null ChatStyle::coloredCache( return cache.get(); } +not_null ChatStyle::collectibleCache( + std::unique_ptr &cache, + const std::shared_ptr &collectible) const { + EnsureBlockquoteCache(cache, [&] { + const auto name = collectibleNameColor(collectible); + auto bg = name; + bg.setAlpha(kDefaultBgOpacity * 255); + + const auto &strip = (_dark && !collectible->darkStrip.empty()) + ? collectible->darkStrip + : collectible->strip; + return ColorIndexValues{ + .outlines = { + strip.empty() ? name : strip[0], + (strip.size() < 2) ? QColor(0, 0, 0, 0) : strip[1], + (strip.size() < 3) ? QColor(0, 0, 0, 0) : strip[2], + }, + .name = name, + .bg = bg, + }; + }); + return cache.get(); +} + +ChatStyle::CollectibleColors &ChatStyle::resolveCollectibleCaches( + const std::shared_ptr &collectible) const { + const auto i = _collectibleCaches.find(collectible); + if (i != end(_collectibleCaches)) { + return i->second; + } + for (auto i = begin(_collectibleCaches); i != end(_collectibleCaches);) { + if (i->first.expired()) { + i = _collectibleCaches.erase(i); + } else { + ++i; + } + } + return _collectibleCaches.emplace(collectible).first->second; +} + const CornersPixmaps &ChatStyle::msgBotKbOverBgAddCornersSmall() const { EnsureCorners( _msgBotKbOverBgAddCornersSmall, diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 8ddf1901bc..61db973b7d 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -267,6 +267,26 @@ struct ColorIndexValues { QColor color, int patternIndex); +struct ColorCollectible { + uint64 giftEmojiId = 0; + QColor accentColor; + std::vector strip; + QColor darkAccentColor; + std::vector darkStrip; + + friend inline bool operator==( + const ColorCollectible &, + const ColorCollectible &) = default; +}; + +struct ColorCollectiblePtrCompare { + bool operator()( + const std::weak_ptr &a, + const std::weak_ptr &b) const { + return a.owner_before(b); + } +}; + class ChatStyle final : public style::palette { public: explicit ChatStyle(rpl::producer colorIndices); @@ -325,16 +345,27 @@ public: [[nodiscard]] const ColorIndexValues &coloredValues( bool selected, uint8 colorIndex) const; + [[nodiscard]] QColor collectibleNameColor( + const std::shared_ptr &collectible) const; [[nodiscard]] not_null coloredQuoteCache( bool selected, uint8 colorIndex) const; [[nodiscard]] not_null coloredReplyCache( bool selected, uint8 colorIndex) const; + [[nodiscard]] not_null collectibleQuoteCache( + bool selected, + const std::shared_ptr &collectible) const; + [[nodiscard]] not_null collectibleReplyCache( + bool selected, + const std::shared_ptr &collectible) const; [[nodiscard]] const style::TextPalette &coloredTextPalette( bool selected, uint8 colorIndex) const; + [[nodiscard]] const style::TextPalette &collectibleTextPalette( + bool selected, + const std::shared_ptr &collectible) const; [[nodiscard]] not_null backgroundEmojiData( uint64 id) const; @@ -448,6 +479,15 @@ private: style::TextPalette data; }; + struct CollectibleColors { + std::unique_ptr quote; + std::unique_ptr quoteSelected; + std::unique_ptr reply; + std::unique_ptr replySelected; + ColoredPalette palette; + ColoredPalette paletteSelected; + }; + void assignPalette(not_null palette); void clearColorIndexCaches(); void updateDarkValue(); @@ -456,6 +496,11 @@ private: ColoredQuotePaintCaches &caches, bool selected, uint8 colorIndex) const; + [[nodiscard]] not_null collectibleCache( + std::unique_ptr &cache, + const std::shared_ptr &collectible) const; + [[nodiscard]] CollectibleColors &resolveCollectibleCaches( + const std::shared_ptr &collectible) const; void make(style::color &my, const style::color &original) const; void make(style::icon &my, const style::icon &original) const; @@ -521,6 +566,10 @@ private: mutable std::array< ColoredPalette, 2 * kColorIndexCount> _coloredTextPalettes; + mutable base::flat_map< + std::weak_ptr, + CollectibleColors, + ColorCollectiblePtrCompare> _collectibleCaches; mutable base::flat_map _backgroundEmojis; style::TextPalette _historyPsaForwardPalette;