mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support server-side colors by index, up to three.
This commit is contained in:
parent
cc8408d11c
commit
5d335341ab
21 changed files with 391 additions and 167 deletions
|
@ -175,7 +175,8 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
|||
, _controller(controller)
|
||||
, _forPeer(args.forPeer)
|
||||
, _fromMessageId(args.fromMessageId)
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(
|
||||
controller->session().colorIndicesValue()))
|
||||
, _serviceHistory(_controller->session().data().history(
|
||||
PeerData::kServiceNotificationsId))
|
||||
, _service(nullptr)
|
||||
|
|
|
@ -133,7 +133,8 @@ void AddMessage(
|
|||
state->delegate = std::make_unique<Delegate>(
|
||||
controller,
|
||||
crl::guard(widget, [=] { widget->update(); }));
|
||||
state->style = std::make_unique<Ui::ChatStyle>();
|
||||
state->style = std::make_unique<Ui::ChatStyle>(
|
||||
controller->session().colorIndicesValue());
|
||||
state->style->apply(controller->defaultChatTheme().get());
|
||||
state->icons.lifetimes = std::vector<rpl::lifetime>(2);
|
||||
|
||||
|
|
|
@ -889,7 +889,7 @@ bool PeerData::changeColorIndex(uint8 index) {
|
|||
if (_colorIndexCloud && _colorIndex == index) {
|
||||
return false;
|
||||
}
|
||||
_colorIndexCloud = true;
|
||||
_colorIndexCloud = 1;
|
||||
_colorIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
@ -898,7 +898,7 @@ bool PeerData::clearColorIndex() {
|
|||
if (!_colorIndexCloud) {
|
||||
return false;
|
||||
}
|
||||
_colorIndexCloud = false;
|
||||
_colorIndexCloud = 0;
|
||||
_colorIndex = Data::DecideColorIndex(id);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -848,19 +848,20 @@ void HistoryMessageReply::paint(
|
|||
? (resolvedMessage->hiddenSenderInfo()->colorIndex + 1)
|
||||
: 0;
|
||||
const auto useColorIndex = colorIndexPlusOne && !context.outbg;
|
||||
const auto twoColored = colorIndexPlusOne
|
||||
&& Ui::ColorIndexTwoColored(colorIndexPlusOne - 1);
|
||||
const auto colorPattern = colorIndexPlusOne
|
||||
? st->colorPatternIndex(colorIndexPlusOne - 1)
|
||||
: 0;
|
||||
const auto cache = !inBubble
|
||||
? (hasQuote
|
||||
? st->serviceQuoteCache(twoColored)
|
||||
: st->serviceReplyCache(twoColored)).get()
|
||||
? st->serviceQuoteCache(colorPattern)
|
||||
: st->serviceReplyCache(colorPattern)).get()
|
||||
: useColorIndex
|
||||
? (hasQuote
|
||||
? st->coloredQuoteCache(selected, colorIndexPlusOne - 1)
|
||||
: st->coloredReplyCache(selected, colorIndexPlusOne - 1)).get()
|
||||
: (hasQuote
|
||||
? (twoColored ? stm->quoteCacheTwo : stm->quoteCache)
|
||||
: (twoColored ? stm->replyCacheTwo : stm->replyCache)).get();
|
||||
? stm->quoteCache[colorPattern]
|
||||
: stm->replyCache[colorPattern]).get();
|
||||
const auto "eSt = hasQuote
|
||||
? st::messageTextStyle.blockquote
|
||||
: st::messageQuoteStyle;
|
||||
|
|
|
@ -196,7 +196,8 @@ PreviewWrap::PreviewWrap(
|
|||
, _box(box)
|
||||
, _history(history)
|
||||
, _theme(DefaultThemeOn(lifetime()))
|
||||
, _style(std::make_unique<Ui::ChatStyle>())
|
||||
, _style(std::make_unique<Ui::ChatStyle>(
|
||||
history->session().colorIndicesValue()))
|
||||
, _delegate(std::make_unique<PreviewDelegate>(
|
||||
box,
|
||||
_style.get(),
|
||||
|
|
|
@ -232,13 +232,12 @@ void ViewButton::draw(
|
|||
Painter &p,
|
||||
const QRect &r,
|
||||
const Ui::ChatPaintContext &context) {
|
||||
const auto st = context.st;
|
||||
const auto stm = context.messageStyle();
|
||||
|
||||
const auto selected = context.selected();
|
||||
const auto twoColored = Ui::ColorIndexTwoColored(_inner->colorIndex);
|
||||
const auto cache = context.outbg
|
||||
? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
|
||||
: context.st->coloredReplyCache(selected, _inner->colorIndex).get();
|
||||
? stm->replyCache[st->colorPatternIndex(_inner->colorIndex)].get()
|
||||
: st->coloredReplyCache(selected, _inner->colorIndex).get();
|
||||
const auto radius = st::historyPagePreview.radius;
|
||||
|
||||
if (_inner->ripple && !_inner->ripple->empty()) {
|
||||
|
|
|
@ -220,9 +220,8 @@ void Game::draw(Painter &p, const PaintContext &context) const {
|
|||
|
||||
const auto colorIndex = parent()->colorIndex();
|
||||
const auto selected = context.selected();
|
||||
const auto twoColored = Ui::ColorIndexTwoColored(colorIndex);
|
||||
const auto cache = context.outbg
|
||||
? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
|
||||
? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
|
||||
: st->coloredReplyCache(selected, colorIndex).get();
|
||||
Ui::Text::ValidateQuotePaintCache(*cache, _st);
|
||||
Ui::Text::FillQuotePaint(p, outer, *cache, _st);
|
||||
|
|
|
@ -344,13 +344,13 @@ void Giveaway::paintChannels(
|
|||
|
||||
const auto size = _channels[0].geometry.height();
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto st = context.st;
|
||||
const auto stm = context.messageStyle();
|
||||
const auto selected = context.selected();
|
||||
const auto colorIndex = parent()->colorIndex();
|
||||
const auto twoColored = Ui::ColorIndexTwoColored(colorIndex);
|
||||
const auto cache = context.outbg
|
||||
? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
|
||||
: context.st->coloredReplyCache(selected, colorIndex).get();
|
||||
? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
|
||||
: st->coloredReplyCache(selected, colorIndex).get();
|
||||
if (_channelCorners[0].isNull() || _channelBg != cache->bg) {
|
||||
_channelBg = cache->bg;
|
||||
_channelCorners = Images::CornersMask(size / 2);
|
||||
|
|
|
@ -523,9 +523,8 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
|
||||
const auto selected = context.selected();
|
||||
const auto colorIndex = parent()->colorIndex();
|
||||
const auto twoColored = Ui::ColorIndexTwoColored(colorIndex);
|
||||
const auto cache = context.outbg
|
||||
? (twoColored ? stm->replyCacheTwo : stm->replyCache).get()
|
||||
? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
|
||||
: st->coloredReplyCache(selected, colorIndex).get();
|
||||
Ui::Text::ValidateQuotePaintCache(*cache, _st);
|
||||
Ui::Text::FillQuotePaint(p, outer, *cache, _st);
|
||||
|
|
|
@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "main/main_app_config.h"
|
||||
|
||||
#include "main/main_account.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "main/main_account.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
|
||||
namespace Main {
|
||||
namespace {
|
||||
|
@ -27,6 +28,8 @@ AppConfig::AppConfig(not_null<Account*> account) : _account(account) {
|
|||
}, _lifetime);
|
||||
}
|
||||
|
||||
AppConfig::~AppConfig() = default;
|
||||
|
||||
void AppConfig::start() {
|
||||
_account->mtpMainSessionValue(
|
||||
) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
|
||||
|
@ -58,6 +61,7 @@ void AppConfig::refresh() {
|
|||
_data.emplace_or_assign(qs(data.vkey()), data.vvalue());
|
||||
});
|
||||
}
|
||||
parseColorIndices();
|
||||
DEBUG_LOG(("getAppConfig result handled."));
|
||||
_refreshed.fire({});
|
||||
}, [](const MTPDhelp_appConfigNotModified &) {});
|
||||
|
@ -171,6 +175,27 @@ std::vector<std::map<QString, QString>> AppConfig::getStringMapArray(
|
|||
});
|
||||
}
|
||||
|
||||
std::vector<int> AppConfig::getIntArray(
|
||||
const QString &key,
|
||||
std::vector<int> &&fallback) const {
|
||||
return getValue(key, [&](const MTPJSONValue &value) {
|
||||
return value.match([&](const MTPDjsonArray &data) {
|
||||
auto result = std::vector<int>();
|
||||
result.reserve(data.vvalue().v.size());
|
||||
for (const auto &entry : data.vvalue().v) {
|
||||
if (entry.type() != mtpc_jsonNumber) {
|
||||
return std::move(fallback);
|
||||
}
|
||||
result.push_back(
|
||||
int(base::SafeRound(entry.c_jsonNumber().vvalue().v)));
|
||||
}
|
||||
return result;
|
||||
}, [&](const auto &data) {
|
||||
return std::move(fallback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool AppConfig::suggestionCurrent(const QString &key) const {
|
||||
return !_dismissedSuggestions.contains(key)
|
||||
&& ranges::contains(
|
||||
|
@ -199,4 +224,121 @@ void AppConfig::dismissSuggestion(const QString &key) {
|
|||
)).send();
|
||||
}
|
||||
|
||||
void AppConfig::parseColorIndices() {
|
||||
constexpr auto parseColor = [](const MTPJSONValue &color) {
|
||||
if (color.type() != mtpc_jsonString) {
|
||||
LOG(("API Error: Bad type for color element."));
|
||||
return uint32();
|
||||
}
|
||||
const auto value = color.c_jsonString().vvalue().v;
|
||||
if (value.size() != 6) {
|
||||
LOG(("API Error: Bad length for color element: %1"
|
||||
).arg(qs(value)));
|
||||
return uint32();
|
||||
}
|
||||
const auto hex = [](char ch) {
|
||||
return (ch >= 'a' && ch <= 'f')
|
||||
? (ch - 'a' + 10)
|
||||
: (ch >= 'A' && ch <= 'F')
|
||||
? (ch - 'A' + 10)
|
||||
: (ch >= '0' && ch <= '9')
|
||||
? (ch - '0')
|
||||
: 0;
|
||||
};
|
||||
auto result = (uint32(1) << 24);
|
||||
for (auto i = 0; i != 6; ++i) {
|
||||
result |= (uint32(hex(value[i])) << ((5 - i) * 4));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
struct ParsedColor {
|
||||
uint8 colorIndex = Ui::kColorIndexCount;
|
||||
std::array<uint32, Ui::kColorPatternsCount> colors;
|
||||
|
||||
explicit operator bool() const {
|
||||
return colorIndex < Ui::kColorIndexCount;
|
||||
}
|
||||
};
|
||||
constexpr auto parseColors = [](const MTPJSONObjectValue &element) {
|
||||
const auto &data = element.data();
|
||||
if (data.vvalue().type() != mtpc_jsonArray) {
|
||||
LOG(("API Error: Bad value for peer_colors element."));
|
||||
return ParsedColor();
|
||||
}
|
||||
const auto &list = data.vvalue().c_jsonArray().vvalue().v;
|
||||
if (list.empty() || list.size() > Ui::kColorPatternsCount) {
|
||||
LOG(("API Error: Bad count for peer_colors element: %1"
|
||||
).arg(list.size()));
|
||||
return ParsedColor();
|
||||
}
|
||||
const auto index = data.vkey().v.toInt();
|
||||
if (index < Ui::kSimpleColorIndexCount
|
||||
|| index >= Ui::kColorIndexCount) {
|
||||
LOG(("API Error: Bad index for peer_colors element: %1"
|
||||
).arg(qs(data.vkey().v)));
|
||||
return ParsedColor();
|
||||
}
|
||||
auto result = ParsedColor{ .colorIndex = uint8(index) };
|
||||
auto fill = result.colors.data();
|
||||
for (const auto &color : list) {
|
||||
*fill++ = parseColor(color);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
constexpr auto checkColorsObjectType = [](const MTPJSONValue &value) {
|
||||
if (value.type() != mtpc_jsonObject) {
|
||||
if (value.type() != mtpc_jsonArray
|
||||
|| !value.c_jsonArray().vvalue().v.empty()) {
|
||||
LOG(("API Error: Bad value for [dark_]peer_colors."));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto colors = std::make_shared<
|
||||
std::array<Ui::ColorIndexData, Ui::kColorIndexCount>>();
|
||||
getValue(u"peer_colors"_q, [&](const MTPJSONValue &value) {
|
||||
if (!checkColorsObjectType(value)) {
|
||||
return;
|
||||
}
|
||||
for (const auto &element : value.c_jsonObject().vvalue().v) {
|
||||
if (const auto parsed = parseColors(element)) {
|
||||
auto &fields = (*colors)[parsed.colorIndex];
|
||||
fields.dark = fields.light = parsed.colors;
|
||||
}
|
||||
}
|
||||
});
|
||||
getValue(u"dark_peer_colors"_q, [&](const MTPJSONValue &value) {
|
||||
if (!checkColorsObjectType(value)) {
|
||||
return;
|
||||
}
|
||||
for (const auto &element : value.c_jsonObject().vvalue().v) {
|
||||
if (const auto parsed = parseColors(element)) {
|
||||
(*colors)[parsed.colorIndex].dark = parsed.colors;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!_colorIndicesCurrent) {
|
||||
_colorIndicesCurrent = std::make_unique<Ui::ColorIndicesCompressed>(
|
||||
Ui::ColorIndicesCompressed{ std::move(colors) });
|
||||
_colorIndicesChanged.fire({});
|
||||
} else if (*_colorIndicesCurrent->colors != *colors) {
|
||||
_colorIndicesCurrent->colors = std::move(colors);
|
||||
_colorIndicesChanged.fire({});
|
||||
}
|
||||
}
|
||||
|
||||
auto AppConfig::colorIndicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed> {
|
||||
return rpl::single(_colorIndicesCurrent
|
||||
? *_colorIndicesCurrent
|
||||
: Ui::ColorIndicesCompressed()
|
||||
) | rpl::then(_colorIndicesChanged.events() | rpl::map([=] {
|
||||
return *_colorIndicesCurrent;
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace Main
|
||||
|
|
|
@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mtproto/sender.h"
|
||||
#include "base/algorithm.h"
|
||||
|
||||
namespace Ui {
|
||||
struct ColorIndicesCompressed;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
|
||||
class Account;
|
||||
|
@ -17,6 +21,7 @@ class Account;
|
|||
class AppConfig final {
|
||||
public:
|
||||
explicit AppConfig(not_null<Account*> account);
|
||||
~AppConfig();
|
||||
|
||||
void start();
|
||||
|
||||
|
@ -30,6 +35,8 @@ public:
|
|||
return getString(key, fallback);
|
||||
} else if constexpr (std::is_same_v<Type, std::vector<QString>>) {
|
||||
return getStringArray(key, std::move(fallback));
|
||||
} else if constexpr (std::is_same_v<Type, std::vector<int>>) {
|
||||
return getIntArray(key, std::move(fallback));
|
||||
} else if constexpr (std::is_same_v<
|
||||
Type,
|
||||
std::vector<std::map<QString, QString>>>) {
|
||||
|
@ -47,10 +54,14 @@ public:
|
|||
const QString &key) const;
|
||||
void dismissSuggestion(const QString &key);
|
||||
|
||||
[[nodiscard]] auto colorIndicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed>;
|
||||
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
void refreshDelayed();
|
||||
void parseColorIndices();
|
||||
|
||||
template <typename Extractor>
|
||||
[[nodiscard]] auto getValue(
|
||||
|
@ -72,6 +83,9 @@ private:
|
|||
[[nodiscard]] std::vector<std::map<QString, QString>> getStringMapArray(
|
||||
const QString &key,
|
||||
std::vector<std::map<QString, QString>> &&fallback) const;
|
||||
[[nodiscard]] std::vector<int> getIntArray(
|
||||
const QString &key,
|
||||
std::vector<int> &&fallback) const;
|
||||
|
||||
const not_null<Account*> _account;
|
||||
std::optional<MTP::Sender> _api;
|
||||
|
@ -80,6 +94,10 @@ private:
|
|||
base::flat_map<QString, MTPJSONValue> _data;
|
||||
rpl::event_stream<> _refreshed;
|
||||
base::flat_set<QString> _dismissedSuggestions;
|
||||
|
||||
rpl::event_stream<> _colorIndicesChanged;
|
||||
std::unique_ptr<Ui::ColorIndicesCompressed> _colorIndicesCurrent;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -476,4 +476,9 @@ Window::SessionController *Session::tryResolveWindow() const {
|
|||
return _windows.front();
|
||||
}
|
||||
|
||||
auto Session::colorIndicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed> {
|
||||
return _account->appConfig().colorIndicesValue();
|
||||
}
|
||||
|
||||
} // namespace Main
|
||||
|
|
|
@ -57,6 +57,10 @@ namespace InlineBots {
|
|||
class AttachWebView;
|
||||
} // namespace InlineBots
|
||||
|
||||
namespace Ui {
|
||||
struct ColorIndicesCompressed;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
|
||||
class Account;
|
||||
|
@ -187,6 +191,9 @@ public:
|
|||
[[nodiscard]] Support::Helper &supportHelper() const;
|
||||
[[nodiscard]] Support::Templates &supportTemplates() const;
|
||||
|
||||
[[nodiscard]] auto colorIndicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed>;
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
||||
|
||||
|
@ -227,6 +234,8 @@ private:
|
|||
QByteArray _tmpPassword;
|
||||
TimeId _tmpPasswordValidUntil = 0;
|
||||
|
||||
rpl::event_stream<Ui::ColorIndicesCompressed> _colorIndicesChanges;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -157,7 +157,7 @@ ReactionView::ReactionView(
|
|||
const Data::SuggestedReaction &reaction)
|
||||
: RpWidget(parent)
|
||||
, _data(reaction)
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(session->colorIndicesValue()))
|
||||
, _pathGradient(
|
||||
std::make_unique<Ui::PathShiftGradient>(
|
||||
st::shadowFg,
|
||||
|
|
|
@ -839,7 +839,9 @@ ForwardsPrivacyController::ForwardsPrivacyController(
|
|||
not_null<Window::SessionController*> controller)
|
||||
: SimpleElementDelegate(controller, [] {})
|
||||
, _controller(controller)
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>()) {
|
||||
, _chatStyle(
|
||||
std::make_unique<Ui::ChatStyle>(
|
||||
controller->session().colorIndicesValue())) {
|
||||
_chatStyle->apply(controller->defaultChatTheme().get());
|
||||
}
|
||||
|
||||
|
|
|
@ -506,7 +506,8 @@ ConfirmContactBox::ConfirmContactBox(
|
|||
const Contact &data,
|
||||
Fn<void(Qt::KeyboardModifiers)> submit)
|
||||
: SimpleElementDelegate(controller, [=] { update(); })
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(
|
||||
history->session().colorIndicesValue()))
|
||||
, _comment(GenerateCommentItem(this, history, data))
|
||||
, _contact(GenerateContactItem(this, history, data))
|
||||
, _submit(submit) {
|
||||
|
|
|
@ -50,6 +50,7 @@ messageQuoteStyle: QuoteStyle(defaultQuoteStyle) {
|
|||
padding: margins(10px, 2px, 4px, 2px);
|
||||
verticalSkip: 4px;
|
||||
outline: 3px;
|
||||
outlineShift: 2px;
|
||||
radius: 5px;
|
||||
}
|
||||
messageTextStyle: TextStyle(defaultTextStyle) {
|
||||
|
|
|
@ -39,8 +39,7 @@ void EnsureBlockquoteCache(
|
|||
cache = std::make_unique<Text::QuotePaintCache>();
|
||||
const auto &colors = values();
|
||||
cache->bg = colors.bg;
|
||||
cache->outline1 = colors.outline1;
|
||||
cache->outline2 = colors.outline2;
|
||||
cache->outlines = colors.outlines;
|
||||
cache->icon = colors.name;
|
||||
}
|
||||
|
||||
|
@ -55,15 +54,15 @@ void EnsurePreCache(
|
|||
const auto bg = bgOverride();
|
||||
cache->bg = bg.value_or(color->c);
|
||||
if (!bg) {
|
||||
cache->bg.setAlphaF(0.12);
|
||||
cache->bg.setAlpha(0.12 * 255);
|
||||
}
|
||||
cache->outline1 = color->c;
|
||||
cache->outline1.setAlphaF(0.9);
|
||||
cache->outline2 = cache->outline1;
|
||||
cache->outlines[0] = color->c;
|
||||
cache->outlines[0].setAlpha(0.9 * 255);
|
||||
cache->outlines[1] = cache->outlines[2] = QColor(0, 0, 0, 0);
|
||||
cache->header = color->c;
|
||||
cache->header.setAlphaF(0.25);
|
||||
cache->icon = cache->outline1;
|
||||
cache->icon.setAlphaF(0.6);
|
||||
cache->header.setAlpha(0.25 * 255);
|
||||
cache->icon = cache->outlines[0];
|
||||
cache->icon.setAlpha(0.6 * 255);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -80,9 +79,8 @@ not_null<Text::QuotePaintCache*> ChatPaintContext::quoteCache(
|
|||
uint8 colorIndex) const {
|
||||
return !outbg
|
||||
? st->coloredQuoteCache(selected(), colorIndex).get()
|
||||
: ColorIndexTwoColored(colorIndex)
|
||||
? messageStyle()->quoteCacheTwo.get()
|
||||
: messageStyle()->quoteCache.get();
|
||||
: messageStyle()->quoteCache[
|
||||
st->colorPatternIndex(colorIndex)].get();
|
||||
}
|
||||
|
||||
int HistoryServiceMsgRadius() {
|
||||
|
@ -110,85 +108,28 @@ 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) {
|
||||
ColorIndexValues SimpleColorIndexValues(QColor color, int patternIndex) {
|
||||
auto bg = color;
|
||||
bg.setAlphaF(0.12);
|
||||
auto outline1 = color;
|
||||
outline1.setAlphaF(0.9);
|
||||
auto outline2 = outline1;
|
||||
if (twoColored) {
|
||||
outline2.setAlphaF(0.5);
|
||||
}
|
||||
return {
|
||||
bg.setAlpha(0.12 * 255);
|
||||
auto result = ColorIndexValues{
|
||||
.name = color,
|
||||
.bg = bg,
|
||||
.outline1 = outline1,
|
||||
.outline2 = outline2,
|
||||
};
|
||||
result.outlines[0] = color;
|
||||
result.outlines[0].setAlpha(0.9 * 255);
|
||||
if (patternIndex > 1) {
|
||||
result.outlines[1] = result.outlines[0];
|
||||
result.outlines[1].setAlpha(0.3 * 255);
|
||||
result.outlines[2] = result.outlines[0];
|
||||
result.outlines[2].setAlpha(0.6 * 255);
|
||||
} else if (patternIndex > 0) {
|
||||
result.outlines[1] = result.outlines[0];
|
||||
result.outlines[1].setAlpha(0.5 * 255);
|
||||
result.outlines[2] = QColor(0, 0, 0, 0);
|
||||
} else {
|
||||
result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int BackgroundEmojiData::CacheIndex(
|
||||
|
@ -202,7 +143,15 @@ int BackgroundEmojiData::CacheIndex(
|
|||
return (base * 2) + (selected ? 1 : 0);
|
||||
};
|
||||
|
||||
ChatStyle::ChatStyle() {
|
||||
ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
|
||||
if (colorIndices) {
|
||||
_colorIndicesLifetime = std::move(
|
||||
colorIndices
|
||||
) | rpl::start_with_next([=](ColorIndicesCompressed &&indices) {
|
||||
_colorIndices = std::move(indices);
|
||||
});
|
||||
}
|
||||
|
||||
finalize();
|
||||
make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
|
||||
make(_imgReplyTextPalette, st::imgReplyTextPalette);
|
||||
|
@ -561,7 +510,7 @@ ChatStyle::ChatStyle() {
|
|||
}
|
||||
|
||||
ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
|
||||
: ChatStyle() {
|
||||
: ChatStyle(rpl::producer<ColorIndicesCompressed>()) {
|
||||
assignPalette(isolated);
|
||||
}
|
||||
|
||||
|
@ -631,17 +580,41 @@ std::span<Text::SpecialColor> ChatStyle::highlightColors() const {
|
|||
return _highlightColors;
|
||||
}
|
||||
|
||||
void ChatStyle::clearColorIndexCaches() {
|
||||
for (auto &style : _messageStyles) {
|
||||
for (auto &cache : style.quoteCache) {
|
||||
cache = nullptr;
|
||||
}
|
||||
for (auto &cache : style.replyCache) {
|
||||
cache = nullptr;
|
||||
}
|
||||
}
|
||||
for (auto &values : _coloredValues) {
|
||||
values.reset();
|
||||
}
|
||||
for (auto &palette : _coloredTextPalettes) {
|
||||
palette.linkFg.reset();
|
||||
}
|
||||
for (auto &cache : _coloredReplyCaches) {
|
||||
cache = nullptr;
|
||||
}
|
||||
for (auto &cache : _coloredQuoteCaches) {
|
||||
cache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
|
||||
*static_cast<style::palette*>(this) = *palette;
|
||||
style::internal::resetIcons();
|
||||
|
||||
clearColorIndexCaches();
|
||||
for (auto &style : _messageStyles) {
|
||||
style.msgBgCornersSmall = {};
|
||||
style.msgBgCornersLarge = {};
|
||||
style.quoteCache = nullptr;
|
||||
style.quoteCacheTwo = nullptr;
|
||||
style.replyCache = nullptr;
|
||||
style.replyCacheTwo = nullptr;
|
||||
style.preCache = nullptr;
|
||||
style.textPalette.linkAlwaysActive
|
||||
= style.semiboldPalette.linkAlwaysActive
|
||||
= (style.textPalette.linkFg->c == style.historyTextFg->c);
|
||||
}
|
||||
for (auto &style : _imageStyles) {
|
||||
style.msgDateImgBgCorners = {};
|
||||
|
@ -657,24 +630,6 @@ void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
|
|||
for (auto &corners : _msgSelectOverlayCorners) {
|
||||
corners = {};
|
||||
}
|
||||
|
||||
for (auto &stm : _messageStyles) {
|
||||
stm.textPalette.linkAlwaysActive
|
||||
= stm.semiboldPalette.linkAlwaysActive
|
||||
= (stm.textPalette.linkFg->c == stm.historyTextFg->c);
|
||||
}
|
||||
for (auto &values : _coloredValues) {
|
||||
values.reset();
|
||||
}
|
||||
for (auto &palette : _coloredTextPalettes) {
|
||||
palette.linkFg.reset();
|
||||
}
|
||||
for (auto &cache : _coloredReplyCaches) {
|
||||
cache = nullptr;
|
||||
}
|
||||
for (auto &cache : _coloredQuoteCaches) {
|
||||
cache = nullptr;
|
||||
}
|
||||
updateDarkValue();
|
||||
|
||||
_paletteChanged.fire({});
|
||||
|
@ -710,19 +665,14 @@ const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
|
|||
result.msgBg,
|
||||
&result.msgShadow);
|
||||
const auto &replyBar = result.msgReplyBarColor->c;
|
||||
EnsureBlockquoteCache(
|
||||
result.replyCache,
|
||||
[&] { 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);
|
||||
for (auto i = 0; i != kColorPatternsCount; ++i) {
|
||||
EnsureBlockquoteCache(
|
||||
result.replyCache[i],
|
||||
[&] { return SimpleColorIndexValues(replyBar, i); });
|
||||
if (!result.quoteCache[i]) {
|
||||
result.quoteCache[i] = std::make_unique<Text::QuotePaintCache>(
|
||||
*result.replyCache[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const auto preBgOverride = [&] {
|
||||
|
@ -763,6 +713,80 @@ const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
int ChatStyle::colorPatternIndex(uint8 colorIndex) const {
|
||||
Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
|
||||
|
||||
if (!_colorIndices.colors
|
||||
|| colorIndex < kSimpleColorIndexCount) {
|
||||
return 0;
|
||||
}
|
||||
auto &data = (*_colorIndices.colors)[colorIndex];
|
||||
auto &colors = _dark ? data.dark : data.light;
|
||||
return colors[2] ? 2 : colors[1] ? 1 : 0;
|
||||
}
|
||||
|
||||
ColorIndexValues ChatStyle::computeColorIndexValues(
|
||||
bool selected,
|
||||
uint8 colorIndex) const {
|
||||
if (!_colorIndices.colors) {
|
||||
colorIndex %= kSimpleColorIndexCount;
|
||||
}
|
||||
if (colorIndex < kSimpleColorIndexCount) {
|
||||
const auto list = std::array{
|
||||
&historyPeer1NameFg(),
|
||||
&historyPeer2NameFg(),
|
||||
&historyPeer3NameFg(),
|
||||
&historyPeer4NameFg(),
|
||||
&historyPeer5NameFg(),
|
||||
&historyPeer6NameFg(),
|
||||
&historyPeer7NameFg(),
|
||||
&historyPeer8NameFg(),
|
||||
};
|
||||
const auto listSelected = std::array{
|
||||
&historyPeer1NameFgSelected(),
|
||||
&historyPeer2NameFgSelected(),
|
||||
&historyPeer3NameFgSelected(),
|
||||
&historyPeer4NameFgSelected(),
|
||||
&historyPeer5NameFgSelected(),
|
||||
&historyPeer6NameFgSelected(),
|
||||
&historyPeer7NameFgSelected(),
|
||||
&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.outlines[0] = result.name;
|
||||
result.outlines[0].setAlphaF(0.9);
|
||||
result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
|
||||
return result;
|
||||
}
|
||||
auto &data = (*_colorIndices.colors)[colorIndex];
|
||||
auto &colors = _dark ? data.dark : data.light;
|
||||
if (!colors[0]) {
|
||||
return computeColorIndexValues(
|
||||
selected,
|
||||
colorIndex % kSimpleColorIndexCount);
|
||||
}
|
||||
const auto color = [&](int index) {
|
||||
const auto v = colors[index];
|
||||
return v
|
||||
? QColor((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
|
||||
: QColor(0, 0, 0, 0);
|
||||
};
|
||||
auto result = ColorIndexValues{
|
||||
.outlines = { color(0), color(1), color(2) }
|
||||
};
|
||||
result.bg = result.outlines[0];
|
||||
result.bg.setAlpha(0.12 * 255);
|
||||
result.name = (_dark && colorPatternIndex(colorIndex) == 1)
|
||||
? result.outlines[1]
|
||||
: result.outlines[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache(
|
||||
bool twoColored) const {
|
||||
const auto index = (twoColored ? 1 : 0);
|
||||
|
@ -791,7 +815,7 @@ const ColorIndexValues &ChatStyle::coloredValues(
|
|||
const auto shift = (selected ? kColorIndexCount : 0);
|
||||
auto &result = _coloredValues[shift + colorIndex];
|
||||
if (!result) {
|
||||
result.emplace(ComputeColorIndexValues(this, selected, colorIndex));
|
||||
result.emplace(computeColorIndexValues(selected, colorIndex));
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ class ChatTheme;
|
|||
class ChatStyle;
|
||||
struct BubblePattern;
|
||||
|
||||
inline constexpr auto kColorIndexCount = uint8(14);
|
||||
inline constexpr auto kColorPatternsCount = Text::kMaxQuoteOutlines;
|
||||
inline constexpr auto kColorIndexCount = uint8(1 << 6);
|
||||
inline constexpr auto kSimpleColorIndexCount = uint8(7);
|
||||
|
||||
struct MessageStyle {
|
||||
|
@ -83,10 +84,12 @@ struct MessageStyle {
|
|||
style::icon historyPollChoiceRight = { Qt::Uninitialized };
|
||||
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::array<
|
||||
std::unique_ptr<Text::QuotePaintCache>,
|
||||
kColorPatternsCount> quoteCache;
|
||||
std::array<
|
||||
std::unique_ptr<Text::QuotePaintCache>,
|
||||
kColorPatternsCount> replyCache;
|
||||
std::unique_ptr<Text::QuotePaintCache> preCache;
|
||||
};
|
||||
|
||||
|
@ -190,22 +193,31 @@ struct ChatPaintContext {
|
|||
[[nodiscard]] int HistoryServiceMsgInvertedRadius();
|
||||
[[nodiscard]] int HistoryServiceMsgInvertedShrink();
|
||||
|
||||
struct ColorIndexData {
|
||||
std::array<uint32, kColorPatternsCount> light = {};
|
||||
std::array<uint32, kColorPatternsCount> dark = {};
|
||||
|
||||
friend inline bool operator==(
|
||||
const ColorIndexData&,
|
||||
const ColorIndexData&) = default;
|
||||
};
|
||||
|
||||
struct ColorIndicesCompressed {
|
||||
std::shared_ptr<std::array<ColorIndexData, kColorIndexCount>> colors;
|
||||
};
|
||||
|
||||
struct ColorIndexValues {
|
||||
std::array<QColor, kColorPatternsCount> outlines;
|
||||
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();
|
||||
explicit ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices);
|
||||
explicit ChatStyle(not_null<const style::palette*> isolated);
|
||||
ChatStyle(const ChatStyle &other) = delete;
|
||||
ChatStyle &operator=(const ChatStyle &other) = delete;
|
||||
~ChatStyle();
|
||||
|
||||
void apply(not_null<ChatTheme*> theme);
|
||||
|
@ -246,6 +258,11 @@ public:
|
|||
bool selected) const;
|
||||
[[nodiscard]] const MessageImageStyle &imageStyle(bool selected) const;
|
||||
|
||||
[[nodiscard]] int colorPatternIndex(uint8 colorIndex) const;
|
||||
[[nodiscard]] ColorIndexValues computeColorIndexValues(
|
||||
bool selected,
|
||||
uint8 colorIndex) const;
|
||||
|
||||
[[nodiscard]] auto serviceQuoteCache(bool twoColored) const
|
||||
-> not_null<Text::QuotePaintCache*>;
|
||||
[[nodiscard]] auto serviceReplyCache(bool twoColored) const
|
||||
|
@ -362,6 +379,7 @@ private:
|
|||
};
|
||||
|
||||
void assignPalette(not_null<const style::palette*> palette);
|
||||
void clearColorIndexCaches();
|
||||
void updateDarkValue();
|
||||
|
||||
[[nodiscard]] not_null<Text::QuotePaintCache*> coloredCache(
|
||||
|
@ -462,11 +480,14 @@ private:
|
|||
style::icon _historyPollChoiceRight = { Qt::Uninitialized };
|
||||
style::icon _historyPollChoiceWrong = { Qt::Uninitialized };
|
||||
|
||||
ColorIndicesCompressed _colorIndices;
|
||||
|
||||
bool _dark = false;
|
||||
|
||||
rpl::event_stream<> _paletteChanged;
|
||||
|
||||
rpl::lifetime _defaultPaletteChangeLifetime;
|
||||
rpl::lifetime _colorIndicesLifetime;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1088,7 +1088,7 @@ SessionController::SessionController(
|
|||
, _invitePeekTimer([=] { checkInvitePeek(); })
|
||||
, _activeChatsFilter(session->data().chatsFilters().defaultId())
|
||||
, _defaultChatTheme(std::make_shared<Ui::ChatTheme>())
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(session->colorIndicesValue()))
|
||||
, _cachedReactionIconFactory(std::make_unique<ReactionIconFactory>())
|
||||
, _giftPremiumValidator(this) {
|
||||
init();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 383b5b8f7e629475e5f22445167aaa7669c5cdd6
|
||||
Subproject commit 611224c52f3192f616018fc7a2c5930667531084
|
Loading…
Add table
Reference in a new issue