| @@ -10,6 +10,9 @@ namespace app { | |||
| /** Implements vertical dragging behavior for ParamWidgets */ | |||
| struct Knob : ParamWidget { | |||
| struct Internal; | |||
| Internal* internal; | |||
| /** Multiplier for mouse movement to adjust knob value */ | |||
| float speed = 1.0; | |||
| /** Drag horizontally instead of vertically. */ | |||
| @@ -19,11 +22,8 @@ struct Knob : ParamWidget { | |||
| /** DEPRECATED. Use `ParamQuantity::snapEnabled`. */ | |||
| bool snap = false; | |||
| /** Value of the knob before dragging. */ | |||
| float oldValue = 0.f; | |||
| /** Fractional value between the param's value and the dragged knob position. */ | |||
| float snapDelta = 0.f; | |||
| Knob(); | |||
| ~Knob(); | |||
| void initParamQuantity() override; | |||
| void onHover(const event::Hover& e) override; | |||
| void onButton(const event::Button& e) override; | |||
| @@ -27,7 +27,15 @@ extern float zoom; | |||
| extern bool invertZoom; | |||
| extern float cableOpacity; | |||
| extern float cableTension; | |||
| extern bool allowCursorLock; | |||
| enum KnobMode { | |||
| KNOB_MODE_LINEAR, | |||
| KNOB_MODE_LINEAR_UNLOCKED, | |||
| KNOB_MODE_LINEAR_SPEED, | |||
| KNOB_MODE_LINEAR_SPEED_UNLOCKED, | |||
| KNOB_MODE_CIRCULAR_ABSOLUTE, | |||
| KNOB_MODE_CIRCULAR_RELATIVE, | |||
| }; | |||
| extern KnobMode knobMode; | |||
| extern float sampleRate; | |||
| extern int threadCount; | |||
| extern bool paramTooltip; | |||
| @@ -12,6 +12,22 @@ namespace app { | |||
| static const float KNOB_SENSITIVITY = 0.0015f; | |||
| struct Knob::Internal { | |||
| /** Value of the knob before dragging. */ | |||
| float oldValue = 0.f; | |||
| /** Fractional value between the param's value and the dragged knob position. */ | |||
| float snapDelta = 0.f; | |||
| }; | |||
| Knob::Knob() { | |||
| internal = new Internal; | |||
| } | |||
| Knob::~Knob() { | |||
| delete internal; | |||
| } | |||
| void Knob::initParamQuantity() { | |||
| ParamWidget::initParamQuantity(); | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| @@ -43,8 +59,8 @@ void Knob::onDragStart(const event::DragStart& e) { | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| if (pq) { | |||
| oldValue = pq->getSmoothValue(); | |||
| snapDelta = 0.f; | |||
| internal->oldValue = pq->getSmoothValue(); | |||
| internal->snapDelta = 0.f; | |||
| } | |||
| APP->window->cursorLock(); | |||
| @@ -59,13 +75,13 @@ void Knob::onDragEnd(const event::DragEnd& e) { | |||
| engine::ParamQuantity* pq = getParamQuantity(); | |||
| if (pq) { | |||
| float newValue = pq->getSmoothValue(); | |||
| if (oldValue != newValue) { | |||
| if (internal->oldValue != newValue) { | |||
| // Push ParamChange history action | |||
| history::ParamChange* h = new history::ParamChange; | |||
| h->name = "move knob"; | |||
| h->moduleId = module->id; | |||
| h->paramId = paramId; | |||
| h->oldValue = oldValue; | |||
| h->oldValue = internal->oldValue; | |||
| h->newValue = newValue; | |||
| APP->history->push(h); | |||
| } | |||
| @@ -103,9 +119,9 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| if (pq->snapEnabled) { | |||
| // Replace delta with an accumulated delta since the last integer knob. | |||
| snapDelta += delta; | |||
| delta = std::trunc(snapDelta); | |||
| snapDelta -= delta; | |||
| internal->snapDelta += delta; | |||
| delta = std::trunc(internal->snapDelta); | |||
| internal->snapDelta -= delta; | |||
| } | |||
| // Set value | |||
| @@ -316,15 +316,39 @@ struct ParamTooltipItem : ui::MenuItem { | |||
| } | |||
| }; | |||
| struct LockModulesItem : ui::MenuItem { | |||
| struct KnobModeValueItem : ui::MenuItem { | |||
| settings::KnobMode knobMode; | |||
| void onAction(const event::Action& e) override { | |||
| settings::lockModules ^= true; | |||
| settings::knobMode = knobMode; | |||
| } | |||
| }; | |||
| struct CursorLockItem : ui::MenuItem { | |||
| struct KnobModeItem : ui::MenuItem { | |||
| ui::Menu* createChildMenu() override { | |||
| ui::Menu* menu = new ui::Menu; | |||
| static const std::string knobModeNames[] = { | |||
| "Linear (locked cursor)", | |||
| "Linear", | |||
| "Adjustable linear (locked cursor)", | |||
| "Adjustable linear", | |||
| "Absolute rotary", | |||
| "Relative rotary", | |||
| }; | |||
| for (int i = 0; i < (int) LENGTHOF(knobModeNames); i++) { | |||
| KnobModeValueItem* item = new KnobModeValueItem; | |||
| item->knobMode = (settings::KnobMode) i; | |||
| item->text = knobModeNames[i]; | |||
| item->rightText = CHECKMARK(settings::knobMode == i); | |||
| menu->addChild(item); | |||
| } | |||
| return menu; | |||
| } | |||
| }; | |||
| struct LockModulesItem : ui::MenuItem { | |||
| void onAction(const event::Action& e) override { | |||
| settings::allowCursorLock ^= true; | |||
| settings::lockModules ^= true; | |||
| } | |||
| }; | |||
| @@ -365,20 +389,20 @@ struct ViewButton : MenuButton { | |||
| menu->box.size.x = box.size.x; | |||
| ParamTooltipItem* paramTooltipItem = new ParamTooltipItem; | |||
| paramTooltipItem->text = "Parameter tooltips"; | |||
| paramTooltipItem->text = "Show tooltips"; | |||
| paramTooltipItem->rightText = CHECKMARK(settings::paramTooltip); | |||
| menu->addChild(paramTooltipItem); | |||
| KnobModeItem* knobModeItem = new KnobModeItem; | |||
| knobModeItem->text = "Knob mode"; | |||
| knobModeItem->rightText = RIGHT_ARROW; | |||
| menu->addChild(knobModeItem); | |||
| LockModulesItem* lockModulesItem = new LockModulesItem; | |||
| lockModulesItem->text = "Lock modules"; | |||
| lockModulesItem->rightText = CHECKMARK(settings::lockModules); | |||
| menu->addChild(lockModulesItem); | |||
| CursorLockItem* cursorLockItem = new CursorLockItem; | |||
| cursorLockItem->text = "Lock cursor while dragging"; | |||
| cursorLockItem->rightText = CHECKMARK(settings::allowCursorLock); | |||
| menu->addChild(cursorLockItem); | |||
| ZoomSlider* zoomSlider = new ZoomSlider; | |||
| zoomSlider->box.size.x = 200.0; | |||
| menu->addChild(zoomSlider); | |||
| @@ -393,6 +417,7 @@ struct ViewButton : MenuButton { | |||
| FrameRateItem* frameRateItem = new FrameRateItem; | |||
| frameRateItem->text = "Frame rate"; | |||
| frameRateItem->rightText = RIGHT_ARROW; | |||
| menu->addChild(frameRateItem); | |||
| FullscreenItem* fullscreenItem = new FullscreenItem; | |||
| @@ -22,7 +22,7 @@ float zoom = 0.0; | |||
| bool invertZoom = false; | |||
| float cableOpacity = 0.5; | |||
| float cableTension = 0.5; | |||
| bool allowCursorLock = true; | |||
| KnobMode knobMode = KNOB_MODE_LINEAR; | |||
| float sampleRate = 44100.0; | |||
| int threadCount = 1; | |||
| bool paramTooltip = false; | |||
| @@ -65,7 +65,7 @@ json_t* toJson() { | |||
| json_object_set_new(rootJ, "cableTension", json_real(cableTension)); | |||
| json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock)); | |||
| json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode)); | |||
| json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); | |||
| @@ -141,9 +141,16 @@ void fromJson(json_t* rootJ) { | |||
| if (tensionJ) | |||
| cableTension = json_number_value(tensionJ); | |||
| // legacy v1 | |||
| json_t* allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); | |||
| if (allowCursorLockJ) | |||
| allowCursorLock = json_is_true(allowCursorLockJ); | |||
| if (allowCursorLockJ) { | |||
| if (json_is_false(allowCursorLockJ)) | |||
| knobMode = KNOB_MODE_LINEAR_UNLOCKED; | |||
| } | |||
| json_t* knobModeJ = json_object_get(rootJ, "knobMode"); | |||
| if (knobModeJ) | |||
| knobMode = (KnobMode) json_integer_value(knobModeJ); | |||
| json_t* sampleRateJ = json_object_get(rootJ, "sampleRate"); | |||
| if (sampleRateJ) | |||
| @@ -478,21 +478,17 @@ void Window::close() { | |||
| } | |||
| void Window::cursorLock() { | |||
| if (settings::allowCursorLock) { | |||
| #if defined ARCH_MAC | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); | |||
| #else | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | |||
| #endif | |||
| internal->ignoreNextMouseDelta = true; | |||
| } | |||
| internal->ignoreNextMouseDelta = true; | |||
| } | |||
| void Window::cursorUnlock() { | |||
| if (settings::allowCursorLock) { | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | |||
| internal->ignoreNextMouseDelta = true; | |||
| } | |||
| glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | |||
| internal->ignoreNextMouseDelta = true; | |||
| } | |||
| bool Window::isCursorLocked() { | |||