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/download_path_box.h"
#include "boxes/local_storage_box.h"
#include "boxes/edit_color_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/input_fields.h"
@ -89,6 +90,7 @@ public:
QColor radiobuttonActive;
tr::phrase<> name;
QString path;
QColor accentColor;
};
DefaultTheme(Scheme scheme, bool checked);
@ -801,7 +803,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("d7f0ff"),
color("ffffff"),
tr::lng_settings_theme_blue,
":/gui/day-blue.tdesktop-theme"
":/gui/day-blue.tdesktop-theme",
color("40a7e3")
},
Scheme{
Type::Default,
@ -821,7 +824,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("6b808d"),
color("5ca7d4"),
tr::lng_settings_theme_midnight,
":/gui/night.tdesktop-theme"
":/gui/night.tdesktop-theme",
color("5288c1")
},
Scheme{
Type::NightGreen,
@ -831,7 +835,8 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
color("6b808d"),
color("74bf93"),
tr::lng_settings_theme_matrix,
":/gui/night-green.tdesktop-theme"
":/gui/night-green.tdesktop-theme",
color("3fc1b0")
},
};
const auto chosen = [&] {
@ -847,6 +852,47 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
return Type(-1);
};
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(
schemes
) | ranges::view::transform([&](const Scheme &scheme) {
@ -859,34 +905,14 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
scheme.name(tr::now),
st::settingsTheme,
std::move(check));
result->addClickHandler([=] {
schemeClicked(scheme, result->clickModifiers());
});
weak->setUpdateCallback([=] { result->update(); });
return result;
}) | ranges::to_vector;
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(
*Window::Theme::Background()
) | rpl::filter([](const Update &update) {

View File

@ -143,12 +143,44 @@ bool readNameAndValue(const char *&from, const char *end, QLatin1String *outName
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 {
Ok,
Bad,
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 size = value.size();
auto data = value.data();
@ -158,6 +190,9 @@ SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance
auto g = readHexUchar(data[3], data[4], error);
auto b = readHexUchar(data[5], data[6], error);
auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255);
if (colorizer) {
Colorize(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));
return SetResult::Ok;
@ -189,13 +224,16 @@ SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance
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>();
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.
value = unsupported.value(value, value);
auto result = setColorSchemeValue(name, value, out);
auto result = setColorSchemeValue(name, value, out, colorizer);
if (result == SetResult::Bad) {
return false;
} else if (result == SetResult::NotFound) {
@ -279,7 +317,11 @@ bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *out
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();
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."));
return false;
}
if (!loadColorScheme(schemeContent, out)) {
if (!loadColorScheme(schemeContent, out, colorizer)) {
return false;
}
Background()->saveAdjustableColors();
@ -331,7 +373,7 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
}
} else {
// Looks like it is not a .zip theme.
if (!loadColorScheme(content, out)) {
if (!loadColorScheme(content, out, colorizer)) {
return false;
}
Background()->saveAdjustableColors();
@ -893,7 +935,9 @@ bool ChatBackground::nightMode() const {
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 oldNightMode = _nightMode;
const auto newNightMode = !_nightMode;
@ -926,7 +970,7 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
path = themePath
? *themePath
: (newNightMode ? NightThemePath() : QString());
ApplyDefaultWithPath(path);
ApplyDefaultWithPath(path, colorizer);
}
// Theme editor could have already reverted the testing of this toggle.
@ -1005,9 +1049,11 @@ bool Apply(std::unique_ptr<Preview> preview) {
return true;
}
void ApplyDefaultWithPath(const QString &themePath) {
void ApplyDefaultWithPath(
const QString &themePath,
const Colorizer *colorizer) {
if (!themePath.isEmpty()) {
if (auto preview = PreviewFromFile(themePath)) {
if (auto preview = PreviewFromFile(themePath, colorizer)) {
Apply(std::move(preview));
}
} else {
@ -1094,21 +1140,27 @@ void SetNightModeValue(bool nightMode) {
}
void ToggleNightMode() {
Background()->toggleNightMode(std::nullopt);
Background()->toggleNightMode(std::nullopt, nullptr);
}
void ToggleNightMode(const QString &path) {
Background()->toggleNightMode(path);
void ToggleNightMode(
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);
if (outContent->size() < 4) {
LOG(("Theme Error: Could not load theme from %1").arg(path));
return false;
}
return loadTheme(*outContent, out->cached, out);
return loadTheme(*outContent, out->cached, out, colorizer);
}
bool IsPaletteTestingPath(const QString &path) {

View File

@ -49,20 +49,35 @@ struct Preview {
QImage preview;
};
struct Colorizer {
int wasHue = 0;
int nowHue = 0;
int hueThreshold = 0;
int saturationThreshold = 0;
};
bool Apply(const QString &filepath);
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);
void KeepApplied();
QString NightThemePath();
[[nodiscard]] bool IsNightMode();
void SetNightModeValue(bool nightMode);
void ToggleNightMode();
void ToggleNightMode(const QString &themePath);
void ToggleNightMode(
const QString &themePath,
const Colorizer *colorizer = nullptr);
[[nodiscard]] bool IsNonDefaultBackground();
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);
QColor CountAverageColor(const QImage &image);
QColor AdjustedColor(QColor original, QColor background);
@ -152,7 +167,9 @@ private:
void setNightModeValue(bool nightMode);
[[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);
[[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground();
@ -162,7 +179,9 @@ private:
friend bool IsNightMode();
friend void SetNightModeValue(bool nightMode);
friend void ToggleNightMode();
friend void ToggleNightMode(const QString &themePath);
friend void ToggleNightMode(
const QString &themePath,
const Colorizer *colorizer);
friend void KeepApplied();
friend bool IsNonDefaultBackground();

View File

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

View File

@ -18,7 +18,9 @@ struct CurrentData {
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(
const QString &filepath,
CurrentData &&data);