Check changes when saving theme.

This commit is contained in:
John Preston 2019-09-08 19:29:43 +03:00
parent cedb2d31af
commit c92a9585e1
6 changed files with 194 additions and 134 deletions

View File

@ -1180,6 +1180,34 @@ void KeepApplied() {
Background()->keepApplied(saved.object, true); Background()->keepApplied(saved.object, true);
} }
void KeepFromEditor(
const QByteArray &originalContent,
const ParsedTheme &originalParsed,
const Data::CloudTheme &cloud,
const QByteArray &themeContent,
const ParsedTheme &themeParsed) {
ClearApplying();
const auto content = themeContent.isEmpty()
? originalContent
: themeContent;
auto saved = Saved();
auto &cache = saved.cache;
auto &object = saved.object;
cache.colors = style::main_palette::save();
cache.paletteChecksum = style::palette::Checksum();
cache.contentChecksum = hashCrc32(content.constData(), content.size());
cache.background = themeParsed.background;
cache.tiled = themeParsed.tiled;
object.cloud = cloud;
object.content = themeContent.isEmpty()
? originalContent
: themeContent;
object.pathAbsolute = object.pathRelative = CachedThemePath(
cloud.documentId);
Local::writeTheme(saved);
Background()->keepApplied(saved.object, true);
}
void Revert() { void Revert() {
if (!AreTestingTheme()) { if (!AreTestingTheme()) {
return; return;

View File

@ -20,6 +20,8 @@ namespace Theme {
inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024; inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
struct ParsedTheme;
[[nodiscard]] bool IsEmbeddedTheme(const QString &path); [[nodiscard]] bool IsEmbeddedTheme(const QString &path);
struct Object { struct Object {
@ -62,6 +64,12 @@ bool Apply(std::unique_ptr<Preview> preview);
void ApplyDefaultWithPath(const QString &themePath); void ApplyDefaultWithPath(const QString &themePath);
bool ApplyEditedPalette(const QByteArray &content); bool ApplyEditedPalette(const QByteArray &content);
void KeepApplied(); void KeepApplied();
void KeepFromEditor(
const QByteArray &originalContent,
const ParsedTheme &originalParsed,
const Data::CloudTheme &cloud,
const QByteArray &themeContent,
const ParsedTheme &themeParsed);
QString NightThemePath(); QString NightThemePath();
[[nodiscard]] bool IsNightMode(); [[nodiscard]] bool IsNightMode();
void SetNightModeValue(bool nightMode); void SetNightModeValue(bool nightMode);
@ -191,6 +199,12 @@ private:
friend void ToggleNightMode(); friend void ToggleNightMode();
friend void ToggleNightMode(const QString &themePath); friend void ToggleNightMode(const QString &themePath);
friend void KeepApplied(); friend void KeepApplied();
friend void KeepFromEditor(
const QByteArray &originalContent,
const ParsedTheme &originalParsed,
const Data::CloudTheme &cloud,
const QByteArray &themeContent,
const ParsedTheme &themeParsed);
friend bool IsNonDefaultBackground(); friend bool IsNonDefaultBackground();
Main::Session *_session = nullptr; Main::Session *_session = nullptr;

View File

@ -25,6 +25,13 @@ namespace Theme {
struct Colorizer; struct Colorizer;
struct ParsedTheme {
QByteArray palette;
QByteArray background;
bool isPng = false;
bool tiled = false;
};
[[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud); [[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud);
[[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text); [[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text);

View File

@ -56,37 +56,14 @@ enum class SaveErrorType {
Link, Link,
}; };
struct ParsedTheme {
QByteArray palette;
QByteArray background;
bool isPng = false;
bool tiled = false;
};
struct PreparedBackground {
QByteArray content;
bool tile = false;
bool isPng = false;
bool changed = false;
};
template <size_t Size>
QByteArray qba(const char(&string)[Size]) {
return QByteArray::fromRawData(string, Size - 1);
}
QByteArray qba(QLatin1String string) {
return QByteArray::fromRawData(string.data(), string.size());
}
class BackgroundSelector : public Ui::RpWidget { class BackgroundSelector : public Ui::RpWidget {
public: public:
BackgroundSelector( BackgroundSelector(
QWidget *parent, QWidget *parent,
const QImage &background, const QImage &background,
const PreparedBackground &data); const ParsedTheme &parsed);
[[nodiscard]] PreparedBackground result() const; [[nodiscard]] ParsedTheme result() const;
int resizeGetHeight(int newWidth) override; int resizeGetHeight(int newWidth) override;
@ -101,19 +78,26 @@ private:
object_ptr<Ui::Checkbox> _tileBackground; object_ptr<Ui::Checkbox> _tileBackground;
QImage _background; QImage _background;
QByteArray _backgroundContent; ParsedTheme _parsed;
bool _isPng = false;
bool _changed = false;
QString _imageText; QString _imageText;
int _thumbnailSize = 0; int _thumbnailSize = 0;
QPixmap _thumbnail; QPixmap _thumbnail;
}; };
template <size_t Size>
QByteArray qba(const char(&string)[Size]) {
return QByteArray::fromRawData(string, Size - 1);
}
QByteArray qba(QLatin1String string) {
return QByteArray::fromRawData(string.data(), string.size());
}
BackgroundSelector::BackgroundSelector( BackgroundSelector::BackgroundSelector(
QWidget *parent, QWidget *parent,
const QImage &background, const QImage &background,
const PreparedBackground &data) const ParsedTheme &parsed)
: RpWidget(parent) : RpWidget(parent)
, _chooseFromFile( , _chooseFromFile(
this, this,
@ -122,14 +106,14 @@ BackgroundSelector::BackgroundSelector(
, _tileBackground( , _tileBackground(
this, this,
tr::lng_settings_bg_tile(tr::now), tr::lng_settings_bg_tile(tr::now),
data.tile, parsed.tiled,
st::defaultBoxCheckbox) st::defaultBoxCheckbox)
, _background(background) , _background(background)
, _backgroundContent(data.content) { , _parsed(parsed) {
_imageText = tr::lng_theme_editor_saved_to_jpg( _imageText = tr::lng_theme_editor_saved_to_jpg(
tr::now, tr::now,
lt_size, lt_size,
formatSizeText(_backgroundContent.size())); formatSizeText(_parsed.background.size()));
_chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); }); _chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); });
_thumbnailSize = st::boxTextFont->height _thumbnailSize = st::boxTextFont->height
@ -201,16 +185,15 @@ void BackgroundSelector::chooseBackgroundFromFile() {
|| format == "jpg" || format == "jpg"
|| format == "png")) { || format == "png")) {
_background = image; _background = image;
_backgroundContent = content; _parsed.background = content;
_isPng = (format == "png"); _parsed.isPng = (format == "png");
_changed = true; const auto phrase = _parsed.isPng
const auto phrase = _isPng
? tr::lng_theme_editor_read_from_png ? tr::lng_theme_editor_read_from_png
: tr::lng_theme_editor_read_from_jpg; : tr::lng_theme_editor_read_from_jpg;
_imageText = phrase( _imageText = phrase(
tr::now, tr::now,
lt_size, lt_size,
formatSizeText(_backgroundContent.size())); formatSizeText(_parsed.background.size()));
_tileBackground->setChecked(false); _tileBackground->setChecked(false);
updateThumbnail(); updateThumbnail();
} }
@ -223,13 +206,18 @@ void BackgroundSelector::chooseBackgroundFromFile() {
crl::guard(this, callback)); crl::guard(this, callback));
} }
PreparedBackground BackgroundSelector::result() const { ParsedTheme BackgroundSelector::result() const {
return { auto result = _parsed;
_backgroundContent, result.tiled = _tileBackground->checked();
_tileBackground->checked(), return result;
_isPng, }
_changed,
}; bool PaletteChanged(
const QByteArray &editorPalette,
const QByteArray &originalPalette,
const Data::CloudTheme &cloud) {
return originalPalette.isEmpty()
|| (editorPalette != WriteCloudToText(cloud) + originalPalette);
} }
void ImportFromFile( void ImportFromFile(
@ -260,32 +248,40 @@ void ImportFromFile(
} }
[[nodiscard]] ParsedTheme ParseTheme( [[nodiscard]] ParsedTheme ParseTheme(
const QByteArray &themeContent, const Object &theme,
bool onlyPalette) { bool onlyPalette = false) {
auto result = ParsedTheme(); auto raw = ParsedTheme();
result.palette = themeContent; raw.palette = theme.content;
const auto result = [&] {
if (const auto colorizer = ColorizerForTheme(theme.pathAbsolute)) {
raw.palette = Editor::ColorizeInContent(
std::move(raw.palette),
colorizer);
}
return raw;
};
zlib::FileToRead file(themeContent); zlib::FileToRead file(theme.content);
unz_global_info globalInfo = { 0 }; unz_global_info globalInfo = { 0 };
file.getGlobalInfo(&globalInfo); file.getGlobalInfo(&globalInfo);
if (file.error() != UNZ_OK) { if (file.error() != UNZ_OK) {
return result; return result();
} }
result.palette = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); raw.palette = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
if (file.error() == UNZ_END_OF_LIST_OF_FILE) { if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
file.clearError(); file.clearError();
result.palette = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); raw.palette = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
} }
if (file.error() != UNZ_OK) { if (file.error() != UNZ_OK) {
LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file.")); LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file."));
return ParsedTheme(); return ParsedTheme();
} else if (onlyPalette) { } else if (onlyPalette) {
return result; return result();
} }
const auto fromFile = [&](const char *filename) { const auto fromFile = [&](const char *filename) {
result.background = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit); raw.background = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit);
if (file.error() == UNZ_OK) { if (file.error() == UNZ_OK) {
return true; return true;
} else if (file.error() == UNZ_END_OF_LIST_OF_FILE) { } else if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
@ -296,30 +292,29 @@ void ImportFromFile(
return false; return false;
}; };
if (!fromFile("background.jpg") || !result.background.isEmpty()) { if (!fromFile("background.jpg") || !raw.background.isEmpty()) {
return result.background.isEmpty() ? ParsedTheme() : result; return raw.background.isEmpty() ? ParsedTheme() : result();
} }
result.isPng = true; raw.isPng = true;
if (!fromFile("background.png") || !result.background.isEmpty()) { if (!fromFile("background.png") || !raw.background.isEmpty()) {
return result.background.isEmpty() ? ParsedTheme() : result; return raw.background.isEmpty() ? ParsedTheme() : result();
} }
result.tiled = true; raw.tiled = true;
if (!fromFile("tiled.png") || !result.background.isEmpty()) { if (!fromFile("tiled.png") || !raw.background.isEmpty()) {
return result.background.isEmpty() ? ParsedTheme() : result; return raw.background.isEmpty() ? ParsedTheme() : result();
} }
result.isPng = false; raw.isPng = false;
if (!fromFile("background.jpg") || !result.background.isEmpty()) { if (!fromFile("background.jpg") || !raw.background.isEmpty()) {
return result.background.isEmpty() ? ParsedTheme() : result; return raw.background.isEmpty() ? ParsedTheme() : result();
} }
return result; return result();
} }
[[nodiscard]] bool CopyColorsToPalette( [[nodiscard]] bool CopyColorsToPalette(
const QString &destination, const QString &destination,
const QString &themePath, const Object &theme,
const QByteArray &themeContent,
const Data::CloudTheme &cloud) { const Data::CloudTheme &cloud) {
auto parsed = ParseTheme(themeContent, true); auto parsed = ParseTheme(theme, true);
if (parsed.palette.isEmpty()) { if (parsed.palette.isEmpty()) {
return false; return false;
} }
@ -330,11 +325,6 @@ void ImportFromFile(
return false; return false;
} }
if (const auto colorizer = ColorizerForTheme(themePath)) {
parsed.palette = Editor::ColorizeInContent(
std::move(parsed.palette),
colorizer);
}
const auto content = WriteCloudToText(cloud) + parsed.palette; const auto content = WriteCloudToText(cloud) + parsed.palette;
if (f.write(content) != content.size()) { if (f.write(content) != content.size()) {
LOG(("Theme Error: could not write palette to '%1'").arg(destination)); LOG(("Theme Error: could not write palette to '%1'").arg(destination));
@ -402,14 +392,12 @@ bool WriteDefaultPalette(
return result; return result;
} }
[[nodiscard]] QByteArray PrepareTheme( [[nodiscard]] QByteArray PackTheme(const ParsedTheme &parsed) {
const QByteArray &palette,
const PreparedBackground &background) {
zlib::FileToWrite zip; zlib::FileToWrite zip;
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
const auto back = std::string(background.tile ? "tiled" : "background") const auto back = std::string(parsed.tiled ? "tiled" : "background")
+ (background.isPng ? ".png" : ".jpg"); + (parsed.isPng ? ".png" : ".jpg");
zip.openNewFile( zip.openNewFile(
back.c_str(), back.c_str(),
&zfi, &zfi,
@ -421,8 +409,8 @@ bool WriteDefaultPalette(
Z_DEFLATED, Z_DEFLATED,
Z_DEFAULT_COMPRESSION); Z_DEFAULT_COMPRESSION);
zip.writeInFile( zip.writeInFile(
background.content.constData(), parsed.background.constData(),
background.content.size()); parsed.background.size());
zip.closeFile(); zip.closeFile();
const auto scheme = "colors.tdesktop-theme"; const auto scheme = "colors.tdesktop-theme";
zip.openNewFile( zip.openNewFile(
@ -435,7 +423,7 @@ bool WriteDefaultPalette(
nullptr, nullptr,
Z_DEFLATED, Z_DEFLATED,
Z_DEFAULT_COMPRESSION); Z_DEFAULT_COMPRESSION);
zip.writeInFile(palette.constData(), palette.size()); zip.writeInFile(parsed.palette.constData(), parsed.palette.size());
zip.closeFile(); zip.closeFile();
zip.close(); zip.close();
@ -513,8 +501,9 @@ SendMediaReady PrepareThemeMedia(
Fn<void()> SavePreparedTheme( Fn<void()> SavePreparedTheme(
not_null<Window::Controller*> window, not_null<Window::Controller*> window,
const QByteArray &palette, const ParsedTheme &parsed,
const PreparedBackground &background, const QByteArray &originalContent,
const ParsedTheme &originalParsed,
const Data::CloudTheme &fields, const Data::CloudTheme &fields,
Fn<void()> done, Fn<void()> done,
Fn<void(SaveErrorType,QString)> fail) { Fn<void(SaveErrorType,QString)> fail) {
@ -530,13 +519,6 @@ Fn<void()> SavePreparedTheme(
rpl::lifetime lifetime; rpl::lifetime lifetime;
}; };
if (fields.title.isEmpty()) {
fail(SaveErrorType::Name, {});
return nullptr;
} else if (!IsGoodSlug(fields.slug)) {
fail(SaveErrorType::Link, {});
return nullptr;
}
const auto session = &window->account().session(); const auto session = &window->account().session();
const auto api = &session->api(); const auto api = &session->api();
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
@ -546,6 +528,9 @@ Fn<void()> SavePreparedTheme(
const auto creating = !fields.id const auto creating = !fields.id
|| (fields.createdBy != session->userId()); || (fields.createdBy != session->userId());
const auto changed = (parsed.background != originalParsed.background)
|| (parsed.tiled != originalParsed.tiled)
|| PaletteChanged(parsed.palette, originalParsed.palette, fields);
const auto finish = [=](const MTPTheme &result) { const auto finish = [=](const MTPTheme &result) {
Background()->clearEditingTheme(ClearEditing::KeepChanges); Background()->clearEditingTheme(ClearEditing::KeepChanges);
@ -559,18 +544,16 @@ Fn<void()> SavePreparedTheme(
LOG(("API Error: Unexpected themeDocumentNotModified.")); LOG(("API Error: Unexpected themeDocumentNotModified."));
return fields; return fields;
}); });
if (cloud.documentId) { if (cloud.documentId && !state->themeContent.isEmpty()) {
const auto document = session->data().document(cloud.documentId); const auto document = session->data().document(cloud.documentId);
document->setDataAndCache(state->themeContent); document->setDataAndCache(state->themeContent);
} }
auto preview = PreviewFromFile( KeepFromEditor(
originalContent,
originalParsed,
cloud,
state->themeContent, state->themeContent,
QString(), parsed);
cloud);
if (preview) {
Apply(std::move(preview));
KeepApplied();
}
}; };
const auto createTheme = [=](const MTPDocument &data) { const auto createTheme = [=](const MTPDocument &data) {
@ -587,10 +570,13 @@ Fn<void()> SavePreparedTheme(
}; };
const auto updateTheme = [=](const MTPDocument &data) { const auto updateTheme = [=](const MTPDocument &data) {
using Flag = MTPaccount_UpdateTheme::Flag;
const auto document = session->data().processDocument(data); const auto document = session->data().processDocument(data);
const auto flags = MTPaccount_UpdateTheme::Flag::f_title const auto flags = Flag::f_title
| MTPaccount_UpdateTheme::Flag::f_slug | Flag::f_slug
| MTPaccount_UpdateTheme::Flag::f_document; | (data.type() == mtpc_documentEmpty
? Flag(0)
: Flag::f_document);
state->requestId = api->request(MTPaccount_UpdateTheme( state->requestId = api->request(MTPaccount_UpdateTheme(
MTP_flags(flags), MTP_flags(flags),
MTP_string(Data::CloudThemes::Format()), MTP_string(Data::CloudThemes::Format()),
@ -639,9 +625,13 @@ Fn<void()> SavePreparedTheme(
}; };
const auto save = [=] { const auto save = [=] {
if (!creating && !changed) {
updateTheme(MTP_documentEmpty(MTP_long(fields.documentId)));
return;
}
state->generating = true; state->generating = true;
crl::async([=] { crl::async([=] {
crl::on_main([=, ready = PrepareTheme(palette, background)]{ crl::on_main([=, ready = PackTheme(parsed)]{
if (!state->generating) { if (!state->generating) {
return; return;
} }
@ -683,6 +673,16 @@ Fn<void()> SavePreparedTheme(
} // namespace } // namespace
bool PaletteChanged(
const QByteArray &editorPalette,
const Data::CloudTheme &cloud) {
auto object = Local::ReadThemeContent();
const auto real = object.content.isEmpty()
? GenerateDefaultPalette()
: ParseTheme(object, true).palette;
return PaletteChanged(editorPalette, real, cloud);
}
void StartEditor( void StartEditor(
not_null<Window::Controller*> window, not_null<Window::Controller*> window,
const Data::CloudTheme &cloud) { const Data::CloudTheme &cloud) {
@ -690,11 +690,7 @@ void StartEditor(
auto object = Local::ReadThemeContent(); auto object = Local::ReadThemeContent();
const auto written = object.content.isEmpty() const auto written = object.content.isEmpty()
? WriteDefaultPalette(path, cloud) ? WriteDefaultPalette(path, cloud)
: CopyColorsToPalette( : CopyColorsToPalette(path, object, cloud);
path,
object.pathAbsolute,
object.content,
cloud);
if (!written) { if (!written) {
window->show(Box<InformBox>(tr::lng_theme_editor_error(tr::now))); window->show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
return; return;
@ -801,16 +797,30 @@ void SaveThemeBox(
const QByteArray &palette) { const QByteArray &palette) {
Expects(window->account().sessionExists()); Expects(window->account().sessionExists());
//Local::ReadThemeContent() const auto original = Local::ReadThemeContent();
const auto background = Background()->createCurrentImage(); const auto originalContent = original.content;
//if (Data::IsThemeWallPaper(Background()->paper())) {
//} // We don't need default palette here, because in case of it we are
auto backgroundContent = QByteArray(); // not interested if the palette was changed, we'll save it anyway.
const auto tiled = Background()->tile(); const auto originalParsed = originalContent.isEmpty()
{ ? ParsedTheme() // GenerateDefaultPalette()
QBuffer buffer(&backgroundContent); : ParseTheme(original);
const auto background = Background()->createCurrentImage();
const auto backgroundIsTiled = Background()->tile();
const auto changed = !Data::IsThemeWallPaper(Background()->paper())
|| originalParsed.background.isEmpty();
auto parsed = ParsedTheme();
parsed.palette = palette;
parsed.isPng = false;
if (changed) {
QBuffer buffer(&parsed.background);
background.save(&buffer, "JPG", 87); background.save(&buffer, "JPG", 87);
} else {
// Use existing background serialization.
parsed.background = originalParsed.background;
parsed.isPng = originalParsed.isPng;
} }
box->setTitle(tr::lng_theme_editor_save_title(Ui::Text::WithEntities)); box->setTitle(tr::lng_theme_editor_save_title(Ui::Text::WithEntities));
@ -868,7 +878,7 @@ void SaveThemeBox(
object_ptr<BackgroundSelector>( object_ptr<BackgroundSelector>(
box, box,
background, background,
PreparedBackground{ backgroundContent, tiled }), parsed),
style::margins( style::margins(
st::boxRowPadding.left(), st::boxRowPadding.left(),
st::themesSmallSkip, st::themesSmallSkip,
@ -886,8 +896,6 @@ void SaveThemeBox(
if (*saving) { if (*saving) {
return; return;
} }
*saving = true;
box->showLoading(true);
const auto done = crl::guard(box, [=] { const auto done = crl::guard(box, [=] {
box->closeBox(); box->closeBox();
window->showRightColumn(nullptr); window->showRightColumn(nullptr);
@ -905,7 +913,7 @@ void SaveThemeBox(
Ui::Toast::Show( Ui::Toast::Show(
tr::lng_create_channel_link_occupied(tr::now)); tr::lng_create_channel_link_occupied(tr::now));
type = SaveErrorType::Link; type = SaveErrorType::Link;
} else { } else if (!error.isEmpty()) {
Ui::Toast::Show(error); Ui::Toast::Show(error);
} }
if (type == SaveErrorType::Name) { if (type == SaveErrorType::Name) {
@ -917,10 +925,21 @@ void SaveThemeBox(
auto fields = cloud; auto fields = cloud;
fields.title = name->getLastText().trimmed(); fields.title = name->getLastText().trimmed();
fields.slug = link->getLastText().trimmed(); fields.slug = link->getLastText().trimmed();
if (fields.title.isEmpty()) {
fail(SaveErrorType::Name, QString());
return;
} else if (!IsGoodSlug(fields.slug)) {
fail(SaveErrorType::Link, QString());
return;
}
*saving = true;
box->showLoading(true);
*cancel = SavePreparedTheme( *cancel = SavePreparedTheme(
window, window,
palette,
back->result(), back->result(),
originalContent,
originalParsed,
fields, fields,
done, done,
fail); fail);
@ -929,15 +948,5 @@ void SaveThemeBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
} }
bool PaletteChanged(
const QByteArray &editorPalette,
const Data::CloudTheme &cloud) {
auto object = Local::ReadThemeContent();
const auto real = object.content.isEmpty()
? GenerateDefaultPalette()
: ParseTheme(object.content, true).palette;
return (editorPalette != WriteCloudToText(cloud) + real);
}
} // namespace Theme } // namespace Theme
} // namespace Window } // namespace Window

View File

@ -906,12 +906,12 @@ void Generator::restoreTextPalette() {
_p->restoreTextPalette(); _p->restoreTextPalette();
} }
[[nodiscard]] QString CachedThemePath(uint64 documentId) { } // namespace
QString CachedThemePath(uint64 documentId) {
return QString::fromLatin1("special://cached-%1").arg(documentId); return QString::fromLatin1("special://cached-%1").arg(documentId);
} }
} // namespace
std::unique_ptr<Preview> PreviewFromFile( std::unique_ptr<Preview> PreviewFromFile(
const QByteArray &bytes, const QByteArray &bytes,
const QString &filepath, const QString &filepath,

View File

@ -22,6 +22,8 @@ struct CurrentData {
bool backgroundTiled = false; bool backgroundTiled = false;
}; };
[[nodiscard]] QString CachedThemePath(uint64 documentId);
std::unique_ptr<Preview> PreviewFromFile( std::unique_ptr<Preview> PreviewFromFile(
const QByteArray &bytes, const QByteArray &bytes,
const QString &filepath, const QString &filepath,