mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-11 01:10:13 +00:00
Apply cloud themes.
This commit is contained in:
parent
ac8f924909
commit
4929de2bfb
@ -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."));
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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()) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user