| @@ -62,10 +62,10 @@ inline float eucmodf(float a, float base) { | |||||
| } | } | ||||
| /** Limits a value between a minimum and maximum | /** 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) { | 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 */ | /** If the magnitude of x if less than eps, return 0 */ | ||||
| @@ -199,11 +199,16 @@ struct Rect { | |||||
| return Rect(min, max.minus(min)); | 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) { | bool contains(Vec v) { | ||||
| 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 */ | |||||
| 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 */ | /** Returns whether this Rect overlaps with another Rect */ | ||||
| bool intersects(Rect r) { | bool intersects(Rect r) { | ||||
| 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) | ||||
| @@ -145,7 +145,7 @@ struct TransparentWidget : virtual Widget { | |||||
| Widget *onScroll(Vec pos, Vec scrollRel) {return NULL;} | 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 { | struct OpaqueWidget : virtual Widget { | ||||
| Widget *onMouseDown(Vec pos, int button) { | Widget *onMouseDown(Vec pos, int button) { | ||||
| Widget *w = Widget::onMouseDown(pos, button); | Widget *w = Widget::onMouseDown(pos, button); | ||||
| @@ -165,6 +165,13 @@ struct OpaqueWidget : virtual Widget { | |||||
| onMouseMoveOpaque(mouseRel); | onMouseMoveOpaque(mouseRel); | ||||
| return this; | 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. | /** "High level" events called by the above lower level events. | ||||
| Use these if you don't care about the clicked position. | 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 onMouseDownOpaque(int button) {} | ||||
| virtual void onMouseUpOpaque(int button) {} | virtual void onMouseUpOpaque(int button) {} | ||||
| virtual void onMouseMoveOpaque(Vec mouseRel) {} | virtual void onMouseMoveOpaque(Vec mouseRel) {} | ||||
| virtual bool onScrollOpaque(Vec scrollRel) {return false;} | |||||
| }; | }; | ||||
| struct SpriteWidget : virtual Widget { | struct SpriteWidget : virtual Widget { | ||||
| @@ -249,9 +257,6 @@ struct Label : Widget { | |||||
| // Deletes itself from parent when clicked | // Deletes itself from parent when clicked | ||||
| struct MenuOverlay : OpaqueWidget { | struct MenuOverlay : OpaqueWidget { | ||||
| void step(); | void step(); | ||||
| Widget *onScroll(Vec pos, Vec scrollRel) { | |||||
| return this; | |||||
| } | |||||
| void onDragDrop(Widget *origin); | void onDragDrop(Widget *origin); | ||||
| }; | }; | ||||
| @@ -262,6 +267,7 @@ struct Menu : OpaqueWidget { | |||||
| // Resizes menu and calls addChild() | // Resizes menu and calls addChild() | ||||
| void pushChild(Widget *child); | void pushChild(Widget *child); | ||||
| void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
| bool onScrollOpaque(Vec scrollRel); | |||||
| }; | }; | ||||
| struct MenuEntry : OpaqueWidget { | struct MenuEntry : OpaqueWidget { | ||||
| @@ -340,8 +346,8 @@ struct ScrollBar : OpaqueWidget { | |||||
| ScrollBar() { | ScrollBar() { | ||||
| box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); | box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); | ||||
| } | } | ||||
| void step(); | |||||
| void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
| void move(float delta); | |||||
| void onDragStart(); | void onDragStart(); | ||||
| void onDragMove(Vec mouseRel); | void onDragMove(Vec mouseRel); | ||||
| void onDragEnd(); | void onDragEnd(); | ||||
| @@ -356,7 +362,7 @@ struct ScrollWidget : OpaqueWidget { | |||||
| ScrollWidget(); | ScrollWidget(); | ||||
| void step(); | void step(); | ||||
| void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
| Widget *onScroll(Vec pos, Vec scrollRel); | |||||
| bool onScrollOpaque(Vec scrollRel); | |||||
| }; | }; | ||||
| struct TextField : OpaqueWidget { | struct TextField : OpaqueWidget { | ||||
| @@ -151,7 +151,7 @@ void cursorEnterCallback(GLFWwindow* window, int entered) { | |||||
| void scrollCallback(GLFWwindow *window, double x, double y) { | void scrollCallback(GLFWwindow *window, double x, double y) { | ||||
| Vec scrollRel = Vec(x, y); | Vec scrollRel = Vec(x, y); | ||||
| // onScroll | // onScroll | ||||
| gScene->onScroll(gMousePos, scrollRel.mult(-95)); | |||||
| gScene->onScroll(gMousePos, scrollRel.mult(-38.0)); | |||||
| } | } | ||||
| void charCallback(GLFWwindow *window, unsigned int codepoint) { | void charCallback(GLFWwindow *window, unsigned int codepoint) { | ||||
| @@ -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 | } // namespace rack | ||||
| @@ -8,6 +8,7 @@ void MenuOverlay::step() { | |||||
| for (Widget *child : children) { | for (Widget *child : children) { | ||||
| child->box = child->box.clamp(Rect(Vec(0, 0), box.size)); | child->box = child->box.clamp(Rect(Vec(0, 0), box.size)); | ||||
| } | } | ||||
| Widget::step(); | Widget::step(); | ||||
| } | } | ||||
| @@ -4,6 +4,13 @@ | |||||
| namespace rack { | 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) { | void ScrollBar::draw(NVGcontext *vg) { | ||||
| float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); | float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); | ||||
| float maxOffset = containerSize - boxSize; | 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); | 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() { | void ScrollBar::onDragStart() { | ||||
| state = BND_ACTIVE; | state = BND_ACTIVE; | ||||
| guiCursorLock(); | guiCursorLock(); | ||||
| } | } | ||||
| void ScrollBar::onDragMove(Vec mouseRel) { | 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() { | void ScrollBar::onDragEnd() { | ||||
| @@ -40,13 +40,10 @@ void ScrollWidget::draw(NVGcontext *vg) { | |||||
| Widget::draw(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; | |||||
| } | } | ||||