| @@ -22,6 +22,7 @@ struct ScrollWidget : widget::OpaqueWidget { | |||
| bool hideScrollbars = false; | |||
| ScrollWidget(); | |||
| ~ScrollWidget(); | |||
| void scrollTo(math::Rect r); | |||
| /** Returns the bound of allowed `offset` values in pixels. */ | |||
| math::Rect getContainerOffsetBound(); | |||
| @@ -29,8 +30,11 @@ struct ScrollWidget : widget::OpaqueWidget { | |||
| math::Vec getHandleOffset(); | |||
| /** Returns the handle size relative to the scrollbar. [0, 1]. */ | |||
| math::Vec getHandleSize(); | |||
| /** The user is considered scrolling with the wheel until the mouse is moved. */ | |||
| bool isScrolling(); | |||
| void draw(const DrawArgs& args) override; | |||
| void step() override; | |||
| void onHover(const HoverEvent& e) override; | |||
| void onButton(const ButtonEvent& e) override; | |||
| void onDragStart(const DragStartEvent& e) override; | |||
| void onDragMove(const DragMoveEvent& e) override; | |||
| @@ -1,6 +1,7 @@ | |||
| #include <app/Knob.hpp> | |||
| #include <context.hpp> | |||
| #include <app/Scene.hpp> | |||
| #include <app/RackScrollWidget.hpp> | |||
| #include <random.hpp> | |||
| #include <history.hpp> | |||
| #include <settings.hpp> | |||
| @@ -226,38 +227,43 @@ void Knob::onDragLeave(const DragLeaveEvent& e) { | |||
| void Knob::onHoverScroll(const HoverScrollEvent& e) { | |||
| ParamWidget::onHoverScroll(e); | |||
| if (settings::knobScroll) { | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| if (pq) { | |||
| float value = pq->getSmoothValue(); | |||
| if (!settings::knobScroll) | |||
| return; | |||
| float rangeRatio; | |||
| if (pq->isBounded()) { | |||
| rangeRatio = pq->getRange(); | |||
| } | |||
| else { | |||
| rangeRatio = 1.f; | |||
| } | |||
| if (APP->scene->rackScroll->isScrolling()) | |||
| return; | |||
| float delta = e.scrollDelta.y; | |||
| delta *= settings::knobScrollSensitivity; | |||
| delta *= getModSpeed(); | |||
| delta *= rangeRatio; | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| if (!pq) | |||
| return; | |||
| // Handle value snapping | |||
| if (pq->snapEnabled) { | |||
| // Replace delta with an accumulated delta since the last integer knob. | |||
| internal->snapDelta += delta; | |||
| delta = std::trunc(internal->snapDelta); | |||
| internal->snapDelta -= delta; | |||
| } | |||
| float value = pq->getSmoothValue(); | |||
| value += delta; | |||
| pq->setSmoothValue(value); | |||
| float rangeRatio; | |||
| if (pq->isBounded()) { | |||
| rangeRatio = pq->getRange(); | |||
| } | |||
| else { | |||
| rangeRatio = 1.f; | |||
| } | |||
| e.consume(this); | |||
| } | |||
| float delta = e.scrollDelta.y; | |||
| delta *= settings::knobScrollSensitivity; | |||
| delta *= getModSpeed(); | |||
| delta *= rangeRatio; | |||
| // Handle value snapping | |||
| if (pq->snapEnabled) { | |||
| // Replace delta with an accumulated delta since the last integer knob. | |||
| internal->snapDelta += delta; | |||
| delta = std::trunc(internal->snapDelta); | |||
| internal->snapDelta -= delta; | |||
| } | |||
| value += delta; | |||
| pq->setSmoothValue(value); | |||
| e.consume(this); | |||
| } | |||
| @@ -114,10 +114,6 @@ void RackScrollWidget::onHoverKey(const HoverKeyEvent& e) { | |||
| void RackScrollWidget::onHoverScroll(const HoverScrollEvent& e) { | |||
| ScrollWidget::onHoverScroll(e); | |||
| if (e.isConsumed()) | |||
| return; | |||
| if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
| // Increase zoom | |||
| float zoomDelta = e.scrollDelta.y / 50 / 4; | |||
| @@ -132,6 +128,10 @@ void RackScrollWidget::onHoverScroll(const HoverScrollEvent& e) { | |||
| internal->zoomPos = e.pos; | |||
| e.consume(this); | |||
| } | |||
| if (e.isConsumed()) | |||
| return; | |||
| ScrollWidget::onHoverScroll(e); | |||
| } | |||
| @@ -6,10 +6,14 @@ namespace rack { | |||
| namespace ui { | |||
| // Internal not currently used | |||
| struct ScrollWidget::Internal { | |||
| bool scrolling = false; | |||
| }; | |||
| ScrollWidget::ScrollWidget() { | |||
| internal = new Internal; | |||
| container = new widget::Widget; | |||
| addChild(container); | |||
| @@ -25,6 +29,11 @@ ScrollWidget::ScrollWidget() { | |||
| } | |||
| ScrollWidget::~ScrollWidget() { | |||
| delete internal; | |||
| } | |||
| void ScrollWidget::scrollTo(math::Rect r) { | |||
| math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos); | |||
| offset = offset.clampSafe(bound); | |||
| @@ -49,6 +58,11 @@ math::Vec ScrollWidget::getHandleSize() { | |||
| } | |||
| bool ScrollWidget::isScrolling() { | |||
| return internal->scrolling; | |||
| } | |||
| void ScrollWidget::draw(const DrawArgs& args) { | |||
| nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
| Widget::draw(args); | |||
| @@ -88,6 +102,15 @@ void ScrollWidget::step() { | |||
| } | |||
| void ScrollWidget::onHover(const HoverEvent& e) { | |||
| OpaqueWidget::onHover(e); | |||
| if (!e.mouseDelta.isZero()) { | |||
| internal->scrolling = false; | |||
| } | |||
| } | |||
| void ScrollWidget::onButton(const ButtonEvent& e) { | |||
| math::Rect offsetBound = getContainerOffsetBound(); | |||
| // Check if scrollable | |||
| @@ -144,6 +167,7 @@ void ScrollWidget::onHoverScroll(const HoverScrollEvent& e) { | |||
| offset = offset.minus(scrollDelta); | |||
| e.consume(this); | |||
| internal->scrolling = true; | |||
| } | |||