Apply cloud themes.

This commit is contained in:
John Preston 2019-09-03 21:04:38 +03:00
parent ac8f924909
commit 4929de2bfb
12 changed files with 280 additions and 152 deletions

View File

@ -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<Main::Session*> 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<MTPTheme> &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."));
});

View File

@ -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<Main::Session*> session,
const MTPDtheme &data);
};
class CloudThemes final {

View File

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

View File

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

View File

@ -998,9 +998,9 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> 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<Ui::VerticalLayout*> 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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -908,24 +908,42 @@ void Generator::restoreTextPalette() {
} // namespace
std::unique_ptr<Preview> PreviewFromFile(const QString &filepath) {
std::unique_ptr<Preview> PreviewFromFile(
const QString &filepath,
const QByteArray &bytes,
const Data::CloudTheme &cloud) {
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;
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<Preview> 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;
}

View File

@ -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<Preview> PreviewFromFile(const QString &filepath);
std::unique_ptr<Preview> PreviewFromFile(
const QString &filepath,
const QByteArray &bytes,
const Data::CloudTheme &cloud);
std::unique_ptr<Preview> GeneratePreview(
const QString &filepath,
const QByteArray &bytes,
const Data::CloudTheme &cloud,
CurrentData &&data);
int DefaultPreviewTitleHeight();

View File

@ -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<Ui::RpWidget>(box),
style::margins(
@ -229,7 +235,27 @@ void CloudListBox(
0,
st::settingsSubsectionTitlePadding.right(),
0));
const auto group = std::make_shared<Ui::RadiobuttonGroup>(-1);
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
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<std::vector<WaitingPair>>();
const auto fallback = std::make_shared<QImage>();
const auto buttonsMap = std::make_shared<base::flat_map<
@ -248,13 +274,13 @@ void CloudListBox(
list
) | ranges::view::transform([&](const Data::CloudTheme &theme)
-> not_null<Ui::RpWidget*> {
const auto document = theme.document;
if (!document) {
if (!theme.documentId) {
index++;
return Ui::CreateChild<Ui::RpWidget>(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());