Don't change real theme when editing.

This commit is contained in:
John Preston 2019-09-05 09:51:46 +03:00
parent 03bdd80b2f
commit 7485f0c960
9 changed files with 175 additions and 113 deletions

View File

@ -1602,6 +1602,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_editor_save_palette" = "Save palette file";
"lng_theme_editor_choose_name" = "Save theme file";
"lng_theme_editor_error" = "The editor encountered an error :( See 'log.txt' for details.";
"lng_theme_editor_sure_close" = "Are you sure you want to close the editor? Your changes won't be saved.";
"lng_theme_editor_done" = "Theme exported successfully!";
"lng_theme_editor_title" = "Edit color palette";
"lng_theme_editor_export_button" = "Export theme";

View File

@ -143,7 +143,7 @@ Application::~Application() {
stopWebLoadManager();
App::deinitMedia();
Window::Theme::Unload();
Window::Theme::Uninitialize();
Media::Player::finish(_audio.get());
style::stopManager();

View File

@ -999,6 +999,9 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
const auto chosen = [] {
const auto &object = Window::Theme::Background()->themeObject();
if (object.cloud.id) {
return Type(-1);
}
for (const auto &scheme : kSchemesList) {
if (object.pathAbsolute == scheme.path) {
return scheme.type;

View File

@ -674,7 +674,7 @@ 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()
// But it should be moved to keyDay or keyNight inside InitialLoadTheme()
// and never used after.
FileKey _themeKeyLegacy = 0;
@ -2554,7 +2554,7 @@ void finish() {
}
}
void loadTheme();
void InitialLoadTheme();
void readLangPack();
void start() {
@ -2612,7 +2612,7 @@ void start() {
_oldSettingsVersion = settingsData.version;
_settingsSalt = salt;
loadTheme();
InitialLoadTheme();
readLangPack();
applyReadContext(std::move(context));
@ -4259,12 +4259,12 @@ Window::Theme::Saved readThemeUsingKey(FileKey key) {
return result;
}
QString loadThemeUsingKey(FileKey key) {
std::optional<QString> InitialLoadThemeUsingKey(FileKey key) {
auto read = readThemeUsingKey(key);
const auto result = read.object.pathAbsolute;
if (read.object.content.isEmpty()
|| !Window::Theme::Load(std::move(read))) {
return QString();
|| !Window::Theme::Initialize(std::move(read))) {
return std::nullopt;
}
return result;
}
@ -4333,7 +4333,7 @@ void clearTheme() {
writeTheme(Window::Theme::Saved());
}
void loadTheme() {
void InitialLoadTheme() {
const auto key = (_themeKeyLegacy != 0)
? _themeKeyLegacy
: (Window::Theme::IsNightMode()
@ -4341,9 +4341,9 @@ void loadTheme() {
: _themeKeyDay);
if (!key) {
return;
} else if (const auto path = loadThemeUsingKey(key); !path.isEmpty()) {
} else if (const auto path = InitialLoadThemeUsingKey(key)) {
if (_themeKeyLegacy) {
Window::Theme::SetNightModeValue(path
Window::Theme::SetNightModeValue(*path
== Window::Theme::NightThemePath());
(Window::Theme::IsNightMode()
? _themeKeyNight

View File

@ -233,37 +233,6 @@ void applyBackground(QImage &&background, bool tiled, Instance *out) {
}
}
bool loadThemeFromCache(const QByteArray &content, const Cached &cache) {
if (cache.paletteChecksum != style::palette::Checksum()) {
return false;
}
if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) {
return false;
}
QImage background;
if (!cache.background.isEmpty()) {
QDataStream stream(cache.background);
QImageReader reader(stream.device());
#ifndef OS_MAC_OLD
reader.setAutoTransform(true);
#endif // OS_MAC_OLD
if (!reader.read(&background) || background.isNull()) {
return false;
}
}
if (!style::main_palette::load(cache.colors)) {
return false;
}
Background()->saveAdjustableColors();
if (!background.isNull()) {
applyBackground(std::move(background), cache.tiled, nullptr);
}
return true;
}
enum class LoadResult {
Loaded,
Failed,
@ -300,17 +269,24 @@ bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *out
bool loadTheme(
const QByteArray &content,
const std::optional<QByteArray> &editedPalette,
Cached &cache,
const Colorizer &colorizer,
Instance *out = nullptr) {
cache = Cached();
zlib::FileToRead file(content);
const auto emptyColorizer = Colorizer();
const auto &applyColorizer = editedPalette ? emptyColorizer : colorizer;
unz_global_info globalInfo = { 0 };
file.getGlobalInfo(&globalInfo);
if (file.error() == UNZ_OK) {
auto schemeContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
auto schemeContent = editedPalette.value_or(QByteArray());
if (schemeContent.isEmpty()) {
schemeContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
}
if (schemeContent.isEmpty()) {
file.clearError();
schemeContent = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
}
@ -318,7 +294,7 @@ bool loadTheme(
LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file."));
return false;
}
if (!loadColorScheme(schemeContent, colorizer, out)) {
if (!loadColorScheme(schemeContent, applyColorizer, out)) {
return false;
}
Background()->saveAdjustableColors();
@ -357,7 +333,7 @@ bool loadTheme(
}
} else {
// Looks like it is not a .zip theme.
if (!loadColorScheme(content, colorizer, out)) {
if (!loadColorScheme(editedPalette.value_or(content), applyColorizer, out)) {
return false;
}
Background()->saveAdjustableColors();
@ -373,6 +349,74 @@ bool loadTheme(
return true;
}
bool InitializeFromCache(
const QByteArray &content,
const Cached &cache) {
if (cache.paletteChecksum != style::palette::Checksum()) {
return false;
}
if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) {
return false;
}
QImage background;
if (!cache.background.isEmpty()) {
QDataStream stream(cache.background);
QImageReader reader(stream.device());
#ifndef OS_MAC_OLD
reader.setAutoTransform(true);
#endif // OS_MAC_OLD
if (!reader.read(&background) || background.isNull()) {
return false;
}
}
if (!style::main_palette::load(cache.colors)) {
return false;
}
Background()->saveAdjustableColors();
if (!background.isNull()) {
applyBackground(std::move(background), cache.tiled, nullptr);
}
return true;
}
[[nodiscard]] std::optional<QByteArray> ReadEditingPalette() {
auto file = QFile(EditingPalettePath());
return file.open(QIODevice::ReadOnly)
? std::make_optional(file.readAll())
: std::nullopt;
}
bool InitializeFromSaved(Saved &&saved) {
if (saved.object.content.size() < 4) {
LOG(("Theme Error: Could not load theme from '%1' (%2)"
).arg(saved.object.pathRelative
).arg(saved.object.pathAbsolute));
return false;
}
const auto editing = ReadEditingPalette();
GlobalBackground.createIfNull();
if (!editing && InitializeFromCache(saved.object.content, saved.cache)) {
return true;
}
const auto colorizer = editing
? Colorizer()
: ColorizerForTheme(saved.object.pathAbsolute);
if (!loadTheme(saved.object.content, editing, saved.cache, colorizer)) {
return false;
}
if (editing) {
Background()->setIsEditingTheme(true);
} else {
Local::writeTheme(saved);
}
return true;
}
QImage validateBackgroundImage(QImage image) {
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
image = std::move(image).convertToFormat(
@ -701,10 +745,18 @@ bool ChatBackground::adjustPaletteRequired() {
}
bool ChatBackground::isEditingTheme() const {
const auto &object = AreTestingTheme()
? GlobalApplying.data.object
: _themeObject;
return IsEditingTheme(object.pathAbsolute);
return _editingTheme;
}
void ChatBackground::setIsEditingTheme(bool editing) {
if (_editingTheme == editing) {
return;
}
_editingTheme = editing;
if (!_editingTheme) {
reapplyWithNightMode(std::nullopt, _nightMode);
KeepApplied();
}
}
void ChatBackground::adjustPaletteUsingBackground(const QImage &image) {
@ -971,12 +1023,14 @@ bool ChatBackground::nightMode() const {
return _nightMode;
}
void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
const auto settingDefault = themePath.has_value();
void ChatBackground::reapplyWithNightMode(
std::optional<QString> themePath,
bool newNightMode) {
const auto settingExactTheme = themePath.has_value();
const auto nightModeChanged = (newNightMode != _nightMode);
const auto oldNightMode = _nightMode;
const auto newNightMode = !_nightMode;
_nightMode = newNightMode;
auto read = settingDefault ? Saved() : Local::readThemeAfterSwitch();
auto read = settingExactTheme ? Saved() : Local::readThemeAfterSwitch();
auto path = read.object.pathAbsolute;
_nightMode = oldNightMode;
@ -990,6 +1044,7 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
preview->instance.cached = std::move(read.cache);
const auto loaded = loadTheme(
preview->object.content,
std::nullopt,
preview->instance.cached,
ColorizerForTheme(path),
&preview->instance);
@ -1009,10 +1064,12 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
// Theme editor could have already reverted the testing of this toggle.
if (AreTestingTheme()) {
GlobalApplying.overrideKeep = [=] {
_nightMode = newNightMode;
if (nightModeChanged) {
_nightMode = newNightMode;
// Restore the value, it was set inside theme testing.
(oldNightMode ? _tileNightValue : _tileDayValue) = oldTileValue;
// Restore the value, it was set inside theme testing.
(oldNightMode ? _tileNightValue : _tileDayValue) = oldTileValue;
}
const auto saved = std::move(GlobalApplying.data);
if (!alreadyOnDisk) {
@ -1020,47 +1077,38 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
Local::writeTheme(saved);
}
ClearApplying();
keepApplied(saved.object, settingDefault);
keepApplied(saved.object, settingExactTheme);
if (tile() != _tileForRevert) {
Local::writeUserSettings();
}
Local::writeSettings();
if (!settingDefault && !Local::readBackground()) {
if (nightModeChanged) {
Local::writeSettings();
}
if (!settingExactTheme && !Local::readBackground()) {
set(Data::ThemeWallPaper());
}
};
}
}
void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
reapplyWithNightMode(themePath, !_nightMode);
}
ChatBackground *Background() {
GlobalBackground.createIfNull();
return GlobalBackground.data();
}
bool Load(Saved &&saved) {
if (saved.object.content.size() < 4) {
LOG(("Theme Error: Could not load theme from '%1' (%2)"
).arg(saved.object.pathRelative
).arg(saved.object.pathAbsolute));
return false;
}
GlobalBackground.createIfNull();
if (loadThemeFromCache(saved.object.content, saved.cache)) {
bool Initialize(Saved &&saved) {
if (InitializeFromSaved(std::move(saved))) {
Background()->setThemeObject(saved.object);
return true;
}
const auto colorizer = ColorizerForTheme(saved.object.pathAbsolute);
if (!loadTheme(saved.object.content, saved.cache, colorizer)) {
return false;
}
Local::writeTheme(saved);
Background()->setThemeObject(saved.object);
return true;
return false;
}
void Unload() {
void Uninitialize() {
GlobalBackground.clear();
GlobalApplying = Applying();
}
@ -1098,30 +1146,13 @@ void ApplyDefaultWithPath(const QString &themePath) {
}
}
bool ApplyEditedPalette(const QString &path, const QByteArray &content) {
Instance out;
bool ApplyEditedPalette(const QByteArray &content) {
auto out = Instance();
if (!loadColorScheme(content, Colorizer(), &out)) {
return false;
}
out.cached.colors = out.palette.save();
out.cached.paletteChecksum = style::palette::Checksum();
out.cached.contentChecksum = hashCrc32(
content.constData(),
content.size());
GlobalApplying.data.object.pathRelative = path.isEmpty()
? QString()
: QDir().relativeFilePath(path);
GlobalApplying.data.object.pathAbsolute = path.isEmpty()
? QString()
: QFileInfo(path).absoluteFilePath();
GlobalApplying.data.object.content = content;
GlobalApplying.data.cache = out.cached;
if (GlobalApplying.paletteForRevert.isEmpty()) {
GlobalApplying.paletteForRevert = style::main_palette::save();
}
Background()->setTestingTheme(std::move(out));
KeepApplied();
style::main_palette::apply(out.palette);
Background()->notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingEdit, Background()->tile()), true);
return true;
}
@ -1188,7 +1219,7 @@ bool LoadFromContent(
return false;
}
return loadTheme(content, out->cached, colorizer, out);
return loadTheme(content, std::nullopt, out->cached, colorizer, out);
}
bool LoadFromFile(
@ -1207,6 +1238,10 @@ QString EditingPalettePath() {
return cWorkingDir() + "tdata/editing-theme.tdesktop-palette";
}
void ClearEditingPalette() {
QFile(EditingPalettePath()).remove();
}
QColor CountAverageColor(const QImage &image) {
Expects(image.format() == QImage::Format_ARGB32_Premultiplied);

View File

@ -36,8 +36,8 @@ struct Saved {
Object object;
Cached cache;
};
bool Load(Saved &&saved);
void Unload();
bool Initialize(Saved &&saved);
void Uninitialize();
struct Instance {
style::palette palette;
@ -57,7 +57,7 @@ bool Apply(
const Data::CloudTheme &cloud = Data::CloudTheme());
bool Apply(std::unique_ptr<Preview> preview);
void ApplyDefaultWithPath(const QString &themePath);
bool ApplyEditedPalette(const QString &path, const QByteArray &content);
bool ApplyEditedPalette(const QByteArray &content);
void KeepApplied();
QString NightThemePath();
[[nodiscard]] bool IsNightMode();
@ -68,6 +68,7 @@ void ToggleNightMode(const QString &themePath);
void Revert();
[[nodiscard]] QString EditingPalettePath();
void ClearEditingPalette();
bool LoadFromFile(
const QString &file,
@ -86,12 +87,15 @@ struct BackgroundUpdate {
TestingTheme,
RevertingTheme,
ApplyingTheme,
ApplyingEdit,
};
BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
}
[[nodiscard]] bool paletteChanged() const {
return (type == Type::TestingTheme || type == Type::RevertingTheme);
return (type == Type::TestingTheme)
|| (type == Type::RevertingTheme)
|| (type == Type::ApplyingEdit);
}
Type type;
bool tiled;
@ -115,6 +119,7 @@ public:
void setThemeObject(const Object &object);
[[nodiscard]] const Object &themeObject() const;
[[nodiscard]] bool isEditingTheme() const;
void setIsEditingTheme(bool editing);
void reset();
void setTestingTheme(Instance &&theme);
@ -164,6 +169,9 @@ private:
void setNightModeValue(bool nightMode);
[[nodiscard]] bool nightMode() const;
void toggleNightMode(std::optional<QString> themePath);
void reapplyWithNightMode(
std::optional<QString> themePath,
bool newNightMode);
void keepApplied(const Object &object, bool write);
[[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground();
@ -191,6 +199,7 @@ private:
Object _themeObject;
QImage _themeImage;
bool _themeTile = false;
bool _editingTheme = false;
Data::WallPaper _paperForRevert
= Data::details::UninitializedWallPaper();

View File

@ -587,7 +587,7 @@ void Editor::Inner::applyEditing(const QString &name, const QString &copyOf, QCo
f.close();
_applyingUpdate = true;
if (!ApplyEditedPalette(_path, newContent)) {
if (!ApplyEditedPalette(newContent)) {
LOG(("Theme Error: could not apply newly composed content :("));
error();
return;
@ -652,7 +652,20 @@ Editor::Editor(QWidget*, not_null<Window::Controller*> window)
_inner->setScrollCallback([this](int top, int bottom) {
_scroll->scrollToY(top, bottom);
});
_close->setClickedCallback([this] { closeEditor(); });
_close->setClickedCallback([=] {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto close = crl::guard(this, [=] {
ClearEditingPalette();
closeEditor();
if (*box) {
(*box)->closeBox();
}
});
*box = _window->show(Box<ConfirmBox>(
tr::lng_theme_editor_sure_close(tr::now),
tr::lng_close(tr::now),
close));
});
_close->show(anim::type::instant);
_select->resizeToWidth(st::windowMinWidth);
@ -759,6 +772,7 @@ void Editor::paintEvent(QPaintEvent *e) {
void Editor::closeEditor() {
if (const auto window = App::wnd()) {
window->showRightColumn(nullptr);
Background()->setIsEditingTheme(false);
}
}

View File

@ -233,11 +233,11 @@ QString BytesToUTF8(QLatin1String string) {
return QString::fromUtf8(string.data(), string.size());
}
void WriteDefaultPalette(const QString &path) {
bool WriteDefaultPalette(const QString &path) {
QFile f(path);
if (!f.open(QIODevice::WriteOnly)) {
LOG(("Theme Error: could not open '%1' for writing.").arg(path));
return;
return false;
}
QTextStream stream(&f);
@ -260,6 +260,7 @@ void WriteDefaultPalette(const QString &path) {
' ')
<< "\n";
}
return true;
}
[[nodiscard]] QString GenerateSlug() {
@ -489,14 +490,12 @@ void StartEditor(
not_null<Window::Controller*> window,
const Data::CloudTheme &cloud) {
const auto path = EditingPalettePath();
if (!Local::copyThemeColorsToPalette(path)) {
WriteDefaultPalette(path);
}
if (!Apply(path, cloud)) {
if (!Local::copyThemeColorsToPalette(path)
&& !WriteDefaultPalette(path)) {
window->show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
return;
}
KeepApplied();
Background()->setIsEditingTheme(true);
window->showRightColumn(Box<Editor>(window));
}

View File

@ -926,6 +926,7 @@ std::unique_ptr<Preview> PreviewFromFile(
return nullptr;
}
} else {
object.content = bytes;
if (!LoadFromContent(bytes, &result->instance)) {
return nullptr;
}