Allow changing accent color in default themes.

This commit is contained in:
John Preston 2019-08-20 19:03:14 +03:00
parent dd136350fb
commit 9cb5423d40
5 changed files with 155 additions and 50 deletions

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_preview_box.h" #include "boxes/background_preview_box.h"
#include "boxes/download_path_box.h" #include "boxes/download_path_box.h"
#include "boxes/local_storage_box.h" #include "boxes/local_storage_box.h"
#include "boxes/edit_color_box.h"
#include "ui/wrap/vertical_layout.h" #include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
@ -89,6 +90,7 @@ public:
QColor radiobuttonActive; QColor radiobuttonActive;
tr::phrase<> name; tr::phrase<> name;
QString path; QString path;
QColor accentColor;
}; };
DefaultTheme(Scheme scheme, bool checked); DefaultTheme(Scheme scheme, bool checked);
@ -801,7 +803,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("d7f0ff"), color("d7f0ff"),
color("ffffff"), color("ffffff"),
tr::lng_settings_theme_blue, tr::lng_settings_theme_blue,
":/gui/day-blue.tdesktop-theme" ":/gui/day-blue.tdesktop-theme",
color("40a7e3")
}, },
Scheme{ Scheme{
Type::Default, Type::Default,
@ -821,7 +824,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("6b808d"), color("6b808d"),
color("5ca7d4"), color("5ca7d4"),
tr::lng_settings_theme_midnight, tr::lng_settings_theme_midnight,
":/gui/night.tdesktop-theme" ":/gui/night.tdesktop-theme",
color("5288c1")
}, },
Scheme{ Scheme{
Type::NightGreen, Type::NightGreen,
@ -831,7 +835,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("6b808d"), color("6b808d"),
color("74bf93"), color("74bf93"),
tr::lng_settings_theme_matrix, tr::lng_settings_theme_matrix,
":/gui/night-green.tdesktop-theme" ":/gui/night-green.tdesktop-theme",
color("3fc1b0")
}, },
}; };
const auto chosen = [&] { const auto chosen = [&] {
@ -847,6 +852,47 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
return Type(-1); return Type(-1);
}; };
const auto group = std::make_shared<Ui::RadioenumGroup<Type>>(chosen()); const auto group = std::make_shared<Ui::RadioenumGroup<Type>>(chosen());
const auto apply = [=](
const Scheme &scheme,
const Window::Theme::Colorizer *colorizer = nullptr) {
const auto isNight = [](const Scheme &scheme) {
const auto type = scheme.type;
return (type != Type::DayBlue) && (type != Type::Default);
};
const auto currentlyIsCustom = (chosen() == Type(-1));
if (Window::Theme::IsNightMode() == isNight(scheme)) {
Window::Theme::ApplyDefaultWithPath(scheme.path, colorizer);
} else {
Window::Theme::ToggleNightMode(scheme.path, colorizer);
}
if (!currentlyIsCustom) {
Window::Theme::KeepApplied();
}
};
const auto applyWithColorize = [=](const Scheme &scheme) {
const auto box = Ui::show(Box<EditColorBox>(
"Choose accent color",
scheme.accentColor));
box->setSaveCallback([=](QColor result) {
auto colorizer = Window::Theme::Colorizer();
colorizer.hueThreshold = 10;
colorizer.saturationThreshold = 10;
colorizer.wasHue = scheme.accentColor.hue();
colorizer.nowHue = result.hue();
apply(scheme, &colorizer);
});
};
const auto schemeClicked = [=](
const Scheme &scheme,
Qt::KeyboardModifiers modifiers) {
if (scheme.accentColor.hue() && (modifiers & Qt::ControlModifier)) {
applyWithColorize(scheme);
} else {
apply(scheme);
}
};
auto buttons = ranges::view::all( auto buttons = ranges::view::all(
schemes schemes
) | ranges::view::transform([&](const Scheme &scheme) { ) | ranges::view::transform([&](const Scheme &scheme) {
@ -859,34 +905,14 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
scheme.name(tr::now), scheme.name(tr::now),
st::settingsTheme, st::settingsTheme,
std::move(check)); std::move(check));
result->addClickHandler([=] {
schemeClicked(scheme, result->clickModifiers());
});
weak->setUpdateCallback([=] { result->update(); }); weak->setUpdateCallback([=] { result->update(); });
return result; return result;
}) | ranges::to_vector; }) | ranges::to_vector;
using Update = const Window::Theme::BackgroundUpdate; using Update = const Window::Theme::BackgroundUpdate;
const auto apply = [=](const Scheme &scheme) {
const auto isNight = [](const Scheme &scheme) {
const auto type = scheme.type;
return (type != Type::DayBlue) && (type != Type::Default);
};
const auto currentlyIsCustom = (chosen() == Type(-1));
if (Window::Theme::IsNightMode() == isNight(scheme)) {
Window::Theme::ApplyDefaultWithPath(scheme.path);
} else {
Window::Theme::ToggleNightMode(scheme.path);
}
if (!currentlyIsCustom) {
Window::Theme::KeepApplied();
}
};
group->setChangedCallback([=](Type type) {
const auto i = ranges::find_if(schemes, [&](const Scheme &scheme) {
return (type == scheme.type && type != chosen());
});
if (i != end(schemes)) {
apply(*i);
}
});
base::ObservableViewer( base::ObservableViewer(
*Window::Theme::Background() *Window::Theme::Background()
) | rpl::filter([](const Update &update) { ) | rpl::filter([](const Update &update) {

View File

@ -143,12 +143,44 @@ bool readNameAndValue(const char *&from, const char *end, QLatin1String *outName
return true; return true;
} }
void Colorize(
uchar &r,
uchar &g,
uchar &b,
not_null<const Colorizer*> colorizer) {
auto color = QColor(int(r), int(g), int(b));
auto hue = 0;
auto saturation = 0;
auto value = 0;
color.getHsv(&hue, &saturation, &value);
if ((saturation < colorizer->saturationThreshold)
|| (std::abs(hue - colorizer->wasHue) > colorizer->hueThreshold)) {
return;
}
const auto changed = hue + (colorizer->nowHue - colorizer->wasHue);
auto nowR = 0;
auto nowG = 0;
auto nowB = 0;
QColor::fromHsv(
(changed + 360) % 360,
saturation,
value
).getRgb(&nowR, &nowG, &nowB);
r = uchar(nowR);
g = uchar(nowG);
b = uchar(nowB);
}
enum class SetResult { enum class SetResult {
Ok, Ok,
Bad, Bad,
NotFound, NotFound,
}; };
SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance *out) { SetResult setColorSchemeValue(
QLatin1String name,
QLatin1String value,
Instance *out,
const Colorizer *colorizer) {
auto result = style::palette::SetResult::Ok; auto result = style::palette::SetResult::Ok;
auto size = value.size(); auto size = value.size();
auto data = value.data(); auto data = value.data();
@ -158,6 +190,9 @@ SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance
auto g = readHexUchar(data[3], data[4], error); auto g = readHexUchar(data[3], data[4], error);
auto b = readHexUchar(data[5], data[6], error); auto b = readHexUchar(data[5], data[6], error);
auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255); auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255);
if (colorizer) {
Colorize(r, g, b, colorizer);
}
if (error) { 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)); 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));
return SetResult::Ok; return SetResult::Ok;
@ -189,13 +224,16 @@ SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance
return SetResult::Bad; return SetResult::Bad;
} }
bool loadColorScheme(const QByteArray &content, Instance *out) { bool loadColorScheme(
const QByteArray &content,
Instance *out,
const Colorizer *colorizer = nullptr) {
auto unsupported = QMap<QLatin1String, QLatin1String>(); auto unsupported = QMap<QLatin1String, QLatin1String>();
return ReadPaletteValues(content, [&unsupported, out](QLatin1String name, QLatin1String value) { return ReadPaletteValues(content, [&](QLatin1String name, QLatin1String value) {
// Find the named value in the already read unsupported list. // Find the named value in the already read unsupported list.
value = unsupported.value(value, value); value = unsupported.value(value, value);
auto result = setColorSchemeValue(name, value, out); auto result = setColorSchemeValue(name, value, out, colorizer);
if (result == SetResult::Bad) { if (result == SetResult::Bad) {
return false; return false;
} else if (result == SetResult::NotFound) { } else if (result == SetResult::NotFound) {
@ -279,7 +317,11 @@ bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *out
return true; return true;
} }
bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr) { bool loadTheme(
const QByteArray &content,
Cached &cache,
Instance *out = nullptr,
const Colorizer *colorizer = nullptr) {
cache = Cached(); cache = Cached();
zlib::FileToRead file(content); zlib::FileToRead file(content);
@ -295,7 +337,7 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file.")); LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file."));
return false; return false;
} }
if (!loadColorScheme(schemeContent, out)) { if (!loadColorScheme(schemeContent, out, colorizer)) {
return false; return false;
} }
Background()->saveAdjustableColors(); Background()->saveAdjustableColors();
@ -331,7 +373,7 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
} }
} else { } else {
// Looks like it is not a .zip theme. // Looks like it is not a .zip theme.
if (!loadColorScheme(content, out)) { if (!loadColorScheme(content, out, colorizer)) {
return false; return false;
} }
Background()->saveAdjustableColors(); Background()->saveAdjustableColors();
@ -893,7 +935,9 @@ bool ChatBackground::nightMode() const {
return _nightMode; return _nightMode;
} }
void ChatBackground::toggleNightMode(std::optional<QString> themePath) { void ChatBackground::toggleNightMode(
std::optional<QString> themePath,
const Colorizer *colorizer) {
const auto settingDefault = themePath.has_value(); const auto settingDefault = themePath.has_value();
const auto oldNightMode = _nightMode; const auto oldNightMode = _nightMode;
const auto newNightMode = !_nightMode; const auto newNightMode = !_nightMode;
@ -926,7 +970,7 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
path = themePath path = themePath
? *themePath ? *themePath
: (newNightMode ? NightThemePath() : QString()); : (newNightMode ? NightThemePath() : QString());
ApplyDefaultWithPath(path); ApplyDefaultWithPath(path, colorizer);
} }
// Theme editor could have already reverted the testing of this toggle. // Theme editor could have already reverted the testing of this toggle.
@ -1005,9 +1049,11 @@ bool Apply(std::unique_ptr<Preview> preview) {
return true; return true;
} }
void ApplyDefaultWithPath(const QString &themePath) { void ApplyDefaultWithPath(
const QString &themePath,
const Colorizer *colorizer) {
if (!themePath.isEmpty()) { if (!themePath.isEmpty()) {
if (auto preview = PreviewFromFile(themePath)) { if (auto preview = PreviewFromFile(themePath, colorizer)) {
Apply(std::move(preview)); Apply(std::move(preview));
} }
} else { } else {
@ -1094,21 +1140,27 @@ void SetNightModeValue(bool nightMode) {
} }
void ToggleNightMode() { void ToggleNightMode() {
Background()->toggleNightMode(std::nullopt); Background()->toggleNightMode(std::nullopt, nullptr);
} }
void ToggleNightMode(const QString &path) { void ToggleNightMode(
Background()->toggleNightMode(path); const QString &path,
const Colorizer *colorizer) {
Background()->toggleNightMode(path, colorizer);
} }
bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) { bool LoadFromFile(
const QString &path,
Instance *out,
QByteArray *outContent,
const Colorizer *colorizer) {
*outContent = readThemeContent(path); *outContent = readThemeContent(path);
if (outContent->size() < 4) { if (outContent->size() < 4) {
LOG(("Theme Error: Could not load theme from %1").arg(path)); LOG(("Theme Error: Could not load theme from %1").arg(path));
return false; return false;
} }
return loadTheme(*outContent, out->cached, out); return loadTheme(*outContent, out->cached, out, colorizer);
} }
bool IsPaletteTestingPath(const QString &path) { bool IsPaletteTestingPath(const QString &path) {

View File

@ -49,20 +49,35 @@ struct Preview {
QImage preview; QImage preview;
}; };
struct Colorizer {
int wasHue = 0;
int nowHue = 0;
int hueThreshold = 0;
int saturationThreshold = 0;
};
bool Apply(const QString &filepath); bool Apply(const QString &filepath);
bool Apply(std::unique_ptr<Preview> preview); bool Apply(std::unique_ptr<Preview> preview);
void ApplyDefaultWithPath(const QString &themePath); void ApplyDefaultWithPath(
const QString &themePath,
const Colorizer *colorizer = nullptr);
bool ApplyEditedPalette(const QString &path, const QByteArray &content); bool ApplyEditedPalette(const QString &path, const QByteArray &content);
void KeepApplied(); void KeepApplied();
QString NightThemePath(); QString NightThemePath();
[[nodiscard]] bool IsNightMode(); [[nodiscard]] bool IsNightMode();
void SetNightModeValue(bool nightMode); void SetNightModeValue(bool nightMode);
void ToggleNightMode(); void ToggleNightMode();
void ToggleNightMode(const QString &themePath); void ToggleNightMode(
const QString &themePath,
const Colorizer *colorizer = nullptr);
[[nodiscard]] bool IsNonDefaultBackground(); [[nodiscard]] bool IsNonDefaultBackground();
void Revert(); void Revert();
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent); bool LoadFromFile(
const QString &file,
Instance *out,
QByteArray *outContent,
const Colorizer *colorizer = nullptr);
bool IsPaletteTestingPath(const QString &path); bool IsPaletteTestingPath(const QString &path);
QColor CountAverageColor(const QImage &image); QColor CountAverageColor(const QImage &image);
QColor AdjustedColor(QColor original, QColor background); QColor AdjustedColor(QColor original, QColor background);
@ -152,7 +167,9 @@ private:
void setNightModeValue(bool nightMode); void setNightModeValue(bool nightMode);
[[nodiscard]] bool nightMode() const; [[nodiscard]] bool nightMode() const;
void toggleNightMode(std::optional<QString> themePath); void toggleNightMode(
std::optional<QString> themePath,
const Colorizer *colorizer);
void keepApplied(const QString &path, bool write); void keepApplied(const QString &path, bool write);
[[nodiscard]] bool isNonDefaultThemeOrBackground(); [[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground(); [[nodiscard]] bool isNonDefaultBackground();
@ -162,7 +179,9 @@ private:
friend bool IsNightMode(); friend bool IsNightMode();
friend void SetNightModeValue(bool nightMode); friend void SetNightModeValue(bool nightMode);
friend void ToggleNightMode(); friend void ToggleNightMode();
friend void ToggleNightMode(const QString &themePath); friend void ToggleNightMode(
const QString &themePath,
const Colorizer *colorizer);
friend void KeepApplied(); friend void KeepApplied();
friend bool IsNonDefaultBackground(); friend bool IsNonDefaultBackground();

View File

@ -908,7 +908,9 @@ void Generator::restoreTextPalette() {
} // namespace } // namespace
std::unique_ptr<Preview> PreviewFromFile(const QString &filepath) { std::unique_ptr<Preview> PreviewFromFile(
const QString &filepath,
const Colorizer *colorizer) {
auto result = std::make_unique<Preview>(); auto result = std::make_unique<Preview>();
result->pathRelative = filepath.isEmpty() result->pathRelative = filepath.isEmpty()
? QString() ? QString()
@ -916,7 +918,11 @@ std::unique_ptr<Preview> PreviewFromFile(const QString &filepath) {
result->pathAbsolute = filepath.isEmpty() result->pathAbsolute = filepath.isEmpty()
? QString() ? QString()
: QFileInfo(filepath).absoluteFilePath(); : QFileInfo(filepath).absoluteFilePath();
if (!LoadFromFile(filepath, &result->instance, &result->content)) { if (!LoadFromFile(
filepath,
&result->instance,
&result->content,
colorizer)) {
return nullptr; return nullptr;
} }
return result; return result;

View File

@ -18,7 +18,9 @@ struct CurrentData {
bool backgroundTiled = false; bool backgroundTiled = false;
}; };
std::unique_ptr<Preview> PreviewFromFile(const QString &filepath); std::unique_ptr<Preview> PreviewFromFile(
const QString &filepath,
const Colorizer *colorizer = nullptr);
std::unique_ptr<Preview> GeneratePreview( std::unique_ptr<Preview> GeneratePreview(
const QString &filepath, const QString &filepath,
CurrentData &&data); CurrentData &&data);