diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 713551373..32c3ffb52 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -321,12 +321,14 @@ contactsMultiSelect: MultiSelect { } contactsPhotoCheckIcon: icon {{ "default_checkbox_check", - windowFgActive, + overviewCheckFgActive, point(3px, 6px) }}; contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) { size: 20px; sizeSmall: 0.3; + bgInactive: overviewCheckBg; + bgActive: overviewCheckBgActive; check: contactsPhotoCheckIcon; } contactsPhotoCheckbox: RoundImageCheckbox { diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 8bdadf8b2..03dcade11 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -48,8 +48,6 @@ overviewCheck: RoundCheckbox(defaultRoundCheckbox) { check: icon {{ "overview_photo_check", overviewCheckFgActive, point(4px, 8px) }}; } overviewSmallCheck: RoundCheckbox(contactsPhotoCheck) { - bgInactive: overviewCheckBg; - bgActive: overviewCheckBgActive; border: overviewCheckBorder; } overviewCheckSkip: 5px; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 1ec2d6d15..f3dbdc992 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -286,7 +286,7 @@ public: QImage prepareRippleMask() const override; bool checkRippleStartPosition(QPoint position) const override; - void setColorizer(const Window::Theme::Colorizer *colorizer); + void setColorizer(const Window::Theme::Colorizer &colorizer); private: void checkedChangedHook(anim::type animated) override; @@ -503,10 +503,10 @@ DefaultTheme::DefaultTheme(Scheme scheme, bool checked) : AbstractCheckView(st::defaultRadio.duration, checked, nullptr) , _scheme(scheme) , _radio(st::defaultRadio, checked, [=] { update(); }) { - setColorizer(nullptr); + setColorizer({}); } -void DefaultTheme::setColorizer(const Window::Theme::Colorizer *colorizer) { +void DefaultTheme::setColorizer(const Window::Theme::Colorizer &colorizer) { _colorized = _scheme; if (colorizer) { Window::Theme::Colorize(_colorized, colorizer); @@ -986,43 +986,25 @@ void SetupDefaultThemes(not_null container) { }; const auto group = std::make_shared>(chosen()); - const auto apply = [=]( - const Scheme &scheme, - const Window::Theme::Colorizer *colorizer = nullptr) { + const auto apply = [=](const Scheme &scheme) { const auto isNight = [](const Scheme &scheme) { const auto type = scheme.type; return (type != Type::DayBlue) && (type != Type::Default); }; const auto currentlyIsCustom = (chosen() == Type(-1)); if (Window::Theme::IsNightMode() == isNight(scheme)) { - Window::Theme::ApplyDefaultWithPath(scheme.path, colorizer); + Window::Theme::ApplyDefaultWithPath(scheme.path); } else { - Window::Theme::ToggleNightMode(scheme.path, colorizer); + Window::Theme::ToggleNightMode(scheme.path); } if (!currentlyIsCustom) { Window::Theme::KeepApplied(); } }; - const auto applyWithColor = [=]( - const Scheme &scheme, - const QColor &color) { - auto &colors = Core::App().settings().themesAccentColors(); - if (colors.get(scheme.type) != color) { - colors.set(scheme.type, color); - Local::writeSettings(); - } - const auto colorizer = Window::Theme::ColorizerFrom(scheme, color); - apply(scheme, &colorizer); - }; const auto schemeClicked = [=]( const Scheme &scheme, Qt::KeyboardModifiers modifiers) { - const auto &colors = Core::App().settings().themesAccentColors(); - if (const auto color = colors.get(scheme.type)) { - applyWithColor(scheme, *color); - } else { - apply(scheme); - } + apply(scheme); }; auto checks = base::flat_map>(); @@ -1062,9 +1044,9 @@ void SetupDefaultThemes(not_null container) { const auto colorizer = Window::Theme::ColorizerFrom( *scheme, *color); - i->second->setColorizer(&colorizer); + i->second->setColorizer(colorizer); } else { - i->second->setColorizer(nullptr); + i->second->setColorizer({}); } } }; @@ -1136,7 +1118,12 @@ void SetupDefaultThemes(not_null container) { if (scheme == end(kSchemesList)) { return; } - applyWithColor(*scheme, color); + auto &colors = Core::App().settings().themesAccentColors(); + if (colors.get(type) != color) { + colors.set(type, color); + Local::writeSettings(); + } + apply(*scheme); }, container->lifetime()); AddSkip(container); diff --git a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp index 1f7db3689..7c5061876 100644 --- a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp +++ b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp @@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/effects/round_checkbox.h" +#include "window/themes/window_theme.h" +#include "ui/rp_widget.h" + namespace Ui { namespace { @@ -237,11 +240,18 @@ QPixmap CheckCaches::paintFrame( CheckCaches *FrameCaches() { static QPointer Instance; - if (auto instance = Instance.data()) { + if (const auto instance = Instance.data()) { return instance; } - auto result = new CheckCaches(QGuiApplication::instance()); + const auto result = new CheckCaches(QGuiApplication::instance()); Instance = result; + const auto subscription = Ui::CreateChild(result); + *subscription = Window::Theme::Background()->add_subscription([=]( + const Window::Theme::BackgroundUpdate &update) { + if (update.paletteChanged()) { + FrameCaches()->clear(); + } + }); return result; } @@ -330,7 +340,6 @@ void RoundCheckbox::setChecked(bool newChecked, SetStyle speed) { } void RoundCheckbox::invalidateCache() { - FrameCaches()->clear(); if (!_inactiveCacheBg.isNull() || !_inactiveCacheFg.isNull()) { prepareInactiveCache(); } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 275b0e14e..cc5e0946e 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -700,7 +700,7 @@ defaultInputField: InputField { heightMax: 148px; } -defaultCheckboxIcon: icon {{ "default_checkbox_check", windowFgActive, point(4px, 7px) }}; +defaultCheckboxIcon: icon {{ "default_checkbox_check", overviewCheckFgActive, point(4px, 7px) }}; defaultCheck: Check { bg: transparent; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 5bbfc0a10..d40326a4a 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -152,8 +152,8 @@ enum class SetResult { SetResult setColorSchemeValue( QLatin1String name, QLatin1String value, - Instance *out, - const Colorizer *colorizer) { + const Colorizer &colorizer, + Instance *out) { auto result = style::palette::SetResult::Ok; auto size = value.size(); auto data = value.data(); @@ -163,8 +163,8 @@ SetResult setColorSchemeValue( auto g = readHexUchar(data[3], data[4], error); auto b = readHexUchar(data[5], data[6], error); auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255); - if (colorizer && !colorizer->ignoreKeys.contains(name)) { - Colorize(r, g, b, colorizer); + if (colorizer) { + Colorize(name, r, g, b, colorizer); } if (error) { LOG(("Theme Warning: Skipping value '%1: %2' (expected a color value in #rrggbb or #rrggbbaa or a previously defined key in the color scheme)").arg(name).arg(value)); @@ -199,14 +199,14 @@ SetResult setColorSchemeValue( bool loadColorScheme( const QByteArray &content, - Instance *out, - const Colorizer *colorizer = nullptr) { + const Colorizer &colorizer, + Instance *out) { auto unsupported = QMap(); return ReadPaletteValues(content, [&](QLatin1String name, QLatin1String value) { // Find the named value in the already read unsupported list. value = unsupported.value(value, value); - auto result = setColorSchemeValue(name, value, out, colorizer); + auto result = setColorSchemeValue(name, value, colorizer, out); if (result == SetResult::Bad) { return false; } else if (result == SetResult::NotFound) { @@ -293,8 +293,8 @@ bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *out bool loadTheme( const QByteArray &content, Cached &cache, - Instance *out = nullptr, - const Colorizer *colorizer = nullptr) { + const Colorizer &colorizer, + Instance *out = nullptr) { cache = Cached(); zlib::FileToRead file(content); @@ -310,7 +310,7 @@ bool loadTheme( LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file.")); return false; } - if (!loadColorScheme(schemeContent, out, colorizer)) { + if (!loadColorScheme(schemeContent, colorizer, out)) { return false; } Background()->saveAdjustableColors(); @@ -349,7 +349,7 @@ bool loadTheme( } } else { // Looks like it is not a .zip theme. - if (!loadColorScheme(content, out, colorizer)) { + if (!loadColorScheme(content, colorizer, out)) { return false; } Background()->saveAdjustableColors(); @@ -911,9 +911,7 @@ bool ChatBackground::nightMode() const { return _nightMode; } -void ChatBackground::toggleNightMode( - std::optional themePath, - const Colorizer *colorizer) { +void ChatBackground::toggleNightMode(std::optional themePath) { const auto settingDefault = themePath.has_value(); const auto oldNightMode = _nightMode; const auto newNightMode = !_nightMode; @@ -935,6 +933,7 @@ void ChatBackground::toggleNightMode( const auto loaded = loadTheme( preview->content, preview->instance.cached, + ColorizerForTheme(path), &preview->instance); if (!loaded) { return false; @@ -946,7 +945,7 @@ void ChatBackground::toggleNightMode( path = themePath ? *themePath : (newNightMode ? NightThemePath() : QString()); - ApplyDefaultWithPath(path, colorizer); + ApplyDefaultWithPath(path); } // Theme editor could have already reverted the testing of this toggle. @@ -993,7 +992,8 @@ bool Load(Saved &&saved) { return true; } - if (!loadTheme(saved.content, saved.cache)) { + const auto colorizer = ColorizerForTheme(saved.pathAbsolute); + if (!loadTheme(saved.content, saved.cache, colorizer)) { return false; } Local::writeTheme(saved); @@ -1025,11 +1025,9 @@ bool Apply(std::unique_ptr preview) { return true; } -void ApplyDefaultWithPath( - const QString &themePath, - const Colorizer *colorizer) { +void ApplyDefaultWithPath(const QString &themePath) { if (!themePath.isEmpty()) { - if (auto preview = PreviewFromFile(themePath, colorizer)) { + if (auto preview = PreviewFromFile(themePath)) { Apply(std::move(preview)); } } else { @@ -1046,12 +1044,14 @@ void ApplyDefaultWithPath( bool ApplyEditedPalette(const QString &path, const QByteArray &content) { Instance out; - if (!loadColorScheme(content, &out)) { + if (!loadColorScheme(content, Colorizer(), &out)) { return false; } out.cached.colors = out.palette.save(); out.cached.paletteChecksum = style::palette::Checksum(); - out.cached.contentChecksum = hashCrc32(content.constData(), content.size()); + out.cached.contentChecksum = hashCrc32( + content.constData(), + content.size()); GlobalApplying.pathRelative = path.isEmpty() ? QString() @@ -1116,27 +1116,25 @@ void SetNightModeValue(bool nightMode) { } void ToggleNightMode() { - Background()->toggleNightMode(std::nullopt, nullptr); + Background()->toggleNightMode(std::nullopt); } -void ToggleNightMode( - const QString &path, - const Colorizer *colorizer) { - Background()->toggleNightMode(path, colorizer); +void ToggleNightMode(const QString &path) { + Background()->toggleNightMode(path); } bool LoadFromFile( const QString &path, Instance *out, - QByteArray *outContent, - const Colorizer *colorizer) { + QByteArray *outContent) { *outContent = readThemeContent(path); if (outContent->size() < 4) { LOG(("Theme Error: Could not load theme from %1").arg(path)); return false; } - return loadTheme(*outContent, out->cached, out, colorizer); + const auto colorizer = ColorizerForTheme(path); + return loadTheme(*outContent, out->cached, colorizer, out); } bool IsPaletteTestingPath(const QString &path) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 484f0eaa9..de8430712 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -18,8 +18,6 @@ namespace Theme { constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; -struct Colorizer; - struct Cached { QByteArray colors; QByteArray background; @@ -53,26 +51,21 @@ struct Preview { bool Apply(const QString &filepath); bool Apply(std::unique_ptr preview); -void ApplyDefaultWithPath( - const QString &themePath, - const Colorizer *colorizer = nullptr); +void ApplyDefaultWithPath(const QString &themePath); bool ApplyEditedPalette(const QString &path, const QByteArray &content); void KeepApplied(); QString NightThemePath(); [[nodiscard]] bool IsNightMode(); void SetNightModeValue(bool nightMode); void ToggleNightMode(); -void ToggleNightMode( - const QString &themePath, - const Colorizer *colorizer = nullptr); +void ToggleNightMode(const QString &themePath); [[nodiscard]] bool IsNonDefaultBackground(); void Revert(); bool LoadFromFile( const QString &file, Instance *out, - QByteArray *outContent, - const Colorizer *colorizer = nullptr); + QByteArray *outContent); bool IsPaletteTestingPath(const QString &path); QColor CountAverageColor(const QImage &image); QColor AdjustedColor(QColor original, QColor background); @@ -162,9 +155,7 @@ private: void setNightModeValue(bool nightMode); [[nodiscard]] bool nightMode() const; - void toggleNightMode( - std::optional themePath, - const Colorizer *colorizer); + void toggleNightMode(std::optional themePath); void keepApplied(const QString &path, bool write); [[nodiscard]] bool isNonDefaultThemeOrBackground(); [[nodiscard]] bool isNonDefaultBackground(); @@ -174,9 +165,7 @@ private: friend bool IsNightMode(); friend void SetNightModeValue(bool nightMode); friend void ToggleNightMode(); - friend void ToggleNightMode( - const QString &themePath, - const Colorizer *colorizer); + friend void ToggleNightMode(const QString &themePath); friend void KeepApplied(); friend bool IsNonDefaultBackground(); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 2eb5136d6..1ff8eebb3 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -188,7 +188,7 @@ QByteArray replaceValueInContent(const QByteArray &content, const QByteArray &na QByteArray ColorizeInContent( QByteArray content, - not_null colorizer) { + const Colorizer &colorizer) { auto validNames = OrderedSet(); content.detach(); auto start = content.constBegin(), data = start, end = data + content.size(); @@ -349,21 +349,10 @@ bool CopyColorsToPalette( return false; } - if (themePath.startsWith(qstr(":/gui"))) { - const auto schemes = EmbeddedThemes(); - const auto i = ranges::find( - schemes, - themePath, - &EmbeddedScheme::path); - if (i != end(schemes)) { - const auto &colors = Core::App().settings().themesAccentColors(); - if (const auto accent = colors.get(i->type)) { - const auto colorizer = ColorizerFrom(*i, *accent); - paletteContent = ColorizeInContent( - std::move(paletteContent), - &colorizer); - } - } + if (const auto colorizer = ColorizerForTheme(themePath)) { + paletteContent = ColorizeInContent( + std::move(paletteContent), + colorizer); } if (f.write(paletteContent) != paletteContent.size()) { LOG(("Theme Error: could not write palette to '%1'").arg(destination)); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index 23a80a7ff..e797faa26 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -404,13 +404,16 @@ void EditorBlock::sortByDistance(const QColor &to) { auto fromSaturation = int(); auto fromLightness = int(); row.value().getHsl(&fromHue, &fromSaturation, &fromLightness); - if (!row.copyOf().isEmpty() && row.copyOf() != "windowBgActive") { + if (!row.copyOf().isEmpty()) { return 365; } const auto a = std::abs(fromHue - toHue); const auto b = 360 + fromHue - toHue; const auto c = 360 + toHue - fromHue; - return std::min(a, std::min(b, c)); + if (std::min(a, std::min(b, c)) > 15) { + return 363; + } + return 255 - fromSaturation; }); } diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index e75c09719..7e473fc39 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -908,9 +908,7 @@ void Generator::restoreTextPalette() { } // namespace -std::unique_ptr PreviewFromFile( - const QString &filepath, - const Colorizer *colorizer) { +std::unique_ptr PreviewFromFile(const QString &filepath) { auto result = std::make_unique(); result->pathRelative = filepath.isEmpty() ? QString() @@ -918,11 +916,7 @@ std::unique_ptr PreviewFromFile( result->pathAbsolute = filepath.isEmpty() ? QString() : QFileInfo(filepath).absoluteFilePath(); - if (!LoadFromFile( - filepath, - &result->instance, - &result->content, - colorizer)) { + if (!LoadFromFile(filepath, &result->instance, &result->content)) { return nullptr; } return result; diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.h b/Telegram/SourceFiles/window/themes/window_theme_preview.h index 067b075db..adde5109a 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.h +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.h @@ -18,9 +18,7 @@ struct CurrentData { bool backgroundTiled = false; }; -std::unique_ptr PreviewFromFile( - const QString &filepath, - const Colorizer *colorizer = nullptr); +std::unique_ptr PreviewFromFile(const QString &filepath); std::unique_ptr GeneratePreview( const QString &filepath, CurrentData &&data); diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp index 0d3ea0bea..9b84d5f54 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp @@ -9,12 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" #include "storage/serialize_common.h" +#include "core/application.h" +#include "core/core_settings.h" namespace Window { namespace Theme { namespace { constexpr auto kMaxAccentColors = 3; +constexpr auto kEnoughLightnessForContrast = 64; const auto kColorizeIgnoredKeys = base::flat_set{ { qstr("boxTextFgGood"), @@ -100,6 +103,7 @@ Colorizer::Color cColor(str_const hex) { Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) { using Color = Colorizer::Color; + using Pair = std::pair; auto result = Colorizer(); result.ignoreKeys = kColorizeIgnoredKeys; @@ -114,26 +118,34 @@ Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) { &result.now.lightness); switch (scheme.type) { case EmbeddedType::DayBlue: - result.lightnessMax = 191; + result.lightnessMax = 160; break; case EmbeddedType::Night: - result.keepContrast = base::flat_map{ { - { qstr("windowFgActive"), cColor("5288c1") }, // windowBgActive - { qstr("activeButtonFg"), cColor("2f6ea5") }, // activeButtonBg - { qstr("profileVerifiedCheckFg"), cColor("5288c1") }, // profileVerifiedCheckBg - { qstr("overviewCheckFgActive"), cColor("5288c1") }, // overviewCheckBgActive + result.keepContrast = base::flat_map{ { + //{ 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; + result.lightnessMin = 96; break; case EmbeddedType::NightGreen: - result.keepContrast = base::flat_map{ { - { qstr("windowFgActive"), cColor("3fc1b0") }, // windowBgActive - { qstr("activeButtonFg"), cColor("2da192") }, // activeButtonBg - { qstr("profileVerifiedCheckFg"), cColor("3fc1b0") }, // profileVerifiedCheckBg - { qstr("overviewCheckFgActive"), cColor("3fc1b0") }, // overviewCheckBgActive - { qstr("callIconFg"), cColor("5ad1c1") }, // callAnswerBg + result.keepContrast = base::flat_map{ { + //{ 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 + { qstr("callIconFg"), Pair{ cColor("5ad1c1"), cColor("26282c") } }, // callAnswerBg, callBg } }; - result.lightnessMin = 64; + result.lightnessMin = 96; break; } result.now.lightness = std::clamp( @@ -143,56 +155,131 @@ Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) { return result; } -void Colorize( - uchar &r, - uchar &g, - uchar &b, - not_null colorizer) { - auto color = QColor(int(r), int(g), int(b)); +Colorizer ColorizerForTheme(const QString &absolutePath) { + if (!absolutePath.startsWith(qstr(":/gui"))) { + return Colorizer(); + } + const auto schemes = EmbeddedThemes(); + const auto i = ranges::find( + schemes, + absolutePath, + &EmbeddedScheme::path); + if (i == end(schemes)) { + return Colorizer(); + } + const auto &colors = Core::App().settings().themesAccentColors(); + if (const auto accent = colors.get(i->type)) { + return ColorizerFrom(*i, *accent); + } + return Colorizer(); +} + +[[nodiscard]] std::optional Colorize( + const Colorizer::Color &color, + const Colorizer &colorizer) { + const auto changeColor = std::abs(color.hue - colorizer.was.hue) + < colorizer.hueThreshold; + if (!changeColor) { + return std::nullopt; + } + const auto nowHue = color.hue + (colorizer.now.hue - colorizer.was.hue); + const auto nowSaturation = ((color.saturation > colorizer.was.saturation) + && (colorizer.now.saturation > colorizer.was.saturation)) + ? (((colorizer.now.saturation * (255 - colorizer.was.saturation)) + + ((color.saturation - colorizer.was.saturation) + * (255 - colorizer.now.saturation))) + / (255 - colorizer.was.saturation)) + : ((color.saturation != colorizer.was.saturation) + && (colorizer.was.saturation != 0)) + ? ((color.saturation * colorizer.now.saturation) + / colorizer.was.saturation) + : colorizer.now.saturation; + const auto nowLightness = (color.lightness > colorizer.was.lightness) + ? (((colorizer.now.lightness * (255 - colorizer.was.lightness)) + + ((color.lightness - colorizer.was.lightness) + * (255 - colorizer.now.lightness))) + / (255 - colorizer.was.lightness)) + : (color.lightness < colorizer.was.lightness) + ? ((color.lightness * colorizer.now.lightness) + / colorizer.was.lightness) + : colorizer.now.lightness; + return Colorizer::Color{ + ((nowHue + 360) % 360), + nowSaturation, + nowLightness + }; +} + +[[nodiscard]] std::optional Colorize( + const QColor &color, + const Colorizer &colorizer) { auto hue = 0; auto saturation = 0; auto lightness = 0; color.getHsl(&hue, &saturation, &lightness); - const auto changeColor = std::abs(hue - colorizer->was.hue) - <= colorizer->hueThreshold; - if (!changeColor) { - return; + const auto result = Colorize( + Colorizer::Color{ hue, saturation, lightness }, + colorizer); + if (!result) { + return std::nullopt; } - const auto nowHue = hue + (colorizer->now.hue - colorizer->was.hue); - const auto nowSaturation = ((saturation > colorizer->was.saturation) - && (colorizer->now.saturation > colorizer->was.saturation)) - ? (((colorizer->now.saturation * (255 - colorizer->was.saturation)) - + ((saturation - colorizer->was.saturation) - * (255 - colorizer->now.saturation))) - / (255 - colorizer->was.saturation)) - : ((saturation != colorizer->was.saturation) - && (colorizer->was.saturation != 0)) - ? ((saturation * colorizer->now.saturation) - / colorizer->was.saturation) - : colorizer->now.saturation; - const auto nowLightness = (lightness > colorizer->was.lightness) - ? (((colorizer->now.lightness * (255 - colorizer->was.lightness)) - + ((lightness - colorizer->was.lightness) - * (255 - colorizer->now.lightness))) - / (255 - colorizer->was.lightness)) - : (lightness < colorizer->was.lightness) - ? ((lightness * colorizer->now.lightness) - / colorizer->was.lightness) - : colorizer->now.lightness; + const auto &fields = *result; + return QColor::fromHsl(fields.hue, fields.saturation, fields.lightness); +} + +void FillColorizeResult(uchar &r, uchar &g, uchar &b, const QColor &color) { auto nowR = 0; auto nowG = 0; auto nowB = 0; - QColor::fromHsl( - ((nowHue + 360) % 360), - nowSaturation, - nowLightness - ).getRgb(&nowR, &nowG, &nowB); + color.getRgb(&nowR, &nowG, &nowB); r = uchar(nowR); g = uchar(nowG); b = uchar(nowB); } -void Colorize(uint32 &pixel, not_null colorizer) { +void Colorize(uchar &r, uchar &g, uchar &b, const Colorizer &colorizer) { + const auto changed = Colorize(QColor(int(r), int(g), int(b)), colorizer); + if (changed) { + FillColorizeResult(r, g, b, *changed); + } +} + +void Colorize( + QLatin1String name, + uchar &r, + uchar &g, + uchar &b, + const Colorizer &colorizer) { + if (colorizer.ignoreKeys.contains(name)) { + return; + } + + const auto i = colorizer.keepContrast.find(name); + if (i == end(colorizer.keepContrast)) { + Colorize(r, g, b, colorizer); + return; + } + const auto check = i->second.first; + const auto rgb = QColor(int(r), int(g), int(b)); + const auto changed = Colorize(rgb, colorizer); + const auto checked = Colorize(check, colorizer).value_or(check); + const auto delta = std::abs(changed.value_or(rgb).lightness() - checked.lightness); + if (delta >= kEnoughLightnessForContrast) { + if (changed) { + FillColorizeResult(r, g, b, *changed); + } + return; + } + const auto replace = i->second.second; + const auto result = Colorize(replace, colorizer).value_or(replace); + FillColorizeResult( + r, + g, + b, + QColor::fromHsl(result.hue, result.saturation, result.lightness)); +} + +void Colorize(uint32 &pixel, const Colorizer &colorizer) { const auto chars = reinterpret_cast(&pixel); Colorize( chars[2], @@ -201,15 +288,7 @@ void Colorize(uint32 &pixel, not_null colorizer) { colorizer); } -void Colorize(QColor &color, not_null colorizer) { - auto r = uchar(color.red()); - auto g = uchar(color.green()); - auto b = uchar(color.blue()); - Colorize(r, g, b, colorizer); - color = QColor(r, g, b, color.alpha()); -} - -void Colorize(QImage &image, not_null colorizer) { +void Colorize(QImage &image, const Colorizer &colorizer) { image = std::move(image).convertToFormat(QImage::Format_ARGB32); const auto bytes = image.bits(); const auto bytesPerLine = image.bytesPerLine(); @@ -223,7 +302,7 @@ void Colorize(QImage &image, not_null colorizer) { } } -void Colorize(EmbeddedScheme &scheme, not_null colorizer) { +void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer) { const auto colors = { &EmbeddedScheme::background, &EmbeddedScheme::sent, @@ -232,17 +311,19 @@ void Colorize(EmbeddedScheme &scheme, not_null colorizer) { &EmbeddedScheme::radiobuttonInactive }; for (const auto color : colors) { - Colorize(scheme.*color, colorizer); + if (const auto changed = Colorize(scheme.*color, colorizer)) { + scheme.*color = changed->toRgb(); + } } } QByteArray Colorize( QLatin1String hexColor, - not_null colorizer) { + const Colorizer &colorizer) { Expects(hexColor.size() == 7 || hexColor.size() == 9); auto color = qColor(str_const(hexColor.data() + 1, 6)); - Colorize(color, colorizer); + const auto changed = Colorize(color, colorizer).value_or(color).toRgb(); auto result = QByteArray(); result.reserve(hexColor.size()); @@ -258,9 +339,9 @@ QByteArray Colorize( addHex(code / 16); addHex(code % 16); }; - addValue(color.red()); - addValue(color.green()); - addValue(color.blue()); + addValue(changed.red()); + addValue(changed.green()); + addValue(changed.blue()); if (hexColor.size() == 9) { result.append(hexColor.data()[7]); result.append(hexColor.data()[8]); @@ -311,7 +392,7 @@ std::vector EmbeddedThemes() { qColor("75bfb5"), tr::lng_settings_theme_matrix, ":/gui/night-green.tdesktop-theme", - qColor("3fc1b0") + qColor("01ffdd") }, }; } diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.h b/Telegram/SourceFiles/window/themes/window_themes_embedded.h index 22bf658dc..adb4f225a 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.h +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.h @@ -59,23 +59,30 @@ struct Colorizer { Color was; Color now; base::flat_set ignoreKeys; - base::flat_map keepContrast; + base::flat_map> keepContrast; + + explicit operator bool() const { + return (hueThreshold > 0); + } }; [[nodiscard]] Colorizer ColorizerFrom( const EmbeddedScheme &scheme, const QColor &color); +[[nodiscard]] Colorizer ColorizerForTheme(const QString &absolutePath); +void Colorize(uchar &r, uchar &g, uchar &b, const Colorizer &colorizer); void Colorize( + QLatin1String name, uchar &r, uchar &g, uchar &b, - not_null colorizer); -void Colorize(QImage &image, not_null colorizer); -void Colorize(EmbeddedScheme &scheme, not_null colorizer); + const Colorizer &colorizer); +void Colorize(QImage &image, const Colorizer &colorizer); +void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer); [[nodiscard]] QByteArray Colorize( QLatin1String hexColor, - not_null colorizer); + const Colorizer &colorizer); [[nodiscard]] std::vector EmbeddedThemes(); [[nodiscard]] std::vector DefaultAccentColors(EmbeddedType type);