diff --git a/include/math.hpp b/include/math.hpp index 41e59a5c..7568153c 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -62,10 +62,10 @@ inline float eucmodf(float a, float base) { } /** Limits a value between a minimum and maximum -If min < max, returns max +If min > max, the limits are switched */ inline float clampf(float x, float min, float max) { - return fmaxf(fminf(x, max), min); + return fmaxf(fminf(x, fmaxf(min, max)), fminf(min, max)); } /** If the magnitude of x if less than eps, return 0 */ @@ -199,11 +199,16 @@ struct Rect { return Rect(min, max.minus(min)); } - /** Returns whether this Rect contains another Rect, 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) { 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) { + 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) { return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x) diff --git a/include/widgets.hpp b/include/widgets.hpp index b3272b7c..43b0719d 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -145,7 +145,7 @@ struct TransparentWidget : virtual Widget { Widget *onScroll(Vec pos, Vec scrollRel) {return NULL;} }; -/** Widget that itself responds to mouse events */ +/** Widget that automatically responds to all mouse events but gives a chance for children to respond instead */ struct OpaqueWidget : virtual Widget { Widget *onMouseDown(Vec pos, int button) { Widget *w = Widget::onMouseDown(pos, button); @@ -165,6 +165,13 @@ struct OpaqueWidget : virtual Widget { onMouseMoveOpaque(mouseRel); return this; } + Widget *onScroll(Vec pos, Vec scrollRel) { + Widget *w = Widget::onScroll(pos, scrollRel); + if (w) return w; + if (onScrollOpaque(scrollRel)) + return this; + return NULL; + } /** "High level" events called by the above lower level events. Use these if you don't care about the clicked position. @@ -172,6 +179,7 @@ struct OpaqueWidget : virtual Widget { virtual void onMouseDownOpaque(int button) {} virtual void onMouseUpOpaque(int button) {} virtual void onMouseMoveOpaque(Vec mouseRel) {} + virtual bool onScrollOpaque(Vec scrollRel) {return false;} }; struct SpriteWidget : virtual Widget { @@ -249,9 +257,6 @@ struct Label : Widget { // Deletes itself from parent when clicked struct MenuOverlay : OpaqueWidget { void step(); - Widget *onScroll(Vec pos, Vec scrollRel) { - return this; - } void onDragDrop(Widget *origin); }; @@ -262,6 +267,7 @@ struct Menu : OpaqueWidget { // Resizes menu and calls addChild() void pushChild(Widget *child); void draw(NVGcontext *vg); + bool onScrollOpaque(Vec scrollRel); }; struct MenuEntry : OpaqueWidget { @@ -340,8 +346,8 @@ struct ScrollBar : OpaqueWidget { ScrollBar() { box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); } + void step(); void draw(NVGcontext *vg); - void move(float delta); void onDragStart(); void onDragMove(Vec mouseRel); void onDragEnd(); @@ -356,7 +362,7 @@ struct ScrollWidget : OpaqueWidget { ScrollWidget(); void step(); void draw(NVGcontext *vg); - Widget *onScroll(Vec pos, Vec scrollRel); + bool onScrollOpaque(Vec scrollRel); }; struct TextField : OpaqueWidget { diff --git a/src/gui.cpp b/src/gui.cpp index 54daebdf..49925c12 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -151,7 +151,7 @@ void cursorEnterCallback(GLFWwindow* window, int entered) { void scrollCallback(GLFWwindow *window, double x, double y) { Vec scrollRel = Vec(x, y); // onScroll - gScene->onScroll(gMousePos, scrollRel.mult(-95)); + gScene->onScroll(gMousePos, scrollRel.mult(-38.0)); } void charCallback(GLFWwindow *window, unsigned int codepoint) { diff --git a/src/widgets/Menu.cpp b/src/widgets/Menu.cpp index 6647b120..da72c0f0 100644 --- a/src/widgets/Menu.cpp +++ b/src/widgets/Menu.cpp @@ -29,4 +29,13 @@ void Menu::draw(NVGcontext *vg) { } +bool Menu::onScrollOpaque(Vec scrollRel) { + if (!parent) + return true; + if (!parent->box.contains(box)) + box.pos = box.pos.plus(scrollRel.neg()); + return true; +} + + } // namespace rack diff --git a/src/widgets/MenuOverlay.cpp b/src/widgets/MenuOverlay.cpp index d58be3ba..841bfb61 100644 --- a/src/widgets/MenuOverlay.cpp +++ b/src/widgets/MenuOverlay.cpp @@ -8,6 +8,7 @@ void MenuOverlay::step() { for (Widget *child : children) { child->box = child->box.clamp(Rect(Vec(0, 0), box.size)); } + Widget::step(); } diff --git a/src/widgets/ScrollBar.cpp b/src/widgets/ScrollBar.cpp index b60c196a..ae7ba6d4 100644 --- a/src/widgets/ScrollBar.cpp +++ b/src/widgets/ScrollBar.cpp @@ -4,6 +4,13 @@ namespace rack { +void ScrollBar::step() { + float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); + float maxOffset = containerSize - boxSize; + containerOffset = clampf(containerOffset, 0.0, maxOffset); + Widget::step(); +} + void ScrollBar::draw(NVGcontext *vg) { float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); float maxOffset = containerSize - boxSize; @@ -13,21 +20,13 @@ void ScrollBar::draw(NVGcontext *vg) { bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size); } -void ScrollBar::move(float delta) { - float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); - float maxOffset = containerSize - boxSize; - containerOffset += delta; - containerOffset = clampf(containerOffset, 0.0, maxOffset); -} - void ScrollBar::onDragStart() { state = BND_ACTIVE; guiCursorLock(); } void ScrollBar::onDragMove(Vec mouseRel) { - float delta = (orientation == VERTICAL ? mouseRel.y : mouseRel.x); - move(delta); + containerOffset += (orientation == VERTICAL ? mouseRel.y : mouseRel.x); } void ScrollBar::onDragEnd() { diff --git a/src/widgets/ScrollWidget.cpp b/src/widgets/ScrollWidget.cpp index 6e0549ba..67bd6082 100644 --- a/src/widgets/ScrollWidget.cpp +++ b/src/widgets/ScrollWidget.cpp @@ -40,13 +40,10 @@ void ScrollWidget::draw(NVGcontext *vg) { Widget::draw(vg); } -Widget *ScrollWidget::onScroll(Vec pos, Vec scrollRel) { - Widget *w = Widget::onScroll(pos, scrollRel); - if (w) return w; - - hScrollBar->move(scrollRel.x); - vScrollBar->move(scrollRel.y); - return this; +bool ScrollWidget::onScrollOpaque(Vec scrollRel) { + hScrollBar->containerOffset += scrollRel.x; + vScrollBar->containerOffset += scrollRel.y; + return true; }