@@ -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 |
@@ -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); | |||
}; | |||
@@ -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) { | |||
@@ -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; | |||
} | |||
@@ -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` | |||
@@ -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<ScrollWidget*>(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<ScrollWidget*>(parent); | |||
assert(scrollWidget); | |||
if (orientation == HORIZONTAL) | |||
scrollWidget->offset.x += mouseRel.x; | |||
else | |||
scrollWidget->offset.y += mouseRel.y; | |||
} | |||
void ScrollBar::onDragEnd() { | |||
@@ -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; | |||
} | |||