| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "window/themes/window_themes_embedded.h"
- #include "window/themes/window_theme.h"
- #include "lang/lang_keys.h"
- #include "storage/serialize_common.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "ui/style/style_palette_colorizer.h"
- namespace Window {
- namespace Theme {
- namespace {
- constexpr auto kMaxAccentColors = 3;
- constexpr auto kDayBaseFile = ":/gui/day-custom-base.tdesktop-theme"_cs;
- constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
- const auto kColorizeIgnoredKeys = base::flat_set<QLatin1String>{ {
- qstr("boxTextFgGood"),
- qstr("boxTextFgError"),
- qstr("callIconFg"),
- qstr("historyPeer1NameFg"),
- qstr("historyPeer1NameFgSelected"),
- qstr("historyPeer1UserpicBg"),
- qstr("historyPeer2NameFg"),
- qstr("historyPeer2NameFgSelected"),
- qstr("historyPeer2UserpicBg"),
- qstr("historyPeer3NameFg"),
- qstr("historyPeer3NameFgSelected"),
- qstr("historyPeer3UserpicBg"),
- qstr("historyPeer4NameFg"),
- qstr("historyPeer4NameFgSelected"),
- qstr("historyPeer4UserpicBg"),
- qstr("historyPeer5NameFg"),
- qstr("historyPeer5NameFgSelected"),
- qstr("historyPeer5UserpicBg"),
- qstr("historyPeer6NameFg"),
- qstr("historyPeer6NameFgSelected"),
- qstr("historyPeer6UserpicBg"),
- qstr("historyPeer7NameFg"),
- qstr("historyPeer7NameFgSelected"),
- qstr("historyPeer7UserpicBg"),
- qstr("historyPeer8NameFg"),
- qstr("historyPeer8NameFgSelected"),
- qstr("historyPeer8UserpicBg"),
- qstr("historyPeer1UserpicBg2"),
- qstr("historyPeer2UserpicBg2"),
- qstr("historyPeer3UserpicBg2"),
- qstr("historyPeer4UserpicBg2"),
- qstr("historyPeer5UserpicBg2"),
- qstr("historyPeer6UserpicBg2"),
- qstr("historyPeer7UserpicBg2"),
- qstr("historyPeer8UserpicBg2"),
- qstr("msgFile1Bg"),
- qstr("msgFile1BgDark"),
- qstr("msgFile1BgOver"),
- qstr("msgFile1BgSelected"),
- qstr("msgFile2Bg"),
- qstr("msgFile2BgDark"),
- qstr("msgFile2BgOver"),
- qstr("msgFile2BgSelected"),
- qstr("msgFile3Bg"),
- qstr("msgFile3BgDark"),
- qstr("msgFile3BgOver"),
- qstr("msgFile3BgSelected"),
- qstr("msgFile4Bg"),
- qstr("msgFile4BgDark"),
- qstr("msgFile4BgOver"),
- qstr("msgFile4BgSelected"),
- qstr("mediaviewFileRedCornerFg"),
- qstr("mediaviewFileYellowCornerFg"),
- qstr("mediaviewFileGreenCornerFg"),
- qstr("mediaviewFileBlueCornerFg"),
- qstr("settingsIconBg1"),
- qstr("settingsIconBg2"),
- qstr("settingsIconBg3"),
- qstr("settingsIconBg4"),
- qstr("settingsIconBg5"),
- qstr("settingsIconBg6"),
- qstr("settingsIconBg8"),
- qstr("settingsIconBgArchive"),
- qstr("premiumButtonBg1"),
- qstr("premiumButtonBg2"),
- qstr("premiumButtonBg3"),
- qstr("premiumIconBg1"),
- qstr("premiumIconBg2"),
- } };
- style::colorizer::Color cColor(std::string_view hex) {
- const auto q = style::ColorFromHex(hex);
- auto hue = int();
- auto saturation = int();
- auto value = int();
- q.getHsv(&hue, &saturation, &value);
- return style::colorizer::Color{ hue, saturation, value };
- }
- } // namespace
- style::colorizer ColorizerFrom(
- const EmbeddedScheme &scheme,
- const QColor &color) {
- using Color = style::colorizer::Color;
- using Pair = std::pair<Color, Color>;
- auto result = style::colorizer();
- result.ignoreKeys = kColorizeIgnoredKeys;
- result.hueThreshold = 15;
- scheme.accentColor.getHsv(
- &result.was.hue,
- &result.was.saturation,
- &result.was.value);
- color.getHsv(
- &result.now.hue,
- &result.now.saturation,
- &result.now.value);
- switch (scheme.type) {
- case EmbeddedType::Default:
- result.lightnessMax = 160;
- break;
- case EmbeddedType::DayBlue:
- result.lightnessMax = 160;
- break;
- case EmbeddedType::Night:
- result.keepContrast = base::flat_map<QLatin1String, Pair>{ {
- //{ qstr("windowFgActive"), Pair{ cColor("5288c1"), cColor("17212b") } }, // windowBgActive
- { qstr("activeButtonFg"), Pair{ cColor("2f6ea5"), cColor("17212b") } }, // activeButtonBg
- { qstr("profileVerifiedCheckFg"), Pair{ cColor("5288c1"), cColor("17212b") } }, // profileVerifiedCheckBg
- { qstr("overviewCheckFgActive"), Pair{ cColor("5288c1"), cColor("17212b") } }, // overviewCheckBgActive
- { qstr("historyFileInIconFg"), Pair{ cColor("3f96d0"), cColor("182533") } }, // msgFileInBg, msgInBg
- { qstr("historyFileInIconFgSelected"), Pair{ cColor("6ab4f4"), cColor("2e70a5") } }, // msgFileInBgSelected, msgInBgSelected
- { qstr("historyFileInRadialFg"), Pair{ cColor("3f96d0"), cColor("182533") } }, // msgFileInBg, msgInBg
- { qstr("historyFileInRadialFgSelected"), Pair{ cColor("6ab4f4"), cColor("2e70a5") } }, // msgFileInBgSelected, msgInBgSelected
- { qstr("historyFileOutIconFg"), Pair{ cColor("4c9ce2"), cColor("2b5278") } }, // msgFileOutBg, msgOutBg
- { qstr("historyFileOutIconFgSelected"), Pair{ cColor("58abf3"), cColor("2e70a5") } }, // msgFileOutBgSelected, msgOutBgSelected
- { qstr("historyFileOutRadialFg"), Pair{ cColor("4c9ce2"), cColor("2b5278") } }, // msgFileOutBg, msgOutBg
- { qstr("historyFileOutRadialFgSelected"), Pair{ cColor("58abf3"), cColor("2e70a5") } }, // msgFileOutBgSelected, msgOutBgSelected
- } };
- result.lightnessMin = 64;
- break;
- case EmbeddedType::NightGreen:
- result.keepContrast = base::flat_map<QLatin1String, Pair>{ {
- //{ qstr("windowFgActive"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // windowBgActive, windowBg
- { qstr("activeButtonFg"), Pair{ cColor("2da192"), cColor("282e33") } }, // activeButtonBg, windowBg
- { qstr("profileVerifiedCheckFg"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // profileVerifiedCheckBg, windowBg
- { qstr("overviewCheckFgActive"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // overviewCheckBgActive
- // callIconFg is used not only over callAnswerBg,
- // so this contrast-forcing breaks other buttons.
- //{ qstr("callIconFg"), Pair{ cColor("5ad1c1"), cColor("1b1f23") } }, // callAnswerBg, callBgOpaque
- } };
- result.lightnessMin = 64;
- break;
- }
- const auto nowLightness = color.lightness();
- const auto limitedLightness = std::clamp(
- nowLightness,
- result.lightnessMin,
- result.lightnessMax);
- if (limitedLightness != nowLightness) {
- QColor::fromHsl(
- color.hslHue(),
- color.hslSaturation(),
- limitedLightness).getHsv(
- &result.now.hue,
- &result.now.saturation,
- &result.now.value);
- }
- return result;
- }
- style::colorizer ColorizerForTheme(const QString &absolutePath) {
- if (!IsEmbeddedTheme(absolutePath)) {
- return {};
- }
- const auto schemes = EmbeddedThemes();
- const auto i = ranges::find(
- schemes,
- absolutePath,
- &EmbeddedScheme::path);
- if (i == end(schemes)) {
- return {};
- }
- const auto &colors = Core::App().settings().themesAccentColors();
- if (const auto accent = colors.get(i->type)) {
- return ColorizerFrom(*i, *accent);
- }
- return {};
- }
- void Colorize(EmbeddedScheme &scheme, const style::colorizer &colorizer) {
- const auto colors = {
- &EmbeddedScheme::background,
- &EmbeddedScheme::sent,
- &EmbeddedScheme::received,
- &EmbeddedScheme::radiobuttonActive,
- &EmbeddedScheme::radiobuttonInactive
- };
- for (const auto color : colors) {
- if (const auto changed = style::colorize(scheme.*color, colorizer)) {
- scheme.*color = changed->toRgb();
- }
- }
- }
- std::vector<EmbeddedScheme> EmbeddedThemes() {
- const auto qColor = [](auto hex) {
- return style::ColorFromHex(hex);
- };
- const auto name = [](auto key) {
- return rpl::deferred([=] { return key(); });
- };
- return {
- EmbeddedScheme{
- EmbeddedType::Default,
- qColor("9bd494"),
- qColor("eaffdc"),
- qColor("ffffff"),
- qColor("eaffdc"),
- qColor("ffffff"),
- name(tr::lng_settings_theme_classic),
- QString(),
- qColor("40a7e3")
- },
- EmbeddedScheme{
- EmbeddedType::DayBlue,
- qColor("7ec4ea"),
- qColor("d7f0ff"),
- qColor("ffffff"),
- qColor("d7f0ff"),
- qColor("ffffff"),
- name(tr::lng_settings_theme_day),
- ":/gui/day-blue.tdesktop-theme",
- qColor("40a7e3")
- },
- EmbeddedScheme{
- EmbeddedType::Night,
- qColor("485761"),
- qColor("5ca7d4"),
- qColor("6b808d"),
- qColor("6b808d"),
- qColor("5ca7d4"),
- name(tr::lng_settings_theme_tinted),
- ":/gui/night.tdesktop-theme",
- qColor("5288c1")
- },
- EmbeddedScheme{
- EmbeddedType::NightGreen,
- qColor("485761"),
- qColor("6b808d"),
- qColor("6b808d"),
- qColor("6b808d"),
- qColor("75bfb5"),
- name(tr::lng_settings_theme_night),
- ":/gui/night-green.tdesktop-theme",
- qColor("3fc1b0")
- },
- };
- }
- std::vector<QColor> DefaultAccentColors(EmbeddedType type) {
- const auto qColor = [](auto hex) {
- return style::ColorFromHex(hex);
- };
- switch (type) {
- case EmbeddedType::DayBlue:
- return {
- qColor("45bce7"),
- qColor("52b440"),
- qColor("d46c99"),
- qColor("df8a49"),
- qColor("9978c8"),
- qColor("c55245"),
- qColor("687b98"),
- qColor("dea922"),
- };
- case EmbeddedType::Default:
- return {
- qColor("45bce7"),
- qColor("52b440"),
- qColor("d46c99"),
- qColor("df8a49"),
- qColor("9978c8"),
- qColor("c55245"),
- qColor("687b98"),
- qColor("dea922"),
- };
- case EmbeddedType::Night:
- return {
- qColor("58bfe8"),
- qColor("466f42"),
- qColor("aa6084"),
- qColor("a46d3c"),
- qColor("917bbd"),
- qColor("ab5149"),
- qColor("697b97"),
- qColor("9b834b"),
- };
- case EmbeddedType::NightGreen:
- return {
- qColor("60a8e7"),
- qColor("4e9c57"),
- qColor("ca7896"),
- qColor("cc925c"),
- qColor("a58ed2"),
- qColor("d27570"),
- qColor("7b8799"),
- qColor("cbac67"),
- };
- }
- Unexpected("Type in Window::Theme::AccentColors.");
- }
- Fn<void(style::palette&)> PreparePaletteCallback(
- bool dark,
- std::optional<QColor> accent) {
- return [=](style::palette &palette) {
- using namespace Theme;
- const auto &embedded = EmbeddedThemes();
- const auto i = ranges::find(
- embedded,
- dark ? EmbeddedType::Night : EmbeddedType::Default,
- &EmbeddedScheme::type);
- Assert(i != end(embedded));
- const auto colorizer = accent
- ? ColorizerFrom(*i, *accent)
- : style::colorizer();
- auto instance = Instance();
- const auto loaded = LoadFromFile(
- (dark ? kNightBaseFile : kDayBaseFile).utf16(),
- &instance,
- nullptr,
- nullptr,
- colorizer);
- Assert(loaded);
- palette.finalize();
- palette = instance.palette;
- };
- }
- Fn<void(style::palette&)> PrepareCurrentPaletteCallback() {
- return [=, data = style::main_palette::save()](style::palette &palette) {
- palette.load(data);
- };
- }
- QByteArray AccentColors::serialize() const {
- auto result = QByteArray();
- if (_data.empty()) {
- return result;
- }
- const auto count = _data.size();
- auto size = sizeof(qint32) * (count + 1)
- + Serialize::colorSize() * count;
- result.reserve(size);
- auto stream = QDataStream(&result, QIODevice::WriteOnly);
- stream.setVersion(QDataStream::Qt_5_1);
- stream << qint32(_data.size());
- for (const auto &[type, color] : _data) {
- stream << static_cast<qint32>(type);
- Serialize::writeColor(stream, color);
- }
- stream.device()->close();
- return result;
- }
- bool AccentColors::setFromSerialized(const QByteArray &serialized) {
- if (serialized.isEmpty()) {
- _data.clear();
- return true;
- }
- auto copy = QByteArray(serialized);
- auto stream = QDataStream(©, QIODevice::ReadOnly);
- stream.setVersion(QDataStream::Qt_5_1);
- auto count = qint32();
- stream >> count;
- if (stream.status() != QDataStream::Ok) {
- return false;
- } else if (count <= 0 || count > kMaxAccentColors) {
- return false;
- }
- auto data = base::flat_map<EmbeddedType, QColor>();
- for (auto i = 0; i != count; ++i) {
- auto type = qint32();
- stream >> type;
- const auto color = Serialize::readColor(stream);
- const auto uncheckedType = static_cast<EmbeddedType>(type);
- switch (uncheckedType) {
- case EmbeddedType::Default:
- case EmbeddedType::DayBlue:
- case EmbeddedType::Night:
- case EmbeddedType::NightGreen:
- data.emplace(uncheckedType, color);
- break;
- default:
- return false;
- }
- }
- if (stream.status() != QDataStream::Ok) {
- return false;
- }
- _data = std::move(data);
- return true;
- }
- void AccentColors::set(EmbeddedType type, const QColor &value) {
- _data.emplace_or_assign(type, value);
- }
- void AccentColors::clear(EmbeddedType type) {
- _data.remove(type);
- }
- std::optional<QColor> AccentColors::get(EmbeddedType type) const {
- const auto i = _data.find(type);
- return (i != end(_data)) ? std::make_optional(i->second) : std::nullopt;
- }
- } // namespace Theme
- } // namespace Window
|