@@ -70,10 +70,12 @@ struct WireWidget : OpaqueWidget { | |||
WireWidget(); | |||
~WireWidget(); | |||
/** Synchronizes the plugged state of the widget to the owned wire */ | |||
void updateWire(); | |||
Vec getOutputPos(); | |||
Vec getInputPos(); | |||
void draw(NVGcontext *vg); | |||
void drawOutputPlug(NVGcontext *vg); | |||
void drawInputPlug(NVGcontext *vg); | |||
void drawPlugs(NVGcontext *vg); | |||
}; | |||
struct RackWidget : OpaqueWidget { | |||
@@ -82,7 +84,6 @@ struct RackWidget : OpaqueWidget { | |||
// Only put WireWidgets in here | |||
Widget *wireContainer; | |||
WireWidget *activeWire = NULL; | |||
std::shared_ptr<Image> railsImage; | |||
RackWidget(); | |||
~RackWidget(); | |||
@@ -124,7 +124,6 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y); | |||
// CPU usage text | |||
if (dynamic_cast<RackScene*>(gScene)->toolbar->cpuUsageButton->value > 0.0) { | |||
@@ -20,9 +20,11 @@ void Panel::draw(NVGcontext *vg) { | |||
nvgFill(vg); | |||
} | |||
// Border color | |||
// Border | |||
nvgBeginPath(vg); | |||
nvgRect(vg, 0.5, 0.5, box.size.x - 1, box.size.y - 1); | |||
nvgStrokeColor(vg, borderColor); | |||
nvgStrokeWidth(vg, 0.5); | |||
nvgStrokeWidth(vg, 1.0); | |||
nvgStroke(vg); | |||
} | |||
@@ -8,14 +8,28 @@ | |||
namespace rack { | |||
static Vec rackGridSize = Vec(15, 380); | |||
struct WireContainer : TransparentWidget { | |||
void draw(NVGcontext *vg) { | |||
// Wire plugs | |||
for (Widget *child : children) { | |||
WireWidget *wire = dynamic_cast<WireWidget*>(child); | |||
assert(wire); | |||
wire->drawPlugs(vg); | |||
} | |||
Widget::draw(vg); | |||
} | |||
}; | |||
RackWidget::RackWidget() { | |||
moduleContainer = new Widget(); | |||
addChild(moduleContainer); | |||
wireContainer = new TransparentWidget(); | |||
wireContainer = new WireContainer(); | |||
addChild(wireContainer); | |||
railsImage = Image::load("res/rails.png"); | |||
} | |||
RackWidget::~RackWidget() { | |||
@@ -227,12 +241,12 @@ void RackWidget::fromJson(json_t *rootJ) { | |||
void RackWidget::repositionModule(ModuleWidget *module) { | |||
// Create possible positions | |||
int x0 = roundf(module->requestedPos.x / 15); | |||
int y0 = roundf(module->requestedPos.y / 380); | |||
int x0 = roundf(module->requestedPos.x / rackGridSize.x); | |||
int y0 = roundf(module->requestedPos.y / rackGridSize.y); | |||
std::vector<Vec> positions; | |||
for (int y = maxi(0, y0 - 2); y < y0 + 2; y++) { | |||
for (int x = maxi(0, x0 - 40); x < x0 + 40; x++) { | |||
positions.push_back(Vec(x*15, y*380)); | |||
positions.push_back(Vec(x * rackGridSize.x, y * rackGridSize.y)); | |||
} | |||
} | |||
@@ -279,7 +293,6 @@ void RackWidget::step() { | |||
} | |||
// Autosave every 15 seconds | |||
// (This is alpha software, expect crashes!) | |||
if (gGuiFrame % (60*15) == 0) { | |||
savePatch("autosave.json"); | |||
} | |||
@@ -295,13 +308,41 @@ void RackWidget::draw(NVGcontext *vg) { | |||
nvgFillColor(vg, nvgRGBf(0.2, 0.2, 0.2)); | |||
nvgFill(vg); | |||
// Rails image | |||
{ | |||
int imageWidth, imageHeight; | |||
nvgImageSize(vg, railsImage->handle, &imageWidth, &imageHeight); | |||
NVGpaint paint = nvgImagePattern(vg, 0.0, 0.0, imageWidth, imageHeight, 0.0, railsImage->handle, 1.0); | |||
nvgFillPaint(vg, paint); | |||
// Rails | |||
// TODO Put this in a framebuffer cache and tile | |||
const float railHeight = 15; | |||
nvgFillColor(vg, nvgRGBf(0.8, 0.8, 0.8)); | |||
nvgStrokeWidth(vg, 1.0); | |||
nvgStrokeColor(vg, nvgRGBf(0.6, 0.6, 0.6)); | |||
float holeRadius = 3.5; | |||
for (float railY = 0; railY < box.size.y; railY += rackGridSize.y) { | |||
// Top rail | |||
nvgBeginPath(vg); | |||
nvgRect(vg, 0, railY, box.size.x, railHeight); | |||
for (float railX = 0; railX < box.size.x; railX += rackGridSize.x) { | |||
nvgCircle(vg, railX + rackGridSize.x / 2, railY + railHeight / 2, holeRadius); | |||
nvgPathWinding(vg, NVG_HOLE); | |||
} | |||
nvgFill(vg); | |||
nvgBeginPath(vg); | |||
nvgMoveTo(vg, 0, railY + railHeight - 0.5); | |||
nvgLineTo(vg, box.size.x, railY + railHeight - 0.5); | |||
nvgStroke(vg); | |||
// Bottom rail | |||
nvgBeginPath(vg); | |||
nvgRect(vg, 0, railY + rackGridSize.y - railHeight, box.size.x, railHeight); | |||
for (float railX = 0; railX < box.size.x; railX += rackGridSize.x) { | |||
nvgCircle(vg, railX + rackGridSize.x / 2, railY + rackGridSize.y - railHeight + railHeight / 2, holeRadius); | |||
nvgPathWinding(vg, NVG_HOLE); | |||
} | |||
nvgFill(vg); | |||
nvgBeginPath(vg); | |||
nvgMoveTo(vg, 0, railY + rackGridSize.y - 0.5); | |||
nvgLineTo(vg, box.size.x, railY + rackGridSize.y - 0.5); | |||
nvgStroke(vg); | |||
} | |||
Widget::draw(vg); | |||
@@ -118,46 +118,49 @@ void WireWidget::updateWire() { | |||
} | |||
} | |||
void WireWidget::draw(NVGcontext *vg) { | |||
Vec absolutePos = getAbsolutePos().minus(box.pos); | |||
float opacity = dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | |||
float tension = dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value; | |||
// Display the actively dragged wire as opaque | |||
if (gRackWidget->activeWire == this) | |||
opacity = 1.0; | |||
// Compute location of outputPos and inputPos | |||
Vec outputPos; | |||
Vec WireWidget::getOutputPos() { | |||
Vec pos; | |||
if (outputPort) { | |||
outputPos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); | |||
pos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); | |||
} | |||
else if (hoveredOutputPort) { | |||
outputPos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); | |||
pos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); | |||
} | |||
else { | |||
outputPos = gMousePos; | |||
pos = gMousePos; | |||
} | |||
return pos.minus(getAbsolutePos().minus(box.pos)); | |||
} | |||
Vec inputPos; | |||
Vec WireWidget::getInputPos() { | |||
Vec pos; | |||
if (inputPort) { | |||
inputPos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); | |||
pos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); | |||
} | |||
else if (hoveredInputPort) { | |||
inputPos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); | |||
pos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); | |||
} | |||
else { | |||
inputPos = gMousePos; | |||
pos = gMousePos; | |||
} | |||
return pos.minus(getAbsolutePos().minus(box.pos)); | |||
} | |||
outputPos = outputPos.minus(absolutePos); | |||
inputPos = inputPos.minus(absolutePos); | |||
void WireWidget::draw(NVGcontext *vg) { | |||
float opacity = dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | |||
float tension = dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value; | |||
drawWire(vg, outputPos, inputPos, color, tension, opacity); | |||
drawPlug(vg, outputPos, color); | |||
drawPlug(vg, inputPos, color); | |||
// Display the actively dragged wire as opaque | |||
if (gRackWidget->activeWire == this) | |||
opacity = 1.0; | |||
drawWire(vg, getOutputPos(), getInputPos(), color, tension, opacity); | |||
drawPlug(vg, getOutputPos(), color); | |||
drawPlug(vg, getInputPos(), color); | |||
} | |||
void WireWidget::drawPlugs(NVGcontext *vg) { | |||
} | |||
} // namespace rack |