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