diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index cff8a298c..16b8a1a8b 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_extensions.h" #include "ui/layers/generic_box.h" #include "ui/effects/radial_animation.h" +#include "ui/style/style_palette_colorizer.h" #include "ui/toast/toast.h" #include "ui/image/image.h" #include "ui/ui_utility.h" diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 8d9697469..979da30c1 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_domain.h" // Domain::activeSessionValue. #include "ui/chat/chat_theme.h" #include "ui/image/image.h" +#include "ui/style/style_palette_colorizer.h" #include "ui/ui_utility.h" #include "boxes/confirm_box.h" #include "boxes/background_box.h" @@ -159,7 +160,7 @@ enum class SetResult { SetResult setColorSchemeValue( QLatin1String name, QLatin1String value, - const Colorizer &colorizer, + const style::colorizer &colorizer, Instance *out) { auto result = style::palette::SetResult::Ok; auto size = value.size(); @@ -171,7 +172,7 @@ SetResult setColorSchemeValue( auto b = readHexUchar(data[5], data[6], error); auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255); if (colorizer) { - Colorize(name, r, g, b, colorizer); + style::colorize(name, r, g, b, colorizer); } if (error) { LOG(("Theme Warning: Skipping value '%1: %2' (expected a color value in #rrggbb or #rrggbbaa or a previously defined key in the color scheme)").arg(name).arg(value)); @@ -206,7 +207,7 @@ SetResult setColorSchemeValue( bool loadColorScheme( const QByteArray &content, - const Colorizer &colorizer, + const style::colorizer &colorizer, Instance *out) { auto unsupported = QMap(); return ReadPaletteValues(content, [&](QLatin1String name, QLatin1String value) { @@ -268,7 +269,7 @@ bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *out bool LoadTheme( const QByteArray &content, - const Colorizer &colorizer, + const style::colorizer &colorizer, const std::optional &editedPalette, Cached *cache = nullptr, Instance *out = nullptr) { @@ -282,7 +283,7 @@ bool LoadTheme( } zlib::FileToRead file(content); - const auto emptyColorizer = Colorizer(); + const auto emptyColorizer = style::colorizer(); const auto &paletteColorizer = editedPalette ? emptyColorizer : colorizer; unz_global_info globalInfo = { 0 }; @@ -331,7 +332,7 @@ bool LoadTheme( return false; } if (colorizer) { - Colorize(background, colorizer); + style::colorize(background, colorizer); } if (cache) { auto buffer = QBuffer(&cache->background); @@ -353,7 +354,7 @@ bool LoadTheme( } } if (out) { - out->palette.finalize(); + out->palette.finalize(paletteColorizer); } if (cache) { if (out) { @@ -1270,7 +1271,7 @@ void ApplyDefaultWithPath(const QString &themePath) { bool ApplyEditedPalette(const QByteArray &content) { auto out = Instance(); - if (!loadColorScheme(content, Colorizer(), &out)) { + if (!loadColorScheme(content, style::colorizer(), &out)) { return false; } style::main_palette::apply(out.palette); @@ -1420,7 +1421,7 @@ bool LoadFromFile( not_null out, Cached *outCache, QByteArray *outContent, - const Colorizer &colorizer) { + const style::colorizer &colorizer) { const auto content = readThemeContent(path); if (outContent) { *outContent = content; @@ -1432,7 +1433,12 @@ bool LoadFromContent( const QByteArray &content, not_null out, Cached *outCache) { - return LoadTheme(content, Colorizer(), std::nullopt, outCache, out); + return LoadTheme( + content, + style::colorizer(), + std::nullopt, + outCache, + out); } QString EditingPalettePath() { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index db3e36680..c085276ef 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "ui/style/style_core_palette.h" +namespace style { +struct colorizer; +} // namespace style + namespace Main { class Session; } // namespace Main @@ -30,7 +34,6 @@ inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024; inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024; struct ParsedTheme; -struct Colorizer; [[nodiscard]] bool IsEmbeddedTheme(const QString &path); @@ -107,7 +110,7 @@ bool LoadFromFile( not_null out, Cached *outCache, QByteArray *outContent, - const Colorizer &colorizer); + const style::colorizer &colorizer); bool LoadFromContent( const QByteArray &content, not_null out, diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 3261ae3b7..8ef071471 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/multi_select.h" #include "ui/widgets/dropdown_menu.h" #include "ui/toast/toast.h" +#include "ui/style/style_palette_colorizer.h" #include "ui/image/image_prepare.h" #include "ui/ui_utility.h" #include "base/parse_helper.h" @@ -156,7 +157,7 @@ bool isValidColorValue(QLatin1String value) { [[nodiscard]] QByteArray ColorizeInContent( QByteArray content, - const Colorizer &colorizer) { + const style::colorizer &colorizer) { auto validNames = OrderedSet(); content.detach(); auto start = content.constBegin(), data = start, end = data + content.size(); @@ -176,7 +177,7 @@ bool isValidColorValue(QLatin1String value) { return "error"; } if (isValidColorValue(value)) { - const auto colorized = Colorize(value, colorizer); + const auto colorized = style::colorize(value, colorizer); Assert(colorized.size() == value.size()); memcpy( content.data() + (data - start) - value.size(), @@ -809,7 +810,7 @@ void Editor::importTheme() { QByteArray Editor::ColorizeInContent( QByteArray content, - const Colorizer &colorizer) { + const style::colorizer &colorizer) { return Window::Theme::ColorizeInContent(content, colorizer); } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.h b/Telegram/SourceFiles/window/themes/window_theme_editor.h index 4daf7473b..c875e79db 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" +namespace style { +struct colorizer; +} // namespace style + namespace Ui { class FlatButton; class ScrollArea; @@ -27,8 +31,6 @@ class Controller; namespace Theme { -struct Colorizer; - struct ParsedTheme { QByteArray palette; QByteArray background; @@ -54,7 +56,7 @@ public: [[nodiscard]] static QByteArray ColorizeInContent( QByteArray content, - const Colorizer &colorizer); + const style::colorizer &colorizer); protected: void paintEvent(QPaintEvent *e) override; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 7e901b1aa..4ac56dc75 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image_prepare.h" #include "ui/toast/toast.h" #include "ui/text/format_values.h" +#include "ui/style/style_palette_colorizer.h" #include "ui/special_fields.h" #include "ui/ui_utility.h" #include "main/main_account.h" diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp index 90251c299..9736fd05f 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image_prepare.h" #include "ui/widgets/popup_menu.h" #include "ui/toast/toast.h" +#include "ui/style/style_palette_colorizer.h" #include "boxes/confirm_box.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -135,7 +136,7 @@ CloudListColors ColorsFromScheme(const EmbeddedScheme &scheme) { CloudListColors ColorsFromScheme( const EmbeddedScheme &scheme, - const Colorizer &colorizer) { + const style::colorizer &colorizer) { if (!colorizer) { return ColorsFromScheme(scheme); } diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h index 116b5284a..7405b65a3 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h +++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.h @@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "base/binary_guard.h" +namespace style { +struct colorizer; +} // namespace style + namespace Ui { class PopupMenu; } // namespace Ui @@ -24,7 +28,6 @@ class SessionController; namespace Theme { struct EmbeddedScheme; -struct Colorizer; struct CloudListColors { QImage background; @@ -37,7 +40,7 @@ struct CloudListColors { [[nodiscard]] CloudListColors ColorsFromScheme(const EmbeddedScheme &scheme); [[nodiscard]] CloudListColors ColorsFromScheme( const EmbeddedScheme &scheme, - const Colorizer &colorizer); + const style::colorizer &colorizer); class CloudListCheck final : public Ui::AbstractCheckView { public: diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp index 646e496c3..e7a4c64dc 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp @@ -11,13 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/serialize_common.h" #include "core/application.h" #include "core/core_settings.h" +#include "ui/style/style_palette_colorizer.h" namespace Window { namespace Theme { namespace { constexpr auto kMaxAccentColors = 3; -constexpr auto kEnoughLightnessForContrast = 64; const auto kColorizeIgnoredKeys = base::flat_set{ { qstr("boxTextFgGood"), @@ -69,44 +69,24 @@ const auto kColorizeIgnoredKeys = base::flat_set{ { qstr("mediaviewFileBlueCornerFg"), } }; -QColor qColor(std::string_view hex) { - Expects(hex.size() == 6); - - const auto component = [](char a, char b) { - const auto convert = [](char ch) { - Expects((ch >= '0' && ch <= '9') - || (ch >= 'A' && ch <= 'F') - || (ch >= 'a' && ch <= 'f')); - - return (ch >= '0' && ch <= '9') - ? int(ch - '0') - : int(ch - ((ch >= 'A' && ch <= 'F') ? 'A' : 'a') + 10); - }; - return convert(a) * 16 + convert(b); - }; - - return QColor( - component(hex[0], hex[1]), - component(hex[2], hex[3]), - component(hex[4], hex[5])); -}; - -Colorizer::Color cColor(std::string_view hex) { - const auto q = qColor(hex); +style::colorizer::Color cColor(std::string_view hex) { + const auto q = style::ColorFromHex(hex); auto hue = int(); auto saturation = int(); auto value = int(); q.getHsv(&hue, &saturation, &value); - return Colorizer::Color{ hue, saturation, value }; + return style::colorizer::Color{ hue, saturation, value }; } } // namespace -Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) { - using Color = Colorizer::Color; +style::colorizer ColorizerFrom( + const EmbeddedScheme &scheme, + const QColor &color) { + using Color = style::colorizer::Color; using Pair = std::pair; - auto result = Colorizer(); + auto result = style::colorizer(); result.ignoreKeys = kColorizeIgnoredKeys; result.hueThreshold = 15; scheme.accentColor.getHsv( @@ -168,9 +148,9 @@ Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) { return result; } -Colorizer ColorizerForTheme(const QString &absolutePath) { +style::colorizer ColorizerForTheme(const QString &absolutePath) { if (absolutePath.isEmpty() || !IsEmbeddedTheme(absolutePath)) { - return Colorizer(); + return {}; } const auto schemes = EmbeddedThemes(); const auto i = ranges::find( @@ -178,150 +158,16 @@ Colorizer ColorizerForTheme(const QString &absolutePath) { absolutePath, &EmbeddedScheme::path); if (i == end(schemes)) { - return Colorizer(); + return {}; } const auto &colors = Core::App().settings().themesAccentColors(); if (const auto accent = colors.get(i->type)) { return ColorizerFrom(*i, *accent); } - return Colorizer(); + return {}; } -[[nodiscard]] std::optional Colorize( - const Colorizer::Color &color, - const Colorizer &colorizer) { - const auto changeColor = std::abs(color.hue - colorizer.was.hue) - < colorizer.hueThreshold; - if (!changeColor) { - return std::nullopt; - } - const auto nowHue = color.hue + (colorizer.now.hue - colorizer.was.hue); - const auto nowSaturation = ((color.saturation > colorizer.was.saturation) - && (colorizer.now.saturation > colorizer.was.saturation)) - ? (((colorizer.now.saturation * (255 - colorizer.was.saturation)) - + ((color.saturation - colorizer.was.saturation) - * (255 - colorizer.now.saturation))) - / (255 - colorizer.was.saturation)) - : ((color.saturation != colorizer.was.saturation) - && (colorizer.was.saturation != 0)) - ? ((color.saturation * colorizer.now.saturation) - / colorizer.was.saturation) - : colorizer.now.saturation; - const auto nowValue = (color.value > colorizer.was.value) - ? (((colorizer.now.value * (255 - colorizer.was.value)) - + ((color.value - colorizer.was.value) - * (255 - colorizer.now.value))) - / (255 - colorizer.was.value)) - : (color.value < colorizer.was.value) - ? ((color.value * colorizer.now.value) - / colorizer.was.value) - : colorizer.now.value; - return Colorizer::Color{ - ((nowHue + 360) % 360), - nowSaturation, - nowValue - }; -} - -[[nodiscard]] std::optional Colorize( - const QColor &color, - const Colorizer &colorizer) { - auto hue = 0; - auto saturation = 0; - auto lightness = 0; - color.getHsv(&hue, &saturation, &lightness); - const auto result = Colorize( - Colorizer::Color{ hue, saturation, lightness }, - colorizer); - if (!result) { - return std::nullopt; - } - const auto &fields = *result; - return QColor::fromHsv(fields.hue, fields.saturation, fields.value); -} - -void FillColorizeResult(uchar &r, uchar &g, uchar &b, const QColor &color) { - auto nowR = 0; - auto nowG = 0; - auto nowB = 0; - color.getRgb(&nowR, &nowG, &nowB); - r = uchar(nowR); - g = uchar(nowG); - b = uchar(nowB); -} - -void Colorize(uchar &r, uchar &g, uchar &b, const Colorizer &colorizer) { - const auto changed = Colorize(QColor(int(r), int(g), int(b)), colorizer); - if (changed) { - FillColorizeResult(r, g, b, *changed); - } -} - -void Colorize( - QLatin1String name, - uchar &r, - uchar &g, - uchar &b, - const Colorizer &colorizer) { - if (colorizer.ignoreKeys.contains(name)) { - return; - } - - const auto i = colorizer.keepContrast.find(name); - if (i == end(colorizer.keepContrast)) { - Colorize(r, g, b, colorizer); - return; - } - const auto check = i->second.first; - const auto rgb = QColor(int(r), int(g), int(b)); - const auto changed = Colorize(rgb, colorizer); - const auto checked = Colorize(check, colorizer).value_or(check); - const auto lightness = [](QColor hsv) { - return hsv.value() - (hsv.value() * hsv.saturation()) / 511; - }; - const auto changedLightness = lightness(changed.value_or(rgb).toHsv()); - const auto checkedLightness = lightness( - QColor::fromHsv(checked.hue, checked.saturation, checked.value)); - const auto delta = std::abs(changedLightness - checkedLightness); - if (delta >= kEnoughLightnessForContrast) { - if (changed) { - FillColorizeResult(r, g, b, *changed); - } - return; - } - const auto replace = i->second.second; - const auto result = Colorize(replace, colorizer).value_or(replace); - FillColorizeResult( - r, - g, - b, - QColor::fromHsv(result.hue, result.saturation, result.value)); -} - -void Colorize(uint32 &pixel, const Colorizer &colorizer) { - const auto chars = reinterpret_cast(&pixel); - Colorize( - chars[2], - chars[1], - chars[0], - colorizer); -} - -void Colorize(QImage &image, const Colorizer &colorizer) { - image = std::move(image).convertToFormat(QImage::Format_ARGB32); - const auto bytes = image.bits(); - const auto bytesPerLine = image.bytesPerLine(); - for (auto line = 0; line != image.height(); ++line) { - const auto ints = reinterpret_cast( - bytes + line * bytesPerLine); - const auto end = ints + image.width(); - for (auto p = ints; p != end; ++p) { - Colorize(*p, colorizer); - } - } -} - -void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer) { +void Colorize(EmbeddedScheme &scheme, const style::colorizer &colorizer) { const auto colors = { &EmbeddedScheme::background, &EmbeddedScheme::sent, @@ -330,45 +176,16 @@ void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer) { &EmbeddedScheme::radiobuttonInactive }; for (const auto color : colors) { - if (const auto changed = Colorize(scheme.*color, colorizer)) { + if (const auto changed = style::colorize(scheme.*color, colorizer)) { scheme.*color = changed->toRgb(); } } } -QByteArray Colorize( - QLatin1String hexColor, - const Colorizer &colorizer) { - Expects(hexColor.size() == 7 || hexColor.size() == 9); - - auto color = qColor(std::string_view(hexColor.data() + 1, 6)); - const auto changed = Colorize(color, colorizer).value_or(color).toRgb(); - - 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(changed.red()); - addValue(changed.green()); - addValue(changed.blue()); - if (hexColor.size() == 9) { - result.append(hexColor.data()[7]); - result.append(hexColor.data()[8]); - } - return result; -} - std::vector EmbeddedThemes() { + const auto qColor = [](auto hex) { + return style::ColorFromHex(hex); + }; return { EmbeddedScheme{ EmbeddedType::Default, @@ -417,6 +234,9 @@ std::vector EmbeddedThemes() { } std::vector DefaultAccentColors(EmbeddedType type) { + const auto qColor = [](auto hex) { + return style::ColorFromHex(hex); + }; switch (type) { case EmbeddedType::DayBlue: return { diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.h b/Telegram/SourceFiles/window/themes/window_themes_embedded.h index 8f4186959..71e9222c2 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.h +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class QImage; +namespace style { +struct colorizer; +} // namespace style + namespace Window { namespace Theme { @@ -47,42 +51,14 @@ private: }; -struct Colorizer { - struct Color { - int hue = 0; - int saturation = 0; - int value = 0; - }; - int hueThreshold = 0; - int lightnessMin = 0; - int lightnessMax = 255; - Color was; - Color now; - base::flat_set ignoreKeys; - base::flat_map> keepContrast; - - explicit operator bool() const { - return (hueThreshold > 0); - } -}; - -[[nodiscard]] Colorizer ColorizerFrom( +[[nodiscard]] style::colorizer ColorizerFrom( const EmbeddedScheme &scheme, const QColor &color); -[[nodiscard]] Colorizer ColorizerForTheme(const QString &absolutePath); +[[nodiscard]] style::colorizer ColorizerForTheme(const QString &absolutePath); -void Colorize(uchar &r, uchar &g, uchar &b, const Colorizer &colorizer); void Colorize( - QLatin1String name, - uchar &r, - uchar &g, - uchar &b, - const Colorizer &colorizer); -void Colorize(QImage &image, const Colorizer &colorizer); -void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer); -[[nodiscard]] QByteArray Colorize( - QLatin1String hexColor, - const Colorizer &colorizer); + EmbeddedScheme &scheme, + const style::colorizer &colorizer); [[nodiscard]] std::vector EmbeddedThemes(); [[nodiscard]] std::vector DefaultAccentColors(EmbeddedType type); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 2bab71a2f..2900ebcb2 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/message_bubble.h" #include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" +#include "ui/style/style_palette_colorizer.h" #include "ui/toast/toast.h" #include "ui/toasts/common_toasts.h" #include "calls/calls_instance.h" // Core::App().calls().inCall(). @@ -95,7 +96,7 @@ constexpr auto kMaxChatEntryHistorySize = 50; &instance, nullptr, nullptr, - accent ? ColorizerFrom(*i, *accent) : Colorizer()); + accent ? ColorizerFrom(*i, *accent) : style::colorizer()); Assert(loaded); palette = instance.palette; } else { diff --git a/Telegram/lib_ui b/Telegram/lib_ui index c88762d0e..cc5ebf21e 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit c88762d0eb860064927bacaa784acd9a4f23c39b +Subproject commit cc5ebf21e747802e60941083da0e0d7d1bfbb2aa