@@ -30,5 +30,14 @@ struct SvgPanel : widget::Widget { | |||||
DEPRECATED typedef SvgPanel SVGPanel; | DEPRECATED typedef SvgPanel SVGPanel; | ||||
struct ThemedSvgPanel : SvgPanel { | |||||
std::shared_ptr<window::Svg> lightSvg; | |||||
std::shared_ptr<window::Svg> darkSvg; | |||||
void step() override; | |||||
void setBackground(std::shared_ptr<window::Svg> lightSvg, std::shared_ptr<window::Svg> darkSvg); | |||||
}; | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack |
@@ -22,5 +22,15 @@ struct SvgScrew : widget::Widget { | |||||
DEPRECATED typedef SvgScrew SVGScrew; | DEPRECATED typedef SvgScrew SVGScrew; | ||||
struct ThemedSvgScrew : SvgScrew { | |||||
std::shared_ptr<window::Svg> lightSvg; | |||||
std::shared_ptr<window::Svg> darkSvg; | |||||
void step() override; | |||||
void setSvg(std::shared_ptr<window::Svg> lightSvg, std::shared_ptr<window::Svg> darkSvg); | |||||
}; | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack |
@@ -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 { | struct SegmentDisplay : widget::Widget { | ||||
int lightsLen = 0; | int lightsLen = 0; | ||||
bool vertical = false; | bool vertical = false; | ||||
@@ -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 <class TPanel = app::SvgPanel> | |||||
TPanel* createPanel(std::string svgPath) { | |||||
TPanel* panel = new TPanel; | |||||
panel->setBackground(window::Svg::load(svgPath)); | panel->setBackground(window::Svg::load(svgPath)); | ||||
return panel; | return panel; | ||||
} | } | ||||
/** Creates a ThemedSvgPanel and loads the light/dark SVGs from the given paths. */ | |||||
template <class TPanel = app::ThemedSvgPanel> | |||||
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 <class TParamWidget> | template <class TParamWidget> | ||||
TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { | TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { | ||||
TParamWidget* o = new TParamWidget; | TParamWidget* o = new TParamWidget; | ||||
@@ -69,6 +69,7 @@ extern bool tooltips; | |||||
extern bool cpuMeter; | extern bool cpuMeter; | ||||
extern bool lockModules; | extern bool lockModules; | ||||
extern bool squeezeModules; | extern bool squeezeModules; | ||||
extern bool preferDarkPanels; | |||||
/** Maximum screen redraw frequency in Hz, or 0 for unlimited. */ | /** Maximum screen redraw frequency in Hz, or 0 for unlimited. */ | ||||
extern float frameRateLimit; | extern float frameRateLimit; | ||||
/** Interval between autosaves in seconds. */ | /** Interval between autosaves in seconds. */ | ||||
@@ -492,6 +492,8 @@ struct ViewButton : MenuButton { | |||||
menu->addChild(createBoolPtrMenuItem("Lock positions", "", &settings::lockModules)); | menu->addChild(createBoolPtrMenuItem("Lock positions", "", &settings::lockModules)); | ||||
menu->addChild(createBoolPtrMenuItem("Smart rearrangement", "", &settings::squeezeModules)); | menu->addChild(createBoolPtrMenuItem("Smart rearrangement", "", &settings::squeezeModules)); | ||||
menu->addChild(createBoolPtrMenuItem("Prefer dark panels", "", &settings::preferDarkPanels)); | |||||
} | } | ||||
}; | }; | ||||
@@ -32,7 +32,8 @@ void SvgPanel::step() { | |||||
if (APP->window->pixelRatio < 2.0) { | if (APP->window->pixelRatio < 2.0) { | ||||
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer | // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer | ||||
fb->oversample = 2.0; | fb->oversample = 2.0; | ||||
} else { | |||||
} | |||||
else { | |||||
fb->oversample = 1.0; | fb->oversample = 1.0; | ||||
} | } | ||||
@@ -40,14 +41,32 @@ void SvgPanel::step() { | |||||
} | } | ||||
void SvgPanel::setBackground(std::shared_ptr<window::Svg> svg) { | void SvgPanel::setBackground(std::shared_ptr<window::Svg> svg) { | ||||
if (svg == this->svg) | |||||
return; | |||||
this->svg = svg; | this->svg = svg; | ||||
sw->setSvg(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); | fb->box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE); | ||||
panelBorder->box.size = fb->box.size; | panelBorder->box.size = fb->box.size; | ||||
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<window::Svg> lightSvg, std::shared_ptr<window::Svg> darkSvg) { | |||||
this->lightSvg = lightSvg; | |||||
this->darkSvg = darkSvg; | |||||
SvgPanel::setBackground(settings::preferDarkPanels ? darkSvg : lightSvg); | |||||
} | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack |
@@ -1,4 +1,5 @@ | |||||
#include <app/SvgScrew.hpp> | #include <app/SvgScrew.hpp> | ||||
#include <settings.hpp> | |||||
namespace rack { | namespace rack { | ||||
@@ -15,11 +16,29 @@ SvgScrew::SvgScrew() { | |||||
void SvgScrew::setSvg(std::shared_ptr<window::Svg> svg) { | void SvgScrew::setSvg(std::shared_ptr<window::Svg> svg) { | ||||
if (sw->svg == svg) | |||||
return; | |||||
sw->setSvg(svg); | sw->setSvg(svg); | ||||
fb->setDirty(); | |||||
fb->box.size = sw->box.size; | fb->box.size = sw->box.size; | ||||
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<window::Svg> lightSvg, std::shared_ptr<window::Svg> darkSvg) { | |||||
this->lightSvg = lightSvg; | |||||
this->darkSvg = darkSvg; | |||||
SvgScrew::setSvg(settings::preferDarkPanels ? darkSvg : lightSvg); | |||||
} | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack |
@@ -43,6 +43,7 @@ bool tooltips = true; | |||||
bool cpuMeter = false; | bool cpuMeter = false; | ||||
bool lockModules = false; | bool lockModules = false; | ||||
bool squeezeModules = true; | bool squeezeModules = true; | ||||
bool preferDarkPanels = false; | |||||
#if defined ARCH_MAC | #if defined ARCH_MAC | ||||
// Most Mac GPUs can't handle rendering the screen every frame, so use 30 Hz by default. | // Most Mac GPUs can't handle rendering the screen every frame, so use 30 Hz by default. | ||||
float frameRateLimit = 30.f; | 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, "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, "frameRateLimit", json_real(frameRateLimit)); | ||||
json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval)); | json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval)); | ||||
@@ -355,6 +358,10 @@ void fromJson(json_t* rootJ) { | |||||
if (squeezeModulesJ) | if (squeezeModulesJ) | ||||
squeezeModules = json_boolean_value(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 | // Legacy setting in Rack <2.2 | ||||
json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval"); | json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval"); | ||||
if (frameSwapIntervalJ) { | if (frameSwapIntervalJ) { | ||||