BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit fdf84b0d27d706b2b2bd6b3a01a797ea3e2fe284
parent ef5f6a04af4bd35de03a5565a69b75b2662b2fcf
Author: Matt Demanett <matt@demanett.net>
Date:   Sun,  9 Aug 2020 00:11:05 -0400

Add context-menu items to set the default skins, and rename menu item to "Panel".

Diffstat:
Msrc/menu.cpp | 32+++++++++++++++++++++++++++++---
Msrc/menu.hpp | 21+++++++++------------
Msrc/module.cpp | 48++++++++++++++++++++++++++++++++++++++++++------
Msrc/module.hpp | 7++++++-
Msrc/skins.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/skins.hpp | 16++++++++++++++--
6 files changed, 147 insertions(+), 26 deletions(-)

diff --git a/src/menu.cpp b/src/menu.cpp @@ -5,6 +5,27 @@ using namespace bogaudio; #define SUBMENUS 1 +void OptionsMenuItem::addItem(const OptionMenuItem& item) { + _items.push_back(item); +} + +void OptionsMenuItem::addSpacer() { + _items.push_back(SpacerOptionMenuItem()); +} + +Menu* OptionsMenuItem::createChildMenu() { + Menu* menu = new Menu; + for (const OptionMenuItem& item : _items) { + if (item.text == "<spacer>") { + menu->addChild(new MenuLabel()); + } + else { + menu->addChild(new OptionMenuItem(item)); + } + } + return menu; +} + void OptionsMenuItem::addToMenu(OptionsMenuItem* item, Menu* menu) { // one way or another, this will cause item to eventually be deleted; call only one per item. #ifdef SUBMENUS @@ -14,9 +35,14 @@ void OptionsMenuItem::addToMenu(OptionsMenuItem* item, Menu* menu) { label += ":"; menu->addChild(createMenuLabel(label.c_str())); for (const OptionMenuItem& omi : item->_items) { - auto nomi = new OptionMenuItem(omi); - nomi->text = std::string("\t") + nomi->text; - menu->addChild(nomi); + if (omi.text == "<spacer>") { + menu->addChild(new MenuLabel()); + } + else { + auto nomi = new OptionMenuItem(omi); + nomi->text = std::string("\t") + nomi->text; + menu->addChild(nomi); + } } delete item; #endif diff --git a/src/menu.hpp b/src/menu.hpp @@ -34,6 +34,12 @@ struct BoolOptionMenuItem : OptionMenuItem { {} }; +struct SpacerOptionMenuItem : OptionMenuItem { + SpacerOptionMenuItem() + : OptionMenuItem("<spacer>", []() { return false; }, []() {}) + {} +}; + struct OptionsMenuItem : MenuItem { std::vector<OptionMenuItem> _items; @@ -42,18 +48,9 @@ struct OptionsMenuItem : MenuItem { this->rightText = "▸"; } - void addItem(const OptionMenuItem& item) { - _items.push_back(item); - } - - Menu* createChildMenu() override { - Menu* menu = new Menu; - for (const OptionMenuItem& item : _items) { - menu->addChild(new OptionMenuItem(item)); - } - return menu; - } - + void addItem(const OptionMenuItem& item); + void addSpacer(); + Menu* createChildMenu() override; static void addToMenu(OptionsMenuItem* item, Menu* menu); }; diff --git a/src/module.cpp b/src/module.cpp @@ -1,7 +1,6 @@ #include "module.hpp" #include "bogaudio.hpp" -#include "skins.hpp" using namespace bogaudio; @@ -101,6 +100,14 @@ void BGModule::addSkinChangeListener(SkinChangeListener* listener) { } +BGModuleWidget::BGModuleWidget() { + Skins::skins().registerDefaultSkinChangeListener(this); +} + +BGModuleWidget::~BGModuleWidget() { + Skins::skins().deregisterDefaultSkinChangeListener(this); +} + void BGModuleWidget::addParam(ParamWidget* param) { ModuleWidget::addParam(param); if (module) { @@ -141,15 +148,31 @@ void BGModuleWidget::appendContextMenu(Menu* menu) { auto m = dynamic_cast<BGModule*>(module); assert(m); if (m->_skinnable) { - auto skins = Skins::skins().available(); - if (skins.size() > 0) { + Skins* skins = &Skins::skins(); + if (skins->available().size() > 0) { // menu->addChild(new MenuLabel()); - OptionsMenuItem* s = new OptionsMenuItem("Skin"); + OptionsMenuItem* s = new OptionsMenuItem("Panel"); + s->addItem(OptionMenuItem("Default", [m]() { return m->_skin == "default"; }, [m]() { m->setSkin("default"); })); - for (auto skin : skins) { + for (auto skin : skins->available()) { std::string key = skin.key; - s->addItem(OptionMenuItem(skin.display.c_str(), [m, key]() { return m->_skin == key; }, [m, key]() { m->setSkin(key); })); + s->addItem(OptionMenuItem( + skin.display.c_str(), + [m, key]() { return m->_skin == key; }, + [m, key]() { m->setSkin(key); } + )); } + + s->addSpacer(); + for (auto skin : skins->available()) { + std::string key = skin.key; + s->addItem(OptionMenuItem( + (std::string("Default to ") + skin.display).c_str(), + [key, skins]() { return skins->defaultKey() == key; }, + [key, skins]() { skins->setDefaultSkin(key); } + )); + } + OptionsMenuItem::addToMenu(s, menu); } } @@ -161,6 +184,19 @@ void BGModuleWidget::skinChanged(const std::string& skin) { updatePanel(); } +void BGModuleWidget::defaultSkinChanged(const std::string& skin) { + if (module) { + auto m = dynamic_cast<BGModule*>(module); + assert(m); + if (m->_skin == "default") { + m->setSkin("default"); + } + } + else { + updatePanel(); + } +} + void BGModuleWidget::setPanel(Vec size, std::string slug, bool skinnable) { _size = size; _slug = slug; diff --git a/src/module.hpp b/src/module.hpp @@ -1,6 +1,7 @@ #pragma once #include "rack.hpp" +#include "skins.hpp" #include <string> #include <vector> @@ -60,12 +61,15 @@ struct BGModule : Module { void addSkinChangeListener(SkinChangeListener* listener); }; -struct BGModuleWidget : ModuleWidget, SkinChangeListener { +struct BGModuleWidget : ModuleWidget, SkinChangeListener, DefaultSkinChangeListener { bool _skinnable = true; SvgPanel* _panel = NULL; Vec _size; std::string _slug; + BGModuleWidget(); + virtual ~BGModuleWidget(); + void appendContextMenu(Menu* menu) override; void addParam(ParamWidget* param); void addInput(PortWidget* input); @@ -74,6 +78,7 @@ struct BGModuleWidget : ModuleWidget, SkinChangeListener { virtual void contextMenu(Menu* menu) {} void skinChanged(const std::string& skin) override; + void defaultSkinChanged(const std::string& skin) override; void setPanel(Vec size, const std::string slug, bool skinnable = true); void updatePanel(); void createScrews(); diff --git a/src/skins.cpp b/src/skins.cpp @@ -2,10 +2,12 @@ #include "skins.hpp" #include "bogaudio.hpp" #include <unistd.h> +#include <fstream> +#include <cstdio> -const Skins& Skins::skins() { +Skins& Skins::skins() { static Skins instance; - std::lock_guard<std::mutex> lock(instance._lock); + std::lock_guard<std::mutex> lock(instance._instanceLock); if (!instance._loaded) { instance.loadSkins(); instance.loadCssValues(); @@ -95,6 +97,49 @@ NVGcolor Skins::cssColorToNVGColor(const char* color, const NVGcolor& ifError) { return ifError; } +void Skins::setDefaultSkin(std::string skinKey) { + if (skinKey == "default") { + skinKey = "light"; + } + std::string path = rack::asset::user("Bogaudio.json"); + std::string error; + if (!validKey(skinKey)) { + error = "invalid key: " + skinKey; + } + else { + std::ofstream f(path); + f << "{\n \"skins\": {\n \"default\": \""; + f << skinKey; + f << "\"\n }\n}\n"; + if (!f) { + error = "error writing \"" + path + "\": " + strerror(errno); + } + } + + if (error.size() > 0) { + WARN("Bogaudio: setting default skin: %s\n", error.c_str()); + } + else { + _default = skinKey; + INFO("Bogaudio: skin information written to %s\n", path.c_str()); + + std::lock_guard<std::mutex> lock(_defaultSkinListenersLock); + for (auto listener : _defaultSkinListeners) { + listener->defaultSkinChanged(_default); + } + } +} + +void Skins::registerDefaultSkinChangeListener(DefaultSkinChangeListener* listener) { + std::lock_guard<std::mutex> lock(_defaultSkinListenersLock); + _defaultSkinListeners.insert(listener); +} + +void Skins::deregisterDefaultSkinChangeListener(DefaultSkinChangeListener* listener) { + std::lock_guard<std::mutex> lock(_defaultSkinListenersLock); + _defaultSkinListeners.erase(listener); +} + void Skins::loadSkins() { _available.push_back(Skin("light", "Light")); _available.push_back(Skin("dark", "Dark")); diff --git a/src/skins.hpp b/src/skins.hpp @@ -4,8 +4,13 @@ #include <mutex> #include <string> #include <unordered_map> +#include <unordered_set> #include <vector> +struct DefaultSkinChangeListener { + virtual void defaultSkinChanged(const std::string& skin) = 0; +}; + struct Skin { std::string key; std::string display; @@ -17,24 +22,31 @@ class Skins { private: typedef std::unordered_map<std::string, std::string> css_values_map; typedef std::unordered_map<std::string, css_values_map> skin_css_values_map; + typedef std::unordered_set<DefaultSkinChangeListener*> default_skin_listeners_set; std::vector<Skin> _available; std::string _default; skin_css_values_map _skinCssValues; + default_skin_listeners_set _defaultSkinListeners; + std::mutex _defaultSkinListenersLock; bool _loaded = false; - std::mutex _lock; + std::mutex _instanceLock; public: Skins() {} Skins(const Skins&) = delete; void operator=(const Skins&) = delete; - static const Skins& skins(); + static Skins& skins(); inline const std::vector<Skin>& available() const { return _available; } inline const std::string& defaultKey() const { return _default; } bool validKey(const std::string& key) const; const char* skinCssValue(const std::string& skinKey, const std::string& valueKey) const; static NVGcolor cssColorToNVGColor(const char* color, const NVGcolor& ifError); + void setDefaultSkin(std::string skinKey); + void registerDefaultSkinChangeListener(DefaultSkinChangeListener* listener); + void deregisterDefaultSkinChangeListener(DefaultSkinChangeListener* listener); + private: void loadSkins(); void loadCssValues();