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