Support two-color quote outlines.

This commit is contained in:
John Preston 2023-10-23 14:58:40 +04:00
parent 8c28ce4c99
commit 4709e11e46
18 changed files with 290 additions and 142 deletions

View file

@ -682,15 +682,19 @@ void HistoryMessageReply::paint(
const auto hasQuote = !_fields.quote.empty(); const auto hasQuote = !_fields.quote.empty();
const auto selected = context.selected(); const auto selected = context.selected();
const auto colorIndexPlusOne = context.outbg ? 0 : _colorIndexPlusOne; const auto colorIndexPlusOne = context.outbg ? 0 : _colorIndexPlusOne;
const auto twoColored = colorIndexPlusOne
&& Ui::ColorIndexTwoColored(colorIndexPlusOne - 1);
const auto cache = !inBubble const auto cache = !inBubble
? (hasQuote ? (hasQuote
? st->serviceQuoteCache() ? st->serviceQuoteCache(twoColored)
: st->serviceReplyCache()).get() : st->serviceReplyCache(twoColored)).get()
: colorIndexPlusOne : colorIndexPlusOne
? (hasQuote ? (hasQuote
? st->coloredQuoteCache(selected, colorIndexPlusOne - 1) ? st->coloredQuoteCache(selected, colorIndexPlusOne - 1)
: st->coloredReplyCache(selected, colorIndexPlusOne - 1)).get() : 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 &quoteSt = hasQuote const auto &quoteSt = hasQuote
? st::messageTextStyle.blockquote ? st::messageTextStyle.blockquote
: st::messageQuoteStyle; : st::messageQuoteStyle;
@ -763,12 +767,10 @@ void HistoryMessageReply::paint(
if (w > textLeft + st::historyReplyPadding.right()) { if (w > textLeft + st::historyReplyPadding.right()) {
w -= textLeft + st::historyReplyPadding.right(); w -= textLeft + st::historyReplyPadding.right();
p.setPen(!inBubble p.setPen(!inBubble
? st->msgImgReplyBarColor() ? st->msgImgReplyBarColor()->c
: colorIndexPlusOne : colorIndexPlusOne
? HistoryView::FromNameFg( ? FromNameFg(context, colorIndexPlusOne - 1)
context, : stm->msgServiceFg->c);
colorIndexPlusOne - 1)
: stm->msgServiceFg);
_name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft); _name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft);
if (originalVia && w > _name.maxWidth() + st::msgServiceFont->spacew) { if (originalVia && w > _name.maxWidth() + st::msgServiceFont->spacew) {
p.setFont(st::msgServiceFont); p.setFont(st::msgServiceFont);
@ -802,7 +804,7 @@ void HistoryMessageReply::paint(
auto copy = std::optional<style::TextPalette>(); auto copy = std::optional<style::TextPalette>();
if (inBubble && _colorIndexPlusOne) { if (inBubble && _colorIndexPlusOne) {
copy.emplace(*replyToTextPalette); copy.emplace(*replyToTextPalette);
owned.emplace(cache->outline); owned.emplace(cache->icon);
copy->linkFg = owned->color(); copy->linkFg = owned->color();
replyToTextPalette = &*copy; replyToTextPalette = &*copy;
} }
@ -821,7 +823,7 @@ void HistoryMessageReply::paint(
} }
} else { } else {
p.setFont(st::msgDateFont); p.setFont(st::msgDateFont);
p.setPen(cache->outline); p.setPen(cache->icon);
p.drawTextLeft( p.drawTextLeft(
x + textLeft, x + textLeft,
(y + (_height - st::msgDateFont->height) / 2), (y + (_height - st::msgDateFont->height) / 2),

View file

@ -467,6 +467,7 @@ Element::Element(
| (IsItemScheduledUntilOnline(data) | (IsItemScheduledUntilOnline(data)
? Flag::ScheduledUntilOnline ? Flag::ScheduledUntilOnline
: Flag())) : Flag()))
, _colorIndex(data->computeColorIndex())
, _context(delegate->elementContext()) { , _context(delegate->elementContext()) {
history()->owner().registerItemView(this); history()->owner().registerItemView(this);
refreshMedia(replacing); refreshMedia(replacing);
@ -490,6 +491,10 @@ not_null<History*> Element::history() const {
return _data->history(); return _data->history();
} }
uint8 Element::colorIndex() const {
return _colorIndex;
}
QDateTime Element::dateTime() const { QDateTime Element::dateTime() const {
return _dateTime; return _dateTime;
} }
@ -557,8 +562,8 @@ void Element::prepareCustomEmojiPaint(
} }
clearCustomEmojiRepaint(); clearCustomEmojiRepaint();
p.setInactive(context.paused); p.setInactive(context.paused);
if (!_heavyCustomEmoji) { if (!(_flags & Flag::HeavyCustomEmoji)) {
_heavyCustomEmoji = true; _flags |= Flag::HeavyCustomEmoji;
history()->owner().registerHeavyViewPart(const_cast<Element*>(this)); history()->owner().registerHeavyViewPart(const_cast<Element*>(this));
} }
} }
@ -572,8 +577,8 @@ void Element::prepareCustomEmojiPaint(
} }
clearCustomEmojiRepaint(); clearCustomEmojiRepaint();
p.setInactive(context.paused); p.setInactive(context.paused);
if (!_heavyCustomEmoji) { if (!(_flags & Flag::HeavyCustomEmoji)) {
_heavyCustomEmoji = true; _flags |= Flag::HeavyCustomEmoji;
history()->owner().registerHeavyViewPart(const_cast<Element*>(this)); history()->owner().registerHeavyViewPart(const_cast<Element*>(this));
} }
} }
@ -1411,7 +1416,7 @@ auto Element::verticalRepaintRange() const -> VerticalRepaintRange {
} }
bool Element::hasHeavyPart() const { bool Element::hasHeavyPart() const {
return _heavyCustomEmoji; return (_flags & Flag::HeavyCustomEmoji);
} }
void Element::checkHeavyPart() { void Element::checkHeavyPart() {
@ -1445,8 +1450,8 @@ void Element::unloadHeavyPart() {
if (_media) { if (_media) {
_media->unloadHeavyPart(); _media->unloadHeavyPart();
} }
if (_heavyCustomEmoji) { if (_flags & Flag::HeavyCustomEmoji) {
_heavyCustomEmoji = false; _flags &= ~Flag::HeavyCustomEmoji;
_text.unloadPersistentAnimation(); _text.unloadPersistentAnimation();
if (const auto reply = data()->Get<HistoryMessageReply>()) { if (const auto reply = data()->Get<HistoryMessageReply>()) {
reply->unloadPersistentAnimation(); reply->unloadPersistentAnimation();
@ -1623,8 +1628,8 @@ Element::~Element() {
// Delete media while owner still exists. // Delete media while owner still exists.
clearSpecialOnlyEmoji(); clearSpecialOnlyEmoji();
base::take(_media); base::take(_media);
if (_heavyCustomEmoji) { if (_flags & Flag::HeavyCustomEmoji) {
_heavyCustomEmoji = false; _flags &= ~Flag::HeavyCustomEmoji;
_text.unloadPersistentAnimation(); _text.unloadPersistentAnimation();
checkHeavyPart(); checkHeavyPart();
} }

View file

@ -277,18 +277,19 @@ class Element
, public base::has_weak_ptr { , public base::has_weak_ptr {
public: public:
enum class Flag : uint16 { enum class Flag : uint16 {
ServiceMessage = 0x0001, ServiceMessage = 0x0001,
NeedsResize = 0x0002, NeedsResize = 0x0002,
AttachedToPrevious = 0x0004, AttachedToPrevious = 0x0004,
AttachedToNext = 0x0008, AttachedToNext = 0x0008,
BubbleAttachedToPrevious = 0x0010, BubbleAttachedToPrevious = 0x0010,
BubbleAttachedToNext = 0x0020, BubbleAttachedToNext = 0x0020,
HiddenByGroup = 0x0040, HiddenByGroup = 0x0040,
SpecialOnlyEmoji = 0x0080, SpecialOnlyEmoji = 0x0080,
CustomEmojiRepainting = 0x0100, CustomEmojiRepainting = 0x0100,
ScheduledUntilOnline = 0x0200, ScheduledUntilOnline = 0x0200,
TopicRootReply = 0x0400, TopicRootReply = 0x0400,
MediaOverriden = 0x0800, MediaOverriden = 0x0800,
HeavyCustomEmoji = 0x1000,
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }
@ -306,6 +307,7 @@ public:
[[nodiscard]] Context context() const; [[nodiscard]] Context context() const;
void refreshDataId(); void refreshDataId();
[[nodiscard]] uint8 colorIndex() const;
[[nodiscard]] QDateTime dateTime() const; [[nodiscard]] QDateTime dateTime() const;
[[nodiscard]] int y() const; [[nodiscard]] int y() const;
@ -592,9 +594,11 @@ private:
int _indexInBlock = -1; int _indexInBlock = -1;
mutable Flags _flags = Flag(0); mutable Flags _flags = Flag(0);
mutable bool _heavyCustomEmoji = false; uint8 _colorIndex = 0;
Context _context = Context(); Context _context = Context();
}; };
constexpr auto size = sizeof(Element);
} // namespace HistoryView } // namespace HistoryView

View file

@ -366,12 +366,6 @@ QString FastReplyText() {
} // namespace } // namespace
style::color FromNameFg(
const Ui::ChatPaintContext &context,
uint8 colorIndex) {
return Ui::FromNameFg(context.st, context.selected(), colorIndex);
}
struct Message::CommentsButton { struct Message::CommentsButton {
std::unique_ptr<Ui::RippleAnimation> ripple; std::unique_ptr<Ui::RippleAnimation> ripple;
std::vector<UserpicInRow> userpics; std::vector<UserpicInRow> userpics;
@ -1358,8 +1352,8 @@ void Message::paintFromName(
Assert(from || info); Assert(from || info);
const auto st = context.st; const auto st = context.st;
const auto nameFg = !context.outbg const auto nameFg = !context.outbg
? FromNameFg(context, from ? from->colorIndex() : info->colorIndex) ? FromNameFg(context, colorIndex())
: stm->msgServiceFg; : stm->msgServiceFg->c;
const auto nameText = [&] { const auto nameText = [&] {
if (from) { if (from) {
validateFromNameText(from); validateFromNameText(from);
@ -1374,11 +1368,8 @@ void Message::paintFromName(
const auto x = availableLeft const auto x = availableLeft
+ std::min(availableWidth - statusWidth, nameText->maxWidth()); + std::min(availableWidth - statusWidth, nameText->maxWidth());
const auto y = trect.top(); const auto y = trect.top();
const auto color = QColor( auto color = nameFg;
nameFg->c.red(), color.setAlpha(115);
nameFg->c.green(),
nameFg->c.blue(),
nameFg->c.alpha() * 115 / 255);
const auto user = from->asUser(); const auto user = from->asUser();
const auto id = user ? user->emojiStatusId() : 0; const auto id = user ? user->emojiStatusId() : 0;
if (_fromNameStatus->id != id) { if (_fromNameStatus->id != id) {
@ -1630,7 +1621,7 @@ void Message::paintText(
.availableWidth = trect.width(), .availableWidth = trect.width(),
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,
@ -3064,14 +3055,13 @@ void Message::updateViewButtonExistence() {
} else if (_viewButton) { } else if (_viewButton) {
return; return;
} }
auto repainter = [=] { repaint(); }; auto make = [=](auto &&from) {
const auto index = item->computeColorIndex(); return std::make_unique<ViewButton>(
_viewButton = sponsored std::forward<decltype(from)>(from),
? std::make_unique<ViewButton>( colorIndex(),
sponsored, [=] { repaint(); });
index, };
std::move(repainter)) _viewButton = sponsored ? make(sponsored) : make(media);
: std::make_unique<ViewButton>(media, index, std::move(repainter));
} }
void Message::initLogEntryOriginal() { void Message::initLogEntryOriginal() {

View file

@ -56,10 +56,6 @@ struct BottomRippleMask {
int shift = 0; int shift = 0;
}; };
[[nodiscard]] style::color FromNameFg(
const Ui::ChatPaintContext &context,
uint8 colorIndex);
class Message final : public Element { class Message final : public Element {
public: public:
Message( Message(

View file

@ -235,8 +235,9 @@ void ViewButton::draw(
const auto stm = context.messageStyle(); const auto stm = context.messageStyle();
const auto selected = context.selected(); const auto selected = context.selected();
const auto twoColored = Ui::ColorIndexTwoColored(_inner->colorIndex);
const auto cache = context.outbg const auto cache = context.outbg
? stm->replyCache.get() ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
: context.st->coloredReplyCache(selected, _inner->colorIndex).get(); : context.st->coloredReplyCache(selected, _inner->colorIndex).get();
const auto radius = st::historyPagePreview.radius; const auto radius = st::historyPagePreview.radius;
@ -249,7 +250,7 @@ void ViewButton::draw(
p.setBrush(cache->bg); p.setBrush(cache->bg);
p.drawRoundedRect(r, radius, radius); p.drawRoundedRect(r, radius, radius);
p.setPen(cache->outline); p.setPen(cache->icon);
_inner->text.drawElided( _inner->text.drawElided(
p, p,
r.left(), r.left(),
@ -266,7 +267,7 @@ void ViewButton::draw(
r.left() + r.width() - icon.width() - padding, r.left() + r.width() - icon.width() - padding,
r.top() + padding, r.top() + padding,
r.width(), r.width(),
cache->outline); cache->icon);
} }
if (_inner->lastWidth != r.width()) { if (_inner->lastWidth != r.width()) {
_inner->lastWidth = r.width(); _inner->lastWidth = r.width();

View file

@ -749,7 +749,7 @@ void Document::draw(
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(parent()->colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,

View file

@ -236,7 +236,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(parent()->colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,

View file

@ -220,15 +220,16 @@ void Game::draw(Painter &p, const PaintContext &context) const {
auto paintw = inner.width(); auto paintw = inner.width();
const auto selected = context.selected(); const auto selected = context.selected();
const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex);
const auto cache = context.outbg const auto cache = context.outbg
? stm->replyCache.get() ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
: st->coloredReplyCache(selected, _colorIndex).get(); : st->coloredReplyCache(selected, _colorIndex).get();
Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::ValidateQuotePaintCache(*cache, _st);
Ui::Text::FillQuotePaint(p, outer, *cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st);
auto lineHeight = UnitedLineHeight(); auto lineHeight = UnitedLineHeight();
if (_titleLines) { if (_titleLines) {
p.setPen(cache->outline); p.setPen(cache->icon);
p.setTextPalette(context.outbg p.setTextPalette(context.outbg
? stm->semiboldPalette ? stm->semiboldPalette
: st->coloredTextPalette(selected, _colorIndex)); : st->coloredTextPalette(selected, _colorIndex));

View file

@ -721,7 +721,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(parent()->colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,

View file

@ -347,8 +347,9 @@ void Giveaway::paintChannels(
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto stm = context.messageStyle(); const auto stm = context.messageStyle();
const auto selected = context.selected(); const auto selected = context.selected();
const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex);
const auto cache = context.outbg const auto cache = context.outbg
? stm->replyCache.get() ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
: context.st->coloredReplyCache(selected, _colorIndex).get(); : context.st->coloredReplyCache(selected, _colorIndex).get();
if (_channelCorners[0].isNull() || _channelBg != cache->bg) { if (_channelCorners[0].isNull() || _channelBg != cache->bg) {
_channelBg = cache->bg; _channelBg = cache->bg;
@ -357,7 +358,7 @@ void Giveaway::paintChannels(
style::colorizeImage(image, cache->bg, &image); style::colorizeImage(image, cache->bg, &image);
} }
} }
p.setPen(cache->outline); p.setPen(cache->icon);
const auto padding = st::chatGiveawayChannelPadding; const auto padding = st::chatGiveawayChannelPadding;
for (const auto &channel : _channels) { for (const auto &channel : _channels) {
const auto &thumbnail = channel.thumbnail; const auto &thumbnail = channel.thumbnail;

View file

@ -368,7 +368,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(parent()->colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,

View file

@ -406,7 +406,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
.availableWidth = captionw, .availableWidth = captionw,
.palette = &stm->textPalette, .palette = &stm->textPalette,
.pre = stm->preCache.get(), .pre = stm->preCache.get(),
.blockquote = stm->quoteCache.get(), .blockquote = context.quoteCache(parent()->colorIndex()),
.colors = context.st->highlightColors(), .colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,

View file

@ -546,8 +546,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
auto attachAdditionalInfoText = _attach ? _attach->additionalInfoString() : QString(); auto attachAdditionalInfoText = _attach ? _attach->additionalInfoString() : QString();
const auto selected = context.selected(); const auto selected = context.selected();
const auto twoColored = Ui::ColorIndexTwoColored(_colorIndex);
const auto cache = context.outbg const auto cache = context.outbg
? stm->replyCache.get() ? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
: st->coloredReplyCache(selected, _colorIndex).get(); : st->coloredReplyCache(selected, _colorIndex).get();
Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::ValidateQuotePaintCache(*cache, _st);
Ui::Text::FillQuotePaint(p, outer, *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; paintw -= pw + st::webPagePhotoDelta;
} }
if (_siteNameLines) { if (_siteNameLines) {
p.setPen(cache->outline); p.setPen(cache->icon);
p.setTextPalette(context.outbg p.setTextPalette(context.outbg
? stm->semiboldPalette ? stm->semiboldPalette
: st->coloredTextPalette(selected, _colorIndex)); : st->coloredTextPalette(selected, _colorIndex));
@ -703,10 +704,10 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
if (_openButtonWidth) { if (_openButtonWidth) {
p.setFont(st::semiboldFont); p.setFont(st::semiboldFont);
p.setPen(cache->outline); p.setPen(cache->icon);
const auto end = inner.y() + inner.height() + _st.padding.bottom(); const auto end = inner.y() + inner.height() + _st.padding.bottom();
const auto line = st::historyPageButtonLine; const auto line = st::historyPageButtonLine;
auto color = cache->outline; auto color = cache->icon;
color.setAlphaF(color.alphaF() * 0.3); color.setAlphaF(color.alphaF() * 0.3);
p.fillRect(inner.x(), end, inner.width(), line, color); p.fillRect(inner.x(), end, inner.width(), line, color);
const auto top = end + st::historyPageButtonPadding.top(); const auto top = end + st::historyPageButtonPadding.top();

View file

@ -31,16 +31,16 @@ void EnsureCorners(
void EnsureBlockquoteCache( void EnsureBlockquoteCache(
std::unique_ptr<Text::QuotePaintCache> &cache, std::unique_ptr<Text::QuotePaintCache> &cache,
const style::color &color) { Fn<ColorIndexValues()> values) {
if (cache) { if (cache) {
return; return;
} }
cache = std::make_unique<Text::QuotePaintCache>(); cache = std::make_unique<Text::QuotePaintCache>();
cache->bg = color->c; const auto &colors = values();
cache->bg.setAlphaF(0.12); cache->bg = colors.bg;
cache->outline = color->c; cache->outline1 = colors.outline1;
cache->outline.setAlphaF(0.9); cache->outline2 = colors.outline2;
cache->icon = cache->outline; cache->icon = colors.name;
} }
void EnsurePreCache( void EnsurePreCache(
@ -56,11 +56,12 @@ void EnsurePreCache(
if (!bg) { if (!bg) {
cache->bg.setAlphaF(0.12); cache->bg.setAlphaF(0.12);
} }
cache->outline = color->c; cache->outline1 = color->c;
cache->outline.setAlphaF(0.9); cache->outline1.setAlphaF(0.9);
cache->outline2 = cache->outline1;
cache->header = color->c; cache->header = color->c;
cache->header.setAlphaF(0.25); cache->header.setAlphaF(0.25);
cache->icon = cache->outline; cache->icon = cache->outline1;
cache->icon.setAlphaF(0.6); cache->icon.setAlphaF(0.6);
} }
@ -74,6 +75,15 @@ not_null<const MessageImageStyle*> ChatPaintContext::imageStyle() const {
return &st->imageStyle(selected()); return &st->imageStyle(selected());
} }
not_null<Text::QuotePaintCache*> ChatPaintContext::quoteCache(
uint8 colorIndex) const {
return !outbg
? st->coloredQuoteCache(selected(), colorIndex).get()
: ColorIndexTwoColored(colorIndex)
? messageStyle()->quoteCacheTwo.get()
: messageStyle()->quoteCache.get();
}
int HistoryServiceMsgRadius() { int HistoryServiceMsgRadius() {
static const auto result = [] { static const auto result = [] {
const auto minMessageHeight = st::msgServicePadding.top() const auto minMessageHeight = st::msgServicePadding.top()
@ -99,6 +109,87 @@ int HistoryServiceMsgInvertedShrink() {
return result; return result;
} }
ColorIndexValues ComputeColorIndexValues(
not_null<const ChatStyle*> 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() { ChatStyle::ChatStyle() {
finalize(); finalize();
make(_historyPsaForwardPalette, st::historyPsaForwardPalette); make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
@ -453,6 +544,8 @@ ChatStyle::ChatStyle() {
&MessageImageStyle::historyVideoMessageMute, &MessageImageStyle::historyVideoMessageMute,
st::historyVideoMessageMute, st::historyVideoMessageMute,
st::historyVideoMessageMuteSelected); st::historyVideoMessageMuteSelected);
updateDarkValue();
} }
ChatStyle::ChatStyle(not_null<const style::palette*> isolated) ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
@ -464,6 +557,13 @@ void ChatStyle::apply(not_null<ChatTheme*> theme) {
applyCustomPalette(theme->palette()); 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) { void ChatStyle::applyCustomPalette(const style::palette *palette) {
assignPalette(palette ? palette : style::main_palette::get().get()); assignPalette(palette ? palette : style::main_palette::get().get());
if (palette) { if (palette) {
@ -547,10 +647,13 @@ void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
= stm.semiboldPalette.linkAlwaysActive = stm.semiboldPalette.linkAlwaysActive
= (stm.textPalette.linkFg->c == stm.historyTextFg->c); = (stm.textPalette.linkFg->c == stm.historyTextFg->c);
} }
for (auto &values : _coloredValues) {
for (auto &palette : _coloredTextPalettes) { values.reset();
palette.inited = false;
} }
for (auto &palette : _coloredTextPalettes) {
palette.linkFg.reset();
}
updateDarkValue();
_paletteChanged.fire({}); _paletteChanged.fire({});
} }
@ -584,20 +687,24 @@ const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
BubbleRadiusLarge(), BubbleRadiusLarge(),
result.msgBg, result.msgBg,
&result.msgShadow); &result.msgShadow);
const auto &replyBar = result.msgReplyBarColor->c;
EnsureBlockquoteCache( EnsureBlockquoteCache(
result.replyCache, result.replyCache,
result.msgReplyBarColor); [&] { return SimpleColorIndexValues(replyBar, false); });
EnsureBlockquoteCache(
result.replyCacheTwo,
[&] { return SimpleColorIndexValues(replyBar, true); });
if (!result.quoteCache) { if (!result.quoteCache) {
result.quoteCache = std::make_unique<Text::QuotePaintCache>( result.quoteCache = std::make_unique<Text::QuotePaintCache>(
*result.replyCache); *result.replyCache);
} }
if (!result.quoteCacheTwo) {
result.quoteCacheTwo = std::make_unique<Text::QuotePaintCache>(
*result.replyCacheTwo);
}
const auto preBgOverride = [&] { const auto preBgOverride = [&] {
const auto withBg = [&](const QColor &color) { return _dark ? QColor(0, 0, 0, 192) : std::optional<QColor>();
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<QColor>();
}; };
EnsurePreCache( EnsurePreCache(
result.preCache, result.preCache,
@ -634,14 +741,37 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
return result; return result;
} }
not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache() const { not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache(
EnsureBlockquoteCache(_serviceQuoteCache, msgServiceFg()); bool twoColored) const {
return _serviceQuoteCache.get(); const auto index = (twoColored ? 1 : 0);
const auto &service = msgServiceFg()->c;
EnsureBlockquoteCache(
_serviceQuoteCache[index],
[&] { return SimpleColorIndexValues(service, twoColored); });
return _serviceQuoteCache[index].get();
} }
not_null<Text::QuotePaintCache*> ChatStyle::serviceReplyCache() const { not_null<Text::QuotePaintCache*> ChatStyle::serviceReplyCache(
EnsureBlockquoteCache(_serviceReplyCache, msgServiceFg()); bool twoColored) const {
return _serviceReplyCache.get(); 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( const style::TextPalette &ChatStyle::coloredTextPalette(
@ -651,14 +781,14 @@ const style::TextPalette &ChatStyle::coloredTextPalette(
const auto shift = (selected ? kColorIndexCount : 0); const auto shift = (selected ? kColorIndexCount : 0);
auto &result = _coloredTextPalettes[shift + colorIndex]; auto &result = _coloredTextPalettes[shift + colorIndex];
if (!result.inited) { if (!result.linkFg) {
result.inited = true; result.linkFg.emplace(FromNameFg(this, selected, colorIndex));
make( make(
result.data, result.data,
(selected (selected
? st::inReplyTextPaletteSelected ? st::inReplyTextPaletteSelected
: st::inReplyTextPalette)); : st::inReplyTextPalette));
result.data.linkFg = FromNameFg(this, selected, colorIndex); result.data.linkFg = result.linkFg->color();
result.data.selectLinkFg = result.data.linkFg; result.data.selectLinkFg = result.data.linkFg;
} }
return result.data; return result.data;
@ -684,7 +814,9 @@ not_null<Text::QuotePaintCache*> ChatStyle::coloredCache(
const auto shift = (selected ? kColorIndexCount : 0); const auto shift = (selected ? kColorIndexCount : 0);
auto &cache = caches[shift + colorIndex]; auto &cache = caches[shift + colorIndex];
EnsureBlockquoteCache(cache, FromNameFg(this, selected, colorIndex)); EnsureBlockquoteCache(cache, [&] {
return coloredValues(selected, colorIndex);
});
return cache.get(); return cache.get();
} }
@ -816,47 +948,21 @@ void ChatStyle::make(
} }
uint8 DecideColorIndex(uint64 id) { uint8 DecideColorIndex(uint64 id) {
return id % kColorIndexCount; return id % kSimpleColorIndexCount;
} }
uint8 ColorIndexToPaletteIndex(uint8 colorIndex) { uint8 ColorIndexToPaletteIndex(uint8 colorIndex) {
Expects(colorIndex >= 0 && colorIndex < kColorIndexCount); Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
const int8 map[] = { 0, 7, 4, 1, 6, 3, 5 }; const int8 map[] = { 0, 7, 4, 1, 6, 3, 5 };
return map[colorIndex]; return map[colorIndex % kSimpleColorIndexCount];
} }
style::color FromNameFg( QColor FromNameFg(
not_null<const ChatStyle*> st, not_null<const ChatStyle*> st,
bool selected, bool selected,
uint8 colorIndex) { uint8 colorIndex) {
Expects(colorIndex >= 0 && colorIndex < kColorIndexCount); return st->coloredValues(selected, colorIndex).name;
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)];
}
} }
void FillComplexOverlayRect( void FillComplexOverlayRect(

View file

@ -27,7 +27,8 @@ class ChatTheme;
class ChatStyle; class ChatStyle;
struct BubblePattern; struct BubblePattern;
inline constexpr auto kColorIndexCount = uint8(7); inline constexpr auto kColorIndexCount = uint8(14);
inline constexpr auto kSimpleColorIndexCount = uint8(7);
struct MessageStyle { struct MessageStyle {
CornersPixmaps msgBgCornersSmall; CornersPixmaps msgBgCornersSmall;
@ -79,7 +80,9 @@ struct MessageStyle {
style::icon historyTranscribeIcon = { Qt::Uninitialized }; style::icon historyTranscribeIcon = { Qt::Uninitialized };
style::icon historyTranscribeHide = { Qt::Uninitialized }; style::icon historyTranscribeHide = { Qt::Uninitialized };
std::unique_ptr<Text::QuotePaintCache> quoteCache; std::unique_ptr<Text::QuotePaintCache> quoteCache;
std::unique_ptr<Text::QuotePaintCache> quoteCacheTwo;
std::unique_ptr<Text::QuotePaintCache> replyCache; std::unique_ptr<Text::QuotePaintCache> replyCache;
std::unique_ptr<Text::QuotePaintCache> replyCacheTwo;
std::unique_ptr<Text::QuotePaintCache> preCache; std::unique_ptr<Text::QuotePaintCache> preCache;
}; };
@ -116,8 +119,6 @@ struct ChatPaintContext {
QRect viewport; QRect viewport;
QRect clip; QRect clip;
TextSelection selection; TextSelection selection;
bool outbg = false;
bool paused = false;
crl::time now = 0; crl::time now = 0;
void translate(int x, int y) { void translate(int x, int y) {
@ -133,6 +134,8 @@ struct ChatPaintContext {
} }
[[nodiscard]] not_null<const MessageStyle*> messageStyle() const; [[nodiscard]] not_null<const MessageStyle*> messageStyle() const;
[[nodiscard]] not_null<const MessageImageStyle*> imageStyle() const; [[nodiscard]] not_null<const MessageImageStyle*> imageStyle() const;
[[nodiscard]] not_null<Text::QuotePaintCache*> quoteCache(
uint8 colorIndex) const;
[[nodiscard]] ChatPaintContext translated(int x, int y) const { [[nodiscard]] ChatPaintContext translated(int x, int y) const {
auto result = *this; auto result = *this;
@ -157,12 +160,27 @@ struct ChatPaintContext {
}; };
SkipDrawingParts skipDrawingParts = SkipDrawingParts::None; SkipDrawingParts skipDrawingParts = SkipDrawingParts::None;
bool outbg = false;
bool paused = false;
}; };
[[nodiscard]] int HistoryServiceMsgRadius(); [[nodiscard]] int HistoryServiceMsgRadius();
[[nodiscard]] int HistoryServiceMsgInvertedRadius(); [[nodiscard]] int HistoryServiceMsgInvertedRadius();
[[nodiscard]] int HistoryServiceMsgInvertedShrink(); [[nodiscard]] int HistoryServiceMsgInvertedShrink();
struct ColorIndexValues {
QColor name;
QColor bg;
QColor outline1;
QColor outline2;
};
[[nodiscard]] ColorIndexValues ComputeColorIndexValues(
not_null<const ChatStyle*> st,
bool selected,
uint8 colorIndex);
[[nodiscard]] bool ColorIndexTwoColored(uint8 colorIndex);
class ChatStyle final : public style::palette { class ChatStyle final : public style::palette {
public: public:
ChatStyle(); ChatStyle();
@ -172,6 +190,10 @@ public:
void applyCustomPalette(const style::palette *palette); void applyCustomPalette(const style::palette *palette);
void applyAdjustedServiceBg(QColor serviceBg); void applyAdjustedServiceBg(QColor serviceBg);
[[nodiscard]] bool dark() const {
return _dark;
}
[[nodiscard]] std::span<Text::SpecialColor> highlightColors() const; [[nodiscard]] std::span<Text::SpecialColor> highlightColors() const;
[[nodiscard]] rpl::producer<> paletteChanged() const { [[nodiscard]] rpl::producer<> paletteChanged() const {
@ -202,10 +224,13 @@ public:
bool selected) const; bool selected) const;
[[nodiscard]] const MessageImageStyle &imageStyle(bool selected) const; [[nodiscard]] const MessageImageStyle &imageStyle(bool selected) const;
[[nodiscard]] auto serviceQuoteCache() const [[nodiscard]] auto serviceQuoteCache(bool twoColored) const
-> not_null<Text::QuotePaintCache*>; -> not_null<Text::QuotePaintCache*>;
[[nodiscard]] auto serviceReplyCache() const [[nodiscard]] auto serviceReplyCache(bool twoColored) const
-> not_null<Text::QuotePaintCache*>; -> not_null<Text::QuotePaintCache*>;
[[nodiscard]] const ColorIndexValues &coloredValues(
bool selected,
uint8 colorIndex) const;
[[nodiscard]] not_null<Text::QuotePaintCache*> coloredQuoteCache( [[nodiscard]] not_null<Text::QuotePaintCache*> coloredQuoteCache(
bool selected, bool selected,
uint8 colorIndex) const; uint8 colorIndex) const;
@ -307,11 +332,12 @@ private:
kColorIndexCount * 2>; kColorIndexCount * 2>;
struct ColoredPalette { struct ColoredPalette {
std::optional<style::owned_color> linkFg;
style::TextPalette data; style::TextPalette data;
bool inited = false;
}; };
void assignPalette(not_null<const style::palette*> palette); void assignPalette(not_null<const style::palette*> palette);
void updateDarkValue();
[[nodiscard]] not_null<Text::QuotePaintCache*> coloredCache( [[nodiscard]] not_null<Text::QuotePaintCache*> coloredCache(
ColoredQuotePaintCaches &caches, ColoredQuotePaintCaches &caches,
@ -368,8 +394,15 @@ private:
int(CachedCornerRadius::kCount)]; int(CachedCornerRadius::kCount)];
mutable std::vector<Text::SpecialColor> _highlightColors; mutable std::vector<Text::SpecialColor> _highlightColors;
mutable std::unique_ptr<Text::QuotePaintCache> _serviceQuoteCache; mutable std::array<
mutable std::unique_ptr<Text::QuotePaintCache> _serviceReplyCache; std::unique_ptr<Text::QuotePaintCache>,
2> _serviceQuoteCache;
mutable std::array<
std::unique_ptr<Text::QuotePaintCache>,
2> _serviceReplyCache;
mutable std::array<
std::optional<ColorIndexValues>,
2 * kColorIndexCount> _coloredValues;
mutable ColoredQuotePaintCaches _coloredQuoteCaches; mutable ColoredQuotePaintCaches _coloredQuoteCaches;
mutable ColoredQuotePaintCaches _coloredReplyCaches; mutable ColoredQuotePaintCaches _coloredReplyCaches;
mutable std::array< mutable std::array<
@ -403,6 +436,8 @@ private:
style::icon _historyPollChoiceRight = { Qt::Uninitialized }; style::icon _historyPollChoiceRight = { Qt::Uninitialized };
style::icon _historyPollChoiceWrong = { Qt::Uninitialized }; style::icon _historyPollChoiceWrong = { Qt::Uninitialized };
bool _dark = false;
rpl::event_stream<> _paletteChanged; rpl::event_stream<> _paletteChanged;
rpl::lifetime _defaultPaletteChangeLifetime; rpl::lifetime _defaultPaletteChangeLifetime;
@ -412,11 +447,17 @@ private:
[[nodiscard]] uint8 DecideColorIndex(uint64 id); [[nodiscard]] uint8 DecideColorIndex(uint64 id);
[[nodiscard]] uint8 ColorIndexToPaletteIndex(uint8 colorIndex); [[nodiscard]] uint8 ColorIndexToPaletteIndex(uint8 colorIndex);
[[nodiscard]] style::color FromNameFg( [[nodiscard]] QColor FromNameFg(
not_null<const ChatStyle*> st, not_null<const ChatStyle*> st,
bool selected, bool selected,
uint8 colorIndex); uint8 colorIndex);
[[nodiscard]] inline QColor FromNameFg(
const ChatPaintContext &context,
uint8 colorIndex) {
return FromNameFg(context.st, context.selected(), colorIndex);
}
void FillComplexOverlayRect( void FillComplexOverlayRect(
QPainter &p, QPainter &p,
QRect rect, QRect rect,

View file

@ -489,8 +489,8 @@ ChatPaintContext ChatTheme::preparePaintContext(
.bubblesPattern = _bubblesBackgroundPattern.get(), .bubblesPattern = _bubblesBackgroundPattern.get(),
.viewport = viewport, .viewport = viewport,
.clip = clip, .clip = clip,
.paused = paused,
.now = now, .now = now,
.paused = paused,
}; };
} }

@ -1 +1 @@
Subproject commit 4d1a5686a7620b163639492e1b81101b677d2472 Subproject commit 71d24af3a840d15a6cc0a3a8a1e9cbe77a604739