diff --git a/include/app/SvgPanel.hpp b/include/app/SvgPanel.hpp index 742dd19a..06ccf712 100644 --- a/include/app/SvgPanel.hpp +++ b/include/app/SvgPanel.hpp @@ -30,5 +30,14 @@ struct SvgPanel : widget::Widget { DEPRECATED typedef SvgPanel SVGPanel; +struct ThemedSvgPanel : SvgPanel { + std::shared_ptr lightSvg; + std::shared_ptr darkSvg; + + void step() override; + void setBackground(std::shared_ptr lightSvg, std::shared_ptr darkSvg); +}; + + } // namespace app } // namespace rack diff --git a/include/app/SvgScrew.hpp b/include/app/SvgScrew.hpp index 2e85690f..d3cb5d6f 100644 --- a/include/app/SvgScrew.hpp +++ b/include/app/SvgScrew.hpp @@ -22,5 +22,15 @@ struct SvgScrew : widget::Widget { DEPRECATED typedef SvgScrew SVGScrew; +struct ThemedSvgScrew : SvgScrew { + std::shared_ptr lightSvg; + std::shared_ptr darkSvg; + + void step() override; + void setSvg(std::shared_ptr lightSvg, std::shared_ptr darkSvg); +}; + + + } // namespace app } // namespace rack diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index e32478b6..2ac524c5 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -984,6 +984,13 @@ struct ScrewBlack : app::SvgScrew { } }; +struct ThemedScrew : app::ThemedSvgScrew { + ThemedScrew() { + setSvg(Svg::load(asset::system("res/ComponentLibrary/ScrewSilver.svg")), Svg::load(asset::system("res/ComponentLibrary/ScrewBlack.svg"))); + } +}; + + struct SegmentDisplay : widget::Widget { int lightsLen = 0; bool vertical = false; diff --git a/include/helpers.hpp b/include/helpers.hpp index e2b59a58..3a02eff8 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -65,13 +65,24 @@ TWidget* createWidgetCentered(math::Vec pos) { } -inline app::SvgPanel* createPanel(std::string svgPath) { - app::SvgPanel* panel = new app::SvgPanel; +/** Creates an SvgPanel and loads the SVG from the given path. */ +template +TPanel* createPanel(std::string svgPath) { + TPanel* panel = new TPanel; panel->setBackground(window::Svg::load(svgPath)); return panel; } +/** Creates a ThemedSvgPanel and loads the light/dark SVGs from the given paths. */ +template +TPanel* createPanel(std::string lightSvgPath, std::string darkSvgPath) { + TPanel* panel = new TPanel; + panel->setBackground(window::Svg::load(lightSvgPath), window::Svg::load(darkSvgPath)); + return panel; +} + + template TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { TParamWidget* o = new TParamWidget; diff --git a/include/settings.hpp b/include/settings.hpp index d2131a65..7c46dd26 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -69,6 +69,7 @@ extern bool tooltips; extern bool cpuMeter; extern bool lockModules; extern bool squeezeModules; +extern bool preferDarkPanels; /** Maximum screen redraw frequency in Hz, or 0 for unlimited. */ extern float frameRateLimit; /** Interval between autosaves in seconds. */ diff --git a/src/app/MenuBar.cpp b/src/app/MenuBar.cpp index 42958ff8..6a14b3b8 100644 --- a/src/app/MenuBar.cpp +++ b/src/app/MenuBar.cpp @@ -492,6 +492,8 @@ struct ViewButton : MenuButton { menu->addChild(createBoolPtrMenuItem("Lock positions", "", &settings::lockModules)); menu->addChild(createBoolPtrMenuItem("Smart rearrangement", "", &settings::squeezeModules)); + + menu->addChild(createBoolPtrMenuItem("Prefer dark panels", "", &settings::preferDarkPanels)); } }; diff --git a/src/app/SvgPanel.cpp b/src/app/SvgPanel.cpp index bfa3851a..045ae757 100644 --- a/src/app/SvgPanel.cpp +++ b/src/app/SvgPanel.cpp @@ -32,7 +32,8 @@ void SvgPanel::step() { if (APP->window->pixelRatio < 2.0) { // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer fb->oversample = 2.0; - } else { + } + else { fb->oversample = 1.0; } @@ -40,14 +41,32 @@ void SvgPanel::step() { } void SvgPanel::setBackground(std::shared_ptr svg) { + if (svg == this->svg) + return; this->svg = svg; sw->setSvg(svg); + fb->setDirty(); + + // Round framebuffer size to nearest grid fb->box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE); panelBorder->box.size = fb->box.size; box.size = fb->box.size; } +void ThemedSvgPanel::step() { + SvgPanel::setBackground(settings::preferDarkPanels ? darkSvg : lightSvg); + SvgPanel::step(); +} + + +void ThemedSvgPanel::setBackground(std::shared_ptr lightSvg, std::shared_ptr darkSvg) { + this->lightSvg = lightSvg; + this->darkSvg = darkSvg; + SvgPanel::setBackground(settings::preferDarkPanels ? darkSvg : lightSvg); +} + + } // namespace app } // namespace rack diff --git a/src/app/SvgScrew.cpp b/src/app/SvgScrew.cpp index 0401c8b2..918c01fb 100644 --- a/src/app/SvgScrew.cpp +++ b/src/app/SvgScrew.cpp @@ -1,4 +1,5 @@ #include +#include namespace rack { @@ -15,11 +16,29 @@ SvgScrew::SvgScrew() { void SvgScrew::setSvg(std::shared_ptr svg) { + if (sw->svg == svg) + return; + sw->setSvg(svg); + fb->setDirty(); + fb->box.size = sw->box.size; box.size = sw->box.size; } +void ThemedSvgScrew::step() { + SvgScrew::setSvg(settings::preferDarkPanels ? darkSvg : lightSvg); + SvgScrew::step(); +} + + +void ThemedSvgScrew::setSvg(std::shared_ptr lightSvg, std::shared_ptr darkSvg) { + this->lightSvg = lightSvg; + this->darkSvg = darkSvg; + SvgScrew::setSvg(settings::preferDarkPanels ? darkSvg : lightSvg); +} + + } // namespace app } // namespace rack diff --git a/src/settings.cpp b/src/settings.cpp index 2dd8cba3..b88be2c0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -43,6 +43,7 @@ bool tooltips = true; bool cpuMeter = false; bool lockModules = false; bool squeezeModules = true; +bool preferDarkPanels = false; #if defined ARCH_MAC // Most Mac GPUs can't handle rendering the screen every frame, so use 30 Hz by default. float frameRateLimit = 30.f; @@ -162,6 +163,8 @@ json_t* toJson() { json_object_set_new(rootJ, "squeezeModules", json_boolean(squeezeModules)); + json_object_set_new(rootJ, "preferDarkPanels", json_boolean(preferDarkPanels)); + json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit)); json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval)); @@ -355,6 +358,10 @@ void fromJson(json_t* rootJ) { if (squeezeModulesJ) squeezeModules = json_boolean_value(squeezeModulesJ); + json_t* preferDarkPanelsJ = json_object_get(rootJ, "preferDarkPanels"); + if (preferDarkPanelsJ) + preferDarkPanels = json_boolean_value(preferDarkPanelsJ); + // Legacy setting in Rack <2.2 json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval"); if (frameSwapIntervalJ) {