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