From d71abe8b4744935fc21b66ed09c9c0ea625db834 Mon Sep 17 00:00:00 2001 From: Vitaly Zdanevich Date: Thu, 25 Jun 2026 22:09:11 +0400 Subject: [PATCH] Add total black embedded theme Add a built-in Black theme option that uses pure black surfaces and a generated black chat background while keeping accent colors adjustable. Co-authored-by: OpenAI ChatGPT --- Telegram/Resources/langs/lang.strings | 1 + Telegram/Resources/qrc/telegram/telegram.qrc | 1 + .../window/themes/window_theme.cpp | 117 +++++++++++++++++- .../window/themes/window_themes_embedded.cpp | 30 ++++- .../window/themes/window_themes_embedded.h | 2 + 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d68c988595224b..3fb4ee41d83735 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -860,6 +860,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_theme_classic" = "Classic"; "lng_settings_theme_tinted" = "Tinted"; "lng_settings_theme_night" = "Night"; +"lng_settings_theme_black" = "Black"; "lng_settings_theme_accent_title" = "Choose accent color"; "lng_settings_theme_system_accent_color" = "System accent color"; "lng_settings_data_storage" = "Data and storage"; diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index fda0ed1ef4bc1c..9a33c44082f01b 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -16,6 +16,7 @@ ../../day-blue.tdesktop-theme ../../night.tdesktop-theme ../../night-green.tdesktop-theme + ../../night-custom-base.tdesktop-theme ../../day-custom-base.tdesktop-theme ../../night-custom-base.tdesktop-theme ../../icons/calls/hands.lottie diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index f6d7d8ace04dba..9ce433399b198d 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -241,6 +241,98 @@ void applyBackground(QImage &&background, bool tiled, Instance *out) { } } +template +void ApplyBlackThemePalette(Callback callback) { + for (const auto key : { + qstr("windowBg"), + qstr("windowShadowFgFallback"), + qstr("lightButtonBg"), + qstr("outlineButtonBg"), + qstr("menuBg"), + qstr("filterInputInactiveBg"), + qstr("filterInputActiveBg"), + qstr("titleBg"), + qstr("titleBgActive"), + qstr("titleButtonBg"), + qstr("titleButtonBgActive"), + qstr("titleButtonCloseBg"), + qstr("titleButtonCloseBgActive"), + qstr("boxBg"), + qstr("boxSearchBg"), + qstr("contactsBg"), + qstr("introBg"), + qstr("dialogsBg"), + qstr("searchedBarBg"), + qstr("topBarBg"), + qstr("emojiPanBg"), + qstr("emojiPanCategories"), + qstr("historyUnreadBarBg"), + qstr("msgInBg"), + qstr("msgOutBg"), + qstr("msgServiceBg"), + qstr("reportSpamBg"), + qstr("historyToDownBg"), + qstr("historyComposeAreaBg"), + qstr("historyPinnedBg"), + qstr("historyReplyBg"), + qstr("historyComposeButtonBg"), + qstr("mainMenuBg"), + qstr("mediaPlayerBg"), + qstr("mediaviewFileBg"), + qstr("mediaviewBg"), + qstr("notificationBg"), + qstr("callBg"), + qstr("botKbBg"), + qstr("sideBarBg"), + }) { + callback(key); + } +} + +[[nodiscard]] QImage BlackThemeBackground() { + auto result = QImage(1, 1, QImage::Format_RGB32); + result.fill(QColor(0, 0, 0)); + return result; +} + +void SaveBlackThemeBackground(const QImage &background, Cached *cache) { + if (!cache) { + return; + } + cache->background = QByteArray(); + auto buffer = QBuffer(&cache->background); + background.save(&buffer, "BMP"); + cache->tiled = false; +} + +void ApplyBlackThemeOverrides(not_null out, Cached *cache) { + const auto black = QColor(0, 0, 0); + ApplyBlackThemePalette([&](QLatin1String key) { + out->palette.setColor(key, black); + }); + + out->background = BlackThemeBackground(); + out->tiled = false; + if (cache) { + cache->colors = out->palette.save(); + SaveBlackThemeBackground(out->background, cache); + } +} + +void ApplyBlackThemeOverrides(Cached *cache) { + ApplyBlackThemePalette([](QLatin1String key) { + style::main_palette::setColor(key, 0, 0, 0, 255); + }); + + auto background = BlackThemeBackground(); + if (cache) { + cache->colors = style::main_palette::save(); + SaveBlackThemeBackground(background, cache); + } + Background()->setThemeData(std::move(background), false); + Background()->saveAdjustableColors(); +} + enum class LoadResult { Loaded, Failed, @@ -426,8 +518,12 @@ bool InitializeFromSaved(Saved &&saved) { } const auto editing = ReadEditingPalette(); + const auto black = !editing + && (saved.object.pathAbsolute == BlackThemePath()); GlobalBackground.createIfNull(); - if (!editing && InitializeFromCache(saved.object.content, saved.cache)) { + if (!editing + && !black + && InitializeFromCache(saved.object.content, saved.cache)) { return true; } @@ -436,6 +532,9 @@ bool InitializeFromSaved(Saved &&saved) { DEBUG_LOG(("Theme: Could not load from saved.")); return false; } + if (black) { + ApplyBlackThemeOverrides(&saved.cache); + } if (editing) { Background()->setEditingTheme(ReadCloudFromText(*editing)); } else { @@ -1223,6 +1322,11 @@ void ChatBackground::reapplyWithNightMode( if (!loaded) { return false; } + if (path == BlackThemePath()) { + ApplyBlackThemeOverrides( + &preview->instance, + &preview->instance.cached); + } Apply(std::move(preview)); return true; }(); @@ -1484,7 +1588,16 @@ bool LoadFromFile( if (outContent) { *outContent = content; } - return LoadTheme(content, colorizer, std::nullopt, outCache, out); + const auto loaded = LoadTheme( + content, + colorizer, + std::nullopt, + outCache, + out); + if (loaded && path == BlackThemePath()) { + ApplyBlackThemeOverrides(out, outCache); + } + return loaded; } bool LoadFromContent( diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp index fa23ca23174df1..389dbabcf95529 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp @@ -22,7 +22,7 @@ namespace Window { namespace Theme { namespace { -constexpr auto kMaxAccentColors = 3; +constexpr auto kMaxAccentColors = 5; constexpr auto kDayBaseFile = ":/gui/day-custom-base.tdesktop-theme"_cs; constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs; @@ -133,6 +133,7 @@ style::colorizer ColorizerFrom( result.lightnessMax = 160; break; case EmbeddedType::Night: + case EmbeddedType::Black: result.keepContrast = base::flat_map{ { //{ qstr("windowFgActive"), Pair{ cColor("5288c1"), cColor("17212b") } }, // windowBgActive { qstr("activeButtonFg"), Pair{ cColor("2f6ea5"), cColor("17212b") } }, // activeButtonBg @@ -189,6 +190,10 @@ std::optional SystemAccentColor() { return accent.isValid() ? std::make_optional(accent) : std::nullopt; } +QString BlackThemePath() { + return u":/gui/black.tdesktop-theme"_q; +} + style::colorizer ColorizerForTheme(const QString &absolutePath) { if (!IsEmbeddedTheme(absolutePath)) { return {}; @@ -281,6 +286,17 @@ std::vector EmbeddedThemes() { ":/gui/night-green.tdesktop-theme", qColor("3fc1b0") }, + EmbeddedScheme{ + EmbeddedType::Black, + qColor("000000"), + qColor("265e8c"), + qColor("24292e"), + qColor("24292e"), + qColor("5ca7d4"), + name(tr::lng_settings_theme_black), + BlackThemePath(), + qColor("5288c1") + }, }; } @@ -333,6 +349,17 @@ std::vector DefaultAccentColors(EmbeddedType type) { qColor("7b8799"), qColor("cbac67"), }; + case EmbeddedType::Black: + return { + qColor("58bfe8"), + qColor("466f42"), + qColor("aa6084"), + qColor("a46d3c"), + qColor("917bbd"), + qColor("ab5149"), + qColor("697b97"), + qColor("9b834b"), + }; } Unexpected("Type in Window::Theme::AccentColors."); } @@ -421,6 +448,7 @@ bool AccentColors::setFromSerialized(const QByteArray &serialized) { case EmbeddedType::DayBlue: case EmbeddedType::Night: case EmbeddedType::NightGreen: + case EmbeddedType::Black: data.emplace(uncheckedType, color); break; default: diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.h b/Telegram/SourceFiles/window/themes/window_themes_embedded.h index fd0a9ba6f66952..adee6aacc5d6fd 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.h +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.h @@ -19,6 +19,7 @@ enum class EmbeddedType { Default, Night, NightGreen, + Black, }; struct EmbeddedScheme { @@ -52,6 +53,7 @@ class AccentColors final { const QColor &color); [[nodiscard]] std::optional SystemAccentColor(); [[nodiscard]] style::colorizer ColorizerForTheme(const QString &absolutePath); +[[nodiscard]] QString BlackThemePath(); void Colorize( EmbeddedScheme &scheme,