From cb338e330ffb015e0e5472f2aadd1561d5dc5f2a Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 19 Jul 2018 17:58:40 +0300 Subject: [PATCH] Allow independently change default / night themes. --- Telegram/SourceFiles/core/crash_reports.cpp | 3 + .../settings/settings_advanced_widget.cpp | 24 +- .../settings/settings_advanced_widget.h | 2 +- .../settings/settings_background_widget.cpp | 11 +- Telegram/SourceFiles/storage/localstorage.cpp | 299 +++++++++----- Telegram/SourceFiles/storage/localstorage.h | 8 +- .../window/themes/window_theme.cpp | 373 ++++++++++++------ .../SourceFiles/window/themes/window_theme.h | 45 ++- .../window/themes/window_theme_editor.cpp | 16 +- .../window/themes/window_theme_preview.cpp | 19 +- .../window/themes/window_theme_preview.h | 1 + .../SourceFiles/window/window_main_menu.cpp | 31 +- 12 files changed, 554 insertions(+), 278 deletions(-) diff --git a/Telegram/SourceFiles/core/crash_reports.cpp b/Telegram/SourceFiles/core/crash_reports.cpp index 6b104caa8..f2ee871e3 100644 --- a/Telegram/SourceFiles/core/crash_reports.cpp +++ b/Telegram/SourceFiles/core/crash_reports.cpp @@ -519,6 +519,9 @@ void SetAnnotationHex(const std::string &key, const QString &value) { } void SetAnnotationRef(const std::string &key, const QString *valuePtr) { + static QMutex mutex; + QMutexLocker lock(&mutex); + if (valuePtr) { ProcessAnnotationRefs[key] = valuePtr; } else { diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp index 3a3a76c11..1b8b3d28a 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp @@ -62,13 +62,10 @@ void AdvancedWidget::createControls() { } else { style::margins slidedPadding(0, marginLarge.bottom() / 2, 0, marginLarge.bottom() - (marginLarge.bottom() / 2)); createChildRow(_useDefaultTheme, marginLarge, slidedPadding, lang(lng_settings_bg_use_default), SLOT(onUseDefaultTheme())); - if (!Window::Theme::IsNonDefaultUsed()) { + if (!Window::Theme::SuggestThemeReset()) { _useDefaultTheme->hide(anim::type::instant); } - createChildRow(_toggleNightTheme, marginLarge, slidedPadding, getNightThemeToggleText(), SLOT(onToggleNightTheme())); - if (Window::Theme::IsNonDefaultUsed()) { - _toggleNightTheme->hide(anim::type::instant); - } + createChildRow(_toggleNightTheme, marginLarge, getNightThemeToggleText(), SLOT(onToggleNightTheme())); } createChildRow(_telegramFAQ, marginLarge, lang(lng_settings_faq), SLOT(onTelegramFAQ())); if (self()) { @@ -78,14 +75,13 @@ void AdvancedWidget::createControls() { } void AdvancedWidget::checkNonDefaultTheme() { - if (self()) return; + if (self()) { + return; + } _useDefaultTheme->toggle( - Window::Theme::IsNonDefaultUsed(), - anim::type::normal); - _toggleNightTheme->entity()->setText(getNightThemeToggleText()); - _toggleNightTheme->toggle( - !Window::Theme::IsNonDefaultUsed(), + Window::Theme::SuggestThemeReset(), anim::type::normal); + _toggleNightTheme->setText(getNightThemeToggleText()); } void AdvancedWidget::onManageLocalStorage() { @@ -120,7 +116,7 @@ void AdvancedWidget::onUseDefaultTheme() { } void AdvancedWidget::onToggleNightTheme() { - Window::Theme::SwitchNightTheme(!Window::Theme::IsNightTheme()); + Window::Theme::ToggleNightMode(); } void AdvancedWidget::onAskQuestion() { @@ -149,7 +145,9 @@ void AdvancedWidget::supportGot(const MTPhelp_Support &support) { } QString AdvancedWidget::getNightThemeToggleText() const { - return lang(Window::Theme::IsNightTheme() ? lng_settings_disable_night_theme : lng_settings_enable_night_theme); + return lang(Window::Theme::IsNightMode() + ? lng_settings_disable_night_theme + : lng_settings_enable_night_theme); } void AdvancedWidget::onTelegramFAQ() { diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.h b/Telegram/SourceFiles/settings/settings_advanced_widget.h index 2cf1af1fc..1f3859299 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.h +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.h @@ -44,7 +44,7 @@ private: LabeledLink *_connectionType = nullptr; #endif // !TDESKTOP_DISABLE_NETWORK_PROXY Ui::SlideWrap *_useDefaultTheme = nullptr; - Ui::SlideWrap *_toggleNightTheme = nullptr; + Ui::LinkButton *_toggleNightTheme = nullptr; Ui::LinkButton *_askQuestion = nullptr; Ui::LinkButton *_telegramFAQ = nullptr; Ui::LinkButton *_logOut = nullptr; diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 896eac833..18ca54590 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -33,15 +33,17 @@ BackgroundRow::BackgroundRow(QWidget *parent) : RpWidget(parent) connect(_chooseFromFile, SIGNAL(clicked()), this, SIGNAL(chooseFromFile())); connect(_editTheme, SIGNAL(clicked()), this, SIGNAL(editTheme())); checkNonDefaultTheme(); - subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { - if (update.type == Window::Theme::BackgroundUpdate::Type::ApplyingTheme) { + using Update = const Window::Theme::BackgroundUpdate; + subscribe(Window::Theme::Background(), [this](Update &update) { + if (update.type == Update::Type::ApplyingTheme + || update.type == Update::Type::New) { checkNonDefaultTheme(); } }); } void BackgroundRow::checkNonDefaultTheme() { - if (Window::Theme::IsNonDefaultUsed()) { + if (Window::Theme::SuggestThemeReset()) { if (!_useDefaultTheme) { _useDefaultTheme.create(this, lang(lng_settings_bg_use_default), st::boxLinkButton); _useDefaultTheme->show(); @@ -190,7 +192,8 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, UserData *self) : BlockWidge subscribe(Window::Theme::Background(), [this](const Update &update) { if (update.type == Update::Type::New) { _background->updateImage(); - } else if (update.type == Update::Type::Start) { + } else if (update.type == Update::Type::Start + || update.type == Update::Type::Changed) { needBackgroundUpdate(update.tiled); } }); diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index da635c746..802ea0967 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -491,7 +491,7 @@ enum { // Local Storage Keys lskStickerImages = 0x05, // data: StorageKey location lskAudios = 0x06, // data: StorageKey location lskRecentStickersOld = 0x07, // no data - lskBackground = 0x08, // no data + lskBackgroundOld = 0x08, // no data lskUserSettings = 0x09, // no data lskRecentHashtagsAndBots = 0x0a, // no data lskStickersOld = 0x0b, // no data @@ -503,6 +503,7 @@ enum { // Local Storage Keys lskTrustedBots = 0x11, // no data lskFavedStickers = 0x12, // no data lskExportSettings = 0x13, // no data + lskBackground = 0x14, // no data }; enum { @@ -539,7 +540,7 @@ enum { dbiCompressPastedImage = 0x1e, dbiLangOld = 0x1f, dbiLangFileOld = 0x20, - dbiTileBackground = 0x21, + dbiTileBackgroundOld = 0x21, dbiAutoLock = 0x22, dbiDialogLastPath = 0x23, dbiRecentEmojiOld = 0x24, @@ -567,7 +568,7 @@ enum { dbiNativeNotifications = 0x44, dbiNotificationsCount = 0x45, dbiNotificationsCorner = 0x46, - dbiThemeKey = 0x47, + dbiThemeKeyOld = 0x47, dbiDialogsWidthRatioOld = 0x48, dbiUseExternalVideoPlayer = 0x49, dbiDcOptions = 0x4a, @@ -580,6 +581,8 @@ enum { dbiSuggestStickersByEmoji = 0x51, dbiSuggestEmoji = 0x52, dbiTxtDomainString = 0x53, + dbiThemeKey = 0x54, + dbiTileBackground = 0x55, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, @@ -624,13 +627,17 @@ FileKey _recentStickersKeyOld = 0; FileKey _installedStickersKey = 0, _featuredStickersKey = 0, _recentStickersKey = 0, _favedStickersKey = 0, _archivedStickersKey = 0; FileKey _savedGifsKey = 0; -FileKey _backgroundKey = 0; -bool _backgroundWasRead = false; +FileKey _backgroundKeyDay = 0; +FileKey _backgroundKeyNight = 0; bool _backgroundCanWrite = true; -FileKey _themeKey = 0; -QString _themeAbsolutePath; -QString _themePaletteAbsolutePath; +FileKey _themeKeyDay = 0; +FileKey _themeKeyNight = 0; + +// Theme key legacy may be read in start() with settings. +// But it should be moved to keyDay or keyNight inside loadTheme() +// and never used after. +FileKey _themeKeyLegacy = 0; bool _readingUserSettings = false; FileKey _userSettingsKey = 0; @@ -1277,12 +1284,23 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting Sandbox::refreshGlobalProxy(); } break; - case dbiThemeKey: { - quint64 themeKey = 0; - stream >> themeKey; + case dbiThemeKeyOld: { + quint64 key = 0; + stream >> key; if (!_checkStreamStatus(stream)) return false; - _themeKey = themeKey; + _themeKeyLegacy = key; + } break; + + case dbiThemeKey: { + quint64 keyDay = 0, keyNight = 0; + quint32 nightMode = 0; + stream >> keyDay >> keyNight >> nightMode; + if (!_checkStreamStatus(stream)) return false; + + _themeKeyDay = keyDay; + _themeKeyNight = keyNight; + Window::Theme::SetNightModeValue(nightMode == 1); } break; case dbiLangPackKey: { @@ -1413,13 +1431,28 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting if (!_checkStreamStatus(stream)) return false; } break; - case dbiTileBackground: { + case dbiTileBackgroundOld: { qint32 v; stream >> v; if (!_checkStreamStatus(stream)) return false; - bool tile = (version < 8005 && !_backgroundKey) ? false : (v == 1); - Window::Theme::Background()->setTile(tile); + bool tile = (version < 8005 && !_backgroundKeyDay) + ? false + : (v == 1); + if (Window::Theme::IsNightMode()) { + Window::Theme::Background()->setTileNightValue(tile); + } else { + Window::Theme::Background()->setTileDayValue(tile); + } + } break; + + case dbiTileBackground: { + qint32 tileDay, tileNight; + stream >> tileDay >> tileNight; + if (!_checkStreamStatus(stream)) return false; + + Window::Theme::Background()->setTileDayValue(tileDay == 1); + Window::Theme::Background()->setTileNightValue(tileNight == 1); } break; case dbiAdaptiveForWide: { @@ -1873,7 +1906,7 @@ void _writeUserSettings() { ? userDataInstance->serialize() : QByteArray(); - uint32 size = 23 * (sizeof(quint32) + sizeof(qint32)); + uint32 size = 22 * (sizeof(quint32) + sizeof(qint32)); size += sizeof(quint32) + Serialize::stringSize(Global::AskDownloadPath() ? QString() : Global::DownloadPath()) + Serialize::bytearraySize(Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); size += sizeof(quint32) + sizeof(qint32); @@ -1886,6 +1919,7 @@ void _writeUserSettings() { size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); size += sizeof(quint32) + 3 * sizeof(qint32); size += sizeof(quint32) + 2 * sizeof(qint32); + size += sizeof(quint32) + 2 * sizeof(qint32); if (!Global::HiddenPinnedMessages().isEmpty()) { size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); } @@ -1895,7 +1929,10 @@ void _writeUserSettings() { EncryptedDescriptor data(size); data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); - data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tileForSave() ? 1 : 0); + data.stream + << quint32(dbiTileBackground) + << qint32(Window::Theme::Background()->tileDay() ? 1 : 0) + << qint32(Window::Theme::Background()->tileNight() ? 1 : 0); data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); data.stream << quint32(dbiReplaceEmoji) << qint32(Global::ReplaceEmoji() ? 1 : 0); @@ -2076,7 +2113,8 @@ ReadMapState _readMap(const QByteArray &pass) { quint64 recentStickersKeyOld = 0; quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0; quint64 savedGifsKey = 0; - quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsAndBotsKey = 0, savedPeersKey = 0, exportSettingsKey = 0; + quint64 backgroundKeyDay = 0, backgroundKeyNight = 0; + quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, savedPeersKey = 0, exportSettingsKey = 0; while (!map.stream.atEnd()) { quint32 keyType; map.stream >> keyType; @@ -2150,8 +2188,13 @@ ReadMapState _readMap(const QByteArray &pass) { case lskRecentStickersOld: { map.stream >> recentStickersKeyOld; } break; + case lskBackgroundOld: { + map.stream >> (Window::Theme::IsNightMode() + ? backgroundKeyNight + : backgroundKeyDay); + } break; case lskBackground: { - map.stream >> backgroundKey; + map.stream >> backgroundKeyDay >> backgroundKeyNight; } break; case lskUserSettings: { map.stream >> userSettingsKey; @@ -2212,7 +2255,8 @@ ReadMapState _readMap(const QByteArray &pass) { _archivedStickersKey = archivedStickersKey; _savedGifsKey = savedGifsKey; _savedPeersKey = savedPeersKey; - _backgroundKey = backgroundKey; + _backgroundKeyDay = backgroundKeyDay; + _backgroundKeyNight = backgroundKeyNight; _userSettingsKey = userSettingsKey; _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; _exportSettingsKey = exportSettingsKey; @@ -2291,7 +2335,7 @@ void _writeMap(WriteMapWhen when) { if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_backgroundKeyDay || _backgroundKeyNight) mapSize += sizeof(quint32) + sizeof(quint64) + sizeof(quint64); if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); @@ -2363,8 +2407,11 @@ void _writeMap(WriteMapWhen when) { if (_savedPeersKey) { mapData.stream << quint32(lskSavedPeers) << quint64(_savedPeersKey); } - if (_backgroundKey) { - mapData.stream << quint32(lskBackground) << quint64(_backgroundKey); + if (_backgroundKeyDay || _backgroundKeyNight) { + mapData.stream + << quint32(lskBackground) + << quint64(_backgroundKeyDay) + << quint64(_backgroundKeyNight); } if (_userSettingsKey) { mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey); @@ -2396,7 +2443,7 @@ void finish() { } } -void readTheme(); +void loadTheme(); void readLangPack(); void start() { @@ -2454,7 +2501,7 @@ void start() { _oldSettingsVersion = settingsData.version; _settingsSalt = salt; - readTheme(); + loadTheme(); readLangPack(); applyReadContext(std::move(context)); @@ -2496,9 +2543,8 @@ void writeSettings() { size += sizeof(qint32) + Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password); } - if (_themeKey) { - size += sizeof(quint32) + sizeof(quint64); - } + // Theme keys and night mode. + size += sizeof(quint32) + sizeof(quint64) * 2 + sizeof(quint32); if (_langPackKey) { size += sizeof(quint32) + sizeof(quint64); } @@ -2534,9 +2580,11 @@ void writeSettings() { } data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6()); - if (_themeKey) { - data.stream << quint32(dbiThemeKey) << quint64(_themeKey); - } + data.stream + << quint32(dbiThemeKey) + << quint64(_themeKeyDay) + << quint64(_themeKeyNight) + << quint32(Window::Theme::IsNightMode() ? 1 : 0); if (_langPackKey) { data.stream << quint32(dbiLangPackKey) << quint64(_langPackKey); } @@ -2640,7 +2688,8 @@ void reset() { _recentStickersKeyOld = 0; _installedStickersKey = _featuredStickersKey = _recentStickersKey = _favedStickersKey = _archivedStickersKey = 0; _savedGifsKey = 0; - _backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = _exportSettingsKey = 0; + _backgroundKeyDay = _backgroundKeyNight = 0; + _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = _exportSettingsKey = 0; _oldMapVersion = _oldSettingsVersion = 0; StoredAuthSessionCache.reset(); _mapChanged = true; @@ -4102,48 +4151,58 @@ void readSavedGifs() { } void writeBackground(int32 id, const QImage &img) { - if (!_working() || !_backgroundCanWrite) return; + if (!_working() || !_backgroundCanWrite) { + return; + } if (!LocalKey) { LOG(("App Error: localkey not created in writeBackground()")); return; } + auto &backgroundKey = Window::Theme::IsNightMode() + ? _backgroundKeyNight + : _backgroundKeyDay; QByteArray bmp; if (!img.isNull()) { QBuffer buf(&bmp); - if (!img.save(&buf, "BMP")) return; + if (!img.save(&buf, "BMP")) { + return; + } } - if (!_backgroundKey) { - _backgroundKey = genKey(); + if (!backgroundKey) { + backgroundKey = genKey(); _mapChanged = true; _writeMap(WriteMapWhen::Fast); } - quint32 size = sizeof(qint32) + sizeof(quint32) + (bmp.isEmpty() ? 0 : (sizeof(quint32) + bmp.size())); + quint32 size = sizeof(qint32) + + sizeof(quint32) + + (bmp.isEmpty() ? 0 : (sizeof(quint32) + bmp.size())); EncryptedDescriptor data(size); data.stream << qint32(id) << bmp; - FileWriteDescriptor file(_backgroundKey); + FileWriteDescriptor file(backgroundKey); file.writeEncrypted(data); } bool readBackground() { - if (_backgroundWasRead) { - return false; - } - _backgroundWasRead = true; - FileReadDescriptor bg; - if (!readEncryptedFile(bg, _backgroundKey)) { - clearKey(_backgroundKey); - _backgroundKey = 0; - _writeMap(); + auto &backgroundKey = Window::Theme::IsNightMode() + ? _backgroundKeyNight + : _backgroundKeyDay; + if (!readEncryptedFile(bg, backgroundKey)) { + if (backgroundKey) { + clearKey(backgroundKey); + backgroundKey = 0; + _mapChanged = true; + _writeMap(); + } return false; } - QByteArray pngData; + QByteArray bmpData; qint32 id; - bg.stream >> id >> pngData; + bg.stream >> id >> bmpData; auto oldEmptyImage = (bg.stream.status() != QDataStream::Ok); if (oldEmptyImage || id == Window::Theme::kInitialBackground @@ -4157,7 +4216,7 @@ bool readBackground() { } _backgroundCanWrite = true; return true; - } else if (id == Window::Theme::kThemeBackground && pngData.isEmpty()) { + } else if (id == Window::Theme::kThemeBackground && bmpData.isEmpty()) { _backgroundCanWrite = false; Window::Theme::Background()->setImage(id); _backgroundCanWrite = true; @@ -4165,7 +4224,7 @@ bool readBackground() { } QImage image; - QBuffer buf(&pngData); + QBuffer buf(&bmpData); QImageReader reader(&buf); #ifndef OS_MAC_OLD reader.setAutoTransform(true); @@ -4179,96 +4238,127 @@ bool readBackground() { return false; } -bool readThemeUsingKey(FileKey key) { +Window::Theme::Saved readThemeUsingKey(FileKey key) { FileReadDescriptor theme; if (!readEncryptedFile(theme, key, FileOption::Safe, SettingsKey)) { - return false; + return {}; } - QByteArray themeContent; - QString pathRelative, pathAbsolute; - Window::Theme::Cached cache; - theme.stream >> themeContent; - theme.stream >> pathRelative >> pathAbsolute; + auto result = Window::Theme::Saved(); + theme.stream >> result.content; + theme.stream >> result.pathRelative >> result.pathAbsolute; if (theme.stream.status() != QDataStream::Ok) { - return false; + return {}; } - _themeAbsolutePath = pathAbsolute; - _themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString(); - - QFile file(pathRelative); - if (pathRelative.isEmpty() || !file.exists()) { - file.setFileName(pathAbsolute); + QFile file(result.pathRelative); + if (result.pathRelative.isEmpty() || !file.exists()) { + file.setFileName(result.pathAbsolute); } auto changed = false; - if (!file.fileName().isEmpty() && file.exists() && file.open(QIODevice::ReadOnly)) { + if (!file.fileName().isEmpty() + && file.exists() + && file.open(QIODevice::ReadOnly)) { if (file.size() > kThemeFileSizeLimit) { - LOG(("Error: theme file too large: %1 (should be less than 5 MB, got %2)").arg(file.fileName()).arg(file.size())); - return false; + LOG(("Error: theme file too large: %1 " + "(should be less than 5 MB, got %2)" + ).arg(file.fileName() + ).arg(file.size())); + return {}; } auto fileContent = file.readAll(); file.close(); - if (themeContent != fileContent) { - themeContent = fileContent; + if (result.content != fileContent) { + result.content = fileContent; changed = true; } } if (!changed) { quint32 backgroundIsTiled = 0; - theme.stream >> cache.paletteChecksum >> cache.contentChecksum >> cache.colors >> cache.background >> backgroundIsTiled; - cache.tiled = (backgroundIsTiled == 1); + theme.stream + >> result.cache.paletteChecksum + >> result.cache.contentChecksum + >> result.cache.colors + >> result.cache.background + >> backgroundIsTiled; + result.cache.tiled = (backgroundIsTiled == 1); if (theme.stream.status() != QDataStream::Ok) { - return false; + return {}; } } - return Window::Theme::Load(pathRelative, pathAbsolute, themeContent, cache); + return result; } -void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache) { - if (content.isEmpty()) { - _themeAbsolutePath = _themePaletteAbsolutePath = QString(); - if (_themeKey) { - clearKey(_themeKey); - _themeKey = 0; +QString loadThemeUsingKey(FileKey key) { + auto read = readThemeUsingKey(key); + const auto result = read.pathAbsolute; + return (!read.content.isEmpty() && Window::Theme::Load(std::move(read))) + ? result + : QString(); +} + +void writeTheme(const Window::Theme::Saved &saved) { + auto &themeKey = Window::Theme::IsNightMode() + ? _themeKeyNight + : _themeKeyDay; + if (saved.content.isEmpty()) { + if (themeKey) { + clearKey(themeKey); + themeKey = 0; writeSettings(); } return; } - _themeAbsolutePath = pathAbsolute; - _themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString(); - if (!_themeKey) { - _themeKey = genKey(FileOption::Safe); + if (!themeKey) { + themeKey = genKey(FileOption::Safe); writeSettings(); } - auto backgroundTiled = static_cast(cache.tiled ? 1 : 0); - quint32 size = Serialize::bytearraySize(content); - size += Serialize::stringSize(pathRelative) + Serialize::stringSize(pathAbsolute); - size += sizeof(int32) * 2 + Serialize::bytearraySize(cache.colors) + Serialize::bytearraySize(cache.background) + sizeof(quint32); + auto backgroundTiled = static_cast(saved.cache.tiled ? 1 : 0); + quint32 size = Serialize::bytearraySize(saved.content); + size += Serialize::stringSize(saved.pathRelative) + Serialize::stringSize(saved.pathAbsolute); + size += sizeof(int32) * 2 + Serialize::bytearraySize(saved.cache.colors) + Serialize::bytearraySize(saved.cache.background) + sizeof(quint32); EncryptedDescriptor data(size); - data.stream << content; - data.stream << pathRelative << pathAbsolute; - data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled; + data.stream << saved.content; + data.stream << saved.pathRelative << saved.pathAbsolute; + data.stream << saved.cache.paletteChecksum << saved.cache.contentChecksum << saved.cache.colors << saved.cache.background << backgroundTiled; - FileWriteDescriptor file(_themeKey, FileOption::Safe); + FileWriteDescriptor file(themeKey, FileOption::Safe); file.writeEncrypted(data, SettingsKey); } void clearTheme() { - writeTheme(QString(), QString(), QByteArray(), Window::Theme::Cached()); + writeTheme(Window::Theme::Saved()); } -void readTheme() { - if (_themeKey && !readThemeUsingKey(_themeKey)) { +void loadTheme() { + const auto key = (_themeKeyLegacy != 0) + ? _themeKeyLegacy + : (Window::Theme::IsNightMode() + ? _themeKeyNight + : _themeKeyDay); + if (!key) { + return; + } else if (const auto path = loadThemeUsingKey(key); !path.isEmpty()) { + if (_themeKeyLegacy) { + Window::Theme::SetNightModeValue(path + == Window::Theme::NightThemePath()); + (Window::Theme::IsNightMode() + ? _themeKeyNight + : _themeKeyDay) = base::take(_themeKeyLegacy); + } + } else { clearTheme(); } } -bool hasTheme() { - return (_themeKey != 0); +Window::Theme::Saved readThemeAfterSwitch() { + const auto key = Window::Theme::IsNightMode() + ? _themeKeyNight + : _themeKeyDay; + return readThemeUsingKey(key); } void readLangPack() { @@ -4297,21 +4387,16 @@ void writeLangPack() { file.writeEncrypted(data, SettingsKey); } -QString themePaletteAbsolutePath() { - return _themePaletteAbsolutePath; -} - -QString themeAbsolutePath() { - return _themeAbsolutePath; -} - bool copyThemeColorsToPalette(const QString &path) { - if (!_themeKey) { + auto &themeKey = Window::Theme::IsNightMode() + ? _themeKeyNight + : _themeKeyDay; + if (!themeKey) { return false; } FileReadDescriptor theme; - if (!readEncryptedFile(theme, _themeKey, FileOption::Safe, SettingsKey)) { + if (!readEncryptedFile(theme, themeKey, FileOption::Safe, SettingsKey)) { return false; } diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 81e5b00bd..9523dce68 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Window { namespace Theme { -struct Cached; +struct Saved; } // namespace Theme } // namespace Window @@ -152,12 +152,10 @@ int32 countSavedGifsHash(); void writeBackground(int32 id, const QImage &img); bool readBackground(); -void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache); +void writeTheme(const Window::Theme::Saved &saved); void clearTheme(); -bool hasTheme(); -QString themeAbsolutePath(); -QString themePaletteAbsolutePath(); bool copyThemeColorsToPalette(const QString &file); +Window::Theme::Saved readThemeAfterSwitch(); void writeLangPack(); diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 5b2c1362a..807e8556b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/themes/window_theme.h" +#include "window/themes/window_theme_preview.h" #include "mainwidget.h" #include "storage/localstorage.h" #include "base/parse_helper.h" @@ -27,7 +28,8 @@ constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme"); struct Data { struct Applying { - QString path; + QString pathRelative; + QString pathAbsolute; QByteArray content; QByteArray paletteForRevert; Cached cached; @@ -195,7 +197,7 @@ void applyBackground(QImage &&background, bool tiled, Instance *out) { } } -bool loadThemeFromCache(const QByteArray &content, Cached &cache) { +bool loadThemeFromCache(const QByteArray &content, const Cached &cache) { if (cache.paletteChecksum != style::palette::Checksum()) { return false; } @@ -205,8 +207,8 @@ bool loadThemeFromCache(const QByteArray &content, Cached &cache) { QImage background; if (!cache.background.isEmpty()) { - QBuffer buffer(&cache.background); - QImageReader reader(&buffer); + QDataStream stream(cache.background); + QImageReader reader(stream.device()); #ifndef OS_MAC_OLD reader.setAutoTransform(true); #endif // OS_MAC_OLD @@ -364,6 +366,24 @@ void adjustColorsUsingBackground(const QImage &img) { adjustColor(st::historyScroll.barBgOver, hue, saturation); } +void ApplyDefaultWithNightMode(bool nightMode) { + if (nightMode) { + if (auto preview = PreviewFromFile(NightThemePath())) { + Apply(std::move(preview)); + } + } else { + instance.createIfNull(); + instance->applying.pathRelative = QString(); + instance->applying.pathAbsolute = QString(); + instance->applying.content = QByteArray(); + instance->applying.cached = Cached(); + if (instance->applying.paletteForRevert.isEmpty()) { + instance->applying.paletteForRevert = style::main_palette::save(); + } + Background()->setTestingDefaultTheme(); + } +} + } // namespace void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { @@ -380,7 +400,10 @@ void ChatBackground::start() { } void ChatBackground::setImage(int32 id, QImage &&image) { - auto resetPalette = (id == kDefaultBackground && _id != kDefaultBackground && !Local::hasTheme()); + auto resetPalette = (id == kDefaultBackground) + && (_id != kDefaultBackground) + && !nightMode() + && _themeAbsolutePath.isEmpty(); if (id == kThemeBackground && _themeImage.isNull()) { id = kDefaultBackground; } else if (resetPalette) { @@ -392,7 +415,7 @@ void ChatBackground::setImage(int32 id, QImage &&image) { } _id = id; if (_id == kThemeBackground) { - _tile = _themeTile; + (nightMode() ? _tileNightValue : _tileDayValue) = _themeTile; setPreparedImage(QImage(_themeImage)); } else if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground @@ -418,10 +441,10 @@ void ChatBackground::setImage(int32 id, QImage &&image) { setPreparedImage(prepareBackgroundImage(std::move(image))); } Assert(!_pixmap.isNull() && !_pixmapForTiled.isNull()); - notify(BackgroundUpdate(BackgroundUpdate::Type::New, _tile)); + notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile())); if (resetPalette) { - notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); - notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _tile), true); + notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true); + notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, tile()), true); } } @@ -429,33 +452,30 @@ void ChatBackground::setPreparedImage(QImage &&image) { image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(cRetinaFactor()); - auto adjustColors = [this] { - auto someThemeApplied = [] { - if (AreTestingTheme()) { - return !instance->applying.path.isEmpty(); - } - return IsNonDefaultUsed() || IsNightTheme(); + auto adjustColors = [&] { + const auto usingThemeBackground = [&] { + return (_id == kThemeBackground) + || (_id == internal::kTestingThemeBackground); }; - auto usingThemeBackground = [this] { - return (_id == kThemeBackground || _id == internal::kTestingThemeBackground); + const auto usingDefaultBackground = [&] { + return (_id == kDefaultBackground) + || (_id == internal::kTestingDefaultBackground); }; - auto usingDefaultBackground = [this] { - return (_id == kDefaultBackground || _id == internal::kTestingDefaultBackground); - }; - auto testingPalette = [] { - if (AreTestingTheme()) { - return IsPaletteTestingPath(instance->applying.path); - } - return !Local::themePaletteAbsolutePath().isEmpty(); + const auto testingPalette = [&] { + const auto path = AreTestingTheme() + ? instance->applying.pathAbsolute + : _themeAbsolutePath; + return IsPaletteTestingPath(path); }; - if (someThemeApplied()) { - return !usingThemeBackground() && !testingPalette(); + if (testingPalette()) { + return false; + } else if (IsNonDefaultThemeOrBackground() || nightMode()) { + return !usingThemeBackground(); } return !usingDefaultBackground(); - }; - - if (adjustColors()) { + }(); + if (adjustColors) { adjustColorsUsingBackground(image); } @@ -494,15 +514,27 @@ int32 ChatBackground::id() const { } bool ChatBackground::tile() const { - return _tile; + return nightMode() ? _tileNightValue : _tileDayValue; } -bool ChatBackground::tileForSave() const { +bool ChatBackground::tileDay() const { if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { - return _tileForRevert; + if (!nightMode()) { + return _tileForRevert; + } } - return tile(); + return _tileDayValue; +} + +bool ChatBackground::tileNight() const { + if (_id == internal::kTestingThemeBackground || + _id == internal::kTestingDefaultBackground) { + if (nightMode()) { + return _tileForRevert; + } + } + return _tileNightValue; } void ChatBackground::ensureStarted() { @@ -515,17 +547,42 @@ void ChatBackground::ensureStarted() { void ChatBackground::setTile(bool tile) { ensureStarted(); - if (_tile != tile) { - _tile = tile; - if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { + const auto old = this->tile(); + if (nightMode()) { + setTileNightValue(tile); + } else { + setTileDayValue(tile); + } + if (this->tile() != old) { + if (_id != internal::kTestingThemeBackground + && _id != internal::kTestingDefaultBackground) { Local::writeUserSettings(); } - notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, _tile)); + notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, tile)); } } +void ChatBackground::setTileDayValue(bool tile) { + ensureStarted(); + _tileDayValue = tile; +} + +void ChatBackground::setTileNightValue(bool tile) { + ensureStarted(); + _tileNightValue = tile; +} + +void ChatBackground::setThemeAbsolutePath(const QString &path) { + _themeAbsolutePath = path; +} + +QString ChatBackground::themeAbsolutePath() const { + return _themeAbsolutePath; +} + void ChatBackground::reset() { - if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { + if (_id == internal::kTestingThemeBackground + || _id == internal::kTestingDefaultBackground) { if (_themeImage.isNull()) { _idForRevert = kDefaultBackground; _imageForRevert = QImage(); @@ -542,10 +599,11 @@ void ChatBackground::reset() { void ChatBackground::saveForRevert() { ensureStarted(); - if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { + if (_id != internal::kTestingThemeBackground + && _id != internal::kTestingDefaultBackground) { _idForRevert = _id; _imageForRevert = std::move(_pixmap).toImage(); - _tileForRevert = _tile; + _tileForRevert = tile(); } } @@ -553,8 +611,10 @@ void ChatBackground::setTestingTheme(Instance &&theme, ChangeMode mode) { style::main_palette::apply(theme.palette); auto switchToThemeBackground = (mode == ChangeMode::SwitchToThemeBackground && !theme.background.isNull()) || (_id == kThemeBackground) - || (_id == kDefaultBackground && !Local::hasTheme()); - if (AreTestingTheme() && IsPaletteTestingPath(instance->applying.path)) { + || (_id == kDefaultBackground + && !nightMode() + && _themeAbsolutePath.isEmpty()); + if (AreTestingTheme() && IsPaletteTestingPath(instance->applying.pathAbsolute)) { // Grab current background image if it is not already custom if (_id != kCustomBackground) { saveForRevert(); @@ -568,47 +628,61 @@ void ChatBackground::setTestingTheme(Instance &&theme, ChangeMode mode) { // Apply current background image so that service bg colors are recounted. setImage(_id, std::move(_pixmap).toImage()); } - notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); + notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true); } void ChatBackground::setTestingDefaultTheme() { style::main_palette::reset(); - if (_id == kThemeBackground) { - saveForRevert(); - setImage(internal::kTestingDefaultBackground); - setTile(false); - } else { - // Apply current background image so that service bg colors are recounted. - setImage(_id, std::move(_pixmap).toImage()); - } - notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); + saveForRevert(); + setImage(internal::kTestingDefaultBackground); + setTile(false); + notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true); } -void ChatBackground::keepApplied() { +void ChatBackground::keepApplied(const QString &path, bool write) { + setThemeAbsolutePath(path); if (_id == internal::kTestingEditorBackground) { _id = kCustomBackground; _themeImage = QImage(); _themeTile = false; - writeNewBackgroundSettings(); + if (write) { + writeNewBackgroundSettings(); + } } else if (_id == internal::kTestingThemeBackground) { _id = kThemeBackground; _themeImage = _pixmap.toImage(); - _themeTile = _tile; - writeNewBackgroundSettings(); + _themeTile = tile(); + if (write) { + writeNewBackgroundSettings(); + } } else if (_id == internal::kTestingDefaultBackground) { _id = kDefaultBackground; _themeImage = QImage(); _themeTile = false; - writeNewBackgroundSettings(); + if (write) { + writeNewBackgroundSettings(); + } } - notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _tile), true); + notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, tile()), true); +} + +bool ChatBackground::isNonDefaultThemeOrBackground() const { + return nightMode() + ? (_themeAbsolutePath != NightThemePath() + || _id != kThemeBackground) + : (!_themeAbsolutePath.isEmpty() + || _id != kDefaultBackground); } void ChatBackground::writeNewBackgroundSettings() { - if (_tile != _tileForRevert) { + if (tile() != _tileForRevert) { Local::writeUserSettings(); } - Local::writeBackground(_id, (_id == kThemeBackground || _id == kDefaultBackground) ? QImage() : _pixmap.toImage()); + Local::writeBackground( + _id, + ((_id == kThemeBackground || _id == kDefaultBackground) + ? QImage() + : _pixmap.toImage())); } void ChatBackground::revert() { @@ -621,30 +695,100 @@ void ChatBackground::revert() { // Apply current background image so that service bg colors are recounted. setImage(_id, std::move(_pixmap).toImage()); } - notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, _tile), true); + notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, tile()), true); } +void ChatBackground::setNightModeValue(bool nightMode) { + _nightMode = nightMode; +} + +bool ChatBackground::nightMode() const { + return _nightMode; +} + +void ChatBackground::toggleNightMode() { + const auto oldNightMode = _nightMode; + const auto newNightMode = !_nightMode; + _nightMode = newNightMode; + auto read = Local::readThemeAfterSwitch(); + auto path = read.pathAbsolute; + + _nightMode = oldNightMode; + auto oldTileValue = (_nightMode ? _tileNightValue : _tileDayValue); + const auto applied = [&] { + if (read.content.isEmpty()) { + return false; + } + auto preview = std::make_unique(); + preview->pathAbsolute = std::move(read.pathAbsolute); + preview->pathRelative = std::move(read.pathRelative); + preview->content = std::move(read.content); + preview->instance.cached = std::move(read.cache); + const auto loaded = loadTheme( + preview->content, + preview->instance.cached, + &preview->instance); + if (!loaded) { + return false; + } + Apply(std::move(preview)); + return true; + }(); + if (!applied) { + path = newNightMode ? NightThemePath() : QString(); + ApplyDefaultWithNightMode(newNightMode); + } + + // Theme editor could have already reverted the testing of this toggle. + if (AreTestingTheme()) { + _nightMode = newNightMode; + if (oldNightMode) { + _tileDayValue = _tileNightValue; + _tileNightValue = oldTileValue; + } else { + _tileNightValue = _tileDayValue; + _tileDayValue = oldTileValue; + } + + // We don't call full KeepApplied() here, because + // we don't need to write theme or overwrite current background. + instance->applying = Data::Applying(); + Local::writeSettings(); + keepApplied(path, false); + if (tile() != _tileForRevert) { + Local::writeUserSettings(); + } + + if (!Local::readBackground()) { + setImage(kThemeBackground); + } + } +} ChatBackground *Background() { instance.createIfNull(); return &instance->background; } -bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache) { - if (content.size() < 4) { - LOG(("Theme Error: Could not load theme from '%1' (%2)").arg(pathRelative).arg(pathAbsolute)); +bool Load(Saved &&saved) { + if (saved.content.size() < 4) { + LOG(("Theme Error: Could not load theme from '%1' (%2)" + ).arg(saved.pathRelative + ).arg(saved.pathAbsolute)); return false; } instance.createIfNull(); - if (loadThemeFromCache(content, cache)) { + if (loadThemeFromCache(saved.content, saved.cache)) { + Background()->setThemeAbsolutePath(saved.pathAbsolute); return true; } - if (!loadTheme(content, cache)) { + if (!loadTheme(saved.content, saved.cache)) { return false; } - Local::writeTheme(pathRelative, pathAbsolute, content, cache); + Local::writeTheme(saved); + Background()->setThemeAbsolutePath(saved.pathAbsolute); return true; } @@ -653,38 +797,16 @@ void Unload() { } bool Apply(const QString &filepath) { - auto preview = std::make_unique(); - preview->path = filepath; - if (!LoadFromFile(preview->path, &preview->instance, &preview->content)) { - return false; + if (auto preview = PreviewFromFile(filepath)) { + return Apply(std::move(preview)); } - return Apply(std::move(preview)); -} - -void SwitchNightTheme(bool enabled) { - if (enabled) { - auto preview = std::make_unique(); - preview->path = str_const_toString(kNightThemeFile); - if (!LoadFromFile(preview->path, &preview->instance, &preview->content)) { - return; - } - instance.createIfNull(); - instance->applying.path = std::move(preview->path); - instance->applying.content = std::move(preview->content); - instance->applying.cached = std::move(preview->instance.cached); - if (instance->applying.paletteForRevert.isEmpty()) { - instance->applying.paletteForRevert = style::main_palette::save(); - } - Background()->setTestingTheme(std::move(preview->instance), ChatBackground::ChangeMode::LeaveCurrentCustomBackground); - } else { - Window::Theme::ApplyDefault(); - } - KeepApplied(); + return false; } bool Apply(std::unique_ptr preview) { instance.createIfNull(); - instance->applying.path = std::move(preview->path); + instance->applying.pathRelative = std::move(preview->pathRelative); + instance->applying.pathAbsolute = std::move(preview->pathAbsolute); instance->applying.content = std::move(preview->content); instance->applying.cached = std::move(preview->instance.cached); if (instance->applying.paletteForRevert.isEmpty()) { @@ -695,14 +817,7 @@ bool Apply(std::unique_ptr preview) { } void ApplyDefault() { - instance.createIfNull(); - instance->applying.path = QString(); - instance->applying.content = QByteArray(); - instance->applying.cached = Cached(); - if (instance->applying.paletteForRevert.isEmpty()) { - instance->applying.paletteForRevert = style::main_palette::save(); - } - Background()->setTestingDefaultTheme(); + ApplyDefaultWithNightMode(IsNightMode()); } bool ApplyEditedPalette(const QString &path, const QByteArray &content) { @@ -715,7 +830,12 @@ bool ApplyEditedPalette(const QString &path, const QByteArray &content) { out.cached.contentChecksum = hashCrc32(content.constData(), content.size()); instance.createIfNull(); - instance->applying.path = path; + instance->applying.pathRelative = path.isEmpty() + ? QString() + : QDir().relativeFilePath(path); + instance->applying.pathAbsolute = path.isEmpty() + ? QString() + : QFileInfo(path).absoluteFilePath(); instance->applying.content = content; instance->applying.cached = out.cached; if (instance->applying.paletteForRevert.isEmpty()) { @@ -727,31 +847,52 @@ bool ApplyEditedPalette(const QString &path, const QByteArray &content) { } void KeepApplied() { - if (!instance) { + if (!AreTestingTheme()) { return; } - auto filepath = instance->applying.path; - auto pathRelative = filepath.isEmpty() ? QString() : QDir().relativeFilePath(filepath); - auto pathAbsolute = filepath.isEmpty() ? QString() : QFileInfo(filepath).absoluteFilePath(); - Local::writeTheme(pathRelative, pathAbsolute, instance->applying.content, instance->applying.cached); + auto saved = Saved(); + saved.pathRelative = instance->applying.pathRelative; + saved.pathAbsolute = instance->applying.pathAbsolute; + saved.content = std::move(instance->applying.content); + saved.cache = std::move(instance->applying.cached); + Local::writeTheme(saved); instance->applying = Data::Applying(); - Background()->keepApplied(); + Background()->keepApplied(saved.pathAbsolute, true); } void Revert() { - if (!instance->applying.paletteForRevert.isEmpty()) { - style::main_palette::load(instance->applying.paletteForRevert); + if (!AreTestingTheme()) { + return; } + style::main_palette::load(instance->applying.paletteForRevert); instance->applying = Data::Applying(); Background()->revert(); } -bool IsNightTheme() { - return (Local::themeAbsolutePath() == str_const_toString(kNightThemeFile)); +QString NightThemePath() { + return str_const_toString(kNightThemeFile); } -bool IsNonDefaultUsed() { - return Local::hasTheme() && !IsNightTheme(); +bool IsNonDefaultThemeOrBackground() { + return Background()->isNonDefaultThemeOrBackground(); +} + +bool IsNightMode() { + return instance ? Background()->nightMode() : false; +} + +void SetNightModeValue(bool nightMode) { + if (instance || nightMode) { + Background()->setNightModeValue(nightMode); + } +} + +void ToggleNightMode() { + Background()->toggleNightMode(); +} + +bool SuggestThemeReset() { + return IsNonDefaultThemeOrBackground(); } bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 166d60991..b24a7070b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -30,7 +30,13 @@ struct Cached { int32 paletteChecksum = 0; int32 contentChecksum = 0; }; -bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache); +struct Saved { + QString pathRelative; + QString pathAbsolute; + QByteArray content; + Cached cache; +}; +bool Load(Saved &&saved); void Unload(); struct Instance { @@ -41,7 +47,8 @@ struct Instance { }; struct Preview { - QString path; + QString pathRelative; + QString pathAbsolute; Instance instance; QByteArray content; QImage preview; @@ -52,9 +59,12 @@ bool Apply(std::unique_ptr preview); void ApplyDefault(); bool ApplyEditedPalette(const QString &path, const QByteArray &content); void KeepApplied(); -bool IsNonDefaultUsed(); -bool IsNightTheme(); -void SwitchNightTheme(bool enabled); +QString NightThemePath(); +bool IsNightMode(); +void SetNightModeValue(bool nightMode); +void ToggleNightMode(); +bool IsNonDefaultThemeOrBackground(); +bool SuggestThemeReset(); void Revert(); bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent); @@ -88,6 +98,10 @@ public: void start(); void setImage(int32 id, QImage &&image = QImage()); void setTile(bool tile); + void setTileDayValue(bool tile); + void setTileNightValue(bool tile); + void setThemeAbsolutePath(const QString &path); + QString themeAbsolutePath() const; void reset(); enum class ChangeMode { @@ -96,7 +110,6 @@ public: }; void setTestingTheme(Instance &&theme, ChangeMode mode = ChangeMode::SwitchToThemeBackground); void setTestingDefaultTheme(); - void keepApplied(); void revert(); int32 id() const; @@ -107,7 +120,8 @@ public: return _pixmapForTiled; } bool tile() const; - bool tileForSave() const; + bool tileDay() const; + bool tileNight() const; private: void ensureStarted(); @@ -115,11 +129,26 @@ private: void setPreparedImage(QImage &&image); void writeNewBackgroundSettings(); + void setNightModeValue(bool nightMode); + bool nightMode() const; + void toggleNightMode(); + void keepApplied(const QString &path, bool write); + bool isNonDefaultThemeOrBackground() const; + + friend bool IsNightMode(); + friend void SetNightModeValue(bool nightMode); + friend void ToggleNightMode(); + friend void KeepApplied(); + friend bool IsNonDefaultThemeOrBackground(); + int32 _id = internal::kUninitializedBackground; QPixmap _pixmap; QPixmap _pixmapForTiled; - bool _tile = false; + bool _nightMode = false; + bool _tileDayValue = false; + bool _tileNightValue = true; + QString _themeAbsolutePath; QImage _themeImage; bool _themeTile = false; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 0eac8acbe..2ccca8cb2 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -785,9 +785,9 @@ void Editor::paintEvent(QPaintEvent *e) { } void Editor::Start() { - auto palettePath = Local::themePaletteAbsolutePath(); - if (palettePath.isEmpty()) { - FileDialog::GetWritePath(App::wnd(), lang(lng_theme_editor_save_palette), "Palette (*.tdesktop-palette)", "colors.tdesktop-palette", [](const QString &path) { + const auto path = Background()->themeAbsolutePath(); + if (path.isEmpty() || !Window::Theme::IsPaletteTestingPath(path)) { + const auto start = [](const QString &path) { if (!Local::copyThemeColorsToPalette(path)) { writeDefaultPalette(path); } @@ -799,9 +799,15 @@ void Editor::Start() { if (auto window = App::wnd()) { window->showRightColumn(Box(path)); } - }); + }; + FileDialog::GetWritePath( + App::wnd(), + lang(lng_theme_editor_save_palette), + "Palette (*.tdesktop-palette)", + "colors.tdesktop-palette", + start); } else if (auto window = App::wnd()) { - window->showRightColumn(Box(palettePath)); + window->showRightColumn(Box(path)); } } diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index 5c7a6ffa8..0dce94b33 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -889,12 +889,25 @@ void Generator::restoreTextPalette() { } // namespace +std::unique_ptr PreviewFromFile(const QString &filepath) { + auto result = std::make_unique(); + result->pathRelative = filepath.isEmpty() + ? QString() + : QDir().relativeFilePath(filepath); + result->pathAbsolute = filepath.isEmpty() + ? QString() + : QFileInfo(filepath).absoluteFilePath(); + if (!LoadFromFile(filepath, &result->instance, &result->content)) { + return nullptr; + } + return result; +} + std::unique_ptr GeneratePreview( const QString &filepath, CurrentData &&data) { - auto result = std::make_unique(); - result->path = filepath; - if (!LoadFromFile(filepath, &result->instance, &result->content)) { + auto result = PreviewFromFile(filepath); + if (!result) { return nullptr; } result->preview = Generator( diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.h b/Telegram/SourceFiles/window/themes/window_theme_preview.h index 5114df03d..adde5109a 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.h +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.h @@ -18,6 +18,7 @@ struct CurrentData { bool backgroundTiled = false; }; +std::unique_ptr PreviewFromFile(const QString &filepath); std::unique_ptr GeneratePreview( const QString &filepath, CurrentData &&data); diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index a24edfda2..a96beaad5 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -44,9 +44,10 @@ MainMenu::MainMenu( checkSelf(); _nightThemeSwitch.setCallback([this] { - if (auto action = *_nightThemeAction) { - if (action->isChecked() != Window::Theme::IsNightTheme()) { - Window::Theme::SwitchNightTheme(action->isChecked()); + if (const auto action = *_nightThemeAction) { + const auto nightMode = Window::Theme::IsNightMode(); + if (action->isChecked() != nightMode) { + Window::Theme::ToggleNightMode(); } } }); @@ -104,19 +105,17 @@ void MainMenu::refreshMenu() { App::wnd()->showSettings(); }, &st::mainMenuSettings, &st::mainMenuSettingsOver); - if (!Window::Theme::IsNonDefaultUsed()) { - _nightThemeAction = std::make_shared>(nullptr); - auto action = _menu->addAction(lang(lng_menu_night_mode), [this] { - if (auto action = *_nightThemeAction) { - action->setChecked(!action->isChecked()); - _nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration); - } - }, &st::mainMenuNightMode, &st::mainMenuNightModeOver); - *_nightThemeAction = action; - action->setCheckable(true); - action->setChecked(Window::Theme::IsNightTheme()); - _menu->finishAnimating(); - } + _nightThemeAction = std::make_shared>(nullptr); + auto action = _menu->addAction(lang(lng_menu_night_mode), [this] { + if (auto action = *_nightThemeAction) { + action->setChecked(!action->isChecked()); + _nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration); + } + }, &st::mainMenuNightMode, &st::mainMenuNightModeOver); + *_nightThemeAction = action; + action->setCheckable(true); + action->setChecked(Window::Theme::IsNightMode()); + _menu->finishAnimating(); updatePhone(); }