diff --git a/include/math.hpp b/include/math.hpp index 7568153c..c01135b1 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -147,6 +147,8 @@ inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) // 2D float vector //////////////////// +struct Rect; + struct Vec { float x, y; @@ -186,6 +188,7 @@ struct Vec { bool isZero() { return x == 0.0 && y == 0.0; } + Vec clamp(Rect bound); }; @@ -237,4 +240,11 @@ struct Rect { }; +inline Vec Vec::clamp(Rect bound) { + return Vec( + clampf(x, bound.pos.x, bound.pos.x + bound.size.x), + clampf(y, bound.pos.y, bound.pos.y + bound.size.y)); +} + + } // namespace rack diff --git a/include/widgets.hpp b/include/widgets.hpp index 43b0719d..d6ac8b96 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -256,8 +256,8 @@ struct Label : Widget { // Deletes itself from parent when clicked struct MenuOverlay : OpaqueWidget { - void step(); void onDragDrop(Widget *origin); + bool onScrollOpaque(Vec scrollRel) {return true;} }; struct Menu : OpaqueWidget { @@ -266,6 +266,8 @@ struct Menu : OpaqueWidget { } // Resizes menu and calls addChild() void pushChild(Widget *child); + void fit(); + void step(); void draw(NVGcontext *vg); bool onScrollOpaque(Vec scrollRel); }; @@ -337,31 +339,30 @@ struct Slider : OpaqueWidget, QuantityWidget { void onDragEnd(); }; +/** Parent must be a ScrollWidget */ struct ScrollBar : OpaqueWidget { enum { VERTICAL, HORIZONTAL } orientation; - float containerOffset = 0.0; - float containerSize = 0.0; BNDwidgetState state = BND_DEFAULT; ScrollBar() { box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); } - void step(); void draw(NVGcontext *vg); void onDragStart(); void onDragMove(Vec mouseRel); void onDragEnd(); }; -// Handles a container with scrollbars +/** Handles a container with ScrollBar */ struct ScrollWidget : OpaqueWidget { Widget *container; - ScrollBar *hScrollBar; - ScrollBar *vScrollBar; + ScrollBar *horizontalScrollBar; + ScrollBar *verticalScrollBar; + Vec offset; ScrollWidget(); void step(); - void draw(NVGcontext *vg); + Widget *onMouseMove(Vec pos, Vec mouseRel); bool onScrollOpaque(Vec scrollRel); }; diff --git a/src/gui.cpp b/src/gui.cpp index 49925c12..34c1ee8b 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(-38.0)); + gScene->onScroll(gMousePos, scrollRel.mult(50.0)); } void charCallback(GLFWwindow *window, unsigned int codepoint) { diff --git a/src/widgets/Menu.cpp b/src/widgets/Menu.cpp index da72c0f0..f4a96955 100644 --- a/src/widgets/Menu.cpp +++ b/src/widgets/Menu.cpp @@ -9,6 +9,17 @@ void Menu::pushChild(Widget *child) { box.size.y += child->box.size.y; } +void Menu::fit() { + // Try to fit into the parent's box + if (parent) + box = box.clamp(Rect(Vec(0, 0), parent->box.size)); +} + +void Menu::step() { + fit(); + Widget::step(); +} + void Menu::draw(NVGcontext *vg) { // Resize the width to the widest child for (Widget *child : children) { @@ -33,7 +44,8 @@ bool Menu::onScrollOpaque(Vec scrollRel) { if (!parent) return true; if (!parent->box.contains(box)) - box.pos = box.pos.plus(scrollRel.neg()); + box.pos = box.pos.plus(scrollRel); + fit(); return true; } diff --git a/src/widgets/MenuOverlay.cpp b/src/widgets/MenuOverlay.cpp index 841bfb61..bb2a4982 100644 --- a/src/widgets/MenuOverlay.cpp +++ b/src/widgets/MenuOverlay.cpp @@ -3,15 +3,6 @@ namespace rack { -void MenuOverlay::step() { - // Try to fit all children into the overlay's box - for (Widget *child : children) { - child->box = child->box.clamp(Rect(Vec(0, 0), box.size)); - } - - Widget::step(); -} - void MenuOverlay::onDragDrop(Widget *origin) { if (origin == this) { // deletes `this` diff --git a/src/widgets/ScrollBar.cpp b/src/widgets/ScrollBar.cpp index ae7ba6d4..a7722d7d 100644 --- a/src/widgets/ScrollBar.cpp +++ b/src/widgets/ScrollBar.cpp @@ -4,17 +4,16 @@ 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; - float offset = containerOffset / maxOffset; + ScrollWidget *scrollWidget = dynamic_cast(parent); + assert(scrollWidget); + Vec containerCorner = scrollWidget->container->getChildrenBoundingBox().getBottomRight(); + + float containerSize = (orientation == HORIZONTAL) ? containerCorner.x : containerCorner.y; + float boxSize = (orientation == HORIZONTAL) ? box.size.x : box.size.y; + float offset = (orientation == HORIZONTAL) ? scrollWidget->offset.x : scrollWidget->offset.y; + offset = offset / (containerSize - boxSize); float size = boxSize / containerSize; size = clampf(size, 0.0, 1.0); bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size); @@ -26,7 +25,12 @@ void ScrollBar::onDragStart() { } void ScrollBar::onDragMove(Vec mouseRel) { - containerOffset += (orientation == VERTICAL ? mouseRel.y : mouseRel.x); + ScrollWidget *scrollWidget = dynamic_cast(parent); + assert(scrollWidget); + if (orientation == HORIZONTAL) + scrollWidget->offset.x += mouseRel.x; + else + scrollWidget->offset.y += mouseRel.y; } void ScrollBar::onDragEnd() { diff --git a/src/widgets/ScrollWidget.cpp b/src/widgets/ScrollWidget.cpp index 67bd6082..34dc5319 100644 --- a/src/widgets/ScrollWidget.cpp +++ b/src/widgets/ScrollWidget.cpp @@ -1,4 +1,5 @@ #include "widgets.hpp" +#include "gui.hpp" namespace rack { @@ -7,42 +8,46 @@ ScrollWidget::ScrollWidget() { container = new Widget(); addChild(container); - hScrollBar = new ScrollBar(); - hScrollBar->orientation = ScrollBar::HORIZONTAL; - addChild(hScrollBar); + horizontalScrollBar = new ScrollBar(); + horizontalScrollBar->orientation = ScrollBar::HORIZONTAL; + addChild(horizontalScrollBar); - vScrollBar = new ScrollBar(); - vScrollBar->orientation = ScrollBar::VERTICAL; - addChild(vScrollBar); + verticalScrollBar = new ScrollBar(); + verticalScrollBar->orientation = ScrollBar::VERTICAL; + addChild(verticalScrollBar); } void ScrollWidget::step() { - Vec b = Vec(box.size.x - vScrollBar->box.size.x, box.size.y - hScrollBar->box.size.y); + // Clamp scroll offset + Vec containerCorner = container->getChildrenBoundingBox().getBottomRight(); + offset = offset.clamp(Rect(Vec(0, 0), containerCorner.minus(box.size))); - hScrollBar->box.pos.y = b.y; - hScrollBar->box.size.x = b.x; + // Resize scroll bars + Vec inner = Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y); + horizontalScrollBar->box.pos.y = inner.y; + horizontalScrollBar->box.size.x = inner.x; + verticalScrollBar->box.pos.x = inner.x; + verticalScrollBar->box.size.y = inner.y; - vScrollBar->box.pos.x = b.x; - vScrollBar->box.size.y = b.y; + // Update the container's positions from the offset + container->box.pos = offset.neg().round(); Widget::step(); } -void ScrollWidget::draw(NVGcontext *vg) { - // Update the scrollbar sizes - Vec c = container->getChildrenBoundingBox().getBottomRight(); - hScrollBar->containerSize = c.x; - vScrollBar->containerSize = c.y; +Widget *ScrollWidget::onMouseMove(Vec pos, Vec mouseRel) { + Widget *w = Widget::onMouseMove(pos, mouseRel); + if (w) return w; - // Update the container's positions from the scrollbar offsets - container->box.pos = Vec(-hScrollBar->containerOffset, -vScrollBar->containerOffset).round(); - - Widget::draw(vg); + if (glfwGetMouseButton(gWindow, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { + offset = offset.minus(mouseRel); + return this; + } + return NULL; } bool ScrollWidget::onScrollOpaque(Vec scrollRel) { - hScrollBar->containerOffset += scrollRel.x; - vScrollBar->containerOffset += scrollRel.y; + offset = offset.minus(scrollRel); return true; }