@@ -258,17 +258,17 @@ struct Rect { | |||
} | |||
/** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right */ | |||
bool contains(Vec v) const { | |||
bool isContaining(Vec v) const { | |||
return pos.x <= v.x && v.x < pos.x + size.x | |||
&& pos.y <= v.y && v.y < pos.y + size.y; | |||
} | |||
/** Returns whether this Rect contains an entire Rect */ | |||
bool contains(Rect r) const { | |||
bool isContaining(Rect r) const { | |||
return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x | |||
&& pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y; | |||
} | |||
/** Returns whether this Rect overlaps with another Rect */ | |||
bool intersects(Rect r) const { | |||
bool isIntersecting(Rect r) const { | |||
return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x) | |||
&& (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y); | |||
} | |||
@@ -313,13 +313,22 @@ struct Rect { | |||
r.pos.y = math::clampSafe(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) const { | |||
/** Expands this Rect to contain `b` */ | |||
Rect expand(Rect b) const { | |||
Rect r; | |||
r.pos.x = std::min(pos.x, other.pos.x); | |||
r.pos.y = std::min(pos.y, other.pos.y); | |||
r.size.x = std::max(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x; | |||
r.size.y = std::max(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y; | |||
r.pos.x = std::min(pos.x, b.pos.x); | |||
r.pos.y = std::min(pos.y, b.pos.y); | |||
r.size.x = std::max(pos.x + size.x, b.pos.x + b.size.x) - r.pos.x; | |||
r.size.y = std::max(pos.y + size.y, b.pos.y + b.size.y) - r.pos.y; | |||
return r; | |||
} | |||
/** Returns the intersection of `this` and `b` */ | |||
Rect intersect(Rect b) const { | |||
Rect r; | |||
r.pos.x = std::max(pos.x, b.pos.x); | |||
r.pos.y = std::max(pos.y, b.pos.y); | |||
r.size.x = std::min(pos.x + size.x, b.pos.x + b.size.x) - r.pos.x; | |||
r.size.y = std::min(pos.y + size.y, b.pos.y + b.size.y) - r.pos.y; | |||
return r; | |||
} | |||
/** Returns a Rect with its position set to zero */ | |||
@@ -335,6 +344,10 @@ struct Rect { | |||
r.size = size.plus(delta.mult(2.f)); | |||
return r; | |||
} | |||
DEPRECATED bool contains(Vec v) const {return isContaining(v);} | |||
DEPRECATED bool contains(Rect r) const {return isContaining(r);} | |||
DEPRECATED bool intersects(Rect r) const {return isIntersecting(r);} | |||
}; | |||
@@ -11,7 +11,8 @@ namespace rack { | |||
struct DrawContext { | |||
mutable NVGcontext *vg; | |||
NVGcontext *vg; | |||
math::Rect clipBox = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY)); | |||
}; | |||
@@ -101,7 +102,7 @@ struct Widget { | |||
// Filter child by visibility and position | |||
if (!child->visible) | |||
continue; | |||
if (!child->box.contains(e.pos)) | |||
if (!child->box.isContaining(e.pos)) | |||
continue; | |||
// Clone event and adjust its position | |||
@@ -22,7 +22,10 @@ static std::set<Model*> sFavoriteModels; | |||
struct ModuleBox : OpaqueWidget { | |||
Model *model; | |||
bool initialized = false; | |||
/** Lazily created */ | |||
Widget *previewWidget = NULL; | |||
/** Number of frames since draw() has been called */ | |||
int visibleFrames = 0; | |||
void setModel(Model *model) { | |||
this->model = model; | |||
@@ -47,9 +50,17 @@ struct ModuleBox : OpaqueWidget { | |||
addChild(pluginLabel); | |||
} | |||
void step() override { | |||
if (previewWidget && ++visibleFrames >= 60) { | |||
removeChild(previewWidget); | |||
delete previewWidget; | |||
previewWidget = NULL; | |||
} | |||
} | |||
void draw(const DrawContext &ctx) override { | |||
// Lazily create ModuleWidget when drawn | |||
if (!initialized) { | |||
if (!previewWidget) { | |||
Widget *transparentWidget = new TransparentWidget; | |||
addChild(transparentWidget); | |||
@@ -71,7 +82,8 @@ struct ModuleBox : OpaqueWidget { | |||
zoomWidget->box.size.y = RACK_GRID_HEIGHT; | |||
float width = std::ceil(zoomWidget->box.size.x); | |||
box.size.x = std::max(box.size.x, width); | |||
initialized = true; | |||
previewWidget = transparentWidget; | |||
} | |||
OpaqueWidget::draw(ctx); | |||
@@ -113,6 +125,7 @@ ModuleBrowser::ModuleBrowser() { | |||
moduleLayout->spacing = math::Vec(10, 10); | |||
moduleScroll->container->addChild(moduleLayout); | |||
for (int i = 0; i < 100; i++) | |||
for (Plugin *plugin : plugin::plugins) { | |||
for (Model *model : plugin->models) { | |||
ModuleBox *moduleBox = new ModuleBox; | |||
@@ -104,7 +104,7 @@ void RackWidget::step() { | |||
// Adjust size and position of rails | |||
Widget *rail = rails->children.front(); | |||
math::Rect bound = getViewport(math::Rect(math::Vec(), box.size)); | |||
if (!rails->box.contains(bound)) { | |||
if (!rails->box.isContaining(bound)) { | |||
math::Vec cellMargin = math::Vec(20, 1); | |||
rails->box.pos = bound.pos.div(RACK_GRID_SIZE).floor().minus(cellMargin).mult(RACK_GRID_SIZE); | |||
rails->box.size = bound.size.plus(cellMargin.mult(RACK_GRID_SIZE).mult(2)); | |||
@@ -369,7 +369,7 @@ bool RackWidget::requestModuleBox(ModuleWidget *m, math::Rect requestedBox) { | |||
// Don't intersect with self | |||
if (m == m2) | |||
continue; | |||
if (requestedBox.intersects(m2->box)) { | |||
if (requestedBox.isIntersecting(m2->box)) { | |||
return false; | |||
} | |||
} | |||
@@ -54,7 +54,7 @@ void Menu::draw(const DrawContext &ctx) { | |||
} | |||
void Menu::onHoverScroll(const event::HoverScroll &e) { | |||
if (parent && !parent->box.contains(box)) | |||
if (parent && !parent->box.isContaining(box)) | |||
box.pos.y += e.scrollDelta.y; | |||
} | |||
@@ -94,25 +94,31 @@ void Widget::step() { | |||
} | |||
void Widget::draw(const DrawContext &ctx) { | |||
// Iterate children | |||
for (Widget *child : children) { | |||
// Don't draw if invisible | |||
if (!child->visible) | |||
continue; | |||
// Don't draw if child is outside bounding box | |||
if (!box.zeroPos().intersects(child->box)) | |||
// Don't draw if child is outside clip box | |||
if (!ctx.clipBox.isIntersecting(child->box)) | |||
continue; | |||
DrawContext childCtx = ctx; | |||
// Intersect child clip box with self | |||
childCtx.clipBox = childCtx.clipBox.intersect(child->box); | |||
childCtx.clipBox.pos = childCtx.clipBox.pos.minus(child->box.pos); | |||
nvgSave(ctx.vg); | |||
nvgTranslate(ctx.vg, child->box.pos.x, child->box.pos.y); | |||
DrawContext childCtx = ctx; | |||
child->draw(childCtx); | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
// Call deprecated draw function, which does nothing by default | |||
child->draw(ctx.vg); | |||
#pragma GCC diagnostic pop | |||
child->draw(childCtx); | |||
// Draw red hitboxes | |||
// if (app()->event->hoveredWidget == child) { | |||
// nvgBeginPath(ctx.vg); | |||
@@ -29,9 +29,12 @@ void ZoomWidget::setZoom(float zoom) { | |||
} | |||
void ZoomWidget::draw(const DrawContext &ctx) { | |||
DrawContext zoomCtx = ctx; | |||
zoomCtx.clipBox.pos = zoomCtx.clipBox.pos.div(zoom); | |||
zoomCtx.clipBox.size = zoomCtx.clipBox.size.div(zoom); | |||
// No need to save the state because that is done in the parent | |||
nvgScale(ctx.vg, zoom, zoom); | |||
Widget::draw(ctx); | |||
Widget::draw(zoomCtx); | |||
} | |||