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