Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to select custom themes in the settings dialog #4570

Merged
merged 9 commits into from
May 19, 2023

Conversation

pajlada
Copy link
Member

@pajlada pajlada commented Apr 21, 2023

Description

This loads themes (any .json file) from the /Themes directory on startup and lists those in the settings page.
The custom themes in that directory will have a Custom: prefix to show that they're custom.
In the settingsfile, we save the name of the theme (e.g. 'forsen', no more info).

When the theme is updated or on Chatterino startup, we find the best matching theme with that name, whether it's a built in theme or a custom theme.

There's currently no mechanism for making a theme that has the same name as a built in theme, and they still show up in the menu. IMO we should just not allow duplicate names for simplicity's sake.

There's a few things left to do, and a few things to discuss before taking this step, even though I still deem it to be "pre-production" even when this PR is merged in.

The changelog entry is left as a Dev entry on purpose since I don't think this is a feature that should be fully announced until there's more testing & better UX for users.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

src/singletons/Theme.cpp Show resolved Hide resolved
src/singletons/Theme.cpp Show resolved Hide resolved
}

} // namespace

namespace chatterino {

const std::map<QString, ThemeDescriptor> Theme::builtInThemes{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for global constant 'builtInThemes' [readability-identifier-naming]

Suggested change
const std::map<QString, ThemeDescriptor> Theme::builtInThemes{
const std::map<QString, ThemeDescriptor> Theme::BUILT_IN_THEMES{

src/singletons/Theme.cpp Show resolved Hide resolved
src/singletons/Theme.cpp Show resolved Hide resolved
{
QFile file(getThemePath(this->themeName));
if (!file.open(QFile::ReadOnly))
QStringList themeNames;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'themeNames' is not initialized [cppcoreguidelines-init-variables]

Suggested change
QStringList themeNames;
QStringList themeNames = 0;

namespace chatterino {

class WindowManager;

struct ThemeDescriptor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: path, custom [cppcoreguidelines-pro-type-member-init]

src/singletons/Theme.hpp:21:

-     QString path;
+     QString path{};

src/singletons/Theme.hpp:23:

-     bool custom;
+     bool custom{};

class Theme final : public Singleton
{
public:
Theme();
static const std::map<QString, ThemeDescriptor> builtInThemes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for global constant 'builtInThemes' [readability-identifier-naming]

Suggested change
static const std::map<QString, ThemeDescriptor> builtInThemes;
static const std::map<QString, ThemeDescriptor> BUILT_IN_THEMES;

static const std::map<QString, ThemeDescriptor> builtInThemes;

// The built in theme that will be used if some theme parsing fails
static const ThemeDescriptor fallbackTheme;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for global constant 'fallbackTheme' [readability-identifier-naming]

Suggested change
static const ThemeDescriptor fallbackTheme;
static const ThemeDescriptor FALLBACK_THEME;

src/widgets/settingspages/GeneralPage.cpp Outdated Show resolved Hide resolved
@dnsge
Copy link
Contributor

dnsge commented May 1, 2023

It'd be great if there was hot reloading of the current theme when the JSON file is modified. This would make creating themes and trying out different colors easy (compared to what I imagine is having to restart Chatterino after every change).

There appears to be a Qt class that would make detecting the file change easy (in theory, barring os-specific annoyances): https://doc.qt.io/qt-6/qfilesystemwatcher.html. I haven't dug through the Theme code that much though, so it's possible that this is more difficult than it sounds.

@pajlada
Copy link
Member Author

pajlada commented May 1, 2023

It'd be great if there was hot reloading of the current theme when the JSON file is modified. This would make creating themes and trying out different colors easy (compared to what I imagine is having to restart Chatterino after every change).

I would like that type of functionality in a V2/V3 kind of thing instead of in the initial release.
Initially I'd like this in a nightly release without any commitments to how/where the themes are located so it can be tested, then we look into specifics for how themes are worked on by users.

I could see a world where theme creation isn't tested in Chatterino itself, but rather a web UI. Or there's a /reload-theme command to do the reloading.

@Nerixyz
Copy link
Contributor

Nerixyz commented May 1, 2023

There appears to be a Qt class that would make detecting the file change easy (in theory, barring os-specific annoyances): https://doc.qt.io/qt-6/qfilesystemwatcher.html. I haven't dug through the Theme code that much though, so it's possible that this is more difficult than it sounds.

This should be the way to go. I had a small implementation that used the file system watcher, and it worked well (Windows & VSCode).

These are only loaded on startup
To make this hot reload, we'd need a file watcher
or have it just refresh any time the settings page is opened
@pajlada pajlada marked this pull request as ready for review May 19, 2023 11:45
@pajlada
Copy link
Member Author

pajlada commented May 19, 2023

image

@pajlada pajlada enabled auto-merge (squash) May 19, 2023 11:47
@pajlada pajlada merged commit 5d0bdc1 into master May 19, 2023
@pajlada pajlada deleted the feat/custom-themes-v2 branch May 19, 2023 12:26
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

}

} // namespace

namespace chatterino {

const std::vector<ThemeDescriptor> Theme::builtInThemes{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for global constant 'builtInThemes' [readability-identifier-naming]

Suggested change
const std::vector<ThemeDescriptor> Theme::builtInThemes{
const std::vector<ThemeDescriptor> Theme::BUILT_IN_THEMES{

{
QFile file(getThemePath(this->themeName));
if (!file.open(QFile::ReadOnly))
std::vector<std::pair<QString, QVariant>> packagedThemes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'packagedThemes' is not initialized [cppcoreguidelines-init-variables]

Suggested change
std::vector<std::pair<QString, QVariant>> packagedThemes;
std::vector<std::pair<QString, QVariant>> packagedThemes = 0;

}
}

std::optional<ThemeDescriptor> Theme::findThemeByKey(const QString &key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: method 'findThemeByKey' can be made static [readability-convert-member-functions-to-static]

Suggested change
std::optional<ThemeDescriptor> Theme::findThemeByKey(const QString &key)
static std::optional<ThemeDescriptor> Theme::findThemeByKey(const QString &key)


namespace chatterino {

class WindowManager;

struct ThemeDescriptor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: key, path, name [cppcoreguidelines-pro-type-member-init]

src/singletons/Theme.hpp:21:

-     QString key;
+     QString key{};

src/singletons/Theme.hpp:25:

-     QString path;
+     QString path{};

src/singletons/Theme.hpp:28:

-     QString name;
+     QString name{};

class Theme final : public Singleton
{
public:
Theme();
static const std::vector<ThemeDescriptor> builtInThemes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for global constant 'builtInThemes' [readability-identifier-naming]

Suggested change
static const std::vector<ThemeDescriptor> builtInThemes;
static const std::vector<ThemeDescriptor> BUILT_IN_THEMES;

template <typename T>
ComboBox *addDropdown(
const QString &text,
const std::vector<std::pair<QString, QVariant>> &items,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: all parameters should be named in a function [readability-named-parameter]

Suggested change
const std::vector<std::pair<QString, QVariant>> &items,
const std::vector<std::pair<QString, QVariant> /*unused*/> &items,

std::function<T(DropdownArgs)> setValue, QString toolTipText = {},
const QString &defaultValueText = {})
{
auto *combo = this->addDropdown(text, {}, std::move(toolTipText));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'combo' is not initialized [cppcoreguidelines-init-variables]

Suggested change
auto *combo = this->addDropdown(text, {}, std::move(toolTipText));
auto *combo = nullptr = this->addDropdown(text, {}, std::move(toolTipText));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants