Show not supported themes placeholders.

This commit is contained in:
John Preston 2019-09-05 23:21:44 +03:00
parent 639b4bdd27
commit 910f16312c
12 changed files with 194 additions and 90 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

View File

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

View File

@ -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<Data::CloudTheme> ChatBackground::editingTheme() const {
return _editingTheme;
}
void ChatBackground::setIsEditingTheme(bool editing) {
if (_editingTheme == editing) {
void ChatBackground::setEditingTheme(
std::optional<Data::CloudTheme> 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).

View File

@ -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<Data::CloudTheme> editingTheme() const;
void setEditingTheme(std::optional<Data::CloudTheme> editing);
void reset();
void setTestingTheme(Instance &&theme);
@ -201,7 +201,7 @@ private:
Object _themeObject;
QImage _themeImage;
bool _themeTile = false;
bool _editingTheme = false;
std::optional<Data::CloudTheme> _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<bool(QLatin1String name, QLatin1String value)> callback);
} // namespace Theme

View File

@ -35,6 +35,14 @@ namespace Window {
namespace Theme {
namespace {
template <size_t Size>
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<QLatin1String>();
@ -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);
}
}

View File

@ -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::Controller*> 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<Ui::PlainShadow> _leftShadow;
object_ptr<Ui::PlainShadow> _topShadow;
object_ptr<Ui::FlatButton> _save;
bool _saving = false;
};

View File

@ -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<void()> SaveTheme(
Fn<void()> SavePreparedTheme(
not_null<Window::Controller*> window,
const QByteArray &palette,
const PreparedBackground &background,
@ -494,12 +541,15 @@ void StartEditor(
not_null<Window::Controller*> 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<InformBox>(tr::lng_theme_editor_error(tr::now)));
return;
}
Background()->setIsEditingTheme(true);
Background()->setEditingTheme(cloud);
window->showRightColumn(Box<Editor>(window, cloud));
}
@ -561,6 +611,40 @@ void CreateForExistingBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void SaveTheme(
not_null<Window::Controller*> window,
const Data::CloudTheme &cloud,
const QByteArray &palette,
Fn<void()> 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<GenericBox*> box,
not_null<Window::Controller*> 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(),

View File

@ -29,6 +29,11 @@ void CreateForExistingBox(
not_null<GenericBox*> box,
not_null<Window::Controller*> window,
const Data::CloudTheme &cloud);
void SaveTheme(
not_null<Window::Controller*> window,
const Data::CloudTheme &cloud,
const QByteArray &palette,
Fn<void()> unlock);
void SaveThemeBox(
not_null<GenericBox*> box,
not_null<Window::Controller*> window,

View File

@ -46,9 +46,9 @@ void Controller::firstShow() {
void Controller::checkThemeEditor() {
using namespace Window::Theme;
if (Background()->isEditingTheme()) {
showRightColumn(
Box<Editor>(this, Background()->themeObject().cloud));
if (const auto editing = Background()->editingTheme()) {
showRightColumn(Box<Editor>(this, *editing));
}
}