From 18eb87ae40a8e481fb413db1023722d029a0778c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 9 Oct 2017 03:55:05 -0400 Subject: [PATCH] Add BlankWidget to Core, refactor ModuleWidget --- CHANGELOG.md | 1 + include/app.hpp | 37 +++++++------ include/components.hpp | 2 - include/engine.hpp | 2 +- src/app/ModuleWidget.cpp | 16 +++--- src/app/Panel.cpp | 12 +++-- src/app/RackWidget.cpp | 64 +++++++++++------------ src/app/SVGPanel.cpp | 6 +-- src/core/Blank.cpp | 109 +++++++++++++++++++++++++++++++++++++++ src/core/core.cpp | 1 + src/core/core.hpp | 11 ++++ 11 files changed, 192 insertions(+), 69 deletions(-) create mode 100644 src/core/Blank.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index d29b3ac0..e7b0a43e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Tip: Use `git checkout v0.3.2` for example to check out any previous version men - Added sub-menus for each plugin, includes optional plugin metadata like URLs - Added new scrolling methods: middle-click-and-drag, shift-scroll, and arrow keys - Added engine pausing in sample rate menu +- Added resizable blank to Core - Support for AMD Phenom II processors - Use self-contained Mac app bundle, no need for a Rack folder diff --git a/include/app.hpp b/include/app.hpp index 272d9197..29d4e18e 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -35,28 +35,30 @@ struct ModuleWidget : OpaqueWidget { ~ModuleWidget(); void setModule(Module *module); - // Convenience functions for adding special widgets (calls addChild()) + /** Convenience functions for adding special widgets (calls addChild()) */ void addInput(Port *input); void addOutput(Port *output); void addParam(ParamWidget *param); - json_t *toJson(); - void fromJson(json_t *root); - - /** Disconnects cables from all ports */ - void disconnect(); - /** Resets the state of the module */ - void initialize(); - /** Randomizes the state of the module - This method just randomizes parameters. Override and call this function if your module contains other state information that you wish to randomize. + virtual json_t *toJson(); + virtual void fromJson(json_t *rootJ); + + /** Disconnects cables from all ports + Called when the user clicks Disconnect Cables in the context menu. + */ + virtual void disconnect(); + /** Resets the parameters of the module and calls the Module's randomize(). + Called when the user clicks Initialize in the context menu. */ - void randomize(); + virtual void initialize(); + /** Randomizes the parameters of the module and calls the Module's randomize(). + Called when the user clicks Randomize in the context menu. + */ + virtual void randomize(); virtual Menu *createContextMenu(); void draw(NVGcontext *vg); - bool requested = false; - Vec requestedPos; Vec dragPos; Widget *onMouseMove(Vec pos, Vec mouseRel); Widget *onHoverKey(Vec pos, int key); @@ -118,14 +120,16 @@ struct RackWidget : OpaqueWidget { void savePatch(std::string filename); void loadPatch(std::string filename); json_t *toJson(); - void fromJson(json_t *root); + void fromJson(json_t *rootJ); void addModule(ModuleWidget *m); /** Transfers ownership to the caller so they must `delete` it if that is the intension */ void deleteModule(ModuleWidget *m); void cloneModule(ModuleWidget *m); + /** Sets a module's box if non-colliding. Returns true if set */ + bool requestModuleBox(ModuleWidget *m, Rect box); /** Moves a module to the closest non-colliding position */ - void repositionModule(ModuleWidget *m); + bool requestModuleBoxNearest(ModuleWidget *m, Rect box); void step(); void draw(NVGcontext *vg); @@ -138,7 +142,6 @@ struct RackRail : TransparentWidget { struct Panel : TransparentWidget { NVGcolor backgroundColor; - NVGcolor borderColor; std::shared_ptr backgroundImage; void draw(NVGcontext *vg); }; @@ -167,7 +170,7 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { int paramId; json_t *toJson(); - void fromJson(json_t *root); + void fromJson(json_t *rootJ); virtual void randomize(); void onMouseDownOpaque(int button); void onChange(); diff --git a/include/components.hpp b/include/components.hpp index ef078c33..ea8603ef 100644 --- a/include/components.hpp +++ b/include/components.hpp @@ -617,14 +617,12 @@ struct LightPanel : Panel { LightPanel() { // backgroundColor = nvgRGB(0xe6, 0xe6, 0xe6); backgroundColor = nvgRGB(0xf0, 0xf0, 0xf0); - borderColor = nvgRGB(0xac, 0xac, 0xac); } }; struct DarkPanel : Panel { DarkPanel() { backgroundColor = nvgRGB(0x17, 0x17, 0x17); - borderColor = nvgRGB(0x5e, 0x5e, 0x5e); } }; diff --git a/include/engine.hpp b/include/engine.hpp index 4d7b3419..50ceded7 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -55,7 +55,7 @@ struct Module { virtual json_t *toJson() { return NULL; } virtual void fromJson(json_t *root) {} - /** Override these to implement behavior when user clicks Initialize and Randomize */ + /** Override these to implement spacial behavior when user clicks Initialize and Randomize */ virtual void initialize() {} virtual void randomize() {} }; diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index daa944f5..3852eed6 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -6,6 +6,7 @@ namespace rack { + ModuleWidget::~ModuleWidget() { // Make sure WireWidget destructors are called *before* removing `module` from the rack. disconnect(); @@ -49,8 +50,8 @@ json_t *ModuleWidget::toJson() { // model json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); // pos - json_t *pos = json_pack("[f, f]", (double) box.pos.x, (double) box.pos.y); - json_object_set_new(rootJ, "pos", pos); + json_t *posJ = json_pack("[f, f]", (double) box.pos.x, (double) box.pos.y); + json_object_set_new(rootJ, "pos", posJ); // params json_t *paramsJ = json_array(); for (ParamWidget *paramWidget : params) { @@ -70,12 +71,10 @@ json_t *ModuleWidget::toJson() { } void ModuleWidget::fromJson(json_t *rootJ) { - initialize(); - // pos - json_t *pos = json_object_get(rootJ, "pos"); + json_t *posJ = json_object_get(rootJ, "pos"); double x, y; - json_unpack(pos, "[F, F]", &x, &y); + json_unpack(posJ, "[F, F]", &x, &y); box.pos = Vec(x, y); // params @@ -197,8 +196,9 @@ void ModuleWidget::onDragStart() { } void ModuleWidget::onDragMove(Vec mouseRel) { - requestedPos = gMousePos.minus(parent->getAbsolutePos()).minus(dragPos); - requested = true; + Rect newBox = box; + newBox.pos = gMousePos.minus(parent->getAbsolutePos()).minus(dragPos); + gRackWidget->requestModuleBoxNearest(this, newBox); } void ModuleWidget::onDragEnd() { diff --git a/src/app/Panel.cpp b/src/app/Panel.cpp index 72a2498c..92d5a33a 100644 --- a/src/app/Panel.cpp +++ b/src/app/Panel.cpp @@ -3,13 +3,16 @@ namespace rack { + void Panel::draw(NVGcontext *vg) { nvgBeginPath(vg); nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); // Background color - nvgFillColor(vg, backgroundColor); - nvgFill(vg); + if (backgroundColor.a > 0) { + nvgFillColor(vg, backgroundColor); + nvgFill(vg); + } // Background image if (backgroundImage) { @@ -21,11 +24,14 @@ void Panel::draw(NVGcontext *vg) { } // Border + NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5); nvgBeginPath(vg); - nvgRect(vg, 0.5, 0.5, box.size.x - 1, box.size.y - 1); + nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0); nvgStrokeColor(vg, borderColor); nvgStrokeWidth(vg, 1.0); nvgStroke(vg); + + Widget::draw(vg); } } // namespace rack diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index f17f08b6..d9b95c84 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -261,15 +261,27 @@ void RackWidget::cloneModule(ModuleWidget *m) { json_t *moduleJ = m->toJson(); clonedModuleWidget->fromJson(moduleJ); json_decref(moduleJ); - clonedModuleWidget->requestedPos = m->box.pos; - clonedModuleWidget->requested = true; + Rect clonedBox = clonedModuleWidget->box; + clonedBox.pos = m->box.pos; + requestModuleBoxNearest(clonedModuleWidget, clonedBox); addModule(clonedModuleWidget); } -void RackWidget::repositionModule(ModuleWidget *m) { +bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) { + for (Widget *child2 : moduleContainer->children) { + if (m == child2) continue; + if (box.intersects(child2->box)) { + return false; + } + } + m->box = box; + return true; +} + +bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) { // Create possible positions - int x0 = roundf(m->requestedPos.x / RACK_GRID_WIDTH); - int y0 = roundf(m->requestedPos.y / RACK_GRID_HEIGHT); + int x0 = roundf(box.pos.x / RACK_GRID_WIDTH); + int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT); std::vector positions; for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) { for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) { @@ -278,27 +290,18 @@ void RackWidget::repositionModule(ModuleWidget *m) { } // Sort possible positions by distance to the requested position - Vec requestedPos = m->requestedPos; - std::sort(positions.begin(), positions.end(), [requestedPos](Vec a, Vec b) { - return a.minus(requestedPos).norm() < b.minus(requestedPos).norm(); + std::sort(positions.begin(), positions.end(), [box](Vec a, Vec b) { + return a.minus(box.pos).norm() < b.minus(box.pos).norm(); }); // Find a position that does not collide - for (Vec pos : positions) { - Rect newBox = Rect(pos, m->box.size); - bool collides = false; - for (Widget *child2 : moduleContainer->children) { - if (m == child2) continue; - if (newBox.intersects(child2->box)) { - collides = true; - break; - } - } - if (collides) continue; - - m->box.pos = pos; - break; + for (Vec position : positions) { + Rect newBox = box; + newBox.pos = position; + if (requestModuleBox(m, newBox)) + return true; } + return false; } void RackWidget::step() { @@ -309,16 +312,6 @@ void RackWidget::step() { // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded. box.size = box.size.max(moduleSize); - // Reposition modules - for (Widget *child : moduleContainer->children) { - ModuleWidget *module = dynamic_cast(child); - assert(module); - if (module->requested) { - repositionModule(module); - module->requested = false; - } - } - // Autosave every 15 seconds if (gGuiFrame % (60*15) == 0) { savePatch(assetLocal("autosave.vcv")); @@ -344,9 +337,12 @@ struct AddModuleMenuItem : MenuItem { Vec modulePos; void onAction() { ModuleWidget *moduleWidget = model->createModuleWidget(); - moduleWidget->requestedPos = modulePos.minus(moduleWidget->box.getCenter()); - moduleWidget->requested = true; gRackWidget->moduleContainer->addChild(moduleWidget); + // Move module nearest to the mouse position + Rect box; + box.size = moduleWidget->box.size; + box.pos = modulePos.minus(box.getCenter()); + gRackWidget->requestModuleBoxNearest(moduleWidget, box); } }; diff --git a/src/app/SVGPanel.cpp b/src/app/SVGPanel.cpp index 73958c81..a0419c4a 100644 --- a/src/app/SVGPanel.cpp +++ b/src/app/SVGPanel.cpp @@ -6,11 +6,7 @@ namespace rack { struct PanelBorder : TransparentWidget { void draw(NVGcontext *vg) { - nvgBeginPath(vg); - nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); - NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5); - nvgBeginPath(vg); nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0); nvgStrokeColor(vg, borderColor); @@ -21,6 +17,8 @@ struct PanelBorder : TransparentWidget { void SVGPanel::setBackground(std::shared_ptr svg) { + clearChildren(); + SVGWidget *sw = new SVGWidget(); sw->wrap(); sw->svg = svg; diff --git a/src/core/Blank.cpp b/src/core/Blank.cpp new file mode 100644 index 00000000..80a5fb7b --- /dev/null +++ b/src/core/Blank.cpp @@ -0,0 +1,109 @@ +#include "core.hpp" + +using namespace rack; + + +struct ModuleResizeHandle : Widget { + bool right = false; + float originalWidth; + float totalX; + ModuleResizeHandle() { + box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT); + } + Widget *onMouseDown(Vec pos, int button) { + if (button == 0) + return this; + return NULL; + } + void onDragStart() { + assert(parent); + originalWidth = parent->box.size.x; + totalX = 0.0; + } + void onDragMove(Vec mouseRel) { + ModuleWidget *m = dynamic_cast(parent); + assert(m); + totalX += mouseRel.x; + float targetWidth = originalWidth; + if (right) + targetWidth += totalX; + else + targetWidth -= totalX; + targetWidth = RACK_GRID_WIDTH * roundf(targetWidth / RACK_GRID_WIDTH); + targetWidth = fmaxf(targetWidth, RACK_GRID_WIDTH * 3); + Rect newBox = m->box; + newBox.size.x = targetWidth; + if (!right) { + newBox.pos.x = m->box.pos.x + m->box.size.x - newBox.size.x; + } + gRackWidget->requestModuleBox(m, newBox); + } + void draw(NVGcontext *vg) { + for (float x = 5.0; x <= 10.0; x += 5.0) { + nvgBeginPath(vg); + const float margin = 5.0; + nvgMoveTo(vg, x + 0.5, margin + 0.5); + nvgLineTo(vg, x + 0.5, box.size.y - margin + 0.5); + nvgStrokeWidth(vg, 1.0); + nvgStrokeColor(vg, nvgRGBAf(0.5, 0.5, 0.5, 0.5)); + nvgStroke(vg); + } + } +}; + + +BlankWidget::BlankWidget() { + box.size = Vec(RACK_GRID_WIDTH * 10, RACK_GRID_HEIGHT); + + { + panel = new LightPanel(); + panel->box.size = box.size; + addChild(panel); + } + + ModuleResizeHandle *leftHandle = new ModuleResizeHandle(); + ModuleResizeHandle *rightHandle = new ModuleResizeHandle(); + rightHandle->right = true; + this->rightHandle = rightHandle; + addChild(leftHandle); + addChild(rightHandle); + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(15, 365))); + topRightScrew = createScrew(Vec(box.size.x - 30, 0)); + bottomRightScrew = createScrew(Vec(box.size.x - 30, 365)); + addChild(topRightScrew); + addChild(bottomRightScrew); +} + +void BlankWidget::step() { + panel->box.size = box.size; + topRightScrew->box.pos.x = box.size.x - 30; + bottomRightScrew->box.pos.x = box.size.x - 30; + if (box.size.x < RACK_GRID_WIDTH * 6) { + topRightScrew->visible = bottomRightScrew->visible = false; + } + else { + topRightScrew->visible = bottomRightScrew->visible = true; + } + rightHandle->box.pos.x = box.size.x - rightHandle->box.size.x; + ModuleWidget::step(); +} + +json_t *BlankWidget::toJson() { + json_t *rootJ = ModuleWidget::toJson(); + + // // width + json_object_set_new(rootJ, "width", json_real(box.size.x)); + + return rootJ; +} + +void BlankWidget::fromJson(json_t *rootJ) { + ModuleWidget::fromJson(rootJ); + + // width + json_t *widthJ = json_object_get(rootJ, "width"); + if (widthJ) + box.size.x = json_number_value(widthJ); +} diff --git a/src/core/core.cpp b/src/core/core.cpp index b5befe3e..6fb678ec 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -8,4 +8,5 @@ void init(rack::Plugin *plugin) { createModel(plugin, "AudioInterface", "Audio Interface"); createModel(plugin, "MidiInterface", "MIDI Interface"); // createModel(plugin, "Bridge", "Bridge"); + createModel(plugin, "Blank", "Blank"); } diff --git a/src/core/core.hpp b/src/core/core.hpp index eadc8d56..e9df40b0 100644 --- a/src/core/core.hpp +++ b/src/core/core.hpp @@ -20,3 +20,14 @@ struct MidiInterfaceWidget : ModuleWidget { struct BridgeWidget : ModuleWidget { BridgeWidget(); }; + +struct BlankWidget : ModuleWidget { + Panel *panel; + Widget *topRightScrew; + Widget *bottomRightScrew; + Widget *rightHandle; + BlankWidget(); + void step(); + json_t *toJson(); + void fromJson(json_t *rootJ); +};