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 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 &quoteSt = 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<style::TextPalette>();
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),

View File

@ -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<History*> 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<Element*>(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<Element*>(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<HistoryMessageReply>()) {
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();
}

View File

@ -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<Flag>;
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

View File

@ -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<Ui::RippleAnimation> ripple;
std::vector<UserpicInRow> 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<ViewButton>(
sponsored,
index,
std::move(repainter))
: std::make_unique<ViewButton>(media, index, std::move(repainter));
auto make = [=](auto &&from) {
return std::make_unique<ViewButton>(
std::forward<decltype(from)>(from),
colorIndex(),
[=] { repaint(); });
};
_viewButton = sponsored ? make(sponsored) : make(media);
}
void Message::initLogEntryOriginal() {

View File

@ -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(

View File

@ -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();

View File

@ -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,

View File

@ -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,

View File

@ -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));

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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();

View File

@ -31,16 +31,16 @@ void EnsureCorners(
void EnsureBlockquoteCache(
std::unique_ptr<Text::QuotePaintCache> &cache,
const style::color &color) {
Fn<ColorIndexValues()> values) {
if (cache) {
return;
}
cache = std::make_unique<Text::QuotePaintCache>();
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<const MessageImageStyle*> ChatPaintContext::imageStyle() const {
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() {
static const auto result = [] {
const auto minMessageHeight = st::msgServicePadding.top()
@ -99,6 +109,87 @@ int HistoryServiceMsgInvertedShrink() {
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() {
finalize();
make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
@ -453,6 +544,8 @@ ChatStyle::ChatStyle() {
&MessageImageStyle::historyVideoMessageMute,
st::historyVideoMessageMute,
st::historyVideoMessageMuteSelected);
updateDarkValue();
}
ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
@ -464,6 +557,13 @@ void ChatStyle::apply(not_null<ChatTheme*> 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<const style::palette*> 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<Text::QuotePaintCache>(
*result.replyCache);
}
if (!result.quoteCacheTwo) {
result.quoteCacheTwo = std::make_unique<Text::QuotePaintCache>(
*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<QColor>();
return _dark ? QColor(0, 0, 0, 192) : std::optional<QColor>();
};
EnsurePreCache(
result.preCache,
@ -634,14 +741,37 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
return result;
}
not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache() const {
EnsureBlockquoteCache(_serviceQuoteCache, msgServiceFg());
return _serviceQuoteCache.get();
not_null<Text::QuotePaintCache*> 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<Text::QuotePaintCache*> ChatStyle::serviceReplyCache() const {
EnsureBlockquoteCache(_serviceReplyCache, msgServiceFg());
return _serviceReplyCache.get();
not_null<Text::QuotePaintCache*> 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<Text::QuotePaintCache*> 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<const ChatStyle*> 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(

View File

@ -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<Text::QuotePaintCache> quoteCache;
std::unique_ptr<Text::QuotePaintCache> quoteCacheTwo;
std::unique_ptr<Text::QuotePaintCache> replyCache;
std::unique_ptr<Text::QuotePaintCache> replyCacheTwo;
std::unique_ptr<Text::QuotePaintCache> 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<const MessageStyle*> messageStyle() 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 {
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<const ChatStyle*> 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<Text::SpecialColor> 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<Text::QuotePaintCache*>;
[[nodiscard]] auto serviceReplyCache() const
[[nodiscard]] auto serviceReplyCache(bool twoColored) const
-> not_null<Text::QuotePaintCache*>;
[[nodiscard]] const ColorIndexValues &coloredValues(
bool selected,
uint8 colorIndex) const;
[[nodiscard]] not_null<Text::QuotePaintCache*> coloredQuoteCache(
bool selected,
uint8 colorIndex) const;
@ -307,11 +332,12 @@ private:
kColorIndexCount * 2>;
struct ColoredPalette {
std::optional<style::owned_color> linkFg;
style::TextPalette data;
bool inited = false;
};
void assignPalette(not_null<const style::palette*> palette);
void updateDarkValue();
[[nodiscard]] not_null<Text::QuotePaintCache*> coloredCache(
ColoredQuotePaintCaches &caches,
@ -368,8 +394,15 @@ private:
int(CachedCornerRadius::kCount)];
mutable std::vector<Text::SpecialColor> _highlightColors;
mutable std::unique_ptr<Text::QuotePaintCache> _serviceQuoteCache;
mutable std::unique_ptr<Text::QuotePaintCache> _serviceReplyCache;
mutable std::array<
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 _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<const ChatStyle*> 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,

View File

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

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