| @@ -8,7 +8,7 @@ run: doxygen | |||||
| http-server html | http-server html | ||||
| upload: doxygen | upload: doxygen | ||||
| rsync html/ vcvrack.com:vcvrack.com/docs/ -ruvz --delete | |||||
| rsync html/ vcvrack.com:vcvrack.com/docs/ -ruz --info=progress2 --delete | |||||
| clean: | clean: | ||||
| rm -rfv html | rm -rfv html | ||||
| @@ -1,5 +1,11 @@ | |||||
| #pragma once | #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 { | namespace rack { | ||||
| @@ -294,7 +300,7 @@ struct SynthTechAlco : app::SvgKnob { | |||||
| minAngle = -0.82*M_PI; | minAngle = -0.82*M_PI; | ||||
| maxAngle = 0.82*M_PI; | maxAngle = 0.82*M_PI; | ||||
| setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); | 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"))); | cap->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco_cap.svg"))); | ||||
| addChild(cap); | addChild(cap); | ||||
| } | } | ||||
| @@ -344,8 +350,8 @@ struct BefacoSlidePot : app::SvgSlider { | |||||
| struct LEDSlider : app::SvgSlider { | struct LEDSlider : app::SvgSlider { | ||||
| LEDSlider() { | 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"))); | setBackgroundSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSlider.svg"))); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -467,7 +473,7 @@ struct RGBLight : app::ModuleLightWidget { | |||||
| template <typename BASE> | template <typename BASE> | ||||
| struct LargeLight : BASE { | struct LargeLight : BASE { | ||||
| LargeLight() { | 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 <typename BASE> | template <typename BASE> | ||||
| struct MediumLight : BASE { | struct MediumLight : BASE { | ||||
| MediumLight() { | 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 <typename BASE> | template <typename BASE> | ||||
| struct SmallLight : BASE { | struct SmallLight : BASE { | ||||
| SmallLight() { | 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 <typename BASE> | template <typename BASE> | ||||
| struct TinyLight : BASE { | struct TinyLight : BASE { | ||||
| TinyLight() { | 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 <typename BASE> | |||||
| struct LEDBezelLight : BASE { | struct LEDBezelLight : BASE { | ||||
| LEDBezelLight() { | LEDBezelLight() { | ||||
| this->bgColor = color::BLACK_TRANSPARENT; | 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. | /** 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 <typename BASE> | template <typename BASE> | ||||
| struct PB61303Light : BASE { | struct PB61303Light : BASE { | ||||
| PB61303Light() { | PB61303Light() { | ||||
| this->bgColor = color::BLACK_TRANSPARENT; | 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)); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -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. | `length` must be a multiple of 32. | ||||
| */ | */ | ||||
| struct RealFFT { | struct RealFFT { | ||||
| @@ -72,8 +72,7 @@ struct RealFFT { | |||||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); | 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) { | void scale(float *x) { | ||||
| float a = 1.f / length; | 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. | `length` must be a multiple of 16. | ||||
| */ | */ | ||||
| struct ComplexFFT { | struct ComplexFFT { | ||||
| @@ -6,7 +6,7 @@ namespace rack { | |||||
| namespace dsp { | namespace dsp { | ||||
| /** Deprecated. Use VuMeter2. */ | |||||
| /** Deprecated. Use VuMeter2 instead. */ | |||||
| struct VuMeter { | struct VuMeter { | ||||
| /** Decibel level difference between adjacent meter lights */ | /** Decibel level difference between adjacent meter lights */ | ||||
| float dBInterval = 3.0; | float dBInterval = 3.0; | ||||
| @@ -34,17 +34,18 @@ DEPRECATED typedef VuMeter VUMeter; | |||||
| /** Models a VU meter with smoothing. | /** 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: | 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 { | struct VuMeter2 { | ||||
| enum Mode { | enum Mode { | ||||
| @@ -1,7 +1,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "common.hpp" | #include "common.hpp" | ||||
| #include <jansson.h> | #include <jansson.h> | ||||
| #include <list> | |||||
| #include <vector> | |||||
| namespace rack { | namespace rack { | ||||
| @@ -14,7 +14,7 @@ struct Model; | |||||
| // Subclass this and return a pointer to a new one when init() is called | // Subclass this and return a pointer to a new one when init() is called | ||||
| struct Plugin { | struct Plugin { | ||||
| /** A list of the models available by this plugin, add with addModel() */ | /** A list of the models available by this plugin, add with addModel() */ | ||||
| std::list<Model*> models; | |||||
| std::vector<Model*> models; | |||||
| /** The file path of the plugin's directory */ | /** The file path of the plugin's directory */ | ||||
| std::string path; | std::string path; | ||||
| /** OS-dependent library handle */ | /** OS-dependent library handle */ | ||||
| @@ -1,4 +1,5 @@ | |||||
| #pragma once | #pragma once | ||||
| // Include most Rack headers for convenience | // Include most Rack headers for convenience | ||||
| #include "common.hpp" | #include "common.hpp" | ||||
| #include "math.hpp" | #include "math.hpp" | ||||
| @@ -11,6 +12,7 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "midi.hpp" | #include "midi.hpp" | ||||
| #include "helpers.hpp" | #include "helpers.hpp" | ||||
| #include "component.hpp" | |||||
| #include "widget/Widget.hpp" | #include "widget/Widget.hpp" | ||||
| #include "widget/TransparentWidget.hpp" | #include "widget/TransparentWidget.hpp" | ||||
| @@ -90,9 +92,12 @@ | |||||
| #include "dsp/vumeter.hpp" | #include "dsp/vumeter.hpp" | ||||
| #include "dsp/window.hpp" | #include "dsp/window.hpp" | ||||
| namespace rack { | 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 | // Import some namespaces for convenience | ||||
| using namespace math; | using namespace math; | ||||
| using namespace widget; | using namespace widget; | ||||
| @@ -101,9 +106,8 @@ using namespace app; | |||||
| using plugin::Plugin; | using plugin::Plugin; | ||||
| using plugin::Model; | using plugin::Model; | ||||
| using namespace engine; | using namespace engine; | ||||
| namespace component {} | |||||
| using namespace component; | using namespace component; | ||||
| #endif | |||||
| } // namespace rack | } // namespace rack | ||||
| @@ -6,6 +6,7 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "patch.hpp" | #include "patch.hpp" | ||||
| #include "settings.hpp" | #include "settings.hpp" | ||||
| #include "engine/Port.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -219,7 +220,7 @@ void CableWidget::draw(const DrawArgs &args) { | |||||
| float thickness = 5; | float thickness = 5; | ||||
| if (isComplete()) { | 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 | // Draw opaque if mouse is hovering over a connected port | ||||
| if (output->channels > 1) { | if (output->channels > 1) { | ||||
| // Increase thickness if output port is polyphonic | // Increase thickness if output port is polyphonic | ||||
| @@ -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 { | struct ModelBox : widget::OpaqueWidget { | ||||
| @@ -92,20 +92,23 @@ struct ModelBox : widget::OpaqueWidget { | |||||
| bool selected = false; | bool selected = false; | ||||
| ModelBox() { | 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) { | void setModel(plugin::Model *model) { | ||||
| this->model = model; | this->model = model; | ||||
| infoWidget = new widget::Widget; | infoWidget = new widget::Widget; | ||||
| infoWidget->box.size.x = 140; | |||||
| infoWidget->box.size.y = box.size.y; | |||||
| infoWidget->hide(); | |||||
| addChild(infoWidget); | addChild(infoWidget); | ||||
| math::Vec pos; | math::Vec pos; | ||||
| // Name label | |||||
| ui::Label *nameLabel = new ui::Label; | ui::Label *nameLabel = new ui::Label; | ||||
| // nameLabel->box.size.x = infoWidget->box.size.x; | // nameLabel->box.size.x = infoWidget->box.size.x; | ||||
| nameLabel->box.pos = pos; | nameLabel->box.pos = pos; | ||||
| @@ -113,6 +116,7 @@ struct ModelBox : widget::OpaqueWidget { | |||||
| infoWidget->addChild(nameLabel); | infoWidget->addChild(nameLabel); | ||||
| pos = nameLabel->box.getBottomLeft(); | pos = nameLabel->box.getBottomLeft(); | ||||
| // Plugin label | |||||
| ui::Label *pluginLabel = new ui::Label; | ui::Label *pluginLabel = new ui::Label; | ||||
| // pluginLabel->box.size.x = infoWidget->box.size.x; | // pluginLabel->box.size.x = infoWidget->box.size.x; | ||||
| pluginLabel->box.pos = pos; | pluginLabel->box.pos = pos; | ||||
| @@ -127,24 +131,23 @@ struct ModelBox : widget::OpaqueWidget { | |||||
| infoWidget->addChild(descriptionLabel); | infoWidget->addChild(descriptionLabel); | ||||
| pos = descriptionLabel->box.getBottomLeft(); | 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() { | void createPreview() { | ||||
| @@ -171,9 +174,8 @@ struct ModelBox : widget::OpaqueWidget { | |||||
| zoomWidget->box.size.y = RACK_GRID_HEIGHT * MODEL_BOX_ZOOM; | zoomWidget->box.size.y = RACK_GRID_HEIGHT * MODEL_BOX_ZOOM; | ||||
| previewWidget->box.size.x = std::ceil(zoomWidget->box.size.x); | 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() { | void deletePreview() { | ||||
| @@ -197,6 +199,16 @@ struct ModelBox : widget::OpaqueWidget { | |||||
| createPreview(); | 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)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
| widget::OpaqueWidget::draw(args); | widget::OpaqueWidget::draw(args); | ||||
| nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
| @@ -261,17 +273,18 @@ struct BrowserSidebar : widget::Widget { | |||||
| searchField = new BrowserSearchField; | searchField = new BrowserSearchField; | ||||
| addChild(searchField); | addChild(searchField); | ||||
| // Plugin list | |||||
| pluginScroll = new ui::ScrollWidget; | pluginScroll = new ui::ScrollWidget; | ||||
| pluginScroll->box.pos = searchField->box.getBottomLeft(); | |||||
| addChild(pluginScroll); | addChild(pluginScroll); | ||||
| pluginList = new ui::List; | pluginList = new ui::List; | ||||
| pluginScroll->container->addChild(pluginList); | pluginScroll->container->addChild(pluginList); | ||||
| std::set<std::string> pluginNames; | |||||
| std::vector<std::string> pluginNames; | |||||
| for (plugin::Plugin *plugin : plugin::plugins) { | 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) { | for (const std::string &pluginName : pluginNames) { | ||||
| ui::MenuItem *item = new ui::MenuItem; | ui::MenuItem *item = new ui::MenuItem; | ||||
| @@ -279,8 +292,8 @@ struct BrowserSidebar : widget::Widget { | |||||
| pluginList->addChild(item); | pluginList->addChild(item); | ||||
| } | } | ||||
| // Tag list | |||||
| tagScroll = new ui::ScrollWidget; | tagScroll = new ui::ScrollWidget; | ||||
| tagScroll->box.pos = searchField->box.getBottomLeft(); | |||||
| addChild(tagScroll); | addChild(tagScroll); | ||||
| tagList = new ui::List; | tagList = new ui::List; | ||||
| @@ -295,11 +308,14 @@ struct BrowserSidebar : widget::Widget { | |||||
| void step() override { | void step() override { | ||||
| searchField->box.size.x = box.size.x; | 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(); | widget::Widget::step(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -313,18 +329,18 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| ModuleBrowser() { | ModuleBrowser() { | ||||
| sidebar = new BrowserSidebar; | sidebar = new BrowserSidebar; | ||||
| sidebar->box.size.x = 300; | |||||
| sidebar->box.size.x = 200; | |||||
| addChild(sidebar); | addChild(sidebar); | ||||
| modelScroll = new ui::ScrollWidget; | modelScroll = new ui::ScrollWidget; | ||||
| addChild(modelScroll); | addChild(modelScroll); | ||||
| modelMargin = new ui::MarginLayout; | modelMargin = new ui::MarginLayout; | ||||
| modelMargin->margin = math::Vec(20, 20); | |||||
| modelMargin->margin = math::Vec(10, 10); | |||||
| modelScroll->container->addChild(modelMargin); | modelScroll->container->addChild(modelMargin); | ||||
| modelContainer = new ui::SequentialLayout; | modelContainer = new ui::SequentialLayout; | ||||
| modelContainer->spacing = math::Vec(20, 20); | |||||
| modelContainer->spacing = math::Vec(10, 10); | |||||
| modelMargin->addChild(modelContainer); | modelMargin->addChild(modelContainer); | ||||
| for (plugin::Plugin *plugin : plugin::plugins) { | for (plugin::Plugin *plugin : plugin::plugins) { | ||||
| @@ -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. | All tags here should be in sentence caps for display consistency. | ||||
| However, tags are case-insensitive in plugin metadata. | However, tags are case-insensitive in plugin metadata. | ||||
| */ | */ | ||||
| @@ -13,8 +13,9 @@ void List::step() { | |||||
| for (widget::Widget *child : children) { | for (widget::Widget *child : children) { | ||||
| if (!child->visible) | if (!child->visible) | ||||
| continue; | continue; | ||||
| // Increment height, set position of child | |||||
| // Set position of child | |||||
| child->box.pos = math::Vec(0.0, box.size.y); | child->box.pos = math::Vec(0.0, box.size.y); | ||||
| // Increment height | |||||
| box.size.y += child->box.size.y; | box.size.y += child->box.size.y; | ||||
| // Resize width of child | // Resize width of child | ||||
| child->box.size.x = box.size.x; | child->box.size.x = box.size.x; | ||||