From 4a769747908150d1caa6592411ea26a71e8a2ff3 Mon Sep 17 00:00:00 2001 From: bsp2 Date: Mon, 3 Sep 2018 19:14:08 +0200 Subject: [PATCH] add numerical param editing; add param copy'n'paste; add param mini-undo --- include/app.hpp | 3 ++ include/global.hpp | 3 +- include/global_ui.hpp | 22 ++++++++++++ include/plugin.hpp | 6 ++++ include/ui.hpp | 7 ++-- include/widgets.hpp | 3 ++ src/app/Knob.cpp | 2 ++ src/app/ModuleWidget.cpp | 56 +++++++++++++++++++++++++----- src/app/ParamWidget.cpp | 26 +++++++++++++- src/app/PluginManagerWidget.cpp | 44 ++++++++++++++++++++++++ src/app/RackWidget.cpp | 19 ++++++++++ src/asset.cpp | 2 +- src/engine.cpp | 34 ++++++++++++------ src/plugin.cpp | 1 + src/ui/Slider.cpp | 15 ++++++-- src/ui/TextField.cpp | 32 +++++++++++++++-- src/vst2_main.cpp | 17 +++++++-- src/widgets/QuantityWidget.cpp | 3 ++ src/window.cpp | 61 +++++++++++++++++++++++++++++++++ 19 files changed, 324 insertions(+), 32 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index c2e24d33..35398c22 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -189,6 +189,8 @@ struct RackWidget : OpaqueWidget { void onMouseMove(EventMouseMove &e) override; void onMouseDown(EventMouseDown &e) override; void onZoom(EventZoom &e) override; + + ParamWidget *findParamWidgetAndUniqueParamIdByWidgetRef (const ParamWidget *ref, int *retUniqueParamId); }; struct RackRail : TransparentWidget { @@ -246,6 +248,7 @@ struct ParamWidget : Component, QuantityWidget { virtual void randomize(); void onMouseDown(EventMouseDown &e) override; void onChange(EventChange &e) override; + void onMouseMove(EventMouseMove &e) override; template static T *create(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { diff --git a/include/global.hpp b/include/global.hpp index 1a7ae93f..256ba790 100644 --- a/include/global.hpp +++ b/include/global.hpp @@ -37,7 +37,8 @@ struct VSTMidiInputDevice; struct VST2QueuedParam { int unique_id; - float norm_value; + float value; + bool b_normalized; }; struct Global { diff --git a/include/global_ui.hpp b/include/global_ui.hpp index b10d0c3a..e13e516b 100644 --- a/include/global_ui.hpp +++ b/include/global_ui.hpp @@ -21,6 +21,8 @@ struct Toolbar; struct RackScene; struct Scene; struct Model; +struct ParamWidget; +struct TextField; // @@ -91,6 +93,17 @@ struct GlobalUI { int bnd_font; } blendish; + struct { + const ParamWidget *last_param_widget; // never dereferenced, may have already been deleted. unset after redraw(). + int last_param_gid; // updated during redraw() + float last_param_value; // updated in onMouseMove() and onChange(). corresponding param may not exist anymore. + float value_clipboard; + TextField *tf_id; + TextField *tf_value; + bool b_lock; // true=don't update info (e.g. when receiving VST parameter updates from host) + int placeholder_framecount; + } param_info; + void init(void) { @@ -130,6 +143,15 @@ struct GlobalUI { blendish.bnd_icon_image = -1; blendish.bnd_font = -1; + + param_info.last_param_widget = NULL; + param_info.last_param_gid = 0; + param_info.last_param_value = 0.0f; + param_info.value_clipboard = 0.0f; + param_info.tf_id = NULL; + param_info.tf_value = NULL; + param_info.b_lock = false; + param_info.placeholder_framecount = 0; } }; diff --git a/include/plugin.hpp b/include/plugin.hpp index 012447c1..97707872 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -15,6 +15,7 @@ namespace rack { } typedef void (*vst2_handle_ui_param_fxn_t) (int uniqueParamId, float normValue); +typedef void (*vst2_queue_param_sync_fxn_t) (int uniqueParamId, float value, bool _bNormalized); typedef void (*rack_set_tls_globals_fxn_t) (rack::Plugin *p); #ifdef RACK_HOST @@ -22,6 +23,7 @@ typedef void (*rack_set_tls_globals_fxn_t) (rack::Plugin *p); // Rack host build: extern void vst2_handle_ui_param (int uniqueParamId, float normValue); +extern void vst2_queue_param_sync (int _uniqueParamId, float _value, bool _bNormalized); #define RACK_PLUGIN_DECLARE(pluginname) #define RACK_PLUGIN_INIT(pluginname) extern "C" void init_plugin_##pluginname##(rack::Plugin *p) @@ -44,6 +46,7 @@ extern void vst2_handle_ui_param (int uniqueParamId, float normValue); #endif // _MSC_VER extern vst2_handle_ui_param_fxn_t vst2_handle_ui_param; +extern vst2_queue_param_sync_fxn_t vst2_queue_param_sync; #ifndef RACK_PLUGIN_SHARED_LIB_BUILD #ifdef RACK_PLUGIN_SHARED @@ -56,6 +59,7 @@ extern vst2_handle_ui_param_fxn_t vst2_handle_ui_param; #endif #define RACK_PLUGIN_INIT(pluginname) \ vst2_handle_ui_param_fxn_t vst2_handle_ui_param; \ +vst2_queue_param_sync_fxn_t vst2_queue_param_sync; \ JSON_SEED_INIT_EXTERNAL \ extern "C" extern volatile uint32_t hashtable_seed; \ namespace rack { \ @@ -75,6 +79,7 @@ RACK_PLUGIN_EXPORT void init_plugin(rack::Plugin *p) rack::plugin = p; \ rack::plugin->set_tls_globals_fxn = &rack::loc_set_tls_globals; \ vst2_handle_ui_param = p->vst2_handle_ui_param_fxn; \ + vst2_queue_param_sync = p->vst2_queue_param_sync_fxn; \ rack::global = p->global; \ rack::global_ui = p->global_ui; \ RACK_PLUGIN_INIT_ID_INTERNAL @@ -143,6 +148,7 @@ struct Plugin { // Set by Rack host (before init_plugin()): // vst2_handle_ui_param_fxn_t vst2_handle_ui_param_fxn = NULL; + vst2_queue_param_sync_fxn_t vst2_queue_param_sync_fxn = NULL; Global *global = NULL; GlobalUI *global_ui = NULL; diff --git a/include/ui.hpp b/include/ui.hpp index c9426794..63d83acd 100644 --- a/include/ui.hpp +++ b/include/ui.hpp @@ -6,7 +6,6 @@ #define CHECKMARK_STRING "✔" #define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") - namespace rack { //////////////////// @@ -233,9 +232,11 @@ struct TextField : OpaqueWidget { /** Inserts text at the cursor, replacing the selection if necessary */ void insertText(std::string text); /** Replaces the entire text */ - void setText(std::string text); + void setText(std::string text); // set text and emit onChange() + void setTextQuiet(std::string text); // just set the text virtual int getTextPosition(Vec mousePos); - virtual void onTextChange() {} + virtual void onTextChange() {} // Type + virtual void onTextEnter() {} // Return }; struct PasswordField : TextField { diff --git a/include/widgets.hpp b/include/widgets.hpp index 49959545..37b0ec52 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -9,6 +9,7 @@ #include "events.hpp" #include "util/color.hpp" +#define INVALID_REVERT_VAL -999999.0f namespace rack { @@ -286,6 +287,7 @@ struct QuantityWidget : VirtualWidget { A precision of 2 will display as "1.00" for example. */ int precision = 2; + float revert_val = INVALID_REVERT_VAL; // value at drag start (Knob, Slider) QuantityWidget(); void setValue(float value); @@ -293,6 +295,7 @@ struct QuantityWidget : VirtualWidget { void setDefaultValue(float defaultValue); /** Generates the display value */ std::string getText(); + void onMouseLeave(EventMouseLeave &e) override; }; diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index 1be1129b..c68aec54 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -13,6 +13,7 @@ static const float KNOB_SENSITIVITY = 0.0015f; Knob::Knob() { // smooth = true; smooth = false; // xxx + revert_val = INVALID_REVERT_VAL; } void Knob::onDragStart(EventDragStart &e) { @@ -20,6 +21,7 @@ void Knob::onDragStart(EventDragStart &e) { // printf("xxx Knob::onDragStart: value=%f\n", value); dragValue = value; randomizable = false; + revert_val = value; } void Knob::onDragMove(EventDragMove &e) { diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index f9b36746..6a822e3c 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -296,6 +296,7 @@ void ModuleWidget::drawShadow(NVGcontext *vg) { } void ModuleWidget::onMouseDown(EventMouseDown &e) { + Widget::onMouseDown(e); if (e.consumed) return; @@ -330,7 +331,7 @@ void ModuleWidget::onMouseMove(EventMouseMove &e) { void ModuleWidget::onHoverKey(EventHoverKey &e) { switch (e.key) { - case 'i'/*GLFW_KEY_I*/: + case 'i': if (windowIsModPressed() && !windowIsShiftPressed()) { reset(); e.consumed = true; @@ -338,7 +339,7 @@ void ModuleWidget::onHoverKey(EventHoverKey &e) { } break; - case 'r'/*GLFW_KEY_R*/: + case 'r': if (windowIsModPressed() && !windowIsShiftPressed()) { randomize(); e.consumed = true; @@ -346,7 +347,7 @@ void ModuleWidget::onHoverKey(EventHoverKey &e) { } break; - case 'd'/*GLFW_KEY_D*/: + case 'd': if (windowIsModPressed() && !windowIsShiftPressed()) { global_ui->app.gRackWidget->cloneModule(this); e.consumed = true; @@ -354,7 +355,7 @@ void ModuleWidget::onHoverKey(EventHoverKey &e) { } break; - case 'u'/*GLFW_KEY_U*/: + case 'u': if (windowIsModPressed() && !windowIsShiftPressed()) { disconnect(); e.consumed = true; @@ -377,14 +378,24 @@ void ModuleWidget::onHoverKey(EventHoverKey &e) { case 'w': if (windowIsModPressed() && !windowIsShiftPressed()) { - global_ui->app.gRackWidget->deleteModule(this); - this->finalizeEvents(); - delete this; - e.consumed = true; - return; + global_ui->param_info.value_clipboard = global_ui->param_info.last_param_value; + printf("xxx CopyParamItem: value=%f\n", global_ui->param_info.value_clipboard); + e.consumed = true; + return; } break; + case 'e': + if (windowIsModPressed() && !windowIsShiftPressed()) { + vst2_queue_param_sync(global_ui->param_info.last_param_gid, + global_ui->param_info.value_clipboard, + false/*bNormalized*/ + ); + printf("xxx PasteParamItem: value=%f\n", global_ui->param_info.value_clipboard); + e.consumed = true; + return; + } + break; } Widget::onHoverKey(e); @@ -443,6 +454,23 @@ struct DeleteMenuItem : MenuItem { } }; +struct CopyParamItem : MenuItem { + void onAction(EventAction &e) override { + global_ui->param_info.value_clipboard = global_ui->param_info.last_param_value; + printf("xxx CopyParamItem: value=%f\n", global_ui->param_info.value_clipboard); + } +}; + +struct PasteParamItem : MenuItem { + void onAction(EventAction &e) override { + vst2_queue_param_sync(global_ui->param_info.last_param_gid, + global_ui->param_info.value_clipboard, + false/*bNormalized*/ + ); + printf("xxx PasteParamItem: value=%f\n", global_ui->param_info.value_clipboard); + } +}; + Menu *ModuleWidget::createContextMenu() { // printf("xxx ModuleWidget::createContextMenu: ENTER\n"); Menu *menu = global_ui->ui.gScene->createMenu(); @@ -483,6 +511,16 @@ Menu *ModuleWidget::createContextMenu() { deleteItem->moduleWidget = this; menu->addChild(deleteItem); + CopyParamItem *copyParamItem = new CopyParamItem(); + copyParamItem->text = "Copy Param Value"; + copyParamItem->rightText = WINDOW_MOD_KEY_NAME "+W"; + menu->addChild(copyParamItem); + + PasteParamItem *pasteParamItem = new PasteParamItem(); + pasteParamItem->text = "Paste Param Value"; + pasteParamItem->rightText = WINDOW_MOD_KEY_NAME "+E"; + menu->addChild(pasteParamItem); + appendContextMenu(menu); return menu; diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index 2ed16b60..9fc9b366 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -1,5 +1,8 @@ +#include "global_pre.hpp" #include "app.hpp" #include "engine.hpp" +#include "global.hpp" +#include "global_ui.hpp" namespace rack { @@ -35,10 +38,29 @@ void ParamWidget::randomize() { } } +void ParamWidget::onMouseMove(EventMouseMove &e) { + QuantityWidget::onMouseMove(e); + if(!global_ui->param_info.b_lock) + global_ui->param_info.last_param_widget = this; +} + void ParamWidget::onMouseDown(EventMouseDown &e) { + printf("xxx ParamWidget::onMouseDown: e.button=%d revert_val=%f\n", e.button, revert_val); if (e.button == 1) { - reset(); + if(INVALID_REVERT_VAL != revert_val) // during mouse drag + { + setValue(revert_val); + revert_val = INVALID_REVERT_VAL; + } + else + { + reset(); + } } + + // if (e.button == 1) { + // reset(); + // } e.consumed = true; e.target = this; } @@ -48,6 +70,8 @@ void ParamWidget::onChange(EventChange &e) { return; // printf("xxx ParamWidget::onChange: paramId=%d value=%f this=%p smooth=%d\n", paramId, value, this, smooth); + if(!global_ui->param_info.b_lock) + global_ui->param_info.last_param_widget = this; if (smooth) engineSetParamSmooth(module, paramId, value); diff --git a/src/app/PluginManagerWidget.cpp b/src/app/PluginManagerWidget.cpp index b9655e8c..0e2a22e0 100644 --- a/src/app/PluginManagerWidget.cpp +++ b/src/app/PluginManagerWidget.cpp @@ -5,9 +5,36 @@ #include "osdialog.h" +#ifdef USE_VST2 +#ifdef RACK_HOST +extern void vst2_queue_param_sync(int _uniqueParamId, float _value, bool _bNormalized); +#endif // RACK_HOST +#endif // USE_VST2 + namespace rack { +#ifdef USE_VST2 +struct ParamIdTextField : TextField { + void onTextEnter() override { + printf("xxx ParamIdTextField: enter param id \"%s\"\n", text); + } +}; + +struct ParamValueTextField : TextField { + ParamIdTextField *tf_id; + + void onTextEnter() override { + printf("xxx ParamValueTextField: enter param value \"%s\"\n", text); + int gid; + float value; + sscanf(tf_id->text.c_str(), "%d", &gid); + sscanf(text.c_str(), "%f", &value); + vst2_queue_param_sync(gid, value, false/*bNormalized*/); + } +}; +#endif // USE_VST2 + struct RegisterButton : Button { void onAction(EventAction &e) override { std::thread t([&]() { @@ -131,6 +158,21 @@ PluginManagerWidget::PluginManagerWidget() { layout->spacing = 5; loginWidget = layout; +#ifdef USE_VST2 + ParamIdTextField *paramIdField = new ParamIdTextField(); + paramIdField->box.size.x = 100; + paramIdField->placeholder = "VST Param Id"; + loginWidget->addChild(paramIdField); + + ParamValueTextField *paramValueField = new ParamValueTextField(); + paramValueField->tf_id = paramIdField; + paramValueField->box.size.x = 100; + paramValueField->placeholder = "Param Value"; + loginWidget->addChild(paramValueField); + + global_ui->param_info.tf_id = (TextField*)paramIdField; + global_ui->param_info.tf_value = (TextField*)paramValueField; +#else Button *registerButton = new RegisterButton(); registerButton->box.size.x = 75; registerButton->text = "Register"; @@ -152,6 +194,8 @@ PluginManagerWidget::PluginManagerWidget() { logInButton->emailField = emailField; logInButton->passwordField = passwordField; loginWidget->addChild(logInButton); +#endif // USE_VST2 + Label *label = new StatusLabel(); loginWidget->addChild(label); diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index d8291e89..7491acfe 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -80,6 +80,7 @@ void RackWidget::clear() { global_ui->app.gRackScene->scrollWidget->offset = Vec(0, 0); #ifdef USE_VST2 global->vst2.next_unique_param_base_id = 1; + global_ui->param_info.placeholder_framecount = (30*30)-1; #endif // USE_VST2 } @@ -530,6 +531,11 @@ void RackWidget::fromJson(json_t *rootJ) { if (!message.empty()) { osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); } + +#ifdef USE_VST2 + global_ui->param_info.placeholder_framecount = (30*30)-10; + global_ui->param_info.last_param_widget = NULL; +#endif // USE_VST2 } void RackWidget::addModule(ModuleWidget *m) { @@ -555,6 +561,19 @@ ModuleWidget *RackWidget::findModuleWidgetByModule(Module *_module) { return NULL; } +ParamWidget *RackWidget::findParamWidgetAndUniqueParamIdByWidgetRef(const ParamWidget *ref, int *retUniqueParamId) { + for(Widget *w : moduleContainer->children) { + ModuleWidget *moduleWidget = dynamic_cast(w); + for(ParamWidget *param : moduleWidget->params) { + if( (void*)param == (void*)ref ) { + *retUniqueParamId = moduleWidget->module->vst2_unique_param_base_id + param->paramId; + return param; + } + } + } + return NULL; +} + void RackWidget::cloneModule(ModuleWidget *m) { // Create new module from model ModuleWidget *clonedModuleWidget = m->model->createModuleWidget(); diff --git a/src/asset.cpp b/src/asset.cpp index 96e483ac..c7e2bd6d 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -97,7 +97,7 @@ std::string assetLocal(std::string filename) { std::string assetPlugin(Plugin *plugin, std::string filename) { - printf("xxx assetPlugin(plugin=%p)\n"); + printf("xxx assetPlugin(plugin=%p)\n", plugin); printf("xxx assetPlugin: filename=\"%s\"\n", filename.c_str()); assert(plugin); return plugin->path + "/" + filename; diff --git a/src/engine.cpp b/src/engine.cpp index d30672be..d97aabab 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -269,6 +269,7 @@ static bool loc_vst2_find_module_and_paramid_by_unique_paramid(int uniqueParamId } return false; } + #endif // USE_VST2 #ifdef USE_VST2 @@ -331,12 +332,13 @@ void engineSetParam(Module *module, int paramId, float value) { #ifdef USE_VST2 } using namespace rack; -void vst2_queue_param(int uniqueParamId, float normValue) { +void vst2_queue_param(int uniqueParamId, float value, bool bNormalized) { // Called from any thread via setParameter() // (note) protected by caller mutex VST2QueuedParam qp; qp.unique_id = uniqueParamId; - qp.norm_value = normValue; + qp.value = value; + qp.b_normalized = bNormalized; global->vst2.queued_params.push_back(qp); } @@ -359,19 +361,31 @@ void vst2_handle_queued_params(void) { ParamWidget *paramWidget = moduleWidget->findParamWidgetByParamId(paramId); if(NULL != paramWidget) { - // Normalize parameter if(isfinite(paramWidget->minValue) && isfinite(paramWidget->maxValue)) { - float paramRange = (paramWidget->maxValue - paramWidget->minValue); - if(paramRange > 0.0f) + if(qp.b_normalized) + { + // De-Normalize parameter + global_ui->param_info.b_lock = true; + float paramRange = (paramWidget->maxValue - paramWidget->minValue); + if(paramRange > 0.0f) + { + // float value = qp.norm_value - 0.5f; + // value *= 2.0f; + float value = (qp.value * paramRange) + paramWidget->minValue; + engineSetParam(module, paramId, value, false/*bVSTAutomate*/); + + // Update UI widget + paramWidget->setValue(value); + } + global_ui->param_info.b_lock = false; + } + else { - // float value = qp.norm_value - 0.5f; - // value *= 2.0f; - float value = (qp.norm_value * paramRange) + paramWidget->minValue; - engineSetParam(module, paramId, value, false/*bVSTAutomate*/); + engineSetParam(module, paramId, qp.value, false/*bVSTAutomate*/); // Update UI widget - paramWidget->setValue(value); + paramWidget->setValue(qp.value); } } } diff --git a/src/plugin.cpp b/src/plugin.cpp index 9a722060..e6b13212 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -135,6 +135,7 @@ static bool loadPlugin(std::string path) { #ifdef USE_VST2 #ifdef RACK_HOST plugin->vst2_handle_ui_param_fxn = &vst2_handle_ui_param; + plugin->vst2_queue_param_sync_fxn = &vst2_queue_param_sync; plugin->global = global; plugin->global_ui = global_ui; #endif // RACK_HOST diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index 648921e7..d0f44b4b 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -14,6 +14,7 @@ void Slider::draw(NVGcontext *vg) { void Slider::onDragStart(EventDragStart &e) { state = BND_ACTIVE; windowCursorLock(); + revert_val = value; } void Slider::onDragMove(EventDragMove &e) { @@ -29,9 +30,17 @@ void Slider::onDragEnd(EventDragEnd &e) { void Slider::onMouseDown(EventMouseDown &e) { if (e.button == 1) { - setValue(defaultValue); - EventAction eAction; - onAction(eAction); + if(INVALID_REVERT_VAL != revert_val) // during mouse drag + { + setValue(revert_val); + revert_val = INVALID_REVERT_VAL; + } + else + { + setValue(defaultValue); + } + EventAction eAction; + onAction(eAction); } e.consumed = true; e.target = this; diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp index f88d8505..8ddb0725 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp @@ -133,11 +133,21 @@ void TextField::onKey(EventKey &e) { break; case LGLW_VKEY_HOME: - selection = cursor = 0; + if(windowIsShiftPressed()) { + cursor = 0; + } + else { + selection = cursor = 0; + } break; case LGLW_VKEY_END: - selection = cursor = text.size(); + if(windowIsShiftPressed()) { + cursor = text.size(); + } + else { + selection = cursor = text.size(); + } break; case 'v': @@ -196,6 +206,18 @@ void TextField::onKey(EventKey &e) { } break; + case 'w': + if (windowIsModPressed()) { + return; // don't consume ctrl-w (copy param value) + } + break; + + case 'e': + if (windowIsModPressed()) { + return; // don't consume ctrl-e (paste param value) + } + break; + case LGLW_VKEY_RETURN: // printf("xxx TextField::onKey: RETURN\n"); if (multiline) { @@ -210,6 +232,7 @@ void TextField::onKey(EventKey &e) { } #endif // RACK_HOST onAction(e); + onTextEnter(); } break; } @@ -237,6 +260,11 @@ void TextField::setText(std::string text) { onTextChange(); } +void TextField::setTextQuiet(std::string text) { + this->text = text; + selection = cursor = text.size(); +} + int TextField::getTextPosition(Vec mousePos) { return bndTextFieldTextPosition(global_ui->window.gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y); } diff --git a/src/vst2_main.cpp b/src/vst2_main.cpp index 78c8d5f9..afdf89a8 100644 --- a/src/vst2_main.cpp +++ b/src/vst2_main.cpp @@ -77,7 +77,7 @@ extern void vst2_editor_redraw (void); extern void vst2_set_samplerate (sF32 _rate); extern void vst2_engine_process (float *const*_in, float **_out, unsigned int _numFrames); extern void vst2_process_midi_input_event (sU8 _a, sU8 _b, sU8 _c); -extern void vst2_queue_param (int uniqueParamId, float normValue); +extern void vst2_queue_param (int uniqueParamId, float value, bool bNormalized); extern void vst2_handle_queued_params (void); extern float vst2_get_param (int uniqueParamId); extern void vst2_get_param_name (int uniqueParamId, char *s, int sMaxLen); @@ -602,6 +602,8 @@ public: } b_editor_open = true; + rack::global_ui->param_info.placeholder_framecount = (30*30)-10; + rack::global_ui->param_info.last_param_widget = NULL; } void closeEditor(void) { @@ -1828,10 +1830,21 @@ void VSTPluginSetParameter(VSTPlugin *vstPlugin, wrapper->lockAudio(); wrapper->setGlobals(); - vst2_queue_param(index, parameter); + vst2_queue_param(index, parameter, true/*bNormalized*/); wrapper->unlockAudio(); } +void vst2_queue_param_sync(int _uniqueParamId, float _value, bool _bNormalized) { + // Called when parameter is edited numerically via textfield + printf("xxx vst2_queue_param_sync ENTER: uniqueParamId=%d value=%f bNormalized=%d\n", _uniqueParamId, _value, _bNormalized); + VSTPluginWrapper *wrapper = rack::global->vst2.wrapper; + + wrapper->lockAudio(); + vst2_queue_param(_uniqueParamId, _value, _bNormalized); + wrapper->unlockAudio(); + printf("xxx vst2_queue_param_sync LEAVE\n"); +} + /** * Query parameter diff --git a/src/widgets/QuantityWidget.cpp b/src/widgets/QuantityWidget.cpp index 62fce5a0..9f3a00af 100644 --- a/src/widgets/QuantityWidget.cpp +++ b/src/widgets/QuantityWidget.cpp @@ -44,5 +44,8 @@ std::string QuantityWidget::getText() { return text; } +void QuantityWidget::onMouseLeave(EventMouseLeave &e) { + revert_val = INVALID_REVERT_VAL; +} } // namespace rack diff --git a/src/window.cpp b/src/window.cpp index 1019497b..4a2135a5 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -457,6 +457,67 @@ void vst2_editor_redraw(void) { global_ui->window.gGuiFrame++; + // Find/validate hover param + if(NULL != global_ui->param_info.last_param_widget) + { + int uniqueParamId; + ParamWidget *paramWidget = + global_ui->app.gRackWidget->findParamWidgetAndUniqueParamIdByWidgetRef(global_ui->param_info.last_param_widget, + &uniqueParamId + ); + if(NULL != paramWidget) + { + global_ui->param_info.last_param_gid = uniqueParamId; + global_ui->param_info.last_param_value = paramWidget->value; + +#if 0 + printf("xxx vst2_editor_redraw: param_info: uniqueParamId=%d value=%f clipboardValue=%f\n", + global_ui->param_info.last_param_gid, + global_ui->param_info.last_param_value, + global_ui->param_info.value_clipboard + ); +#endif + + char buf[64]; + sprintf(buf, "%d", global_ui->param_info.last_param_gid); + global_ui->param_info.tf_id->setTextQuiet(buf); + sprintf(buf, "%f", global_ui->param_info.last_param_value); + + // Delete trailing zeros + { + char *d = buf; + while(0 != *d) + d++; + d--; + if(d > buf) + { + while('0' == *d) + { + if(((d-1) > buf) && ('.' != d[-1])) + *d-- = 0; + else + break; + } + } + } + + global_ui->param_info.tf_value->setTextQuiet(buf); + } + + global_ui->param_info.last_param_widget = NULL; + global_ui->param_info.placeholder_framecount = 1; + } + else if(0 != global_ui->param_info.placeholder_framecount) + { + if(++global_ui->param_info.placeholder_framecount > (30*30)) + { + global_ui->param_info.tf_id->setTextQuiet(""); + global_ui->param_info.tf_value->setTextQuiet(""); + global_ui->param_info.placeholder_framecount = 0; + } + } + + #if 0 // Set window title // (note) the VST plugin editor window title is set by the VST host