Add theme variants to switch easily between Light/Dark themes

This commit is contained in:
David Capello 2021-05-22 00:42:36 -03:00
parent e7f0832ff3
commit c99000a2c3
5 changed files with 143 additions and 34 deletions

View File

@ -1,9 +1,9 @@
{ {
"name": "aseprite-theme", "name": "aseprite-theme",
"displayName": "Aseprite Default Theme", "displayName": "Aseprite Default Theme",
"description": "Default Aseprite Pixel-Art Theme", "description": "Default Aseprite Pixel-Art Themes",
"version": "1.0", "version": "1.0",
"author": { "name": "David Capello", "email": "davidcapello@gmail.com", "url": "http://davidcapello.com/" }, "author": { "name": "David Capello", "email": "david@igarastudio.com", "url": "http://davidcapello.com/" },
"contributors": [ "contributors": [
{ "name": "Ilija Melentijevic", "url": "http://ilkke.blogspot.com/" }, { "name": "Ilija Melentijevic", "url": "http://ilkke.blogspot.com/" },
{ "name": "Nicolas Desilets", "url": "https://twitter.com/MapleGecko" } { "name": "Nicolas Desilets", "url": "https://twitter.com/MapleGecko" }
@ -15,8 +15,8 @@
], ],
"contributes": { "contributes": {
"themes": [ "themes": [
{ "id": "default", "path": "." }, { "id": "default", "path": ".", "variant": "Light" },
{ "id": "default-dark", "path": "./dark" } { "id": "default-dark", "path": "./dark", "variant": "Dark" }
] ]
} }
} }

View File

@ -39,7 +39,7 @@
<listitem text="300%" value="3" /> <listitem text="300%" value="3" />
<listitem text="400%" value="4" /> <listitem text="400%" value="4" />
</combobox> </combobox>
<boxfiller /> <hbox id="theme_variants" />
<label text="@.ui_scaling" /> <label text="@.ui_scaling" />
<combobox id="ui_scale"> <combobox id="ui_scale">

View File

@ -110,11 +110,13 @@ class OptionsWindow : public app::gen::Options {
class ThemeItem : public ListItem { class ThemeItem : public ListItem {
public: public:
ThemeItem(const std::string& path, ThemeItem(const std::string& id,
const std::string& name) const std::string& path,
: ListItem(name.empty() ? "-- " + path + " --": name), const std::string& displayName = std::string(),
const std::string& variant = std::string())
: ListItem(createLabel(path, id, displayName, variant)),
m_path(path), m_path(path),
m_name(name) { m_name(id) {
} }
const std::string& themePath() const { return m_path; } const std::string& themePath() const { return m_path; }
@ -129,6 +131,30 @@ class OptionsWindow : public app::gen::Options {
} }
private: private:
static std::string createLabel(const std::string& path,
const std::string& id,
const std::string& displayName,
const std::string& variant) {
if (displayName.empty()) {
if (id.empty())
return fmt::format("-- {} --", path);
else
return id;
}
else if (id == displayName) {
if (variant.empty())
return id;
else
return fmt::format("{} - {}", id, variant);
}
else {
if (variant.empty())
return displayName;
else
return fmt::format("{} - {}", displayName, variant);
}
}
std::string m_path; std::string m_path;
std::string m_name; std::string m_name;
}; };
@ -190,6 +216,24 @@ class OptionsWindow : public app::gen::Options {
Extension* m_extension; Extension* m_extension;
}; };
class ThemeVariantItem : public ButtonSet::Item {
public:
ThemeVariantItem(OptionsWindow* options,
const std::string& id,
const std::string& variant)
: m_options(options)
, m_themeId(id) {
setText(variant);
}
private:
void onClick() override {
m_options->setUITheme(m_themeId, true,
false); // Don't recreate variants
}
OptionsWindow* m_options;
std::string m_themeId;
};
public: public:
OptionsWindow(Context* context, int& curSection) OptionsWindow(Context* context, int& curSection)
: m_context(context) : m_context(context)
@ -204,6 +248,9 @@ public:
{ {
sectionListbox()->Change.connect([this]{ onChangeSection(); }); sectionListbox()->Change.connect([this]{ onChangeSection(); });
// Theme variants
fillThemeVariants();
// Default extension to save files // Default extension to save files
fillExtensionsCombobox(defaultExtension(), m_pref.saveFile.defaultExtension()); fillExtensionsCombobox(defaultExtension(), m_pref.saveFile.defaultExtension());
fillExtensionsCombobox(exportImageDefaultExtension(), m_pref.exportFile.imageDefaultExtension()); fillExtensionsCombobox(exportImageDefaultExtension(), m_pref.exportFile.imageDefaultExtension());
@ -823,6 +870,42 @@ public:
private: private:
void fillThemeVariants() {
ButtonSet* list = nullptr;
for (Extension* ext : App::instance()->extensions()) {
if (ext->isCurrentTheme()) {
// Number of variants
int c = 0;
for (auto it : ext->themes()) {
if (!it.second.variant.empty())
++c;
}
if (c >= 2) {
list = new ButtonSet(c);
for (auto it : ext->themes()) {
if (!it.second.variant.empty()) {
auto item = list->addItem(
new ThemeVariantItem(this, it.first, it.second.variant));
if (it.first == Preferences::instance().theme.selected())
list->setSelectedItem(item, false);
}
}
}
break;
}
}
if (list) {
themeVariants()->addChild(list);
}
if (m_themeVars) {
themeVariants()->removeChild(m_themeVars);
m_themeVars->deferDelete();
}
m_themeVars = list;
}
void fillExtensionsCombobox(ui::ComboBox* combobox, void fillExtensionsCombobox(ui::ComboBox* combobox,
const std::string& defExt) { const std::string& defExt) {
base::paths exts = get_writable_extensions(); base::paths exts = get_writable_extensions();
@ -1196,7 +1279,7 @@ private:
new SeparatorInView(base::normalize_path(path), HORIZONTAL)); new SeparatorInView(base::normalize_path(path), HORIZONTAL));
} }
ThemeItem* item = new ThemeItem(fullPath, fn); ThemeItem* item = new ThemeItem(fn, fullPath);
themeList()->addChild(item); themeList()->addChild(item);
// Selected theme // Selected theme
@ -1221,11 +1304,14 @@ private:
} }
for (auto it : ext->themes()) { for (auto it : ext->themes()) {
ThemeItem* item = new ThemeItem(it.second, it.first); ThemeItem* item = new ThemeItem(it.first,
it.second.path,
ext->displayName(),
it.second.variant);
themeList()->addChild(item); themeList()->addChild(item);
// Selected theme // Selected theme
if (it.second == selectedPath) if (it.second.path == selectedPath)
themeList()->selectChild(item); themeList()->selectChild(item);
} }
} }
@ -1296,7 +1382,8 @@ private:
} }
void setUITheme(const std::string& themeName, void setUITheme(const std::string& themeName,
const bool updateScaling) { const bool updateScaling,
const bool recreateVariantsFields = true) {
try { try {
if (themeName != m_pref.theme.selected()) { if (themeName != m_pref.theme.selected()) {
auto theme = static_cast<skin::SkinTheme*>(ui::get_theme()); auto theme = static_cast<skin::SkinTheme*>(ui::get_theme());
@ -1343,6 +1430,9 @@ private:
selectScalingItems(); selectScalingItems();
} }
} }
if (recreateVariantsFields)
fillThemeVariants();
} }
} }
catch (const std::exception& ex) { catch (const std::exception& ex) {
@ -1618,6 +1708,7 @@ private:
std::vector<os::ColorSpaceRef> m_colorSpaces; std::vector<os::ColorSpaceRef> m_colorSpaces;
std::string m_templateTextForDisplayCS; std::string m_templateTextForDisplayCS;
RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector; RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector;
ButtonSet* m_themeVars = nullptr;
}; };
class OptionsCommand : public Command { class OptionsCommand : public Command {

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -263,9 +263,9 @@ void Extension::addLanguage(const std::string& id, const std::string& path)
updateCategory(Category::Languages); updateCategory(Category::Languages);
} }
void Extension::addTheme(const std::string& id, const std::string& path) void Extension::addTheme(const std::string& id, const std::string& path, const std::string& variant)
{ {
m_themes[id] = path; m_themes[id] = ThemeInfo(path, variant);
updateCategory(Category::Themes); updateCategory(Category::Themes);
} }
@ -789,7 +789,7 @@ std::string Extensions::themePath(const std::string& themeId)
auto it = ext->themes().find(themeId); auto it = ext->themes().find(themeId);
if (it != ext->themes().end()) if (it != ext->themes().end())
return it->second; return it->second.path;
} }
return std::string(); return std::string();
} }
@ -1026,7 +1026,7 @@ Extension* Extensions::loadExtension(const std::string& path,
// The path must be always relative to the extension // The path must be always relative to the extension
langPath = base::join_path(path, langPath); langPath = base::join_path(path, langPath);
LOG("EXT: New language '%s' in '%s'\n", LOG("EXT: New language id=%s path=%s\n",
langId.c_str(), langId.c_str(),
langPath.c_str()); langPath.c_str());
@ -1040,15 +1040,17 @@ Extension* Extensions::loadExtension(const std::string& path,
for (const auto& theme : themes.array_items()) { for (const auto& theme : themes.array_items()) {
std::string themeId = theme["id"].string_value(); std::string themeId = theme["id"].string_value();
std::string themePath = theme["path"].string_value(); std::string themePath = theme["path"].string_value();
std::string themeVariant = theme["variant"].string_value();
// The path must be always relative to the extension // The path must be always relative to the extension
themePath = base::join_path(path, themePath); themePath = base::join_path(path, themePath);
LOG("EXT: New theme '%s' in '%s'\n", LOG("EXT: New theme id=%s path=%s variant=%s\n",
themeId.c_str(), themeId.c_str(),
themePath.c_str()); themePath.c_str(),
themeVariant.c_str());
extension->addTheme(themeId, themePath); extension->addTheme(themeId, themePath, themeVariant);
} }
} }
@ -1062,7 +1064,7 @@ Extension* Extensions::loadExtension(const std::string& path,
// The path must be always relative to the extension // The path must be always relative to the extension
palPath = base::join_path(path, palPath); palPath = base::join_path(path, palPath);
LOG("EXT: New palette '%s' in '%s'\n", LOG("EXT: New palette id=%s path=%s\n",
palId.c_str(), palId.c_str(),
palPath.c_str()); palPath.c_str());
@ -1083,7 +1085,7 @@ Extension* Extensions::loadExtension(const std::string& path,
// The path must be always relative to the extension // The path must be always relative to the extension
matPath = base::join_path(path, matPath); matPath = base::join_path(path, matPath);
LOG("EXT: New dithering matrix '%s' in '%s'\n", LOG("EXT: New dithering matrix id=%s path=%s\n",
matId.c_str(), matId.c_str(),
matPath.c_str()); matPath.c_str());
@ -1103,7 +1105,7 @@ Extension* Extensions::loadExtension(const std::string& path,
// The path must be always relative to the extension // The path must be always relative to the extension
scriptPath = base::join_path(path, scriptPath); scriptPath = base::join_path(path, scriptPath);
LOG("EXT: New script '%s'\n", scriptPath.c_str()); LOG("EXT: New script path=%s\n", scriptPath.c_str());
extension->addScript(scriptPath); extension->addScript(scriptPath);
} }
@ -1116,7 +1118,7 @@ Extension* Extensions::loadExtension(const std::string& path,
// The path must be always relative to the extension // The path must be always relative to the extension
scriptPath = base::join_path(path, scriptPath); scriptPath = base::join_path(path, scriptPath);
LOG("EXT: New script '%s'\n", scriptPath.c_str()); LOG("EXT: New script path=%s\n", scriptPath.c_str());
extension->addScript(scriptPath); extension->addScript(scriptPath);
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -18,9 +18,9 @@
namespace app { namespace app {
// Key=theme/palette/etc. id // Key=id
// Value=theme/palette/etc. path // Value=path
typedef std::map<std::string, std::string> ExtensionItems; using ExtensionItems = std::map<std::string, std::string>;
class Extensions; class Extensions;
@ -34,6 +34,7 @@ namespace app {
class Extension { class Extension {
friend class Extensions; friend class Extensions;
public: public:
enum class Category { enum class Category {
None, None,
Languages, Languages,
@ -61,6 +62,20 @@ namespace app {
mutable bool m_loaded = false; mutable bool m_loaded = false;
}; };
struct ThemeInfo {
std::string path;
std::string variant;
ThemeInfo() = default;
ThemeInfo(const std::string& path,
const std::string& variant)
: path(path)
, variant(variant) { }
};
using Themes = std::map<std::string, ThemeInfo>;
using DitheringMatrices = std::map<std::string, DitheringMatrixInfo>;
Extension(const std::string& path, Extension(const std::string& path,
const std::string& name, const std::string& name,
const std::string& version, const std::string& version,
@ -79,11 +94,11 @@ namespace app {
const Category category() const { return m_category; } const Category category() const { return m_category; }
const ExtensionItems& languages() const { return m_languages; } const ExtensionItems& languages() const { return m_languages; }
const ExtensionItems& themes() const { return m_themes; } const Themes& themes() const { return m_themes; }
const ExtensionItems& palettes() const { return m_palettes; } const ExtensionItems& palettes() const { return m_palettes; }
void addLanguage(const std::string& id, const std::string& path); void addLanguage(const std::string& id, const std::string& path);
void addTheme(const std::string& id, const std::string& path); void addTheme(const std::string& id, const std::string& path, const std::string& variant);
void addPalette(const std::string& id, const std::string& path); void addPalette(const std::string& id, const std::string& path);
void addDitheringMatrix(const std::string& id, void addDitheringMatrix(const std::string& id,
const std::string& path, const std::string& path,
@ -107,11 +122,12 @@ namespace app {
void addScript(const std::string& fn); void addScript(const std::string& fn);
#endif #endif
bool isCurrentTheme() const;
private: private:
void enable(const bool state); void enable(const bool state);
void uninstall(); void uninstall();
void uninstallFiles(const std::string& path); void uninstallFiles(const std::string& path);
bool isCurrentTheme() const;
bool isDefaultTheme() const; bool isDefaultTheme() const;
void updateCategory(const Category newCategory); void updateCategory(const Category newCategory);
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
@ -120,9 +136,9 @@ namespace app {
#endif #endif
ExtensionItems m_languages; ExtensionItems m_languages;
ExtensionItems m_themes; Themes m_themes;
ExtensionItems m_palettes; ExtensionItems m_palettes;
std::map<std::string, DitheringMatrixInfo> m_ditheringMatrices; DitheringMatrices m_ditheringMatrices;
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
struct ScriptItem { struct ScriptItem {