@@ -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); | |||