Allow independently change default / night themes.

This commit is contained in:
John Preston 2018-07-19 17:58:40 +03:00
parent d12bd0824d
commit cb338e330f
12 changed files with 554 additions and 278 deletions

View file

@ -519,6 +519,9 @@ void SetAnnotationHex(const std::string &key, const QString &value) {
} }
void SetAnnotationRef(const std::string &key, const QString *valuePtr) { void SetAnnotationRef(const std::string &key, const QString *valuePtr) {
static QMutex mutex;
QMutexLocker lock(&mutex);
if (valuePtr) { if (valuePtr) {
ProcessAnnotationRefs[key] = valuePtr; ProcessAnnotationRefs[key] = valuePtr;
} else { } else {

View file

@ -62,13 +62,10 @@ void AdvancedWidget::createControls() {
} else { } else {
style::margins slidedPadding(0, marginLarge.bottom() / 2, 0, marginLarge.bottom() - (marginLarge.bottom() / 2)); 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())); 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); _useDefaultTheme->hide(anim::type::instant);
} }
createChildRow(_toggleNightTheme, marginLarge, slidedPadding, getNightThemeToggleText(), SLOT(onToggleNightTheme())); createChildRow(_toggleNightTheme, marginLarge, getNightThemeToggleText(), SLOT(onToggleNightTheme()));
if (Window::Theme::IsNonDefaultUsed()) {
_toggleNightTheme->hide(anim::type::instant);
}
} }
createChildRow(_telegramFAQ, marginLarge, lang(lng_settings_faq), SLOT(onTelegramFAQ())); createChildRow(_telegramFAQ, marginLarge, lang(lng_settings_faq), SLOT(onTelegramFAQ()));
if (self()) { if (self()) {
@ -78,14 +75,13 @@ void AdvancedWidget::createControls() {
} }
void AdvancedWidget::checkNonDefaultTheme() { void AdvancedWidget::checkNonDefaultTheme() {
if (self()) return; if (self()) {
return;
}
_useDefaultTheme->toggle( _useDefaultTheme->toggle(
Window::Theme::IsNonDefaultUsed(), Window::Theme::SuggestThemeReset(),
anim::type::normal);
_toggleNightTheme->entity()->setText(getNightThemeToggleText());
_toggleNightTheme->toggle(
!Window::Theme::IsNonDefaultUsed(),
anim::type::normal); anim::type::normal);
_toggleNightTheme->setText(getNightThemeToggleText());
} }
void AdvancedWidget::onManageLocalStorage() { void AdvancedWidget::onManageLocalStorage() {
@ -120,7 +116,7 @@ void AdvancedWidget::onUseDefaultTheme() {
} }
void AdvancedWidget::onToggleNightTheme() { void AdvancedWidget::onToggleNightTheme() {
Window::Theme::SwitchNightTheme(!Window::Theme::IsNightTheme()); Window::Theme::ToggleNightMode();
} }
void AdvancedWidget::onAskQuestion() { void AdvancedWidget::onAskQuestion() {
@ -149,7 +145,9 @@ void AdvancedWidget::supportGot(const MTPhelp_Support &support) {
} }
QString AdvancedWidget::getNightThemeToggleText() const { 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() { void AdvancedWidget::onTelegramFAQ() {

View file

@ -44,7 +44,7 @@ private:
LabeledLink *_connectionType = nullptr; LabeledLink *_connectionType = nullptr;
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY #endif // !TDESKTOP_DISABLE_NETWORK_PROXY
Ui::SlideWrap<Ui::LinkButton> *_useDefaultTheme = nullptr; Ui::SlideWrap<Ui::LinkButton> *_useDefaultTheme = nullptr;
Ui::SlideWrap<Ui::LinkButton> *_toggleNightTheme = nullptr; Ui::LinkButton *_toggleNightTheme = nullptr;
Ui::LinkButton *_askQuestion = nullptr; Ui::LinkButton *_askQuestion = nullptr;
Ui::LinkButton *_telegramFAQ = nullptr; Ui::LinkButton *_telegramFAQ = nullptr;
Ui::LinkButton *_logOut = nullptr; Ui::LinkButton *_logOut = nullptr;

View file

@ -33,15 +33,17 @@ BackgroundRow::BackgroundRow(QWidget *parent) : RpWidget(parent)
connect(_chooseFromFile, SIGNAL(clicked()), this, SIGNAL(chooseFromFile())); connect(_chooseFromFile, SIGNAL(clicked()), this, SIGNAL(chooseFromFile()));
connect(_editTheme, SIGNAL(clicked()), this, SIGNAL(editTheme())); connect(_editTheme, SIGNAL(clicked()), this, SIGNAL(editTheme()));
checkNonDefaultTheme(); checkNonDefaultTheme();
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { using Update = const Window::Theme::BackgroundUpdate;
if (update.type == Window::Theme::BackgroundUpdate::Type::ApplyingTheme) { subscribe(Window::Theme::Background(), [this](Update &update) {
if (update.type == Update::Type::ApplyingTheme
|| update.type == Update::Type::New) {
checkNonDefaultTheme(); checkNonDefaultTheme();
} }
}); });
} }
void BackgroundRow::checkNonDefaultTheme() { void BackgroundRow::checkNonDefaultTheme() {
if (Window::Theme::IsNonDefaultUsed()) { if (Window::Theme::SuggestThemeReset()) {
if (!_useDefaultTheme) { if (!_useDefaultTheme) {
_useDefaultTheme.create(this, lang(lng_settings_bg_use_default), st::boxLinkButton); _useDefaultTheme.create(this, lang(lng_settings_bg_use_default), st::boxLinkButton);
_useDefaultTheme->show(); _useDefaultTheme->show();
@ -190,7 +192,8 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, UserData *self) : BlockWidge
subscribe(Window::Theme::Background(), [this](const Update &update) { subscribe(Window::Theme::Background(), [this](const Update &update) {
if (update.type == Update::Type::New) { if (update.type == Update::Type::New) {
_background->updateImage(); _background->updateImage();
} else if (update.type == Update::Type::Start) { } else if (update.type == Update::Type::Start
|| update.type == Update::Type::Changed) {
needBackgroundUpdate(update.tiled); needBackgroundUpdate(update.tiled);
} }
}); });

View file

@ -491,7 +491,7 @@ enum { // Local Storage Keys
lskStickerImages = 0x05, // data: StorageKey location lskStickerImages = 0x05, // data: StorageKey location
lskAudios = 0x06, // data: StorageKey location lskAudios = 0x06, // data: StorageKey location
lskRecentStickersOld = 0x07, // no data lskRecentStickersOld = 0x07, // no data
lskBackground = 0x08, // no data lskBackgroundOld = 0x08, // no data
lskUserSettings = 0x09, // no data lskUserSettings = 0x09, // no data
lskRecentHashtagsAndBots = 0x0a, // no data lskRecentHashtagsAndBots = 0x0a, // no data
lskStickersOld = 0x0b, // no data lskStickersOld = 0x0b, // no data
@ -503,6 +503,7 @@ enum { // Local Storage Keys
lskTrustedBots = 0x11, // no data lskTrustedBots = 0x11, // no data
lskFavedStickers = 0x12, // no data lskFavedStickers = 0x12, // no data
lskExportSettings = 0x13, // no data lskExportSettings = 0x13, // no data
lskBackground = 0x14, // no data
}; };
enum { enum {
@ -539,7 +540,7 @@ enum {
dbiCompressPastedImage = 0x1e, dbiCompressPastedImage = 0x1e,
dbiLangOld = 0x1f, dbiLangOld = 0x1f,
dbiLangFileOld = 0x20, dbiLangFileOld = 0x20,
dbiTileBackground = 0x21, dbiTileBackgroundOld = 0x21,
dbiAutoLock = 0x22, dbiAutoLock = 0x22,
dbiDialogLastPath = 0x23, dbiDialogLastPath = 0x23,
dbiRecentEmojiOld = 0x24, dbiRecentEmojiOld = 0x24,
@ -567,7 +568,7 @@ enum {
dbiNativeNotifications = 0x44, dbiNativeNotifications = 0x44,
dbiNotificationsCount = 0x45, dbiNotificationsCount = 0x45,
dbiNotificationsCorner = 0x46, dbiNotificationsCorner = 0x46,
dbiThemeKey = 0x47, dbiThemeKeyOld = 0x47,
dbiDialogsWidthRatioOld = 0x48, dbiDialogsWidthRatioOld = 0x48,
dbiUseExternalVideoPlayer = 0x49, dbiUseExternalVideoPlayer = 0x49,
dbiDcOptions = 0x4a, dbiDcOptions = 0x4a,
@ -580,6 +581,8 @@ enum {
dbiSuggestStickersByEmoji = 0x51, dbiSuggestStickersByEmoji = 0x51,
dbiSuggestEmoji = 0x52, dbiSuggestEmoji = 0x52,
dbiTxtDomainString = 0x53, dbiTxtDomainString = 0x53,
dbiThemeKey = 0x54,
dbiTileBackground = 0x55,
dbiEncryptedWithSalt = 333, dbiEncryptedWithSalt = 333,
dbiEncrypted = 444, dbiEncrypted = 444,
@ -624,13 +627,17 @@ FileKey _recentStickersKeyOld = 0;
FileKey _installedStickersKey = 0, _featuredStickersKey = 0, _recentStickersKey = 0, _favedStickersKey = 0, _archivedStickersKey = 0; FileKey _installedStickersKey = 0, _featuredStickersKey = 0, _recentStickersKey = 0, _favedStickersKey = 0, _archivedStickersKey = 0;
FileKey _savedGifsKey = 0; FileKey _savedGifsKey = 0;
FileKey _backgroundKey = 0; FileKey _backgroundKeyDay = 0;
bool _backgroundWasRead = false; FileKey _backgroundKeyNight = 0;
bool _backgroundCanWrite = true; bool _backgroundCanWrite = true;
FileKey _themeKey = 0; FileKey _themeKeyDay = 0;
QString _themeAbsolutePath; FileKey _themeKeyNight = 0;
QString _themePaletteAbsolutePath;
// 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; bool _readingUserSettings = false;
FileKey _userSettingsKey = 0; FileKey _userSettingsKey = 0;
@ -1277,12 +1284,23 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
Sandbox::refreshGlobalProxy(); Sandbox::refreshGlobalProxy();
} break; } break;
case dbiThemeKey: { case dbiThemeKeyOld: {
quint64 themeKey = 0; quint64 key = 0;
stream >> themeKey; stream >> key;
if (!_checkStreamStatus(stream)) return false; 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; } break;
case dbiLangPackKey: { case dbiLangPackKey: {
@ -1413,13 +1431,28 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
if (!_checkStreamStatus(stream)) return false; if (!_checkStreamStatus(stream)) return false;
} break; } break;
case dbiTileBackground: { case dbiTileBackgroundOld: {
qint32 v; qint32 v;
stream >> v; stream >> v;
if (!_checkStreamStatus(stream)) return false; if (!_checkStreamStatus(stream)) return false;
bool tile = (version < 8005 && !_backgroundKey) ? false : (v == 1); bool tile = (version < 8005 && !_backgroundKeyDay)
Window::Theme::Background()->setTile(tile); ? 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; } break;
case dbiAdaptiveForWide: { case dbiAdaptiveForWide: {
@ -1873,7 +1906,7 @@ void _writeUserSettings() {
? userDataInstance->serialize() ? userDataInstance->serialize()
: QByteArray(); : 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) + Serialize::stringSize(Global::AskDownloadPath() ? QString() : Global::DownloadPath()) + Serialize::bytearraySize(Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark());
size += sizeof(quint32) + sizeof(qint32); size += sizeof(quint32) + sizeof(qint32);
@ -1886,6 +1919,7 @@ void _writeUserSettings() {
size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath());
size += sizeof(quint32) + 3 * sizeof(qint32); size += sizeof(quint32) + 3 * sizeof(qint32);
size += sizeof(quint32) + 2 * sizeof(qint32); size += sizeof(quint32) + 2 * sizeof(qint32);
size += sizeof(quint32) + 2 * sizeof(qint32);
if (!Global::HiddenPinnedMessages().isEmpty()) { if (!Global::HiddenPinnedMessages().isEmpty()) {
size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId));
} }
@ -1895,7 +1929,10 @@ void _writeUserSettings() {
EncryptedDescriptor data(size); EncryptedDescriptor data(size);
data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); 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(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0);
data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock());
data.stream << quint32(dbiReplaceEmoji) << qint32(Global::ReplaceEmoji() ? 1 : 0); data.stream << quint32(dbiReplaceEmoji) << qint32(Global::ReplaceEmoji() ? 1 : 0);
@ -2076,7 +2113,8 @@ ReadMapState _readMap(const QByteArray &pass) {
quint64 recentStickersKeyOld = 0; quint64 recentStickersKeyOld = 0;
quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0; quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0;
quint64 savedGifsKey = 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()) { while (!map.stream.atEnd()) {
quint32 keyType; quint32 keyType;
map.stream >> keyType; map.stream >> keyType;
@ -2150,8 +2188,13 @@ ReadMapState _readMap(const QByteArray &pass) {
case lskRecentStickersOld: { case lskRecentStickersOld: {
map.stream >> recentStickersKeyOld; map.stream >> recentStickersKeyOld;
} break; } break;
case lskBackgroundOld: {
map.stream >> (Window::Theme::IsNightMode()
? backgroundKeyNight
: backgroundKeyDay);
} break;
case lskBackground: { case lskBackground: {
map.stream >> backgroundKey; map.stream >> backgroundKeyDay >> backgroundKeyNight;
} break; } break;
case lskUserSettings: { case lskUserSettings: {
map.stream >> userSettingsKey; map.stream >> userSettingsKey;
@ -2212,7 +2255,8 @@ ReadMapState _readMap(const QByteArray &pass) {
_archivedStickersKey = archivedStickersKey; _archivedStickersKey = archivedStickersKey;
_savedGifsKey = savedGifsKey; _savedGifsKey = savedGifsKey;
_savedPeersKey = savedPeersKey; _savedPeersKey = savedPeersKey;
_backgroundKey = backgroundKey; _backgroundKeyDay = backgroundKeyDay;
_backgroundKeyNight = backgroundKeyNight;
_userSettingsKey = userSettingsKey; _userSettingsKey = userSettingsKey;
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
_exportSettingsKey = exportSettingsKey; _exportSettingsKey = exportSettingsKey;
@ -2291,7 +2335,7 @@ void _writeMap(WriteMapWhen when) {
if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_savedPeersKey) 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 (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
@ -2363,8 +2407,11 @@ void _writeMap(WriteMapWhen when) {
if (_savedPeersKey) { if (_savedPeersKey) {
mapData.stream << quint32(lskSavedPeers) << quint64(_savedPeersKey); mapData.stream << quint32(lskSavedPeers) << quint64(_savedPeersKey);
} }
if (_backgroundKey) { if (_backgroundKeyDay || _backgroundKeyNight) {
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey); mapData.stream
<< quint32(lskBackground)
<< quint64(_backgroundKeyDay)
<< quint64(_backgroundKeyNight);
} }
if (_userSettingsKey) { if (_userSettingsKey) {
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey); mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
@ -2396,7 +2443,7 @@ void finish() {
} }
} }
void readTheme(); void loadTheme();
void readLangPack(); void readLangPack();
void start() { void start() {
@ -2454,7 +2501,7 @@ void start() {
_oldSettingsVersion = settingsData.version; _oldSettingsVersion = settingsData.version;
_settingsSalt = salt; _settingsSalt = salt;
readTheme(); loadTheme();
readLangPack(); readLangPack();
applyReadContext(std::move(context)); 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); size += sizeof(qint32) + Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password);
} }
if (_themeKey) { // Theme keys and night mode.
size += sizeof(quint32) + sizeof(quint64); size += sizeof(quint32) + sizeof(quint64) * 2 + sizeof(quint32);
}
if (_langPackKey) { if (_langPackKey) {
size += sizeof(quint32) + sizeof(quint64); size += sizeof(quint32) + sizeof(quint64);
} }
@ -2534,9 +2580,11 @@ void writeSettings() {
} }
data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6()); data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6());
if (_themeKey) { data.stream
data.stream << quint32(dbiThemeKey) << quint64(_themeKey); << quint32(dbiThemeKey)
} << quint64(_themeKeyDay)
<< quint64(_themeKeyNight)
<< quint32(Window::Theme::IsNightMode() ? 1 : 0);
if (_langPackKey) { if (_langPackKey) {
data.stream << quint32(dbiLangPackKey) << quint64(_langPackKey); data.stream << quint32(dbiLangPackKey) << quint64(_langPackKey);
} }
@ -2640,7 +2688,8 @@ void reset() {
_recentStickersKeyOld = 0; _recentStickersKeyOld = 0;
_installedStickersKey = _featuredStickersKey = _recentStickersKey = _favedStickersKey = _archivedStickersKey = 0; _installedStickersKey = _featuredStickersKey = _recentStickersKey = _favedStickersKey = _archivedStickersKey = 0;
_savedGifsKey = 0; _savedGifsKey = 0;
_backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = _exportSettingsKey = 0; _backgroundKeyDay = _backgroundKeyNight = 0;
_userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = _exportSettingsKey = 0;
_oldMapVersion = _oldSettingsVersion = 0; _oldMapVersion = _oldSettingsVersion = 0;
StoredAuthSessionCache.reset(); StoredAuthSessionCache.reset();
_mapChanged = true; _mapChanged = true;
@ -4102,48 +4151,58 @@ void readSavedGifs() {
} }
void writeBackground(int32 id, const QImage &img) { void writeBackground(int32 id, const QImage &img) {
if (!_working() || !_backgroundCanWrite) return; if (!_working() || !_backgroundCanWrite) {
return;
}
if (!LocalKey) { if (!LocalKey) {
LOG(("App Error: localkey not created in writeBackground()")); LOG(("App Error: localkey not created in writeBackground()"));
return; return;
} }
auto &backgroundKey = Window::Theme::IsNightMode()
? _backgroundKeyNight
: _backgroundKeyDay;
QByteArray bmp; QByteArray bmp;
if (!img.isNull()) { if (!img.isNull()) {
QBuffer buf(&bmp); QBuffer buf(&bmp);
if (!img.save(&buf, "BMP")) return; if (!img.save(&buf, "BMP")) {
return;
}
} }
if (!_backgroundKey) { if (!backgroundKey) {
_backgroundKey = genKey(); backgroundKey = genKey();
_mapChanged = true; _mapChanged = true;
_writeMap(WriteMapWhen::Fast); _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); EncryptedDescriptor data(size);
data.stream << qint32(id) << bmp; data.stream << qint32(id) << bmp;
FileWriteDescriptor file(_backgroundKey); FileWriteDescriptor file(backgroundKey);
file.writeEncrypted(data); file.writeEncrypted(data);
} }
bool readBackground() { bool readBackground() {
if (_backgroundWasRead) {
return false;
}
_backgroundWasRead = true;
FileReadDescriptor bg; FileReadDescriptor bg;
if (!readEncryptedFile(bg, _backgroundKey)) { auto &backgroundKey = Window::Theme::IsNightMode()
clearKey(_backgroundKey); ? _backgroundKeyNight
_backgroundKey = 0; : _backgroundKeyDay;
_writeMap(); if (!readEncryptedFile(bg, backgroundKey)) {
if (backgroundKey) {
clearKey(backgroundKey);
backgroundKey = 0;
_mapChanged = true;
_writeMap();
}
return false; return false;
} }
QByteArray pngData; QByteArray bmpData;
qint32 id; qint32 id;
bg.stream >> id >> pngData; bg.stream >> id >> bmpData;
auto oldEmptyImage = (bg.stream.status() != QDataStream::Ok); auto oldEmptyImage = (bg.stream.status() != QDataStream::Ok);
if (oldEmptyImage if (oldEmptyImage
|| id == Window::Theme::kInitialBackground || id == Window::Theme::kInitialBackground
@ -4157,7 +4216,7 @@ bool readBackground() {
} }
_backgroundCanWrite = true; _backgroundCanWrite = true;
return true; return true;
} else if (id == Window::Theme::kThemeBackground && pngData.isEmpty()) { } else if (id == Window::Theme::kThemeBackground && bmpData.isEmpty()) {
_backgroundCanWrite = false; _backgroundCanWrite = false;
Window::Theme::Background()->setImage(id); Window::Theme::Background()->setImage(id);
_backgroundCanWrite = true; _backgroundCanWrite = true;
@ -4165,7 +4224,7 @@ bool readBackground() {
} }
QImage image; QImage image;
QBuffer buf(&pngData); QBuffer buf(&bmpData);
QImageReader reader(&buf); QImageReader reader(&buf);
#ifndef OS_MAC_OLD #ifndef OS_MAC_OLD
reader.setAutoTransform(true); reader.setAutoTransform(true);
@ -4179,96 +4238,127 @@ bool readBackground() {
return false; return false;
} }
bool readThemeUsingKey(FileKey key) { Window::Theme::Saved readThemeUsingKey(FileKey key) {
FileReadDescriptor theme; FileReadDescriptor theme;
if (!readEncryptedFile(theme, key, FileOption::Safe, SettingsKey)) { if (!readEncryptedFile(theme, key, FileOption::Safe, SettingsKey)) {
return false; return {};
} }
QByteArray themeContent; auto result = Window::Theme::Saved();
QString pathRelative, pathAbsolute; theme.stream >> result.content;
Window::Theme::Cached cache; theme.stream >> result.pathRelative >> result.pathAbsolute;
theme.stream >> themeContent;
theme.stream >> pathRelative >> pathAbsolute;
if (theme.stream.status() != QDataStream::Ok) { if (theme.stream.status() != QDataStream::Ok) {
return false; return {};
} }
_themeAbsolutePath = pathAbsolute; QFile file(result.pathRelative);
_themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString(); if (result.pathRelative.isEmpty() || !file.exists()) {
file.setFileName(result.pathAbsolute);
QFile file(pathRelative);
if (pathRelative.isEmpty() || !file.exists()) {
file.setFileName(pathAbsolute);
} }
auto changed = false; 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) { 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())); LOG(("Error: theme file too large: %1 "
return false; "(should be less than 5 MB, got %2)"
).arg(file.fileName()
).arg(file.size()));
return {};
} }
auto fileContent = file.readAll(); auto fileContent = file.readAll();
file.close(); file.close();
if (themeContent != fileContent) { if (result.content != fileContent) {
themeContent = fileContent; result.content = fileContent;
changed = true; changed = true;
} }
} }
if (!changed) { if (!changed) {
quint32 backgroundIsTiled = 0; quint32 backgroundIsTiled = 0;
theme.stream >> cache.paletteChecksum >> cache.contentChecksum >> cache.colors >> cache.background >> backgroundIsTiled; theme.stream
cache.tiled = (backgroundIsTiled == 1); >> result.cache.paletteChecksum
>> result.cache.contentChecksum
>> result.cache.colors
>> result.cache.background
>> backgroundIsTiled;
result.cache.tiled = (backgroundIsTiled == 1);
if (theme.stream.status() != QDataStream::Ok) { 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) { QString loadThemeUsingKey(FileKey key) {
if (content.isEmpty()) { auto read = readThemeUsingKey(key);
_themeAbsolutePath = _themePaletteAbsolutePath = QString(); const auto result = read.pathAbsolute;
if (_themeKey) { return (!read.content.isEmpty() && Window::Theme::Load(std::move(read)))
clearKey(_themeKey); ? result
_themeKey = 0; : 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(); writeSettings();
} }
return; return;
} }
_themeAbsolutePath = pathAbsolute; if (!themeKey) {
_themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString(); themeKey = genKey(FileOption::Safe);
if (!_themeKey) {
_themeKey = genKey(FileOption::Safe);
writeSettings(); writeSettings();
} }
auto backgroundTiled = static_cast<quint32>(cache.tiled ? 1 : 0); auto backgroundTiled = static_cast<quint32>(saved.cache.tiled ? 1 : 0);
quint32 size = Serialize::bytearraySize(content); quint32 size = Serialize::bytearraySize(saved.content);
size += Serialize::stringSize(pathRelative) + Serialize::stringSize(pathAbsolute); size += Serialize::stringSize(saved.pathRelative) + Serialize::stringSize(saved.pathAbsolute);
size += sizeof(int32) * 2 + Serialize::bytearraySize(cache.colors) + Serialize::bytearraySize(cache.background) + sizeof(quint32); size += sizeof(int32) * 2 + Serialize::bytearraySize(saved.cache.colors) + Serialize::bytearraySize(saved.cache.background) + sizeof(quint32);
EncryptedDescriptor data(size); EncryptedDescriptor data(size);
data.stream << content; data.stream << saved.content;
data.stream << pathRelative << pathAbsolute; data.stream << saved.pathRelative << saved.pathAbsolute;
data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled; 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); file.writeEncrypted(data, SettingsKey);
} }
void clearTheme() { void clearTheme() {
writeTheme(QString(), QString(), QByteArray(), Window::Theme::Cached()); writeTheme(Window::Theme::Saved());
} }
void readTheme() { void loadTheme() {
if (_themeKey && !readThemeUsingKey(_themeKey)) { 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(); clearTheme();
} }
} }
bool hasTheme() { Window::Theme::Saved readThemeAfterSwitch() {
return (_themeKey != 0); const auto key = Window::Theme::IsNightMode()
? _themeKeyNight
: _themeKeyDay;
return readThemeUsingKey(key);
} }
void readLangPack() { void readLangPack() {
@ -4297,21 +4387,16 @@ void writeLangPack() {
file.writeEncrypted(data, SettingsKey); file.writeEncrypted(data, SettingsKey);
} }
QString themePaletteAbsolutePath() {
return _themePaletteAbsolutePath;
}
QString themeAbsolutePath() {
return _themeAbsolutePath;
}
bool copyThemeColorsToPalette(const QString &path) { bool copyThemeColorsToPalette(const QString &path) {
if (!_themeKey) { auto &themeKey = Window::Theme::IsNightMode()
? _themeKeyNight
: _themeKeyDay;
if (!themeKey) {
return false; return false;
} }
FileReadDescriptor theme; FileReadDescriptor theme;
if (!readEncryptedFile(theme, _themeKey, FileOption::Safe, SettingsKey)) { if (!readEncryptedFile(theme, themeKey, FileOption::Safe, SettingsKey)) {
return false; return false;
} }

View file

@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window { namespace Window {
namespace Theme { namespace Theme {
struct Cached; struct Saved;
} // namespace Theme } // namespace Theme
} // namespace Window } // namespace Window
@ -152,12 +152,10 @@ int32 countSavedGifsHash();
void writeBackground(int32 id, const QImage &img); void writeBackground(int32 id, const QImage &img);
bool readBackground(); 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(); void clearTheme();
bool hasTheme();
QString themeAbsolutePath();
QString themePaletteAbsolutePath();
bool copyThemeColorsToPalette(const QString &file); bool copyThemeColorsToPalette(const QString &file);
Window::Theme::Saved readThemeAfterSwitch();
void writeLangPack(); void writeLangPack();

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/themes/window_theme_preview.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "base/parse_helper.h" #include "base/parse_helper.h"
@ -27,7 +28,8 @@ constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
struct Data { struct Data {
struct Applying { struct Applying {
QString path; QString pathRelative;
QString pathAbsolute;
QByteArray content; QByteArray content;
QByteArray paletteForRevert; QByteArray paletteForRevert;
Cached cached; 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()) { if (cache.paletteChecksum != style::palette::Checksum()) {
return false; return false;
} }
@ -205,8 +207,8 @@ bool loadThemeFromCache(const QByteArray &content, Cached &cache) {
QImage background; QImage background;
if (!cache.background.isEmpty()) { if (!cache.background.isEmpty()) {
QBuffer buffer(&cache.background); QDataStream stream(cache.background);
QImageReader reader(&buffer); QImageReader reader(stream.device());
#ifndef OS_MAC_OLD #ifndef OS_MAC_OLD
reader.setAutoTransform(true); reader.setAutoTransform(true);
#endif // OS_MAC_OLD #endif // OS_MAC_OLD
@ -364,6 +366,24 @@ void adjustColorsUsingBackground(const QImage &img) {
adjustColor(st::historyScroll.barBgOver, hue, saturation); 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 } // namespace
void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) {
@ -380,7 +400,10 @@ void ChatBackground::start() {
} }
void ChatBackground::setImage(int32 id, QImage &&image) { 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()) { if (id == kThemeBackground && _themeImage.isNull()) {
id = kDefaultBackground; id = kDefaultBackground;
} else if (resetPalette) { } else if (resetPalette) {
@ -392,7 +415,7 @@ void ChatBackground::setImage(int32 id, QImage &&image) {
} }
_id = id; _id = id;
if (_id == kThemeBackground) { if (_id == kThemeBackground) {
_tile = _themeTile; (nightMode() ? _tileNightValue : _tileDayValue) = _themeTile;
setPreparedImage(QImage(_themeImage)); setPreparedImage(QImage(_themeImage));
} else if (_id == internal::kTestingThemeBackground } else if (_id == internal::kTestingThemeBackground
|| _id == internal::kTestingDefaultBackground || _id == internal::kTestingDefaultBackground
@ -418,10 +441,10 @@ void ChatBackground::setImage(int32 id, QImage &&image) {
setPreparedImage(prepareBackgroundImage(std::move(image))); setPreparedImage(prepareBackgroundImage(std::move(image)));
} }
Assert(!_pixmap.isNull() && !_pixmapForTiled.isNull()); Assert(!_pixmap.isNull() && !_pixmapForTiled.isNull());
notify(BackgroundUpdate(BackgroundUpdate::Type::New, _tile)); notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile()));
if (resetPalette) { if (resetPalette) {
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _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 = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(cRetinaFactor()); image.setDevicePixelRatio(cRetinaFactor());
auto adjustColors = [this] { auto adjustColors = [&] {
auto someThemeApplied = [] { const auto usingThemeBackground = [&] {
if (AreTestingTheme()) { return (_id == kThemeBackground)
return !instance->applying.path.isEmpty(); || (_id == internal::kTestingThemeBackground);
}
return IsNonDefaultUsed() || IsNightTheme();
}; };
auto usingThemeBackground = [this] { const auto usingDefaultBackground = [&] {
return (_id == kThemeBackground || _id == internal::kTestingThemeBackground); return (_id == kDefaultBackground)
|| (_id == internal::kTestingDefaultBackground);
}; };
auto usingDefaultBackground = [this] { const auto testingPalette = [&] {
return (_id == kDefaultBackground || _id == internal::kTestingDefaultBackground); const auto path = AreTestingTheme()
}; ? instance->applying.pathAbsolute
auto testingPalette = [] { : _themeAbsolutePath;
if (AreTestingTheme()) { return IsPaletteTestingPath(path);
return IsPaletteTestingPath(instance->applying.path);
}
return !Local::themePaletteAbsolutePath().isEmpty();
}; };
if (someThemeApplied()) { if (testingPalette()) {
return !usingThemeBackground() && !testingPalette(); return false;
} else if (IsNonDefaultThemeOrBackground() || nightMode()) {
return !usingThemeBackground();
} }
return !usingDefaultBackground(); return !usingDefaultBackground();
}; }();
if (adjustColors) {
if (adjustColors()) {
adjustColorsUsingBackground(image); adjustColorsUsingBackground(image);
} }
@ -494,15 +514,27 @@ int32 ChatBackground::id() const {
} }
bool ChatBackground::tile() const { bool ChatBackground::tile() const {
return _tile; return nightMode() ? _tileNightValue : _tileDayValue;
} }
bool ChatBackground::tileForSave() const { bool ChatBackground::tileDay() const {
if (_id == internal::kTestingThemeBackground || if (_id == internal::kTestingThemeBackground ||
_id == internal::kTestingDefaultBackground) { _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() { void ChatBackground::ensureStarted() {
@ -515,17 +547,42 @@ void ChatBackground::ensureStarted() {
void ChatBackground::setTile(bool tile) { void ChatBackground::setTile(bool tile) {
ensureStarted(); ensureStarted();
if (_tile != tile) { const auto old = this->tile();
_tile = tile; if (nightMode()) {
if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { setTileNightValue(tile);
} else {
setTileDayValue(tile);
}
if (this->tile() != old) {
if (_id != internal::kTestingThemeBackground
&& _id != internal::kTestingDefaultBackground) {
Local::writeUserSettings(); 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() { void ChatBackground::reset() {
if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { if (_id == internal::kTestingThemeBackground
|| _id == internal::kTestingDefaultBackground) {
if (_themeImage.isNull()) { if (_themeImage.isNull()) {
_idForRevert = kDefaultBackground; _idForRevert = kDefaultBackground;
_imageForRevert = QImage(); _imageForRevert = QImage();
@ -542,10 +599,11 @@ void ChatBackground::reset() {
void ChatBackground::saveForRevert() { void ChatBackground::saveForRevert() {
ensureStarted(); ensureStarted();
if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { if (_id != internal::kTestingThemeBackground
&& _id != internal::kTestingDefaultBackground) {
_idForRevert = _id; _idForRevert = _id;
_imageForRevert = std::move(_pixmap).toImage(); _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); style::main_palette::apply(theme.palette);
auto switchToThemeBackground = (mode == ChangeMode::SwitchToThemeBackground && !theme.background.isNull()) auto switchToThemeBackground = (mode == ChangeMode::SwitchToThemeBackground && !theme.background.isNull())
|| (_id == kThemeBackground) || (_id == kThemeBackground)
|| (_id == kDefaultBackground && !Local::hasTheme()); || (_id == kDefaultBackground
if (AreTestingTheme() && IsPaletteTestingPath(instance->applying.path)) { && !nightMode()
&& _themeAbsolutePath.isEmpty());
if (AreTestingTheme() && IsPaletteTestingPath(instance->applying.pathAbsolute)) {
// Grab current background image if it is not already custom // Grab current background image if it is not already custom
if (_id != kCustomBackground) { if (_id != kCustomBackground) {
saveForRevert(); saveForRevert();
@ -568,47 +628,61 @@ void ChatBackground::setTestingTheme(Instance &&theme, ChangeMode mode) {
// Apply current background image so that service bg colors are recounted. // Apply current background image so that service bg colors are recounted.
setImage(_id, std::move(_pixmap).toImage()); setImage(_id, std::move(_pixmap).toImage());
} }
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
} }
void ChatBackground::setTestingDefaultTheme() { void ChatBackground::setTestingDefaultTheme() {
style::main_palette::reset(); style::main_palette::reset();
if (_id == kThemeBackground) { saveForRevert();
saveForRevert(); setImage(internal::kTestingDefaultBackground);
setImage(internal::kTestingDefaultBackground); setTile(false);
setTile(false); notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
} 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);
} }
void ChatBackground::keepApplied() { void ChatBackground::keepApplied(const QString &path, bool write) {
setThemeAbsolutePath(path);
if (_id == internal::kTestingEditorBackground) { if (_id == internal::kTestingEditorBackground) {
_id = kCustomBackground; _id = kCustomBackground;
_themeImage = QImage(); _themeImage = QImage();
_themeTile = false; _themeTile = false;
writeNewBackgroundSettings(); if (write) {
writeNewBackgroundSettings();
}
} else if (_id == internal::kTestingThemeBackground) { } else if (_id == internal::kTestingThemeBackground) {
_id = kThemeBackground; _id = kThemeBackground;
_themeImage = _pixmap.toImage(); _themeImage = _pixmap.toImage();
_themeTile = _tile; _themeTile = tile();
writeNewBackgroundSettings(); if (write) {
writeNewBackgroundSettings();
}
} else if (_id == internal::kTestingDefaultBackground) { } else if (_id == internal::kTestingDefaultBackground) {
_id = kDefaultBackground; _id = kDefaultBackground;
_themeImage = QImage(); _themeImage = QImage();
_themeTile = false; _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() { void ChatBackground::writeNewBackgroundSettings() {
if (_tile != _tileForRevert) { if (tile() != _tileForRevert) {
Local::writeUserSettings(); 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() { void ChatBackground::revert() {
@ -621,30 +695,100 @@ void ChatBackground::revert() {
// Apply current background image so that service bg colors are recounted. // Apply current background image so that service bg colors are recounted.
setImage(_id, std::move(_pixmap).toImage()); 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>();
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() { ChatBackground *Background() {
instance.createIfNull(); instance.createIfNull();
return &instance->background; return &instance->background;
} }
bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache) { bool Load(Saved &&saved) {
if (content.size() < 4) { if (saved.content.size() < 4) {
LOG(("Theme Error: Could not load theme from '%1' (%2)").arg(pathRelative).arg(pathAbsolute)); LOG(("Theme Error: Could not load theme from '%1' (%2)"
).arg(saved.pathRelative
).arg(saved.pathAbsolute));
return false; return false;
} }
instance.createIfNull(); instance.createIfNull();
if (loadThemeFromCache(content, cache)) { if (loadThemeFromCache(saved.content, saved.cache)) {
Background()->setThemeAbsolutePath(saved.pathAbsolute);
return true; return true;
} }
if (!loadTheme(content, cache)) { if (!loadTheme(saved.content, saved.cache)) {
return false; return false;
} }
Local::writeTheme(pathRelative, pathAbsolute, content, cache); Local::writeTheme(saved);
Background()->setThemeAbsolutePath(saved.pathAbsolute);
return true; return true;
} }
@ -653,38 +797,16 @@ void Unload() {
} }
bool Apply(const QString &filepath) { bool Apply(const QString &filepath) {
auto preview = std::make_unique<Preview>(); if (auto preview = PreviewFromFile(filepath)) {
preview->path = filepath; return Apply(std::move(preview));
if (!LoadFromFile(preview->path, &preview->instance, &preview->content)) {
return false;
} }
return Apply(std::move(preview)); return false;
}
void SwitchNightTheme(bool enabled) {
if (enabled) {
auto preview = std::make_unique<Preview>();
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();
} }
bool Apply(std::unique_ptr<Preview> preview) { bool Apply(std::unique_ptr<Preview> preview) {
instance.createIfNull(); 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.content = std::move(preview->content);
instance->applying.cached = std::move(preview->instance.cached); instance->applying.cached = std::move(preview->instance.cached);
if (instance->applying.paletteForRevert.isEmpty()) { if (instance->applying.paletteForRevert.isEmpty()) {
@ -695,14 +817,7 @@ bool Apply(std::unique_ptr<Preview> preview) {
} }
void ApplyDefault() { void ApplyDefault() {
instance.createIfNull(); ApplyDefaultWithNightMode(IsNightMode());
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();
} }
bool ApplyEditedPalette(const QString &path, const QByteArray &content) { 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()); out.cached.contentChecksum = hashCrc32(content.constData(), content.size());
instance.createIfNull(); 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.content = content;
instance->applying.cached = out.cached; instance->applying.cached = out.cached;
if (instance->applying.paletteForRevert.isEmpty()) { if (instance->applying.paletteForRevert.isEmpty()) {
@ -727,31 +847,52 @@ bool ApplyEditedPalette(const QString &path, const QByteArray &content) {
} }
void KeepApplied() { void KeepApplied() {
if (!instance) { if (!AreTestingTheme()) {
return; return;
} }
auto filepath = instance->applying.path; auto saved = Saved();
auto pathRelative = filepath.isEmpty() ? QString() : QDir().relativeFilePath(filepath); saved.pathRelative = instance->applying.pathRelative;
auto pathAbsolute = filepath.isEmpty() ? QString() : QFileInfo(filepath).absoluteFilePath(); saved.pathAbsolute = instance->applying.pathAbsolute;
Local::writeTheme(pathRelative, pathAbsolute, instance->applying.content, instance->applying.cached); saved.content = std::move(instance->applying.content);
saved.cache = std::move(instance->applying.cached);
Local::writeTheme(saved);
instance->applying = Data::Applying(); instance->applying = Data::Applying();
Background()->keepApplied(); Background()->keepApplied(saved.pathAbsolute, true);
} }
void Revert() { void Revert() {
if (!instance->applying.paletteForRevert.isEmpty()) { if (!AreTestingTheme()) {
style::main_palette::load(instance->applying.paletteForRevert); return;
} }
style::main_palette::load(instance->applying.paletteForRevert);
instance->applying = Data::Applying(); instance->applying = Data::Applying();
Background()->revert(); Background()->revert();
} }
bool IsNightTheme() { QString NightThemePath() {
return (Local::themeAbsolutePath() == str_const_toString(kNightThemeFile)); return str_const_toString(kNightThemeFile);
} }
bool IsNonDefaultUsed() { bool IsNonDefaultThemeOrBackground() {
return Local::hasTheme() && !IsNightTheme(); 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) { bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) {

View file

@ -30,7 +30,13 @@ struct Cached {
int32 paletteChecksum = 0; int32 paletteChecksum = 0;
int32 contentChecksum = 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(); void Unload();
struct Instance { struct Instance {
@ -41,7 +47,8 @@ struct Instance {
}; };
struct Preview { struct Preview {
QString path; QString pathRelative;
QString pathAbsolute;
Instance instance; Instance instance;
QByteArray content; QByteArray content;
QImage preview; QImage preview;
@ -52,9 +59,12 @@ bool Apply(std::unique_ptr<Preview> preview);
void ApplyDefault(); void ApplyDefault();
bool ApplyEditedPalette(const QString &path, const QByteArray &content); bool ApplyEditedPalette(const QString &path, const QByteArray &content);
void KeepApplied(); void KeepApplied();
bool IsNonDefaultUsed(); QString NightThemePath();
bool IsNightTheme(); bool IsNightMode();
void SwitchNightTheme(bool enabled); void SetNightModeValue(bool nightMode);
void ToggleNightMode();
bool IsNonDefaultThemeOrBackground();
bool SuggestThemeReset();
void Revert(); void Revert();
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent); bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
@ -88,6 +98,10 @@ public:
void start(); void start();
void setImage(int32 id, QImage &&image = QImage()); void setImage(int32 id, QImage &&image = QImage());
void setTile(bool tile); void setTile(bool tile);
void setTileDayValue(bool tile);
void setTileNightValue(bool tile);
void setThemeAbsolutePath(const QString &path);
QString themeAbsolutePath() const;
void reset(); void reset();
enum class ChangeMode { enum class ChangeMode {
@ -96,7 +110,6 @@ public:
}; };
void setTestingTheme(Instance &&theme, ChangeMode mode = ChangeMode::SwitchToThemeBackground); void setTestingTheme(Instance &&theme, ChangeMode mode = ChangeMode::SwitchToThemeBackground);
void setTestingDefaultTheme(); void setTestingDefaultTheme();
void keepApplied();
void revert(); void revert();
int32 id() const; int32 id() const;
@ -107,7 +120,8 @@ public:
return _pixmapForTiled; return _pixmapForTiled;
} }
bool tile() const; bool tile() const;
bool tileForSave() const; bool tileDay() const;
bool tileNight() const;
private: private:
void ensureStarted(); void ensureStarted();
@ -115,11 +129,26 @@ private:
void setPreparedImage(QImage &&image); void setPreparedImage(QImage &&image);
void writeNewBackgroundSettings(); 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; int32 _id = internal::kUninitializedBackground;
QPixmap _pixmap; QPixmap _pixmap;
QPixmap _pixmapForTiled; QPixmap _pixmapForTiled;
bool _tile = false; bool _nightMode = false;
bool _tileDayValue = false;
bool _tileNightValue = true;
QString _themeAbsolutePath;
QImage _themeImage; QImage _themeImage;
bool _themeTile = false; bool _themeTile = false;

View file

@ -785,9 +785,9 @@ void Editor::paintEvent(QPaintEvent *e) {
} }
void Editor::Start() { void Editor::Start() {
auto palettePath = Local::themePaletteAbsolutePath(); const auto path = Background()->themeAbsolutePath();
if (palettePath.isEmpty()) { if (path.isEmpty() || !Window::Theme::IsPaletteTestingPath(path)) {
FileDialog::GetWritePath(App::wnd(), lang(lng_theme_editor_save_palette), "Palette (*.tdesktop-palette)", "colors.tdesktop-palette", [](const QString &path) { const auto start = [](const QString &path) {
if (!Local::copyThemeColorsToPalette(path)) { if (!Local::copyThemeColorsToPalette(path)) {
writeDefaultPalette(path); writeDefaultPalette(path);
} }
@ -799,9 +799,15 @@ void Editor::Start() {
if (auto window = App::wnd()) { if (auto window = App::wnd()) {
window->showRightColumn(Box<Editor>(path)); window->showRightColumn(Box<Editor>(path));
} }
}); };
FileDialog::GetWritePath(
App::wnd(),
lang(lng_theme_editor_save_palette),
"Palette (*.tdesktop-palette)",
"colors.tdesktop-palette",
start);
} else if (auto window = App::wnd()) { } else if (auto window = App::wnd()) {
window->showRightColumn(Box<Editor>(palettePath)); window->showRightColumn(Box<Editor>(path));
} }
} }

View file

@ -889,12 +889,25 @@ void Generator::restoreTextPalette() {
} // namespace } // namespace
std::unique_ptr<Preview> PreviewFromFile(const QString &filepath) {
auto result = std::make_unique<Preview>();
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<Preview> GeneratePreview( std::unique_ptr<Preview> GeneratePreview(
const QString &filepath, const QString &filepath,
CurrentData &&data) { CurrentData &&data) {
auto result = std::make_unique<Preview>(); auto result = PreviewFromFile(filepath);
result->path = filepath; if (!result) {
if (!LoadFromFile(filepath, &result->instance, &result->content)) {
return nullptr; return nullptr;
} }
result->preview = Generator( result->preview = Generator(

View file

@ -18,6 +18,7 @@ struct CurrentData {
bool backgroundTiled = false; bool backgroundTiled = false;
}; };
std::unique_ptr<Preview> PreviewFromFile(const QString &filepath);
std::unique_ptr<Preview> GeneratePreview( std::unique_ptr<Preview> GeneratePreview(
const QString &filepath, const QString &filepath,
CurrentData &&data); CurrentData &&data);

View file

@ -44,9 +44,10 @@ MainMenu::MainMenu(
checkSelf(); checkSelf();
_nightThemeSwitch.setCallback([this] { _nightThemeSwitch.setCallback([this] {
if (auto action = *_nightThemeAction) { if (const auto action = *_nightThemeAction) {
if (action->isChecked() != Window::Theme::IsNightTheme()) { const auto nightMode = Window::Theme::IsNightMode();
Window::Theme::SwitchNightTheme(action->isChecked()); if (action->isChecked() != nightMode) {
Window::Theme::ToggleNightMode();
} }
} }
}); });
@ -104,19 +105,17 @@ void MainMenu::refreshMenu() {
App::wnd()->showSettings(); App::wnd()->showSettings();
}, &st::mainMenuSettings, &st::mainMenuSettingsOver); }, &st::mainMenuSettings, &st::mainMenuSettingsOver);
if (!Window::Theme::IsNonDefaultUsed()) { _nightThemeAction = std::make_shared<QPointer<QAction>>(nullptr);
_nightThemeAction = std::make_shared<QPointer<QAction>>(nullptr); auto action = _menu->addAction(lang(lng_menu_night_mode), [this] {
auto action = _menu->addAction(lang(lng_menu_night_mode), [this] { if (auto action = *_nightThemeAction) {
if (auto action = *_nightThemeAction) { action->setChecked(!action->isChecked());
action->setChecked(!action->isChecked()); _nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration);
_nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration); }
} }, &st::mainMenuNightMode, &st::mainMenuNightModeOver);
}, &st::mainMenuNightMode, &st::mainMenuNightModeOver); *_nightThemeAction = action;
*_nightThemeAction = action; action->setCheckable(true);
action->setCheckable(true); action->setChecked(Window::Theme::IsNightMode());
action->setChecked(Window::Theme::IsNightTheme()); _menu->finishAnimating();
_menu->finishAnimating();
}
updatePhone(); updatePhone();
} }