| @@ -36,6 +36,7 @@ struct Knob : ParamWidget { | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void onDragLeave(const event::DragLeave& e) override; | |||
| void onHoverScroll(const event::HoverScroll& e) override; | |||
| }; | |||
| @@ -49,7 +49,9 @@ enum KnobMode { | |||
| KNOB_MODE_ROTARY_RELATIVE, | |||
| }; | |||
| extern KnobMode knobMode; | |||
| extern bool knobScroll; | |||
| extern float knobLinearSensitivity; | |||
| extern float knobScrollSensitivity; | |||
| extern float sampleRate; | |||
| extern int threadCount; | |||
| extern bool tooltips; | |||
| @@ -105,11 +105,25 @@ void Knob::onDragEnd(const event::DragEnd& e) { | |||
| h->newValue = newValue; | |||
| APP->history->push(h); | |||
| } | |||
| // Reset snap delta | |||
| internal->snapDelta = 0.f; | |||
| } | |||
| ParamWidget::onDragEnd(e); | |||
| } | |||
| static float getModSpeed() { | |||
| int mods = APP->window->getMods(); | |||
| if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL) | |||
| return 1 / 16.f; | |||
| else if ((mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) | |||
| return 4.f; | |||
| else if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) | |||
| return 1 / 256.f; | |||
| else | |||
| return 1.f; | |||
| } | |||
| void Knob::onDragMove(const event::DragMove& e) { | |||
| if (e.button != GLFW_MOUSE_BUTTON_LEFT) | |||
| return; | |||
| @@ -121,17 +135,6 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| if (pq) { | |||
| float value = smooth ? pq->getSmoothValue() : pq->getValue(); | |||
| // Scale by mod keys | |||
| float modScale = 1.f; | |||
| // Drag slower if Ctrl is held | |||
| int mods = APP->window->getMods(); | |||
| if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL) | |||
| modScale /= 16.f; | |||
| if ((mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) | |||
| modScale *= 4.f; | |||
| if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) | |||
| modScale /= 256.f; | |||
| // Ratio between parameter value scale / (angle range / 2*pi) | |||
| float rangeRatio; | |||
| if (pq->isBounded()) { | |||
| @@ -145,7 +148,7 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| if (linearMode) { | |||
| float delta = (horizontal ? e.mouseDelta.x : -e.mouseDelta.y); | |||
| delta *= settings::knobLinearSensitivity; | |||
| delta *= modScale; | |||
| delta *= getModSpeed(); | |||
| delta *= rangeRatio; | |||
| // Scale delta if in scaled linear knob mode | |||
| @@ -188,7 +191,7 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| float deltaAngle = math::eucMod(angle - internal->dragAngle + float(M_PI), float(2 * M_PI)) - float(M_PI); | |||
| internal->dragAngle = angle; | |||
| float delta = deltaAngle / float(2 * M_PI) * rangeRatio; | |||
| delta *= modScale; | |||
| delta *= getModSpeed(); | |||
| // Handle value snapping | |||
| if (pq->snapEnabled) { | |||
| @@ -221,5 +224,46 @@ void Knob::onDragLeave(const event::DragLeave& e) { | |||
| } | |||
| void Knob::onHoverScroll(const event::HoverScroll& e) { | |||
| ParamWidget::onHoverScroll(e); | |||
| if (settings::knobScroll) { | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| if (pq) { | |||
| float value = smooth ? pq->getSmoothValue() : pq->getValue(); | |||
| float rangeRatio; | |||
| if (pq->isBounded()) { | |||
| rangeRatio = pq->getRange(); | |||
| } | |||
| else { | |||
| rangeRatio = 1.f; | |||
| } | |||
| 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; | |||
| if (smooth) | |||
| pq->setSmoothValue(value); | |||
| else | |||
| pq->setValue(value); | |||
| e.consume(this); | |||
| } | |||
| } | |||
| } | |||
| } // namespace app | |||
| } // namespace rack | |||
| @@ -384,6 +384,12 @@ struct KnobModeItem : ui::MenuItem { | |||
| } | |||
| }; | |||
| struct KnobScrollItem : ui::MenuItem { | |||
| void onAction(const event::Action& e) override { | |||
| settings::knobScroll ^= true; | |||
| } | |||
| }; | |||
| struct LockModulesItem : ui::MenuItem { | |||
| void onAction(const event::Action& e) override { | |||
| settings::lockModules ^= true; | |||
| @@ -432,7 +438,7 @@ struct ViewButton : MenuButton { | |||
| menu->addChild(tooltipsItem); | |||
| AllowCursorLockItem* allowCursorLockItem = new AllowCursorLockItem; | |||
| allowCursorLockItem->text = "Lock cursor when dragging parameters"; | |||
| allowCursorLockItem->text = "Hide cursor when dragging parameters"; | |||
| allowCursorLockItem->rightText = CHECKMARK(settings::allowCursorLock); | |||
| menu->addChild(allowCursorLockItem); | |||
| @@ -441,6 +447,11 @@ struct ViewButton : MenuButton { | |||
| knobModeItem->rightText = RIGHT_ARROW; | |||
| menu->addChild(knobModeItem); | |||
| KnobScrollItem* knobScrollItem = new KnobScrollItem; | |||
| knobScrollItem->text = "Scroll wheel knob control"; | |||
| knobScrollItem->rightText = CHECKMARK(settings::knobScroll); | |||
| menu->addChild(knobScrollItem); | |||
| LockModulesItem* lockModulesItem = new LockModulesItem; | |||
| lockModulesItem->text = "Lock module positions"; | |||
| lockModulesItem->rightText = CHECKMARK(settings::lockModules); | |||
| @@ -127,6 +127,10 @@ void RackScrollWidget::onHoverKey(const event::HoverKey& e) { | |||
| void RackScrollWidget::onHoverScroll(const event::HoverScroll& 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; | |||
| @@ -135,10 +139,7 @@ void RackScrollWidget::onHoverScroll(const event::HoverScroll& e) { | |||
| settings::zoom += zoomDelta; | |||
| zoomPos = e.pos; | |||
| e.consume(this); | |||
| return; | |||
| } | |||
| ScrollWidget::onHoverScroll(e); | |||
| } | |||
| @@ -25,7 +25,9 @@ float cableOpacity = 0.5; | |||
| float cableTension = 0.5; | |||
| bool allowCursorLock = true; | |||
| KnobMode knobMode = KNOB_MODE_LINEAR; | |||
| bool knobScroll = false; | |||
| float knobLinearSensitivity = 0.001f; | |||
| float knobScrollSensitivity = 0.001f; | |||
| float sampleRate = 0; | |||
| int threadCount = 1; | |||
| bool tooltips = true; | |||
| @@ -91,8 +93,12 @@ json_t* toJson() { | |||
| json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode)); | |||
| json_object_set_new(rootJ, "knobScroll", json_boolean(knobScroll)); | |||
| json_object_set_new(rootJ, "knobLinearSensitivity", json_real(knobLinearSensitivity)); | |||
| json_object_set_new(rootJ, "knobScrollSensitivity", json_real(knobScrollSensitivity)); | |||
| json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); | |||
| json_object_set_new(rootJ, "threadCount", json_integer(threadCount)); | |||
| @@ -207,10 +213,18 @@ void fromJson(json_t* rootJ) { | |||
| if (knobModeJ) | |||
| knobMode = (KnobMode) json_integer_value(knobModeJ); | |||
| json_t* knobScrollJ = json_object_get(rootJ, "knobScroll"); | |||
| if (knobScrollJ) | |||
| knobScroll = json_boolean_value(knobScrollJ); | |||
| json_t* knobLinearSensitivityJ = json_object_get(rootJ, "knobLinearSensitivity"); | |||
| if (knobLinearSensitivityJ) | |||
| knobLinearSensitivity = json_number_value(knobLinearSensitivityJ); | |||
| json_t* knobScrollSensitivityJ = json_object_get(rootJ, "knobScrollSensitivity"); | |||
| if (knobScrollSensitivityJ) | |||
| knobScrollSensitivity = json_number_value(knobScrollSensitivityJ); | |||
| json_t* sampleRateJ = json_object_get(rootJ, "sampleRate"); | |||
| if (sampleRateJ) | |||
| sampleRate = json_number_value(sampleRateJ); | |||