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) {
static QMutex mutex;
QMutexLocker lock(&mutex);
if (valuePtr) {
ProcessAnnotationRefs[key] = valuePtr;
} else {

View file

@ -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() {

View file

@ -44,7 +44,7 @@ private:
LabeledLink *_connectionType = nullptr;
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
Ui::SlideWrap<Ui::LinkButton> *_useDefaultTheme = nullptr;
Ui::SlideWrap<Ui::LinkButton> *_toggleNightTheme = nullptr;
Ui::LinkButton *_toggleNightTheme = nullptr;
Ui::LinkButton *_askQuestion = nullptr;
Ui::LinkButton *_telegramFAQ = 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(_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);
}
});

View file

@ -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<quint32>(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<quint32>(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;
}

View file

@ -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();

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_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>();
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>();
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>();
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> 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> 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) {

View file

@ -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> 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;

View file

@ -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<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()) {
window->showRightColumn(Box<Editor>(palettePath));
window->showRightColumn(Box<Editor>(path));
}
}

View file

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

View file

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

View file

@ -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<QPointer<QAction>>(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<QPointer<QAction>>(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();
}