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,