| @@ -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 <typename T = ParamWidget> | |||
| static T *create(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
| @@ -37,7 +37,8 @@ struct VSTMidiInputDevice; | |||
| struct VST2QueuedParam { | |||
| int unique_id; | |||
| float norm_value; | |||
| float value; | |||
| bool b_normalized; | |||
| }; | |||
| struct Global { | |||
| @@ -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; | |||
| } | |||
| }; | |||
| @@ -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; | |||
| @@ -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 { | |||
| @@ -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; | |||
| }; | |||
| @@ -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) { | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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<ModuleWidget*>(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(); | |||
| @@ -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; | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -44,5 +44,8 @@ std::string QuantityWidget::getText() { | |||
| return text; | |||
| } | |||
| void QuantityWidget::onMouseLeave(EventMouseLeave &e) { | |||
| revert_val = INVALID_REVERT_VAL; | |||
| } | |||
| } // namespace rack | |||
| @@ -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 | |||