diff --git a/include/app/ModuleWidget.hpp b/include/app/ModuleWidget.hpp index 7aea4442..371793ee 100644 --- a/include/app/ModuleWidget.hpp +++ b/include/app/ModuleWidget.hpp @@ -111,6 +111,7 @@ struct ModuleWidget : widget::OpaqueWidget { INTERNAL bool& dragEnabled(); INTERNAL math::Vec& oldPos(); INTERNAL engine::Module* releaseModule(); + INTERNAL bool& selected(); }; diff --git a/include/app/RackWidget.hpp b/include/app/RackWidget.hpp index e43f476b..1e692c4b 100644 --- a/include/app/RackWidget.hpp +++ b/include/app/RackWidget.hpp @@ -38,8 +38,12 @@ struct RackWidget : widget::OpaqueWidget { void onHover(const HoverEvent& e) override; void onHoverKey(const HoverKeyEvent& e) override; - void onDragHover(const DragHoverEvent& e) override; void onButton(const ButtonEvent& e) override; + void onDragStart(const DragStartEvent& e) override; + void onDragEnd(const DragEndEvent& e) override; + void onDragHover(const DragHoverEvent& e) override; + + // Rack methods /** Completely clear the rack's modules and cables */ void clear(); @@ -65,6 +69,7 @@ struct RackWidget : widget::OpaqueWidget { bool isEmpty(); void updateModuleOldPositions(); history::ComplexAction* getModuleDragAction(); + void updateModuleSelections(); // Cable methods diff --git a/include/math.hpp b/include/math.hpp index 6545e1a1..330a1c96 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -308,10 +308,16 @@ struct Rect { Rect() {} Rect(Vec pos, Vec size) : pos(pos), size(size) {} Rect(float posX, float posY, float sizeX, float sizeY) : pos(Vec(posX, posY)), size(Vec(sizeX, sizeY)) {} - /** Constructs a Rect from the upper-left position `a` and lower-right pos `b`. */ + /** Constructs a Rect from a top-left and bottom-right vector. + */ static Rect fromMinMax(Vec a, Vec b) { return Rect(a, b.minus(a)); } + /** Constructs a Rect from any two opposite corners. + */ + static Rect fromCorners(Vec a, Vec b) { + return fromMinMax(a.min(b), a.max(b)); + } /** Returns the infinite Rect. */ static Rect inf() { return Rect(Vec(-INFINITY, -INFINITY), Vec(INFINITY, INFINITY)); diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index fdc7fb2a..7fbb9f37 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -418,6 +418,8 @@ struct ModuleWidget::Internal { math::Vec oldPos; widget::Widget* panel = NULL; + + bool selected = false; }; @@ -597,6 +599,17 @@ void ModuleWidget::draw(const DrawArgs& args) { // bndLabel(args.vg, 0, 0, INFINITY, INFINITY, -1, debugText.c_str()); // } + // Selection + if (internal->selected) { + nvgBeginPath(args.vg); + nvgRect(args.vg, 0.0, 0.0, VEC_ARGS(box.size)); + nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 0.25)); + nvgFill(args.vg); + nvgStrokeWidth(args.vg, 2.0); + nvgStrokeColor(args.vg, nvgRGBAf(1, 0, 0, 0.5)); + nvgStroke(args.vg); + } + nvgResetScissor(args.vg); } @@ -1210,5 +1223,11 @@ engine::Module* ModuleWidget::releaseModule() { } +bool& ModuleWidget::selected() { + return internal->selected; +} + + + } // namespace app } // namespace rack \ No newline at end of file diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index ad191256..08da83fc 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -72,12 +72,15 @@ struct CableContainer : widget::TransparentWidget { }; -// struct RackWidget::Internal { -// }; +struct RackWidget::Internal { + bool selecting = false; + math::Vec selectionStart; + math::Vec selectionEnd; +}; RackWidget::RackWidget() { - // internal = new Internal; + internal = new Internal; rail = new RailWidget; addChild(rail); @@ -91,7 +94,7 @@ RackWidget::RackWidget() { RackWidget::~RackWidget() { clear(); - // delete internal; + delete internal; } void RackWidget::step() { @@ -104,11 +107,24 @@ void RackWidget::draw(const DrawArgs& args) { nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1)); Widget::draw(args); + + // Draw selection rectangle + if (internal->selecting) { + nvgBeginPath(args.vg); + math::Rect selectionBox = math::Rect::fromCorners(internal->selectionStart, internal->selectionEnd); + nvgRect(args.vg, RECT_ARGS(selectionBox)); + nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 0.25)); + nvgFill(args.vg); + nvgStrokeWidth(args.vg, 2.0); + nvgStrokeColor(args.vg, nvgRGBAf(1, 0, 0, 0.5)); + nvgStroke(args.vg); + } } void RackWidget::onHover(const HoverEvent& e) { // Set before calling children's onHover() mousePos = e.pos; + OpaqueWidget::onHover(e); } @@ -125,12 +141,6 @@ void RackWidget::onHoverKey(const HoverKeyEvent& e) { } } -void RackWidget::onDragHover(const DragHoverEvent& e) { - // Set before calling children's onDragHover() - mousePos = e.pos; - OpaqueWidget::onDragHover(e); -} - void RackWidget::onButton(const ButtonEvent& e) { Widget::onButton(e); e.stopPropagating(); @@ -141,6 +151,37 @@ void RackWidget::onButton(const ButtonEvent& e) { APP->scene->moduleBrowser->show(); e.consume(this); } + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { + e.consume(this); + } +} + +void RackWidget::onDragStart(const DragStartEvent& e) { + if (e.button == GLFW_MOUSE_BUTTON_LEFT) { + // Deselect all modules + updateModuleSelections(); + internal->selecting = true; + internal->selectionStart = mousePos; + internal->selectionEnd = mousePos; + } +} + +void RackWidget::onDragEnd(const DragEndEvent& e) { + if (e.button == GLFW_MOUSE_BUTTON_LEFT) { + internal->selecting = false; + } +} + +void RackWidget::onDragHover(const DragHoverEvent& e) { + // Set before calling children's onDragHover() + mousePos = e.pos; + + if (internal->selecting) { + internal->selectionEnd = mousePos; + updateModuleSelections(); + } + + OpaqueWidget::onDragHover(e); } void RackWidget::clear() { @@ -590,6 +631,15 @@ history::ComplexAction* RackWidget::getModuleDragAction() { return h; } +void RackWidget::updateModuleSelections() { + math::Rect selectionBox = math::Rect::fromCorners(internal->selectionStart, internal->selectionEnd); + for (widget::Widget* w : moduleContainer->children) { + ModuleWidget* mw = dynamic_cast(w); + assert(mw); + bool selected = internal->selecting && selectionBox.intersects(mw->box); + mw->selected() = selected; + } +} void RackWidget::clearCables() { incompleteCable = NULL;