diff --git a/include/math.hpp b/include/math.hpp index bd89f646..73bee8e5 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -271,6 +271,21 @@ struct Rect { r.pos.y = clampf(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y); return r; } + /** Expands this Rect to contain `other` */ + Rect expand(Rect other) { + Rect r; + r.pos.x = fminf(pos.x, other.pos.x); + r.pos.y = fminf(pos.y, other.pos.y); + r.size.x = fmaxf(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x; + r.size.y = fmaxf(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y; + return r; + } + /** Returns a Rect with its position set to zero */ + Rect zeroPos() { + Rect r; + r.size = size; + return r; + } }; diff --git a/include/widgets.hpp b/include/widgets.hpp index 17150936..a3fe6d92 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -56,8 +56,13 @@ struct Widget { virtual ~Widget(); - Vec getAbsolutePos(); Rect getChildrenBoundingBox(); + /** Returns `v` transformed into the coordinate system of `relative` */ + virtual Vec getRelativeOffset(Vec v, Widget *relative); + /** Returns `v` transformed into world coordinates */ + Vec getAbsoluteOffset(Vec v) { + return getRelativeOffset(v, NULL); + } /** Returns a subset of the given Rect bounded by the box of this widget and all ancestors */ virtual Rect getViewport(Rect r); @@ -149,6 +154,7 @@ struct TransformWidget : Widget { struct ZoomWidget : Widget { float zoom = 1.0; + Vec getRelativeOffset(Vec v, Widget *relative) override; Rect getViewport(Rect r) override; void setZoom(float zoom); void draw(NVGcontext *vg) override; diff --git a/src/app/RackScrollWidget.cpp b/src/app/RackScrollWidget.cpp index b2d4357f..d3a9dc36 100644 --- a/src/app/RackScrollWidget.cpp +++ b/src/app/RackScrollWidget.cpp @@ -6,7 +6,7 @@ namespace rack { void RackScrollWidget::step() { - Vec pos = gMousePos.minus(getAbsolutePos()); + Vec pos = gRackWidget->lastMousePos; // Scroll rack if dragging cable near the edge of the screen if (gRackWidget->wireContainer->activeWire) { float margin = 20.0; diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index f890485a..5a04460a 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -39,7 +39,7 @@ struct QuitItem : MenuItem { struct FileChoice : ChoiceButton { void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; { @@ -70,7 +70,7 @@ struct SampleRateItem : MenuItem { struct SampleRateChoice : ChoiceButton { void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; PauseItem *pauseItem = new PauseItem(); diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 90ef6636..be314394 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -124,31 +124,27 @@ void WireWidget::updateWire() { } Vec WireWidget::getOutputPos() { - Vec pos; if (outputPort) { - pos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); + return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), gRackWidget); } else if (hoveredOutputPort) { - pos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); + return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), gRackWidget); } else { return gRackWidget->lastMousePos; } - return pos.minus(getAbsolutePos().minus(box.pos)); } Vec WireWidget::getInputPos() { - Vec pos; if (inputPort) { - pos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); + return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), gRackWidget); } else if (hoveredInputPort) { - pos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); + return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), gRackWidget); } else { return gRackWidget->lastMousePos; } - return pos.minus(getAbsolutePos().minus(box.pos)); } void WireWidget::draw(NVGcontext *vg) { diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 69a28caa..0da52dc0 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -335,7 +335,7 @@ struct AudioChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; int deviceCount = audioInterface->getDeviceCount(); @@ -373,7 +373,7 @@ struct SampleRateChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000}; @@ -404,7 +404,7 @@ struct BlockSizeChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index d37ef9ff..ac7c9626 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -201,7 +201,7 @@ struct ClockRatioChoice : ChoiceButton { void onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; for (unsigned long ratio = 0; ratio < ratioNames.size(); ratio++) { diff --git a/src/core/MidiIO.cpp b/src/core/MidiIO.cpp index 346b93e0..ed87bbcf 100644 --- a/src/core/MidiIO.cpp +++ b/src/core/MidiIO.cpp @@ -182,7 +182,7 @@ void MidiItem::onAction() { void MidiChoice::onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { @@ -217,7 +217,7 @@ void ChannelItem::onAction() { void ChannelChoice::onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 5f27f7b3..60e43a07 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -259,7 +259,7 @@ struct ModeChoice : ChoiceButton { void onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; for (unsigned long i = 0; i < modeNames.size(); i++) { diff --git a/src/widgets/Tooltip.cpp b/src/widgets/Tooltip.cpp index bc177264..d6184fab 100644 --- a/src/widgets/Tooltip.cpp +++ b/src/widgets/Tooltip.cpp @@ -6,7 +6,7 @@ namespace rack { void Tooltip::step() { // Follow the mouse - box.pos = gMousePos.minus(parent->getAbsolutePos()); + box.pos = gMousePos; // Wrap size to contents // box.size = getChildrenBoundingBox().getBottomRight(); diff --git a/src/widgets/Widget.cpp b/src/widgets/Widget.cpp index 5fbab712..65af2567 100644 --- a/src/widgets/Widget.cpp +++ b/src/widgets/Widget.cpp @@ -16,28 +16,28 @@ Widget::~Widget() { clearChildren(); } -Vec Widget::getAbsolutePos() { - // Recursively compute position offset from parents - if (!parent) { - return box.pos; - } - else { - return box.pos.plus(parent->getAbsolutePos()); +Rect Widget::getChildrenBoundingBox() { + Rect bound; + for (Widget *child : children) { + if (child == children.front()) { + bound = child->box; + } + else { + bound = bound.expand(child->box); + } } + return bound; } -Rect Widget::getChildrenBoundingBox() { - if (children.empty()) { - return Rect(); +Vec Widget::getRelativeOffset(Vec v, Widget *relative) { + if (this == relative) { + return v; } - - Vec topLeft = Vec(INFINITY, INFINITY); - Vec bottomRight = Vec(-INFINITY, -INFINITY); - for (Widget *child : children) { - topLeft = topLeft.min(child->box.pos); - bottomRight = bottomRight.max(child->box.getBottomRight()); + v = v.plus(box.pos); + if (parent) { + v = parent->getRelativeOffset(v, relative); } - return Rect(topLeft, bottomRight.minus(topLeft)); + return v; } Rect Widget::getViewport(Rect r) { diff --git a/src/widgets/ZoomWidget.cpp b/src/widgets/ZoomWidget.cpp index d65ab035..a0bb4b0c 100644 --- a/src/widgets/ZoomWidget.cpp +++ b/src/widgets/ZoomWidget.cpp @@ -3,6 +3,11 @@ namespace rack { + +Vec ZoomWidget::getRelativeOffset(Vec v, Widget *relative) { + return Widget::getRelativeOffset(v.mult(zoom), relative); +} + Rect ZoomWidget::getViewport(Rect r) { r.pos = r.pos.mult(zoom); r.size = r.size.mult(zoom);