From f967c681102bf21311ab0e5bc23790c5f7f3da73 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 17 Feb 2019 02:58:19 -0500 Subject: [PATCH] Header cleanup. ModuleBrowser work. --- docs/Makefile | 2 +- include/component.hpp | 28 +++++++----- include/dsp/fft.hpp | 9 ++-- include/dsp/vumeter.hpp | 23 +++++----- include/plugin/Plugin.hpp | 4 +- include/rack.hpp | 8 +++- src/app/CableWidget.cpp | 3 +- src/app/ModuleBrowser.cpp | 92 +++++++++++++++++++++++---------------- src/plugin.cpp | 2 +- src/ui/List.cpp | 3 +- 10 files changed, 101 insertions(+), 73 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index db1b87dc..0d4a9321 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -8,7 +8,7 @@ run: doxygen http-server html upload: doxygen - rsync html/ vcvrack.com:vcvrack.com/docs/ -ruvz --delete + rsync html/ vcvrack.com:vcvrack.com/docs/ -ruz --info=progress2 --delete clean: rm -rfv html diff --git a/include/component.hpp b/include/component.hpp index 9fe44ab1..ebd2a3d8 100644 --- a/include/component.hpp +++ b/include/component.hpp @@ -1,5 +1,11 @@ #pragma once -#include "rack.hpp" +#include "app/SvgKnob.hpp" +#include "app/SvgSlider.hpp" +#include "app/SvgPort.hpp" +#include "app/ModuleLightWidget.hpp" +#include "app/SvgSwitch.hpp" +#include "app/SvgScrew.hpp" +#include "asset.hpp" namespace rack { @@ -294,7 +300,7 @@ struct SynthTechAlco : app::SvgKnob { minAngle = -0.82*M_PI; maxAngle = 0.82*M_PI; setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); - SvgWidget *cap = new SvgWidget; + widget::SvgWidget *cap = new widget::SvgWidget; cap->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco_cap.svg"))); addChild(cap); } @@ -344,8 +350,8 @@ struct BefacoSlidePot : app::SvgSlider { struct LEDSlider : app::SvgSlider { LEDSlider() { - maxHandlePos = mm2px(math::Vec(0.738, 0.738).plus(math::Vec(2, 0))); - minHandlePos = mm2px(math::Vec(0.738, 22.078).plus(math::Vec(2, 0))); + maxHandlePos = app::mm2px(math::Vec(0.738, 0.738).plus(math::Vec(2, 0))); + minHandlePos = app::mm2px(math::Vec(0.738, 22.078).plus(math::Vec(2, 0))); setBackgroundSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSlider.svg"))); } }; @@ -467,7 +473,7 @@ struct RGBLight : app::ModuleLightWidget { template struct LargeLight : BASE { LargeLight() { - this->box.size = mm2px(math::Vec(5.179, 5.179)); + this->box.size = app::mm2px(math::Vec(5.179, 5.179)); } }; @@ -475,7 +481,7 @@ struct LargeLight : BASE { template struct MediumLight : BASE { MediumLight() { - this->box.size = mm2px(math::Vec(3.176, 3.176)); + this->box.size = app::mm2px(math::Vec(3.176, 3.176)); } }; @@ -483,7 +489,7 @@ struct MediumLight : BASE { template struct SmallLight : BASE { SmallLight() { - this->box.size = mm2px(math::Vec(2.176, 2.176)); + this->box.size = app::mm2px(math::Vec(2.176, 2.176)); } }; @@ -491,7 +497,7 @@ struct SmallLight : BASE { template struct TinyLight : BASE { TinyLight() { - this->box.size = mm2px(math::Vec(1.088, 1.088)); + this->box.size = app::mm2px(math::Vec(1.088, 1.088)); } }; @@ -500,18 +506,18 @@ template struct LEDBezelLight : BASE { LEDBezelLight() { this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = mm2px(math::Vec(6.0, 6.0)); + this->box.size = app::mm2px(math::Vec(6.0, 6.0)); } }; /** A light to displayed over PB61303. Must add a color by subclassing or templating. -Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by mm2px(math::Vec(0.5, 0.5)). +Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by app::mm2px(math::Vec(0.5, 0.5)). */ template struct PB61303Light : BASE { PB61303Light() { this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = mm2px(math::Vec(9.0, 9.0)); + this->box.size = app::mm2px(math::Vec(9.0, 9.0)); } }; diff --git a/include/dsp/fft.hpp b/include/dsp/fft.hpp index 665ea4d4..323ec157 100644 --- a/include/dsp/fft.hpp +++ b/include/dsp/fft.hpp @@ -18,8 +18,8 @@ void alignedDelete(T *p) { } -/** Real-valued FFT context -Wrapper for PFFFT (https://bitbucket.org/jpommier/pffft/) +/** Real-valued FFT context. +Wrapper for [PFFFT](https://bitbucket.org/jpommier/pffft/) `length` must be a multiple of 32. */ struct RealFFT { @@ -72,8 +72,7 @@ struct RealFFT { pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); } - /** Scales the RFFT so that - scale(IFFT(FFT(x))) = x + /** Scales the RFFT so that `scale(IFFT(FFT(x))) = x`. */ void scale(float *x) { float a = 1.f / length; @@ -84,7 +83,7 @@ struct RealFFT { }; -/** Complex-valued FFT context +/** Complex-valued FFT context. `length` must be a multiple of 16. */ struct ComplexFFT { diff --git a/include/dsp/vumeter.hpp b/include/dsp/vumeter.hpp index 2cf6e270..4cfc1975 100644 --- a/include/dsp/vumeter.hpp +++ b/include/dsp/vumeter.hpp @@ -6,7 +6,7 @@ namespace rack { namespace dsp { -/** Deprecated. Use VuMeter2. */ +/** Deprecated. Use VuMeter2 instead. */ struct VuMeter { /** Decibel level difference between adjacent meter lights */ float dBInterval = 3.0; @@ -34,17 +34,18 @@ DEPRECATED typedef VuMeter VUMeter; /** Models a VU meter with smoothing. -Supports peak and RMS (root-mean-square) metering +Supports peak and RMS (root-mean-square) metering. Usage example for a strip of lights with 3dB increments: - - // Update VuMeter state every frame. - vuMeter.process(deltaTime, output); - // Iterate lights every ~512 frames (less than a screen refresh). - for (int i = 0; i < 6; i++) { - float b = vuMeter.getBrightness(-3.f * (i + 1), -3.f * i); - // No need to use setSmoothBrightness() since VuMeter2 smooths the value for you. - lights[i].setBrightness(b); - } +``` +// Update VuMeter state every frame. +vuMeter.process(deltaTime, output); +// Iterate lights every ~512 frames (less than a screen refresh). +for (int i = 0; i < 6; i++) { + float b = vuMeter.getBrightness(-3.f * (i + 1), -3.f * i); + // No need to use setSmoothBrightness() since VuMeter2 smooths the value for you. + lights[i].setBrightness(b); +} +``` */ struct VuMeter2 { enum Mode { diff --git a/include/plugin/Plugin.hpp b/include/plugin/Plugin.hpp index 9168b641..4fc12176 100644 --- a/include/plugin/Plugin.hpp +++ b/include/plugin/Plugin.hpp @@ -1,7 +1,7 @@ #pragma once #include "common.hpp" #include -#include +#include namespace rack { @@ -14,7 +14,7 @@ struct Model; // Subclass this and return a pointer to a new one when init() is called struct Plugin { /** A list of the models available by this plugin, add with addModel() */ - std::list models; + std::vector models; /** The file path of the plugin's directory */ std::string path; /** OS-dependent library handle */ diff --git a/include/rack.hpp b/include/rack.hpp index 3d9a0ab7..3a4f83fc 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -1,4 +1,5 @@ #pragma once + // Include most Rack headers for convenience #include "common.hpp" #include "math.hpp" @@ -11,6 +12,7 @@ #include "app.hpp" #include "midi.hpp" #include "helpers.hpp" +#include "component.hpp" #include "widget/Widget.hpp" #include "widget/TransparentWidget.hpp" @@ -90,9 +92,12 @@ #include "dsp/vumeter.hpp" #include "dsp/window.hpp" + namespace rack { +/** Define this macro before including this header to prevent common namespaces from being included in the main `rack::` namespace. */ +#ifndef RACK_FLATTEN_NAMESPACES // Import some namespaces for convenience using namespace math; using namespace widget; @@ -101,9 +106,8 @@ using namespace app; using plugin::Plugin; using plugin::Model; using namespace engine; - -namespace component {} using namespace component; +#endif } // namespace rack diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index 274c3cdb..f40fa2ec 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -6,6 +6,7 @@ #include "app.hpp" #include "patch.hpp" #include "settings.hpp" +#include "engine/Port.hpp" namespace rack { @@ -219,7 +220,7 @@ void CableWidget::draw(const DrawArgs &args) { float thickness = 5; if (isComplete()) { - Output *output = &cable->outputModule->outputs[cable->outputId]; + engine::Output *output = &cable->outputModule->outputs[cable->outputId]; // Draw opaque if mouse is hovering over a connected port if (output->channels > 1) { // Increase thickness if output port is polyphonic diff --git a/src/app/ModuleBrowser.cpp b/src/app/ModuleBrowser.cpp index ef61b878..d074bdcd 100644 --- a/src/app/ModuleBrowser.cpp +++ b/src/app/ModuleBrowser.cpp @@ -79,7 +79,7 @@ struct BrowserOverlay : widget::OpaqueWidget { }; -static const float MODEL_BOX_ZOOM = 0.5f; +static const float MODEL_BOX_ZOOM = 1.0f; struct ModelBox : widget::OpaqueWidget { @@ -92,20 +92,23 @@ struct ModelBox : widget::OpaqueWidget { bool selected = false; ModelBox() { - box.size.x = 0.f; - box.size.y = std::ceil(RACK_GRID_HEIGHT * MODEL_BOX_ZOOM); + // Approximate size as 10HP before we know the actual size. + // We need a nonzero size, otherwise the parent widget will consider it not in the draw bounds, so its preview will not be lazily created. + box.size.x = 10 * RACK_GRID_WIDTH * MODEL_BOX_ZOOM; + box.size.y = RACK_GRID_HEIGHT * MODEL_BOX_ZOOM; + box.size = box.size.ceil(); } void setModel(plugin::Model *model) { this->model = model; infoWidget = new widget::Widget; - infoWidget->box.size.x = 140; - infoWidget->box.size.y = box.size.y; + infoWidget->hide(); addChild(infoWidget); math::Vec pos; + // Name label ui::Label *nameLabel = new ui::Label; // nameLabel->box.size.x = infoWidget->box.size.x; nameLabel->box.pos = pos; @@ -113,6 +116,7 @@ struct ModelBox : widget::OpaqueWidget { infoWidget->addChild(nameLabel); pos = nameLabel->box.getBottomLeft(); + // Plugin label ui::Label *pluginLabel = new ui::Label; // pluginLabel->box.size.x = infoWidget->box.size.x; pluginLabel->box.pos = pos; @@ -127,24 +131,23 @@ struct ModelBox : widget::OpaqueWidget { infoWidget->addChild(descriptionLabel); pos = descriptionLabel->box.getBottomLeft(); - pos.y = infoWidget->box.size.y; - for (const std::string &tag : model->tags) { - ui::Button *tagButton = new ui::Button; - tagButton->box.size.x = infoWidget->box.size.x; - tagButton->box.pos = pos; - tagButton->box.pos.y -= tagButton->box.size.y; - tagButton->text = tag; - infoWidget->addChild(tagButton); - pos = tagButton->box.getTopLeft(); - } - - ui::Button *favoriteButton = new ui::Button; - favoriteButton->box.size.x = infoWidget->box.size.x; - favoriteButton->box.pos = pos; - favoriteButton->box.pos.y -= favoriteButton->box.size.y; - favoriteButton->text = "★"; - infoWidget->addChild(favoriteButton); - pos = favoriteButton->box.getTopLeft(); + // for (const std::string &tag : model->tags) { + // ui::Button *tagButton = new ui::Button; + // tagButton->box.size.x = infoWidget->box.size.x; + // tagButton->box.pos = pos; + // tagButton->text = tag; + // infoWidget->addChild(tagButton); + // pos = tagButton->box.getTopLeft(); + // } + + // // Favorite button + // ui::Button *favoriteButton = new ui::Button; + // favoriteButton->box.size.x = box.size.x; + // favoriteButton->box.pos = pos; + // favoriteButton->box.pos.y -= favoriteButton->box.size.y; + // favoriteButton->text = "★"; + // addChild(favoriteButton); + // pos = favoriteButton->box.getTopLeft(); } void createPreview() { @@ -171,9 +174,8 @@ struct ModelBox : widget::OpaqueWidget { zoomWidget->box.size.y = RACK_GRID_HEIGHT * MODEL_BOX_ZOOM; previewWidget->box.size.x = std::ceil(zoomWidget->box.size.x); - // Reposition infoWidget - infoWidget->box.pos.x = previewWidget->box.size.x; - box.size.x = previewWidget->box.size.x + infoWidget->box.size.x; + infoWidget->box.size = previewWidget->box.size; + box.size.x = previewWidget->box.size.x; } void deletePreview() { @@ -197,6 +199,16 @@ struct ModelBox : widget::OpaqueWidget { createPreview(); } + // Draw shadow + nvgBeginPath(args.vg); + float r = 10; // Blur radius + float c = 10; // Corner radius + nvgRect(args.vg, -r, -r, box.size.x + 2*r, box.size.y + 2*r); + NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.5); + NVGcolor transparentColor = nvgRGBAf(0, 0, 0, 0); + nvgFillPaint(args.vg, nvgBoxGradient(args.vg, 0, 0, box.size.x, box.size.y, c, r, shadowColor, transparentColor)); + nvgFill(args.vg); + nvgScissor(args.vg, RECT_ARGS(args.clipBox)); widget::OpaqueWidget::draw(args); nvgResetScissor(args.vg); @@ -261,17 +273,18 @@ struct BrowserSidebar : widget::Widget { searchField = new BrowserSearchField; addChild(searchField); + // Plugin list pluginScroll = new ui::ScrollWidget; - pluginScroll->box.pos = searchField->box.getBottomLeft(); addChild(pluginScroll); pluginList = new ui::List; pluginScroll->container->addChild(pluginList); - std::set pluginNames; + std::vector pluginNames; for (plugin::Plugin *plugin : plugin::plugins) { - pluginNames.insert(plugin->name); + pluginNames.push_back(plugin->name); } + std::sort(pluginNames.begin(), pluginNames.end(), string::CaseInsensitiveCompare()); for (const std::string &pluginName : pluginNames) { ui::MenuItem *item = new ui::MenuItem; @@ -279,8 +292,8 @@ struct BrowserSidebar : widget::Widget { pluginList->addChild(item); } + // Tag list tagScroll = new ui::ScrollWidget; - tagScroll->box.pos = searchField->box.getBottomLeft(); addChild(tagScroll); tagList = new ui::List; @@ -295,11 +308,14 @@ struct BrowserSidebar : widget::Widget { void step() override { searchField->box.size.x = box.size.x; - pluginScroll->box.size.y = box.size.y - searchField->box.size.y; - pluginList->box.size.x = pluginScroll->box.size.x = box.size.x / 2; - tagScroll->box.pos.x = box.size.x / 2; - tagScroll->box.size.y = box.size.y - searchField->box.size.y; - tagList->box.size.x = tagScroll->box.size.x = box.size.x / 2; + pluginScroll->box.pos = searchField->box.getBottomLeft(); + pluginScroll->box.size.y = (box.size.y - searchField->box.size.y) / 2; + pluginScroll->box.size.x = box.size.x; + pluginList->box.size.x = pluginScroll->box.size.x; + tagScroll->box.pos = pluginScroll->box.getBottomLeft().floor(); + tagScroll->box.size.y = (box.size.y - searchField->box.size.y) / 2; + tagScroll->box.size.x = box.size.x; + tagList->box.size.x = tagScroll->box.size.x; widget::Widget::step(); } }; @@ -313,18 +329,18 @@ struct ModuleBrowser : widget::OpaqueWidget { ModuleBrowser() { sidebar = new BrowserSidebar; - sidebar->box.size.x = 300; + sidebar->box.size.x = 200; addChild(sidebar); modelScroll = new ui::ScrollWidget; addChild(modelScroll); modelMargin = new ui::MarginLayout; - modelMargin->margin = math::Vec(20, 20); + modelMargin->margin = math::Vec(10, 10); modelScroll->container->addChild(modelMargin); modelContainer = new ui::SequentialLayout; - modelContainer->spacing = math::Vec(20, 20); + modelContainer->spacing = math::Vec(10, 10); modelMargin->addChild(modelContainer); for (plugin::Plugin *plugin : plugin::plugins) { diff --git a/src/plugin.cpp b/src/plugin.cpp index 134a87b7..13d970d2 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -527,7 +527,7 @@ Model *getModel(const std::string &pluginSlug, const std::string &modelSlug) { } -/** List of allowed tags in human display form. +/** List of allowed tags in human display form, alphabetized. All tags here should be in sentence caps for display consistency. However, tags are case-insensitive in plugin metadata. */ diff --git a/src/ui/List.cpp b/src/ui/List.cpp index 1521bb2b..344f18c7 100644 --- a/src/ui/List.cpp +++ b/src/ui/List.cpp @@ -13,8 +13,9 @@ void List::step() { for (widget::Widget *child : children) { if (!child->visible) continue; - // Increment height, set position of child + // Set position of child child->box.pos = math::Vec(0.0, box.size.y); + // Increment height box.size.y += child->box.size.y; // Resize width of child child->box.size.x = box.size.x;