From a3e993253c87e2ff3753394f35ac4180f95ecef4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 26 Aug 2019 19:36:23 +0300 Subject: [PATCH] Keep colorized theme in editor. --- Telegram/SourceFiles/core/utils.h | 10 +- Telegram/SourceFiles/storage/localstorage.cpp | 13 ++- Telegram/SourceFiles/storage/localstorage.h | 2 +- .../window/themes/window_theme.cpp | 33 ------- .../SourceFiles/window/themes/window_theme.h | 7 +- .../window/themes/window_theme_editor.cpp | 97 ++++++++++++++++++- .../window/themes/window_theme_editor.h | 5 + .../window/themes/window_themes_embedded.cpp | 32 ++++++ .../window/themes/window_themes_embedded.h | 3 + 9 files changed, 158 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 947530bd26..39db593758 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -67,8 +67,12 @@ T *SharedMemoryLocation() { // see https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf class str_const { // constexpr string public: - template - constexpr str_const(const char(&a)[N]) : _str(a), _size(N - 1) { + constexpr str_const(const char *str, std::size_t size) + : _str(str) + , _size(size) { + } + template + constexpr str_const(const char(&a)[N]) : str_const(a, N - 1) { } constexpr char operator[](std::size_t n) const { return (n < _size) ? _str[n] : @@ -79,7 +83,7 @@ public: #endif // OS_MAC_OLD } constexpr std::size_t size() const { return _size; } - const char *c_str() const { return _str; } + constexpr const char *c_str() const { return _str; } private: const char* const _str; diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index eb86cd2f1a..e9cbe4be2a 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_drafts.h" #include "data/data_user.h" #include "boxes/send_files_box.h" -#include "window/themes/window_theme.h" #include "ui/widgets/input_fields.h" #include "ui/emoji_config.h" #include "export/export_settings.h" @@ -33,7 +32,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "apiwrap.h" #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" @@ -4424,7 +4425,7 @@ std::vector readRecentLanguages() { return result; } -bool copyThemeColorsToPalette(const QString &path) { +bool copyThemeColorsToPalette(const QString &destination) { auto &themeKey = Window::Theme::IsNightMode() ? _themeKeyNight : _themeKeyDay; @@ -4438,12 +4439,16 @@ bool copyThemeColorsToPalette(const QString &path) { } QByteArray themeContent; - theme.stream >> themeContent; + QString pathRelative, pathAbsolute; + theme.stream >> themeContent >> pathRelative >> pathAbsolute; if (theme.stream.status() != QDataStream::Ok) { return false; } - return Window::Theme::CopyColorsToPalette(path, themeContent); + return Window::Theme::CopyColorsToPalette( + destination, + pathAbsolute, + themeContent); } void writeRecentHashtagsAndBots() { diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 67065eb05d..462ea51dc5 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -152,7 +152,7 @@ bool readBackground(); void writeTheme(const Window::Theme::Saved &saved); void clearTheme(); -bool copyThemeColorsToPalette(const QString &file); +bool copyThemeColorsToPalette(const QString &destination); Window::Theme::Saved readThemeAfterSwitch(); void writeLangPack(); diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index a8472a6b38..5bbfc0a106 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -32,7 +32,6 @@ namespace { constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024; constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024; constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024; -constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme"); constexpr auto kMinimumTiledSize = 512; @@ -1229,38 +1228,6 @@ void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect & } } -bool CopyColorsToPalette(const QString &path, const QByteArray &themeContent) { - 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(path)); - return false; - } - } - - QFile f(path); - if (!f.open(QIODevice::WriteOnly)) { - LOG(("Theme Error: could not open file for write '%1'").arg(path)); - return false; - } - - if (f.write(paletteContent) != paletteContent.size()) { - LOG(("Theme Error: could not write palette to '%1'").arg(path)); - return false; - } - return true; -} - bool ReadPaletteValues(const QByteArray &content, Fn callback) { if (content.size() > kThemeSchemeSizeLimit) { LOG(("Theme Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size())); diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 1865e56a17..484f0eaa95 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -16,6 +16,8 @@ class Session; namespace Window { namespace Theme { +constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; + struct Colorizer; struct Cached { @@ -212,7 +214,10 @@ ChatBackground *Background(); void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from); -bool CopyColorsToPalette(const QString &path, const QByteArray &themeContent); +bool CopyColorsToPalette( + const QString &destination, + const QString &themePath, + const QByteArray &themeContent); bool ReadPaletteValues(const QByteArray &content, Fn callback); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 00a2421bf4..2eb5136d6b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" #include "window/themes/window_theme_editor_block.h" +#include "window/themes/window_themes_embedded.h" #include "mainwindow.h" #include "layout.h" #include "storage/localstorage.h" @@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/parse_helper.h" #include "base/zlib_help.h" #include "core/file_utilities.h" +#include "core/application.h" #include "boxes/edit_color_box.h" #include "lang/lang_keys.h" @@ -184,6 +186,46 @@ QByteArray replaceValueInContent(const QByteArray &content, const QByteArray &na return QByteArray(); } +QByteArray ColorizeInContent( + QByteArray content, + not_null colorizer) { + auto validNames = OrderedSet(); + content.detach(); + auto start = content.constBegin(), data = start, end = data + content.size(); + while (data != end) { + skipWhitespacesAndComments(data, end); + if (data == end) break; + + auto foundName = base::parse::readName(data, end); + skipWhitespacesAndComments(data, end); + if (data == end || *data != ':') { + return "error"; + } + ++data; + skipWhitespacesAndComments(data, end); + auto valueStart = data; + auto value = readValue(data, end); + auto valueEnd = data; + if (value.size() == 0) { + return "error"; + } + if (isValidColorValue(value)) { + const auto colorized = Colorize(value, colorizer); + Assert(colorized.size() == value.size()); + memcpy( + content.data() + (data - start) - value.size(), + colorized.data(), + value.size()); + } + skipWhitespacesAndComments(data, end); + if (data == end || *data != ';') { + return "error"; + } + ++data; + } + return content; +} + QString bytesToUtf8(QLatin1String bytes) { return QString::fromUtf8(bytes.data(), bytes.size()); } @@ -279,6 +321,57 @@ private: }; +bool CopyColorsToPalette( + const QString &destination, + const QString &themePath, + const QByteArray &themeContent) { + 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 (themePath.startsWith(qstr(":/gui"))) { + const auto schemes = EmbeddedThemes(); + const auto i = ranges::find( + schemes, + themePath, + &EmbeddedScheme::path); + if (i != end(schemes)) { + const auto &colors = Core::App().settings().themesAccentColors(); + if (const auto accent = colors.get(i->type)) { + const auto colorizer = ColorizerFrom(*i, *accent); + 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; +} + Editor::Inner::Inner(QWidget *parent, const QString &path) : TWidget(parent) , _path(path) , _existingRows(this, EditorBlock::Type::Existing, &_context) @@ -497,14 +590,14 @@ QString colorString(QColor color) { auto result = QString(); result.reserve(9); result.append('#'); - auto addHex = [&result](int code) { + const auto addHex = [&](int code) { if (code >= 0 && code < 10) { result.append('0' + code); } else if (code >= 10 && code < 16) { result.append('a' + (code - 10)); } }; - auto addValue = [addHex](int code) { + const auto addValue = [&](int code) { addHex(code / 16); addHex(code % 16); }; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.h b/Telegram/SourceFiles/window/themes/window_theme_editor.h index 0c94a50fb7..3a3efbefe7 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.h @@ -18,6 +18,11 @@ class PlainShadow; namespace Window { namespace Theme { +bool CopyColorsToPalette( + const QString &destination, + const QString &themePath, + const QByteArray &themeContent); + class Editor : public TWidget { public: Editor(QWidget*, const QString &path); diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp index a14e89501a..a5b97e53a3 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp @@ -215,6 +215,38 @@ void Colorize(EmbeddedScheme &scheme, not_null colorizer) { } } +QByteArray Colorize( + QLatin1String hexColor, + not_null colorizer) { + Expects(hexColor.size() == 7 || hexColor.size() == 9); + + auto color = qColor(str_const(hexColor.data() + 1, 6)); + Colorize(color, colorizer); + + auto result = QByteArray(); + result.reserve(hexColor.size()); + result.append(hexColor.data()[0]); + const auto addHex = [&](int code) { + if (code >= 0 && code < 10) { + result.append('0' + code); + } else if (code >= 10 && code < 16) { + result.append('a' + (code - 10)); + } + }; + const auto addValue = [&](int code) { + addHex(code / 16); + addHex(code % 16); + }; + addValue(color.red()); + addValue(color.green()); + addValue(color.blue()); + if (hexColor.size() == 9) { + result.append(hexColor.data()[7]); + result.append(hexColor.data()[8]); + } + return result; +} + std::vector EmbeddedThemes() { return { EmbeddedScheme{ diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.h b/Telegram/SourceFiles/window/themes/window_themes_embedded.h index 0bb2de2f4f..88a87b7f77 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.h +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.h @@ -71,6 +71,9 @@ void Colorize( not_null colorizer); void Colorize(QImage &image, not_null colorizer); void Colorize(EmbeddedScheme &scheme, not_null colorizer); +[[nodiscard]] QByteArray Colorize( + QLatin1String hexColor, + not_null colorizer); [[nodiscard]] std::vector EmbeddedThemes(); [[nodiscard]] std::vector DefaultAccentColors(EmbeddedType type);