mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-25 16:54:25 +00:00
Allow changing accent color in default themes.
This commit is contained in:
parent
dd136350fb
commit
9cb5423d40
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user