From 910f16312c8f848c7b47c06c28d45058e5e3f441 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 5 Sep 2019 23:21:44 +0300 Subject: [PATCH] Show not supported themes placeholders. --- Telegram/Resources/icons/theme_preview.png | Bin 0 -> 1556 bytes Telegram/Resources/icons/theme_preview@2x.png | Bin 0 -> 3237 bytes Telegram/Resources/icons/theme_preview@3x.png | Bin 0 -> 5399 bytes Telegram/SourceFiles/storage/localstorage.cpp | 19 +-- Telegram/SourceFiles/storage/localstorage.h | 6 +- .../window/themes/window_theme.cpp | 24 ++-- .../SourceFiles/window/themes/window_theme.h | 11 +- .../window/themes/window_theme_editor.cpp | 89 ++++++++------ .../window/themes/window_theme_editor.h | 13 +- .../window/themes/window_theme_editor_box.cpp | 111 ++++++++++++++++-- .../window/themes/window_theme_editor_box.h | 5 + .../SourceFiles/window/window_controller.cpp | 6 +- 12 files changed, 194 insertions(+), 90 deletions(-) create mode 100644 Telegram/Resources/icons/theme_preview.png create mode 100644 Telegram/Resources/icons/theme_preview@2x.png create mode 100644 Telegram/Resources/icons/theme_preview@3x.png diff --git a/Telegram/Resources/icons/theme_preview.png b/Telegram/Resources/icons/theme_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..605ce5eb5f8a688a966f684ec4b531746f1c740b GIT binary patch literal 1556 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 X4yDZ}I_LSM00000NkvXXu0mjfb=4Y} literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/theme_preview@3x.png b/Telegram/Resources/icons/theme_preview@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6090ba7ca435d2d6e7542084e7e89619ca994748 GIT binary patch literal 5399 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 BLk9o= literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index a75495380..3bb20a0b4 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "window/themes/window_theme.h" #include "window/window_session_controller.h" -#include "window/themes/window_theme_editor.h" #include "base/flags.h" #include "data/data_session.h" #include "history/history.h" @@ -4479,26 +4478,30 @@ std::vector readRecentLanguages() { return result; } -bool copyThemeColorsToPalette(const QString &destination) { +Window::Theme::Object ReadThemeContent() { using namespace Window::Theme; + auto &themeKey = IsNightMode() ? _themeKeyNight : _themeKeyDay; if (!themeKey) { - return false; + return Object(); } FileReadDescriptor theme; if (!readEncryptedFile(theme, themeKey, FileOption::Safe, SettingsKey)) { - return false; + return Object(); } - QByteArray themeContent; + QByteArray content; QString pathRelative, pathAbsolute; - theme.stream >> themeContent >> pathRelative >> pathAbsolute; + theme.stream >> content >> pathRelative >> pathAbsolute; if (theme.stream.status() != QDataStream::Ok) { - return false; + return Object(); } - return CopyColorsToPalette(destination, pathAbsolute, themeContent); + auto result = Object(); + result.pathAbsolute = pathAbsolute; + result.content = content; + return result; } void writeRecentHashtagsAndBots() { diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 462ea51dc..6d7641ca0 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -26,6 +26,7 @@ class EncryptionKey; namespace Window { namespace Theme { +struct Object; struct Saved; } // namespace Theme } // namespace Window @@ -152,8 +153,9 @@ bool readBackground(); void writeTheme(const Window::Theme::Saved &saved); void clearTheme(); -bool copyThemeColorsToPalette(const QString &destination); -Window::Theme::Saved readThemeAfterSwitch(); +[[nodiscard]] Window::Theme::Saved readThemeAfterSwitch(); + +[[nodiscard]] Window::Theme::Object ReadThemeContent(); void writeLangPack(); void pushRecentLanguage(const Lang::Language &language); diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 11a030635..26a3c7f2a 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme_preview.h" #include "window/themes/window_themes_embedded.h" +#include "window/themes/window_theme_editor.h" #include "mainwidget.h" #include "main/main_session.h" #include "apiwrap.h" @@ -49,14 +50,6 @@ Applying GlobalApplying; inline bool AreTestingTheme() { return !GlobalApplying.paletteForRevert.isEmpty(); -}; - -[[nodiscard]] bool IsEditingTheme(const QString &path) { - static const auto kEditingPath = QFileInfo( - EditingPalettePath() - ).absoluteFilePath(); - return !path.compare(kEditingPath, Qt::CaseInsensitive) - && QFileInfo(path).exists(); } bool CalculateIsMonoColorImage(const QImage &image) { @@ -408,7 +401,7 @@ bool InitializeFromSaved(Saved &&saved) { return false; } if (editing) { - Background()->setIsEditingTheme(true); + Background()->setEditingTheme(ReadCloudFromText(*editing)); } else { Local::writeTheme(saved); } @@ -544,7 +537,7 @@ void ChatBackground::checkUploadWallPaper() { } if (!Data::IsCustomWallPaper(_paper) || _original.isNull() - || isEditingTheme()) { + || _editingTheme.has_value()) { return; } @@ -734,7 +727,7 @@ bool ChatBackground::adjustPaletteRequired() { || Data::details::IsTestingDefaultWallPaper(_paper); }; - if (isEditingTheme()) { + if (_editingTheme.has_value()) { return false; } else if (isNonDefaultThemeOrBackground() || nightMode()) { return !usingThemeBackground(); @@ -742,12 +735,13 @@ bool ChatBackground::adjustPaletteRequired() { return !usingDefaultBackground(); } -bool ChatBackground::isEditingTheme() const { +std::optional ChatBackground::editingTheme() const { return _editingTheme; } -void ChatBackground::setIsEditingTheme(bool editing) { - if (_editingTheme == editing) { +void ChatBackground::setEditingTheme( + std::optional editing) { + if (!_editingTheme && !editing) { return; } _editingTheme = editing; @@ -913,7 +907,7 @@ void ChatBackground::setTestingTheme(Instance &&theme) { || (Data::IsDefaultWallPaper(_paper) && !nightMode() && _themeObject.pathAbsolute.isEmpty()); - if (AreTestingTheme() && isEditingTheme()) { + if (AreTestingTheme() && _editingTheme.has_value()) { // Grab current background image if it is not already custom // Use prepared pixmap, not original image, because we're // for sure switching to a non-pattern wall-paper (testing editor). diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 75ee9dee2..fbbe85226 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -120,8 +120,8 @@ public: void setTileNightValue(bool tile); void setThemeObject(const Object &object); [[nodiscard]] const Object &themeObject() const; - [[nodiscard]] bool isEditingTheme() const; - void setIsEditingTheme(bool editing); + [[nodiscard]] std::optional editingTheme() const; + void setEditingTheme(std::optional editing); void reset(); void setTestingTheme(Instance &&theme); @@ -201,7 +201,7 @@ private: Object _themeObject; QImage _themeImage; bool _themeTile = false; - bool _editingTheme = false; + std::optional _editingTheme; Data::WallPaper _paperForRevert = Data::details::UninitializedWallPaper(); @@ -221,11 +221,6 @@ ChatBackground *Background(); void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from); -bool CopyColorsToPalette( - const QString &destination, - const QString &themePath, - const QByteArray &themeContent); - bool ReadPaletteValues(const QByteArray &content, Fn callback); } // namespace Theme diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 675f54058..2e4ecdaae 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -35,6 +35,14 @@ namespace Window { namespace Theme { namespace { +template +QByteArray qba(const char(&string)[Size]) { + return QByteArray::fromRawData(string, Size - 1); +} + +const auto kCloudInTextStart = qba("// THEME EDITOR SERVICE INFO START\n"); +const auto kCloudInTextEnd = qba("// THEME EDITOR SERVICE INFO END\n\n"); + struct ReadColorResult { ReadColorResult(QColor color, bool error = false) : color(color), error(error) { } @@ -186,7 +194,7 @@ QByteArray replaceValueInContent(const QByteArray &content, const QByteArray &na return QByteArray(); } -QByteArray ColorizeInContent( +[[nodiscard]] QByteArray ColorizeInContent( QByteArray content, const Colorizer &colorizer) { auto validNames = OrderedSet(); @@ -294,44 +302,41 @@ private: }; -bool CopyColorsToPalette( - const QString &destination, - const QString &themePath, - const QByteArray &themeContent) { - auto paletteContent = themeContent; +[[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud) { + auto result = QByteArray(); + const auto add = [&](const QByteArray &key, const QString &value) { + result.append("// " + key + ": " + value.toLatin1() + "\n"); + }; + result.append(kCloudInTextStart); + add("ID", QString::number(cloud.id)); + add("ACCESS", QString::number(cloud.accessHash)); + result.append(kCloudInTextEnd); + return result; +} - zlib::FileToRead file(themeContent); - - unz_global_info globalInfo = { 0 }; - file.getGlobalInfo(&globalInfo); - if (file.error() == UNZ_OK) { - paletteContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); - if (file.error() == UNZ_END_OF_LIST_OF_FILE) { - file.clearError(); - paletteContent = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); - } - if (file.error() != UNZ_OK) { - LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file, while copying to '%1'.").arg(destination)); +[[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text) { + const auto index = text.indexOf(kCloudInTextEnd); + if (index <= 1) { + return Data::CloudTheme(); + } + auto result = Data::CloudTheme(); + const auto list = text.mid(0, index - 1).split('\n'); + const auto take = [&](uint64 &value, int index) { + if (list.size() <= index) { return false; } + const auto &entry = list[index]; + const auto position = entry.indexOf(": "); + if (position < 0) { + return false; + } + value = QString::fromLatin1(entry.mid(position + 2)).toULongLong(); + return true; + }; + if (!take(result.id, 1) || !take(result.accessHash, 2)) { + return Data::CloudTheme(); } - - QFile f(destination); - if (!f.open(QIODevice::WriteOnly)) { - LOG(("Theme Error: could not open file for write '%1'").arg(destination)); - return false; - } - - if (const auto colorizer = ColorizerForTheme(themePath)) { - paletteContent = ColorizeInContent( - std::move(paletteContent), - colorizer); - } - if (f.write(paletteContent) != paletteContent.size()) { - LOG(("Theme Error: could not write palette to '%1'").arg(destination)); - return false; - } - return true; + return result; } Editor::Inner::Inner(QWidget *parent, const QString &path) : TWidget(parent) @@ -680,12 +685,22 @@ Editor::Editor( resizeToWidth(st::windowMinWidth); } +QByteArray Editor::ColorizeInContent( + QByteArray content, + const Colorizer &colorizer) { + return Window::Theme::ColorizeInContent(content, colorizer); +} + void Editor::save() { if (!_window->account().sessionExists()) { Ui::Toast::Show(tr::lng_theme_editor_need_auth(tr::now)); return; + } else if (_saving) { + return; } - Ui::show(Box(SaveThemeBox, _window, _cloud, _inner->paletteContent())); + _saving = true; + const auto unlock = crl::guard(this, [=] { _saving = false; }); + SaveTheme(_window, _cloud, _inner->paletteContent(), unlock); } void Editor::resizeEvent(QResizeEvent *e) { @@ -776,7 +791,7 @@ void Editor::paintEvent(QPaintEvent *e) { void Editor::closeEditor() { if (const auto window = App::wnd()) { window->showRightColumn(nullptr); - Background()->setIsEditingTheme(false); + Background()->setEditingTheme(std::nullopt); } } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.h b/Telegram/SourceFiles/window/themes/window_theme_editor.h index 84ec3ef5b..db8e94530 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.h @@ -23,10 +23,10 @@ class Controller; namespace Theme { -bool CopyColorsToPalette( - const QString &destination, - const QString &themePath, - const QByteArray &themeContent); +struct Colorizer; + +[[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud); +[[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text); class Editor : public TWidget { public: @@ -35,6 +35,10 @@ public: not_null window, const Data::CloudTheme &cloud); + [[nodiscard]] static QByteArray ColorizeInContent( + QByteArray content, + const Colorizer &colorizer); + protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; @@ -57,6 +61,7 @@ private: object_ptr _leftShadow; object_ptr _topShadow; object_ptr _save; + bool _saving = false; }; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 4aa85fa3b..bf0566bac 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -86,6 +86,7 @@ private: QByteArray _backgroundContent; bool _isPng = false; QString _imageText; + int _thumbnailSize = 0; QPixmap _thumbnail; }; @@ -112,12 +113,12 @@ BackgroundSelector::BackgroundSelector( formatSizeText(_backgroundContent.size())); _chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); }); - const auto height = st::boxTextFont->height + _thumbnailSize = st::boxTextFont->height + st::themesSmallSkip + _chooseFromFile->heightNoMargins() + st::themesSmallSkip + _tileBackground->heightNoMargins(); - resize(width(), height); + resize(width(), _thumbnailSize + st::themesSmallSkip); updateThumbnail(); } @@ -125,7 +126,7 @@ BackgroundSelector::BackgroundSelector( void BackgroundSelector::paintEvent(QPaintEvent *e) { Painter p(this); - const auto left = height() + st::themesSmallSkip; + const auto left = _thumbnailSize + st::themesSmallSkip; p.setPen(st::boxTextFg); p.setFont(st::boxTextFont); @@ -135,14 +136,14 @@ void BackgroundSelector::paintEvent(QPaintEvent *e) { } int BackgroundSelector::resizeGetHeight(int newWidth) { - const auto left = height() + st::themesSmallSkip; + const auto left = _thumbnailSize + st::themesSmallSkip; _chooseFromFile->moveToLeft(left, st::boxTextFont->height + st::themesSmallSkip); _tileBackground->moveToLeft(left, st::boxTextFont->height + st::themesSmallSkip + _chooseFromFile->height() + st::themesSmallSkip); return height(); } void BackgroundSelector::updateThumbnail() { - const auto size = height(); + const auto size = _thumbnailSize; auto back = QImage( QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); @@ -233,11 +234,55 @@ void ImportFromFile( crl::guard(parent, callback)); } -QString BytesToUTF8(QLatin1String string) { +[[nodiscard]] QString BytesToUTF8(QLatin1String string) { return QString::fromUtf8(string.data(), string.size()); } -bool WriteDefaultPalette(const QString &path) { +[[nodiscard]] bool CopyColorsToPalette( + const QString &destination, + const QString &themePath, + const QByteArray &themeContent, + const Data::CloudTheme &cloud) { + auto paletteContent = themeContent; + + zlib::FileToRead file(themeContent); + + unz_global_info globalInfo = { 0 }; + file.getGlobalInfo(&globalInfo); + if (file.error() == UNZ_OK) { + paletteContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); + if (file.error() == UNZ_END_OF_LIST_OF_FILE) { + file.clearError(); + paletteContent = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); + } + if (file.error() != UNZ_OK) { + LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file, while copying to '%1'.").arg(destination)); + return false; + } + } + + QFile f(destination); + if (!f.open(QIODevice::WriteOnly)) { + LOG(("Theme Error: could not open file for write '%1'").arg(destination)); + return false; + } + + if (const auto colorizer = ColorizerForTheme(themePath)) { + paletteContent = Editor::ColorizeInContent( + std::move(paletteContent), + colorizer); + } + paletteContent = WriteCloudToText(cloud) + paletteContent; + if (f.write(paletteContent) != paletteContent.size()) { + LOG(("Theme Error: could not write palette to '%1'").arg(destination)); + return false; + } + return true; +} + +bool WriteDefaultPalette( + const QString &path, + const Data::CloudTheme &cloud) { QFile f(path); if (!f.open(QIODevice::WriteOnly)) { LOG(("Theme Error: could not open '%1' for writing.").arg(path)); @@ -247,6 +292,8 @@ bool WriteDefaultPalette(const QString &path) { QTextStream stream(&f); stream.setCodec("UTF-8"); + stream << QString::fromLatin1(WriteCloudToText(cloud)); + auto rows = style::main_palette::data(); for (const auto &row : std::as_const(rows)) { stream @@ -396,7 +443,7 @@ SendMediaReady PrepareThemeMedia( 0); } -Fn SaveTheme( +Fn SavePreparedTheme( not_null window, const QByteArray &palette, const PreparedBackground &background, @@ -494,12 +541,15 @@ void StartEditor( not_null window, const Data::CloudTheme &cloud) { const auto path = EditingPalettePath(); - if (!Local::copyThemeColorsToPalette(path) - && !WriteDefaultPalette(path)) { + auto object = Local::ReadThemeContent(); + const auto written = object.content.isEmpty() + ? WriteDefaultPalette(path, cloud) + : CopyColorsToPalette(path, object.pathAbsolute, object.content, cloud); + if (!written) { window->show(Box(tr::lng_theme_editor_error(tr::now))); return; } - Background()->setIsEditingTheme(true); + Background()->setEditingTheme(cloud); window->showRightColumn(Box(window, cloud)); } @@ -561,6 +611,40 @@ void CreateForExistingBox( box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } +void SaveTheme( + not_null window, + const Data::CloudTheme &cloud, + const QByteArray &palette, + Fn unlock) { + Expects(window->account().sessionExists()); + + using Data::CloudTheme; + + const auto save = [=](const CloudTheme &fields) { + window->show(Box(SaveThemeBox, window, fields, palette)); + }; + if (cloud.id) { + window->account().session().api().request(MTPaccount_GetTheme( + MTP_string(Data::CloudThemes::Format()), + MTP_inputTheme(MTP_long(cloud.id), MTP_long(cloud.accessHash)), + MTP_long(0) + )).done([=](const MTPTheme &result) { + unlock(); + result.match([&](const MTPDtheme &data) { + save(CloudTheme::Parse(&window->account().session(), data)); + }, [&](const MTPDthemeDocumentNotModified &data) { + LOG(("API Error: Unexpected themeDocumentNotModified.")); + save(CloudTheme()); + }); + }).fail([=](const RPCError &error) { + unlock(); + save(CloudTheme()); + }).send(); + } else { + save(CloudTheme()); + } +} + void SaveThemeBox( not_null box, not_null window, @@ -594,7 +678,7 @@ void SaveThemeBox( linkWrap, st::createThemeLink, rpl::single(qsl("link")), - cloud.slug, + cloud.slug.isEmpty() ? GenerateSlug() : cloud.slug, true); linkWrap->widthValue( ) | rpl::start_with_next([=](int width) { @@ -657,6 +741,7 @@ void SaveThemeBox( const auto fail = crl::guard(box, [=]( SaveErrorType type, const QString &text) { + *saving = false; if (!text.isEmpty()) { Ui::Toast::Show(text); } @@ -666,7 +751,7 @@ void SaveThemeBox( link->showError(); } }); - *cancel = SaveTheme( + *cancel = SavePreparedTheme( window, palette, back->result(), diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.h b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h index 158da4575..128082201 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h @@ -29,6 +29,11 @@ void CreateForExistingBox( not_null box, not_null window, const Data::CloudTheme &cloud); +void SaveTheme( + not_null window, + const Data::CloudTheme &cloud, + const QByteArray &palette, + Fn unlock); void SaveThemeBox( not_null box, not_null window, diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 0b6372612..93d89dccd 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -46,9 +46,9 @@ void Controller::firstShow() { void Controller::checkThemeEditor() { using namespace Window::Theme; - if (Background()->isEditingTheme()) { - showRightColumn( - Box(this, Background()->themeObject().cloud)); + + if (const auto editing = Background()->editingTheme()) { + showRightColumn(Box(this, *editing)); } }