From 4929de2bfbe1ec970a9b87c81bc86e99c9504a0f Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Sep 2019 21:04:38 +0300 Subject: [PATCH] Apply cloud themes. --- .../SourceFiles/data/data_cloud_themes.cpp | 29 ++-- Telegram/SourceFiles/data/data_cloud_themes.h | 8 +- Telegram/SourceFiles/data/data_document.cpp | 2 +- .../media/view/media_view_overlay_widget.cpp | 13 +- .../SourceFiles/settings/settings_chat.cpp | 7 +- Telegram/SourceFiles/storage/localstorage.cpp | 141 ++++++++++++------ Telegram/SourceFiles/ui/widgets/checkbox.cpp | 15 +- .../window/themes/window_theme.cpp | 104 ++++++------- .../SourceFiles/window/themes/window_theme.h | 26 ++-- .../window/themes/window_theme_preview.cpp | 38 +++-- .../window/themes/window_theme_preview.h | 11 +- .../themes/window_themes_cloud_list.cpp | 38 ++++- 12 files changed, 280 insertions(+), 152 deletions(-) diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index 56536df5f4..6d2bf3fc90 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -8,11 +8,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "data/data_session.h" +#include "data/data_document.h" #include "main/main_session.h" #include "apiwrap.h" namespace Data { +CloudTheme CloudTheme::Parse( + not_null session, + const MTPDtheme &data) { + const auto document = data.vdocument(); + return { + data.vid().v, + data.vaccess_hash().v, + qs(data.vslug()), + qs(data.vtitle()), + (document + ? session->data().processDocument(*document)->id + : DocumentId(0)), + data.is_creator() ? session->userId() : UserId(0) + }; +} + QString CloudThemes::Format() { static const auto kResult = QString::fromLatin1("tdesktop"); return kResult; @@ -46,17 +63,7 @@ void CloudThemes::parseThemes(const QVector &list) { _list.reserve(list.size()); for (const auto &theme : list) { theme.match([&](const MTPDtheme &data) { - const auto document = data.vdocument(); - _list.push_back({ - data.vid().v, - data.vaccess_hash().v, - qs(data.vslug()), - qs(data.vtitle()), - (document - ? _session->data().processDocument(*document).get() - : nullptr), - data.is_creator() - }); + _list.push_back(CloudTheme::Parse(_session, data)); }, [&](const MTPDthemeDocumentNotModified &data) { LOG(("API Error: Unexpected themeDocumentNotModified.")); }); diff --git a/Telegram/SourceFiles/data/data_cloud_themes.h b/Telegram/SourceFiles/data/data_cloud_themes.h index 0879504750..e3860d6a02 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.h +++ b/Telegram/SourceFiles/data/data_cloud_themes.h @@ -18,8 +18,12 @@ struct CloudTheme { uint64 accessHash = 0; QString slug; QString title; - DocumentData *document = nullptr; - bool creator = false; + DocumentId documentId = 0; + UserId createdBy = 0; + + static CloudTheme Parse( + not_null session, + const MTPDtheme &data); }; class CloudThemes final { diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index b9279108e4..f42ca8e258 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -318,7 +318,7 @@ void DocumentOpenClickHandler::Open( LaunchWithWarning(location.name(), context); }; const auto &location = data->location(true); - if (data->isTheme() && !location.isEmpty() && location.accessEnable()) { + if (data->isTheme() && data->loaded(DocumentData::FilePathResolve::Checked)) { Core::App().showDocument(data, context); location.accessDisable(); } else if (data->canBePlayed()) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 254d70b8ee..d94a1def14 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2192,8 +2192,10 @@ void OverlayWidget::playbackWaitingChange(bool waiting) { void OverlayWidget::initThemePreview() { Assert(_doc && _doc->isTheme()); + const auto bytes = _doc->data(); auto &location = _doc->location(); - if (location.isEmpty() || !location.accessEnable()) { + if (bytes.isEmpty() + && (location.isEmpty() || !location.accessEnable())) { return; } _themePreviewShown = true; @@ -2203,12 +2205,21 @@ void OverlayWidget::initThemePreview() { current.backgroundImage = Window::Theme::Background()->createCurrentImage(); current.backgroundTiled = Window::Theme::Background()->tile(); + const auto &cloudList = _doc->session().data().cloudThemes().list(); + const auto i = ranges::find( + cloudList, + _doc->id, + &Data::CloudTheme::documentId); + const auto cloud = (i != end(cloudList)) ? *i : Data::CloudTheme(); + const auto path = _doc->location().name(); const auto id = _themePreviewId = rand_value(); const auto weak = make_weak(this); crl::async([=, data = std::move(current)]() mutable { auto preview = Window::Theme::GeneratePreview( path, + bytes, + cloud, std::move(data)); crl::on_main(weak, [=, result = std::move(preview)]() mutable { if (id != _themePreviewId) { diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index c7fa89ac79..31a6c33a3b 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -998,9 +998,9 @@ void SetupDefaultThemes(not_null container) { container.get()); const auto chosen = [] { - const auto path = Window::Theme::Background()->themeAbsolutePath(); + const auto &object = Window::Theme::Background()->themeObject(); for (const auto &scheme : kSchemesList) { - if (path == scheme.path) { + if (object.pathAbsolute == scheme.path) { return scheme.type; } } @@ -1013,7 +1013,8 @@ void SetupDefaultThemes(not_null container) { const auto type = scheme.type; return (type != Type::DayBlue) && (type != Type::Default); }; - const auto currentlyIsCustom = (chosen() == Type(-1)); + const auto currentlyIsCustom = (chosen() == Type(-1)) + && !Window::Theme::Background()->themeObject().cloud.id; if (Window::Theme::IsNightMode() == isNight(scheme)) { Window::Theme::ApplyDefaultWithPath(scheme.path); } else { diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index e9cbe4be2a..d163a52910 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -67,6 +67,8 @@ constexpr auto kStickersVersionTag = quint32(-1); constexpr auto kStickersSerializeVersion = 1; constexpr auto kMaxSavedStickerSetsCount = 1000; +const auto kThemeNewPathRelativeTag = qstr("special://new_tag"); + using Database = Storage::Cache::Database; using FileKey = quint64; @@ -4182,50 +4184,74 @@ bool readBackground() { } Window::Theme::Saved readThemeUsingKey(FileKey key) { + using namespace Window::Theme; + FileReadDescriptor theme; if (!readEncryptedFile(theme, key, FileOption::Safe, SettingsKey)) { return {}; } - auto result = Window::Theme::Saved(); - theme.stream >> result.content; - theme.stream >> result.pathRelative >> result.pathAbsolute; + auto tag = QString(); + auto result = Saved(); + auto &object = result.object; + auto &cache = result.cache; + theme.stream >> object.content; + theme.stream >> tag >> object.pathAbsolute; + const auto isCloud = (object.pathAbsolute == kThemePathAbsoluteCloud); + if (tag == kThemeNewPathRelativeTag) { + if (isCloud) { + auto creator = qint32(); + theme.stream + >> object.cloud.id + >> object.cloud.accessHash + >> object.cloud.slug + >> object.cloud.title + >> object.cloud.documentId + >> creator; + object.cloud.createdBy = creator; + } else { + theme.stream >> object.pathRelative; + } + } else { + object.pathRelative = tag; + } if (theme.stream.status() != QDataStream::Ok) { return {}; } - 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.size() > kThemeFileSizeLimit) { - LOG(("Error: theme file too large: %1 " - "(should be less than 5 MB, got %2)" - ).arg(file.fileName() - ).arg(file.size())); - return {}; + auto ignoreCache = false; + if (!isCloud) { + QFile file(object.pathRelative); + if (object.pathRelative.isEmpty() || !file.exists()) { + file.setFileName(object.pathAbsolute); } - auto fileContent = file.readAll(); - file.close(); - if (result.content != fileContent) { - result.content = fileContent; - changed = true; + 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 {}; + } + auto fileContent = file.readAll(); + file.close(); + if (object.content != fileContent) { + object.content = fileContent; + ignoreCache = true; + } } } - if (!changed) { + if (!ignoreCache) { quint32 backgroundIsTiled = 0; theme.stream - >> result.cache.paletteChecksum - >> result.cache.contentChecksum - >> result.cache.colors - >> result.cache.background + >> cache.paletteChecksum + >> cache.contentChecksum + >> cache.colors + >> cache.background >> backgroundIsTiled; - result.cache.tiled = (backgroundIsTiled == 1); + cache.tiled = (backgroundIsTiled == 1); if (theme.stream.status() != QDataStream::Ok) { return {}; } @@ -4235,20 +4261,24 @@ Window::Theme::Saved readThemeUsingKey(FileKey key) { 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(); + const auto result = read.object.pathAbsolute; + if (read.object.content.isEmpty() + || !Window::Theme::Load(std::move(read))) { + return QString(); + } + return result; } void writeTheme(const Window::Theme::Saved &saved) { + using namespace Window::Theme; + if (_themeKeyLegacy) { return; } - auto &themeKey = Window::Theme::IsNightMode() + auto &themeKey = IsNightMode() ? _themeKeyNight : _themeKeyDay; - if (saved.content.isEmpty()) { + if (saved.object.content.isEmpty()) { if (themeKey) { clearKey(themeKey); themeKey = 0; @@ -4262,14 +4292,41 @@ void writeTheme(const Window::Theme::Saved &saved) { writeSettings(); } - auto backgroundTiled = static_cast(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); + const auto &object = saved.object; + const auto &cache = saved.cache; + const auto tag = QString(kThemeNewPathRelativeTag); + const auto isCloud = (saved.object.pathAbsolute == kThemePathAbsoluteCloud); + quint32 size = Serialize::bytearraySize(object.content); + size += Serialize::stringSize(tag) + Serialize::stringSize(object.pathAbsolute); + if (isCloud) { + size += sizeof(uint64) * 3 + + Serialize::stringSize(object.cloud.slug) + + Serialize::stringSize(object.cloud.title) + + sizeof(qint32); + } else { + size += Serialize::stringSize(object.pathRelative); + } + size += sizeof(int32) * 2 + Serialize::bytearraySize(cache.colors) + Serialize::bytearraySize(cache.background) + sizeof(quint32); EncryptedDescriptor data(size); - 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; + data.stream << object.content; + data.stream << tag << object.pathAbsolute; + if (isCloud) { + data.stream + << object.cloud.id + << object.cloud.accessHash + << object.cloud.slug + << object.cloud.title + << object.cloud.documentId + << qint32(object.cloud.createdBy); + } else { + data.stream << object.pathRelative; + } + data.stream + << cache.paletteChecksum + << cache.contentChecksum + << cache.colors + << cache.background + << quint32(cache.tiled ? 1 : 0); FileWriteDescriptor file(themeKey, FileOption::Safe); file.writeEncrypted(data, SettingsKey); diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index 51d1a5ec0f..fadd41a95d 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -50,8 +50,8 @@ void AbstractCheckView::setChecked(bool checked, anim::type animated) { _checked ? 1. : 0., _duration); } + checkedChangedHook(animated); if (changed) { - checkedChangedHook(animated); _checks.fire_copy(_checked); } } @@ -575,7 +575,7 @@ void Checkbox::paintEvent(QPaintEvent *e) { availableTextWidth, width()); } - } else { + } else if (_allowMultiline || _text.countHeight(width() - _st.margin.left() - _st.margin.right()) < 2 * _st.style.font->height) { _text.drawLeft( p, _st.margin.left(), @@ -583,6 +583,13 @@ void Checkbox::paintEvent(QPaintEvent *e) { width() - _st.margin.left() - _st.margin.right(), width(), style::al_top); + } else { + _text.drawLeftElided( + p, + _st.margin.left(), + textTop, + width() - _st.margin.left() - _st.margin.right(), + width()); } } } @@ -636,7 +643,9 @@ int Checkbox::resizeGetHeight(int newWidth) { ? (newWidth - _st.margin.left() - _st.margin.right()) : qMax(width() - leftSkip, 1); const auto textBottom = _st.textPosition.y() - + _text.countHeight(availableTextWidth); + + ((centered && !_allowMultiline) + ? _st.style.font->height + : _text.countHeight(availableTextWidth)); return std::max(result, textBottom); } diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index f50e8fecbc..8f3cdad2c5 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -37,11 +37,8 @@ constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme"); constexpr auto kMinimumTiledSize = 512; struct Applying { - QString pathRelative; - QString pathAbsolute; - QByteArray content; + Saved data; QByteArray paletteForRevert; - Cached cached; Fn overrideKeep; }; @@ -383,15 +380,6 @@ QImage validateBackgroundImage(QImage image) { return image; } -void WriteAppliedTheme() { - auto saved = Saved(); - saved.pathRelative = GlobalApplying.pathRelative; - saved.pathAbsolute = GlobalApplying.pathAbsolute; - saved.content = std::move(GlobalApplying.content); - saved.cache = std::move(GlobalApplying.cached); - Local::writeTheme(saved); -} - void ClearApplying() { GlobalApplying = Applying(); } @@ -556,7 +544,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) { const auto needResetAdjustable = Data::IsDefaultWallPaper(paper) && !Data::IsDefaultWallPaper(_paper) && !nightMode() - && _themeAbsolutePath.isEmpty(); + && _themeObject.pathAbsolute.isEmpty(); if (Data::IsThemeWallPaper(paper) && _themeImage.isNull()) { setPaper(Data::DefaultWallPaper()); } else { @@ -711,10 +699,10 @@ bool ChatBackground::adjustPaletteRequired() { } bool ChatBackground::isEditingTheme() const { - const auto path = AreTestingTheme() - ? GlobalApplying.pathAbsolute - : _themeAbsolutePath; - return IsEditingTheme(path); + const auto &object = AreTestingTheme() + ? GlobalApplying.data.object + : _themeObject; + return IsEditingTheme(object.pathAbsolute); } void ChatBackground::adjustPaletteUsingBackground(const QImage &image) { @@ -812,12 +800,12 @@ void ChatBackground::setTileNightValue(bool tile) { _tileNightValue = tile; } -void ChatBackground::setThemeAbsolutePath(const QString &path) { - _themeAbsolutePath = path; +void ChatBackground::setThemeObject(const Object &object) { + _themeObject = object; } -QString ChatBackground::themeAbsolutePath() const { - return _themeAbsolutePath; +const Object &ChatBackground::themeObject() const { + return _themeObject; } void ChatBackground::reset() { @@ -871,7 +859,7 @@ void ChatBackground::setTestingTheme(Instance &&theme) { || Data::IsThemeWallPaper(_paper) || (Data::IsDefaultWallPaper(_paper) && !nightMode() - && _themeAbsolutePath.isEmpty()); + && _themeObject.pathAbsolute.isEmpty()); if (AreTestingTheme() && isEditingTheme()) { // Grab current background image if it is not already custom // Use prepared pixmap, not original image, because we're @@ -905,8 +893,8 @@ void ChatBackground::setTestingDefaultTheme() { notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true); } -void ChatBackground::keepApplied(const QString &path, bool write) { - setThemeAbsolutePath(path); +void ChatBackground::keepApplied(const Object &object, bool write) { + setThemeObject(object); if (Data::details::IsTestingEditorWallPaper(_paper)) { setPaper(Data::CustomWallPaper()); _themeImage = QImage(); @@ -935,15 +923,15 @@ void ChatBackground::keepApplied(const QString &path, bool write) { bool ChatBackground::isNonDefaultThemeOrBackground() { start(); return nightMode() - ? (_themeAbsolutePath != NightThemePath() + ? (_themeObject.pathAbsolute != NightThemePath() || !Data::IsThemeWallPaper(_paper)) - : (!_themeAbsolutePath.isEmpty() + : (!_themeObject.pathAbsolute.isEmpty() || !Data::IsDefaultWallPaper(_paper)); } bool ChatBackground::isNonDefaultBackground() { start(); - return _themeAbsolutePath.isEmpty() + return _themeObject.pathAbsolute.isEmpty() ? !Data::IsDefaultWallPaper(_paper) : !Data::IsThemeWallPaper(_paper); } @@ -987,21 +975,19 @@ void ChatBackground::toggleNightMode(std::optional themePath) { const auto newNightMode = !_nightMode; _nightMode = newNightMode; auto read = settingDefault ? Saved() : Local::readThemeAfterSwitch(); - auto path = read.pathAbsolute; + auto path = read.object.pathAbsolute; _nightMode = oldNightMode; auto oldTileValue = (_nightMode ? _tileNightValue : _tileDayValue); const auto alreadyOnDisk = [&] { - if (read.content.isEmpty()) { + if (read.object.content.isEmpty()) { return false; } auto preview = std::make_unique(); - preview->pathAbsolute = std::move(read.pathAbsolute); - preview->pathRelative = std::move(read.pathRelative); - preview->content = std::move(read.content); + preview->object = std::move(read.object); preview->instance.cached = std::move(read.cache); const auto loaded = loadTheme( - preview->content, + preview->object.content, preview->instance.cached, ColorizerForTheme(path), &preview->instance); @@ -1026,12 +1012,13 @@ void ChatBackground::toggleNightMode(std::optional themePath) { // Restore the value, it was set inside theme testing. (oldNightMode ? _tileNightValue : _tileDayValue) = oldTileValue; + const auto saved = std::move(GlobalApplying.data); if (!alreadyOnDisk) { // First-time switch to default night mode should write it. - WriteAppliedTheme(); + Local::writeTheme(saved); } ClearApplying(); - keepApplied(path, settingDefault); + keepApplied(saved.object, settingDefault); if (tile() != _tileForRevert) { Local::writeUserSettings(); } @@ -1049,25 +1036,25 @@ ChatBackground *Background() { } bool Load(Saved &&saved) { - if (saved.content.size() < 4) { + if (saved.object.content.size() < 4) { LOG(("Theme Error: Could not load theme from '%1' (%2)" - ).arg(saved.pathRelative - ).arg(saved.pathAbsolute)); + ).arg(saved.object.pathRelative + ).arg(saved.object.pathAbsolute)); return false; } GlobalBackground.createIfNull(); - if (loadThemeFromCache(saved.content, saved.cache)) { - Background()->setThemeAbsolutePath(saved.pathAbsolute); + if (loadThemeFromCache(saved.object.content, saved.cache)) { + Background()->setThemeObject(saved.object); return true; } - const auto colorizer = ColorizerForTheme(saved.pathAbsolute); - if (!loadTheme(saved.content, saved.cache, colorizer)) { + const auto colorizer = ColorizerForTheme(saved.object.pathAbsolute); + if (!loadTheme(saved.object.content, saved.cache, colorizer)) { return false; } Local::writeTheme(saved); - Background()->setThemeAbsolutePath(saved.pathAbsolute); + Background()->setThemeObject(saved.object); return true; } @@ -1077,17 +1064,15 @@ void Unload() { } bool Apply(const QString &filepath) { - if (auto preview = PreviewFromFile(filepath)) { + if (auto preview = PreviewFromFile(filepath, {}, {})) { return Apply(std::move(preview)); } return false; } bool Apply(std::unique_ptr preview) { - GlobalApplying.pathRelative = std::move(preview->pathRelative); - GlobalApplying.pathAbsolute = std::move(preview->pathAbsolute); - GlobalApplying.content = std::move(preview->content); - GlobalApplying.cached = std::move(preview->instance.cached); + GlobalApplying.data.object = std::move(preview->object); + GlobalApplying.data.cache = std::move(preview->instance.cached); if (GlobalApplying.paletteForRevert.isEmpty()) { GlobalApplying.paletteForRevert = style::main_palette::save(); } @@ -1097,14 +1082,11 @@ bool Apply(std::unique_ptr preview) { void ApplyDefaultWithPath(const QString &themePath) { if (!themePath.isEmpty()) { - if (auto preview = PreviewFromFile(themePath)) { + if (auto preview = PreviewFromFile(themePath, {}, {})) { Apply(std::move(preview)); } } else { - GlobalApplying.pathRelative = QString(); - GlobalApplying.pathAbsolute = QString(); - GlobalApplying.content = QByteArray(); - GlobalApplying.cached = Cached(); + GlobalApplying.data = Saved(); if (GlobalApplying.paletteForRevert.isEmpty()) { GlobalApplying.paletteForRevert = style::main_palette::save(); } @@ -1123,14 +1105,14 @@ bool ApplyEditedPalette(const QString &path, const QByteArray &content) { content.constData(), content.size()); - GlobalApplying.pathRelative = path.isEmpty() + GlobalApplying.data.object.pathRelative = path.isEmpty() ? QString() : QDir().relativeFilePath(path); - GlobalApplying.pathAbsolute = path.isEmpty() + GlobalApplying.data.object.pathAbsolute = path.isEmpty() ? QString() : QFileInfo(path).absoluteFilePath(); - GlobalApplying.content = content; - GlobalApplying.cached = out.cached; + GlobalApplying.data.object.content = content; + GlobalApplying.data.cache = out.cached; if (GlobalApplying.paletteForRevert.isEmpty()) { GlobalApplying.paletteForRevert = style::main_palette::save(); } @@ -1150,10 +1132,10 @@ void KeepApplied() { onstack(); return; } - const auto path = GlobalApplying.pathAbsolute; - WriteAppliedTheme(); + const auto saved = std::move(GlobalApplying.data); + Local::writeTheme(saved); ClearApplying(); - Background()->keepApplied(path, true); + Background()->keepApplied(saved.object, true); } void Revert() { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 83a1350ee0..76766e8d20 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "data/data_wall_paper.h" +#include "data/data_cloud_themes.h" namespace Main { class Session; @@ -16,8 +17,15 @@ class Session; namespace Window { namespace Theme { -constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; +inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; +inline const auto kThemePathAbsoluteCloud = qstr("special://cloud"); +struct Object { + QString pathRelative; + QString pathAbsolute; + QByteArray content; + Data::CloudTheme cloud; +}; struct Cached { QByteArray colors; QByteArray background; @@ -26,9 +34,7 @@ struct Cached { int32 contentChecksum = 0; }; struct Saved { - QString pathRelative; - QString pathAbsolute; - QByteArray content; + Object object; Cached cache; }; bool Load(Saved &&saved); @@ -42,10 +48,8 @@ struct Instance { }; struct Preview { - QString pathRelative; - QString pathAbsolute; + Object object; Instance instance; - QByteArray content; QImage preview; }; @@ -107,8 +111,8 @@ public: void setTile(bool tile); void setTileDayValue(bool tile); void setTileNightValue(bool tile); - void setThemeAbsolutePath(const QString &path); - [[nodiscard]] QString themeAbsolutePath() const; + void setThemeObject(const Object &object); + [[nodiscard]] const Object &themeObject() const; [[nodiscard]] bool isEditingTheme() const; void reset(); @@ -159,7 +163,7 @@ private: void setNightModeValue(bool nightMode); [[nodiscard]] bool nightMode() const; void toggleNightMode(std::optional themePath); - void keepApplied(const QString &path, bool write); + void keepApplied(const Object &object, bool write); [[nodiscard]] bool isNonDefaultThemeOrBackground(); [[nodiscard]] bool isNonDefaultBackground(); void checkUploadWallPaper(); @@ -183,7 +187,7 @@ private: bool _isMonoColorImage = false; - QString _themeAbsolutePath; + Object _themeObject; QImage _themeImage; bool _themeTile = false; diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index 7e473fc39a..211159fe62 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -908,24 +908,42 @@ void Generator::restoreTextPalette() { } // namespace -std::unique_ptr PreviewFromFile(const QString &filepath) { +std::unique_ptr PreviewFromFile( + const QString &filepath, + const QByteArray &bytes, + const Data::CloudTheme &cloud) { auto result = std::make_unique(); - 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; + auto &object = result->object; + object.cloud = cloud; + if (cloud.documentId || filepath.isEmpty()) { + object.pathRelative = QString(); + object.pathAbsolute = QString(kThemePathAbsoluteCloud); + } else { + object.pathRelative = filepath.isEmpty() + ? QString() + : QDir().relativeFilePath(filepath); + object.pathAbsolute = filepath.isEmpty() + ? QString() + : QFileInfo(filepath).absoluteFilePath(); + } + if (bytes.isEmpty()) { + if (!LoadFromFile(filepath, &result->instance, &object.content)) { + return nullptr; + } + } else { + if (!LoadFromContent(bytes, &result->instance)) { + return nullptr; + } } return result; } std::unique_ptr GeneratePreview( const QString &filepath, + const QByteArray &bytes, + const Data::CloudTheme &cloud, CurrentData &&data) { - auto result = PreviewFromFile(filepath); + auto result = PreviewFromFile(filepath, bytes, cloud); if (!result) { return nullptr; } diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.h b/Telegram/SourceFiles/window/themes/window_theme_preview.h index adde5109a5..b38d350b3f 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.h +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" +namespace Data { +struct CloudTheme; +} // namespace Data + namespace Window { namespace Theme { @@ -18,9 +22,14 @@ struct CurrentData { bool backgroundTiled = false; }; -std::unique_ptr PreviewFromFile(const QString &filepath); +std::unique_ptr PreviewFromFile( + const QString &filepath, + const QByteArray &bytes, + const Data::CloudTheme &cloud); std::unique_ptr GeneratePreview( const QString &filepath, + const QByteArray &bytes, + const Data::CloudTheme &cloud, CurrentData &&data); int DefaultPreviewTitleHeight(); diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp index 88fffe9b98..3048e15ca0 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "data/data_file_origin.h" #include "data/data_document.h" +#include "data/data_session.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "styles/style_settings.h" @@ -222,6 +223,11 @@ void CloudListBox( box->setTitle(tr::lng_settings_bg_cloud_themes()); box->setWidth(st::boxWideWidth); + const auto currentId = Background()->themeObject().cloud.documentId; + ranges::stable_sort(list, std::less<>(), [&](const Data::CloudTheme &t) { + return !t.documentId ? 2 : (t.documentId == currentId) ? 0 : 1; + }); + const auto content = box->addRow( object_ptr(box), style::margins( @@ -229,7 +235,27 @@ void CloudListBox( 0, st::settingsSubsectionTitlePadding.right(), 0)); - const auto group = std::make_shared(-1); + const auto group = std::make_shared(); + const auto resolveCurrent = [=] { + const auto currentId = Background()->themeObject().cloud.id; + const auto i = currentId + ? ranges::find(list, currentId, &Data::CloudTheme::id) + : end(list); + group->setValue(i - begin(list)); + }; + + resolveCurrent(); + auto checker = Background()->add_subscription([=](const BackgroundUpdate &update) { + if (update.type == BackgroundUpdate::Type::ApplyingTheme + || update.type == BackgroundUpdate::Type::New) { + resolveCurrent(); + } + }); + group->setChangedCallback([=](int selected) { + resolveCurrent(); + }); + Ui::AttachAsChild(box, std::move(checker)); + const auto waiting = std::make_shared>(); const auto fallback = std::make_shared(); const auto buttonsMap = std::make_shared not_null { - const auto document = theme.document; - if (!document) { + if (!theme.documentId) { + index++; return Ui::CreateChild(content); } - if (document) { - document->save(Data::FileOrigin(), QString()); // #TODO themes - } + const auto document = window->session().data().document( + theme.documentId); + document->save(Data::FileOrigin(), QString()); // #TODO themes auto colors = ColorsFromTheme( document->filepath(), document->data());