Browse Source

Merge branch 'master' of https://github.com/VCVRack/Rack into 2017-10-bontric-midi-interface-revamp

tags/v0.4.0
ben 7 years ago
parent
commit
adec9fa265
14 changed files with 248 additions and 69 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +20
    -17
      include/app.hpp
  3. +0
    -2
      include/components.hpp
  4. +1
    -1
      include/engine.hpp
  5. +8
    -8
      src/app/ModuleWidget.cpp
  6. +9
    -3
      src/app/Panel.cpp
  7. +2
    -0
      src/app/Port.cpp
  8. +30
    -34
      src/app/RackWidget.cpp
  9. +2
    -4
      src/app/SVGPanel.cpp
  10. +5
    -0
      src/core/AudioInterface.cpp
  11. +109
    -0
      src/core/Blank.cpp
  12. +44
    -0
      src/core/Bridge.cpp
  13. +2
    -0
      src/core/core.cpp
  14. +15
    -0
      src/core/core.hpp

+ 1
- 0
CHANGELOG.md View File

@@ -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 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 new scrolling methods: middle-click-and-drag, shift-scroll, and arrow keys
- Added engine pausing in sample rate menu - Added engine pausing in sample rate menu
- Added resizable blank to Core
- Support for AMD Phenom II processors - Support for AMD Phenom II processors
- Use self-contained Mac app bundle, no need for a Rack folder - Use self-contained Mac app bundle, no need for a Rack folder




+ 20
- 17
include/app.hpp View File

@@ -35,28 +35,30 @@ struct ModuleWidget : OpaqueWidget {


~ModuleWidget(); ~ModuleWidget();
void setModule(Module *module); 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 addInput(Port *input);
void addOutput(Port *output); void addOutput(Port *output);
void addParam(ParamWidget *param); 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(); virtual Menu *createContextMenu();


void draw(NVGcontext *vg); void draw(NVGcontext *vg);


bool requested = false;
Vec requestedPos;
Vec dragPos; Vec dragPos;
Widget *onMouseMove(Vec pos, Vec mouseRel); Widget *onMouseMove(Vec pos, Vec mouseRel);
Widget *onHoverKey(Vec pos, int key); Widget *onHoverKey(Vec pos, int key);
@@ -118,14 +120,16 @@ struct RackWidget : OpaqueWidget {
void savePatch(std::string filename); void savePatch(std::string filename);
void loadPatch(std::string filename); void loadPatch(std::string filename);
json_t *toJson(); json_t *toJson();
void fromJson(json_t *root);
void fromJson(json_t *rootJ);


void addModule(ModuleWidget *m); void addModule(ModuleWidget *m);
/** Transfers ownership to the caller so they must `delete` it if that is the intension */ /** Transfers ownership to the caller so they must `delete` it if that is the intension */
void deleteModule(ModuleWidget *m); void deleteModule(ModuleWidget *m);
void cloneModule(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 */ /** Moves a module to the closest non-colliding position */
void repositionModule(ModuleWidget *m);
bool requestModuleBoxNearest(ModuleWidget *m, Rect box);
void step(); void step();
void draw(NVGcontext *vg); void draw(NVGcontext *vg);


@@ -138,7 +142,6 @@ struct RackRail : TransparentWidget {


struct Panel : TransparentWidget { struct Panel : TransparentWidget {
NVGcolor backgroundColor; NVGcolor backgroundColor;
NVGcolor borderColor;
std::shared_ptr<Image> backgroundImage; std::shared_ptr<Image> backgroundImage;
void draw(NVGcontext *vg); void draw(NVGcontext *vg);
}; };
@@ -167,7 +170,7 @@ struct ParamWidget : OpaqueWidget, QuantityWidget {
int paramId; int paramId;


json_t *toJson(); json_t *toJson();
void fromJson(json_t *root);
void fromJson(json_t *rootJ);
virtual void randomize(); virtual void randomize();
void onMouseDownOpaque(int button); void onMouseDownOpaque(int button);
void onChange(); void onChange();


+ 0
- 2
include/components.hpp View File

@@ -617,14 +617,12 @@ struct LightPanel : Panel {
LightPanel() { LightPanel() {
// backgroundColor = nvgRGB(0xe6, 0xe6, 0xe6); // backgroundColor = nvgRGB(0xe6, 0xe6, 0xe6);
backgroundColor = nvgRGB(0xf0, 0xf0, 0xf0); backgroundColor = nvgRGB(0xf0, 0xf0, 0xf0);
borderColor = nvgRGB(0xac, 0xac, 0xac);
} }
}; };


struct DarkPanel : Panel { struct DarkPanel : Panel {
DarkPanel() { DarkPanel() {
backgroundColor = nvgRGB(0x17, 0x17, 0x17); backgroundColor = nvgRGB(0x17, 0x17, 0x17);
borderColor = nvgRGB(0x5e, 0x5e, 0x5e);
} }
}; };




+ 1
- 1
include/engine.hpp View File

@@ -55,7 +55,7 @@ struct Module {
virtual json_t *toJson() { return NULL; } virtual json_t *toJson() { return NULL; }
virtual void fromJson(json_t *root) {} 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 initialize() {}
virtual void randomize() {} virtual void randomize() {}
}; };


+ 8
- 8
src/app/ModuleWidget.cpp View File

@@ -6,6 +6,7 @@


namespace rack { namespace rack {



ModuleWidget::~ModuleWidget() { ModuleWidget::~ModuleWidget() {
// Make sure WireWidget destructors are called *before* removing `module` from the rack. // Make sure WireWidget destructors are called *before* removing `module` from the rack.
disconnect(); disconnect();
@@ -49,8 +50,8 @@ json_t *ModuleWidget::toJson() {
// model // model
json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); json_object_set_new(rootJ, "model", json_string(model->slug.c_str()));
// pos // 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 // params
json_t *paramsJ = json_array(); json_t *paramsJ = json_array();
for (ParamWidget *paramWidget : params) { for (ParamWidget *paramWidget : params) {
@@ -70,12 +71,10 @@ json_t *ModuleWidget::toJson() {
} }


void ModuleWidget::fromJson(json_t *rootJ) { void ModuleWidget::fromJson(json_t *rootJ) {
initialize();

// pos // pos
json_t *pos = json_object_get(rootJ, "pos");
json_t *posJ = json_object_get(rootJ, "pos");
double x, y; double x, y;
json_unpack(pos, "[F, F]", &x, &y);
json_unpack(posJ, "[F, F]", &x, &y);
box.pos = Vec(x, y); box.pos = Vec(x, y);


// params // params
@@ -197,8 +196,9 @@ void ModuleWidget::onDragStart() {
} }


void ModuleWidget::onDragMove(Vec mouseRel) { 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() { void ModuleWidget::onDragEnd() {


+ 9
- 3
src/app/Panel.cpp View File

@@ -3,13 +3,16 @@


namespace rack { namespace rack {



void Panel::draw(NVGcontext *vg) { void Panel::draw(NVGcontext *vg) {
nvgBeginPath(vg); nvgBeginPath(vg);
nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);


// Background color // Background color
nvgFillColor(vg, backgroundColor);
nvgFill(vg);
if (backgroundColor.a > 0) {
nvgFillColor(vg, backgroundColor);
nvgFill(vg);
}


// Background image // Background image
if (backgroundImage) { if (backgroundImage) {
@@ -21,11 +24,14 @@ void Panel::draw(NVGcontext *vg) {
} }


// Border // Border
NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
nvgBeginPath(vg); 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); nvgStrokeColor(vg, borderColor);
nvgStrokeWidth(vg, 1.0); nvgStrokeWidth(vg, 1.0);
nvgStroke(vg); nvgStroke(vg);

Widget::draw(vg);
} }


} // namespace rack } // namespace rack

+ 2
- 0
src/app/Port.cpp View File

@@ -28,6 +28,8 @@ void Port::onMouseDownOpaque(int button) {
} }


void Port::onDragEnd() { void Port::onDragEnd() {
// FIXME
// If the source Port is deleted, this will be called, removing the cable
gRackWidget->wireContainer->commitActiveWire(); gRackWidget->wireContainer->commitActiveWire();
} }




+ 30
- 34
src/app/RackWidget.cpp View File

@@ -261,15 +261,27 @@ void RackWidget::cloneModule(ModuleWidget *m) {
json_t *moduleJ = m->toJson(); json_t *moduleJ = m->toJson();
clonedModuleWidget->fromJson(moduleJ); clonedModuleWidget->fromJson(moduleJ);
json_decref(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); 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 // 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<Vec> positions; std::vector<Vec> positions;
for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) { for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) {
for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) { 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 // 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 // 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() { 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. // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded.
box.size = box.size.max(moduleSize); box.size = box.size.max(moduleSize);


// Reposition modules
for (Widget *child : moduleContainer->children) {
ModuleWidget *module = dynamic_cast<ModuleWidget*>(child);
assert(module);
if (module->requested) {
repositionModule(module);
module->requested = false;
}
}

// Autosave every 15 seconds // Autosave every 15 seconds
if (gGuiFrame % (60*15) == 0) { if (gGuiFrame % (60*15) == 0) {
savePatch(assetLocal("autosave.vcv")); savePatch(assetLocal("autosave.vcv"));
@@ -344,9 +337,12 @@ struct AddModuleMenuItem : MenuItem {
Vec modulePos; Vec modulePos;
void onAction() { void onAction() {
ModuleWidget *moduleWidget = model->createModuleWidget(); ModuleWidget *moduleWidget = model->createModuleWidget();
moduleWidget->requestedPos = modulePos.minus(moduleWidget->box.getCenter());
moduleWidget->requested = true;
gRackWidget->moduleContainer->addChild(moduleWidget); 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);
} }
}; };




+ 2
- 4
src/app/SVGPanel.cpp View File

@@ -6,11 +6,7 @@ namespace rack {


struct PanelBorder : TransparentWidget { struct PanelBorder : TransparentWidget {
void draw(NVGcontext *vg) { 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); NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);

nvgBeginPath(vg); nvgBeginPath(vg);
nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0); nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
nvgStrokeColor(vg, borderColor); nvgStrokeColor(vg, borderColor);
@@ -21,6 +17,8 @@ struct PanelBorder : TransparentWidget {




void SVGPanel::setBackground(std::shared_ptr<SVG> svg) { void SVGPanel::setBackground(std::shared_ptr<SVG> svg) {
clearChildren();

SVGWidget *sw = new SVGWidget(); SVGWidget *sw = new SVGWidget();
sw->wrap(); sw->wrap();
sw->svg = svg; sw->svg = svg;


+ 5
- 0
src/core/AudioInterface.cpp View File

@@ -432,6 +432,11 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
addChild(panel); addChild(panel);
} }


// addChild(createScrew<ScrewSilver>(Vec(15, 0)));
// addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
// addChild(createScrew<ScrewSilver>(Vec(15, 365)));
// addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));

float margin = 5; float margin = 5;
float labelHeight = 15; float labelHeight = 15;
float yPos = margin; float yPos = margin;


+ 109
- 0
src/core/Blank.cpp View File

@@ -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<ModuleWidget*>(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<ScrewSilver>(Vec(15, 0)));
addChild(createScrew<ScrewSilver>(Vec(15, 365)));
topRightScrew = createScrew<ScrewSilver>(Vec(box.size.x - 30, 0));
bottomRightScrew = createScrew<ScrewSilver>(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);
}

+ 44
- 0
src/core/Bridge.cpp View File

@@ -0,0 +1,44 @@
#include "core.hpp"

using namespace rack;


struct Bridge : Module {
enum ParamIds {
NUM_PARAMS
};
enum InputIds {
NUM_INPUTS
};
enum OutputIds {
NUM_OUTPUTS
};

Bridge() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
}
~Bridge() {
}
void step();
};


void Bridge::step() {
}


BridgeWidget::BridgeWidget() {
Bridge *module = new Bridge();
setModule(module);
box.size = Vec(15*8, 380);

{
Panel *panel = new LightPanel();
panel->box.size = box.size;
addChild(panel);
}

addChild(createScrew<ScrewSilver>(Vec(15, 0)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
addChild(createScrew<ScrewSilver>(Vec(15, 365)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
}

+ 2
- 0
src/core/core.cpp View File

@@ -8,4 +8,6 @@ void init(rack::Plugin *plugin) {
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface"); createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface");
createModel<MidiToCVWidget>(plugin, "MIDIToCVInterface", "MIDI-to-CV Interface"); createModel<MidiToCVWidget>(plugin, "MIDIToCVInterface", "MIDI-to-CV Interface");
createModel<MIDICCToCVWidget>(plugin, "MIDICCToCVInterface", "MIDI CC-to-CV Interface"); createModel<MIDICCToCVWidget>(plugin, "MIDICCToCVInterface", "MIDI CC-to-CV Interface");
// createModel<BridgeWidget>(plugin, "Bridge", "Bridge");
createModel<BlankWidget>(plugin, "Blank", "Blank");
} }

+ 15
- 0
src/core/core.hpp View File

@@ -21,3 +21,18 @@ struct MIDICCToCVWidget : ModuleWidget {
MIDICCToCVWidget(); MIDICCToCVWidget();
void step(); void step();
}; };

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);
};

Loading…
Cancel
Save