From 4c23dc9cc0037841bf3225506955d5c5989847e8 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 7 Jan 2019 15:47:12 -0500 Subject: [PATCH] Added param value entry. Make ParamQuantity automatically compute its displayPrecision. --- include/engine/Param.hpp | 4 +-- include/ui/MenuOverlay.hpp | 30 +++------------------ include/ui/Quantity.hpp | 7 +++++ include/ui/TextField.hpp | 1 + src/app/ParamQuantity.cpp | 16 ++++++----- src/app/ParamWidget.cpp | 54 +++++++++++++++++++++++++++++++++++++- src/ui/TextField.cpp | 8 ++++-- 7 files changed, 81 insertions(+), 39 deletions(-) diff --git a/include/engine/Param.hpp b/include/engine/Param.hpp index b1df1b92..cdef1fdd 100644 --- a/include/engine/Param.hpp +++ b/include/engine/Param.hpp @@ -16,18 +16,16 @@ struct Param { /** Set to 0 for linear, nonzero for exponential */ float displayBase = 0.f; float displayMultiplier = 1.f; - int displayPrecision = 2; std::string label; std::string unit; - void setup(float minValue, float maxValue, float defaultValue, std::string label = "", std::string unit = "", int displayPrecision = 2, float displayBase = 0.f, float displayMultiplier = 1.f) { + void setup(float minValue, float maxValue, float defaultValue, std::string label = "", std::string unit = "", float displayBase = 0.f, float displayMultiplier = 1.f) { this->value = defaultValue; this->minValue = minValue; this->maxValue = maxValue; this->defaultValue = defaultValue; this->label = label; this->unit = unit; - this->displayPrecision = displayPrecision; this->displayBase = displayBase; this->displayMultiplier = displayMultiplier; } diff --git a/include/ui/MenuOverlay.hpp b/include/ui/MenuOverlay.hpp index 2b529152..1c3e84f6 100644 --- a/include/ui/MenuOverlay.hpp +++ b/include/ui/MenuOverlay.hpp @@ -8,33 +8,9 @@ namespace rack { /** Deletes itself from parent when clicked */ struct MenuOverlay : OpaqueWidget { - void step() override { - // Adopt parent's size - box.size = parent->box.size; - - // Fit all children in the box - for (Widget *child : children) { - child->box = child->box.nudge(box.zeroPos()); - } - - Widget::step(); - } - - void onButton(const event::Button &e) override { - OpaqueWidget::onButton(e); - - if (e.getConsumed() == this && e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { - requestedDelete = true; - } - } - - void onHoverKey(const event::HoverKey &e) override { - OpaqueWidget::onHoverKey(e); - - if (e.getConsumed() == this && e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { - requestedDelete = true; - } - } + void step() override; + void onButton(const event::Button &e) override; + void onHoverKey(const event::HoverKey &e) override; }; diff --git a/include/ui/Quantity.hpp b/include/ui/Quantity.hpp index c228d959..cd3f81f3 100644 --- a/include/ui/Quantity.hpp +++ b/include/ui/Quantity.hpp @@ -50,6 +50,13 @@ struct Quantity { return string::f("%.*f", getDisplayPrecision(), getDisplayValue()); } + virtual void setDisplayValueString(std::string s) { + float displayValue = 0.f; + int n = std::sscanf(s.c_str(), "%f", &displayValue); + if (n == 1) + setDisplayValue(displayValue); + } + /** The name of the quantity */ virtual std::string getLabel() {return "";} diff --git a/include/ui/TextField.hpp b/include/ui/TextField.hpp index 52d212b8..c1bcae8f 100644 --- a/include/ui/TextField.hpp +++ b/include/ui/TextField.hpp @@ -32,6 +32,7 @@ struct TextField : OpaqueWidget { /** Replaces the entire text */ void setText(std::string text); + void selectAll(); virtual int getTextPosition(math::Vec mousePos); }; diff --git a/src/app/ParamQuantity.cpp b/src/app/ParamQuantity.cpp index 9decd864..f03c4152 100644 --- a/src/app/ParamQuantity.cpp +++ b/src/app/ParamQuantity.cpp @@ -51,7 +51,7 @@ float ParamQuantity::getDisplayValue() { return Quantity::getDisplayValue(); if (getParam()->displayBase == 0.f) { // Linear - return getParam()->value * getParam()->displayMultiplier; + return getValue() * getParam()->displayMultiplier; } else if (getParam()->displayBase == 1.f) { // Fixed (special case of exponential) @@ -59,7 +59,7 @@ float ParamQuantity::getDisplayValue() { } else { // Exponential - return std::pow(getParam()->displayBase, getParam()->value) * getParam()->displayMultiplier; + return std::pow(getParam()->displayBase, getValue()) * getParam()->displayMultiplier; } } @@ -68,22 +68,26 @@ void ParamQuantity::setDisplayValue(float displayValue) { return; if (getParam()->displayBase == 0.f) { // Linear - getParam()->value = displayValue / getParam()->displayMultiplier; + setValue(displayValue / getParam()->displayMultiplier); } else if (getParam()->displayBase == 1.f) { // Fixed - getParam()->value = getParam()->displayMultiplier; + setValue(getParam()->displayMultiplier); } else { // Exponential - getParam()->value = std::log(displayValue / getParam()->displayMultiplier) / std::log(getParam()->displayBase); + setValue(std::log(displayValue / getParam()->displayMultiplier) / std::log(getParam()->displayBase)); } } int ParamQuantity::getDisplayPrecision() { if (!module) return Quantity::getDisplayPrecision(); - return getParam()->displayPrecision; + float displayValue = getDisplayValue(); + if (displayValue == 0.f) + return 0; + float log = std::log10(std::abs(getDisplayValue())); + return (int) std::ceil(math::clamp(-log + 3.f, 0.f, 6.f)); } std::string ParamQuantity::getLabel() { diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index d3c98d86..7f1b0f45 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -1,4 +1,6 @@ #include "app/ParamWidget.hpp" +#include "ui/MenuOverlay.hpp" +#include "ui/TextField.hpp" #include "app/Scene.hpp" #include "context.hpp" #include "settings.hpp" @@ -8,6 +10,43 @@ namespace rack { +struct ParamField : TextField { + ParamWidget *paramWidget; + + void step() override { + // Keep selected + context()->event->setSelected(this); + } + + void setParamWidget(ParamWidget *paramWidget) { + this->paramWidget = paramWidget; + if (paramWidget->quantity) + text = paramWidget->quantity->getDisplayValueString(); + selectAll(); + } + + void onSelectKey(const event::SelectKey &e) override { + if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) { + if (paramWidget->quantity) + paramWidget->quantity->setDisplayValueString(text); + + MenuOverlay *overlay = getAncestorOfType(); + overlay->requestedDelete = true; + e.consume(this); + } + + if (e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { + MenuOverlay *overlay = getAncestorOfType(); + overlay->requestedDelete = true; + e.consume(this); + } + + if (!e.getConsumed()) + TextField::onSelectKey(e); + } +}; + + ParamWidget::~ParamWidget() { if (quantity) delete quantity; @@ -27,7 +66,7 @@ void ParamWidget::step() { if (tooltip) { if (quantity) tooltip->text = quantity->getString(); - tooltip->box.pos = getAbsoluteOffset(box.size); + tooltip->box.pos = getAbsoluteOffset(box.size).round(); } OpaqueWidget::step(); @@ -50,6 +89,19 @@ void ParamWidget::onButton(const event::Button &e) { // dynamic_cast(quantity)->getParam()->reset(); } + // Shift-click to open value entry + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & GLFW_MOD_SHIFT) && !(e.mods & GLFW_MOD_CONTROL)) { + // Create ParamField + MenuOverlay *overlay = new MenuOverlay; + context()->scene->addChild(overlay); + + ParamField *paramField = new ParamField; + paramField->box.size.x = 100; + paramField->box.pos = getAbsoluteOffset(box.size).round(); + paramField->setParamWidget(this); + overlay->addChild(paramField); + } + OpaqueWidget::onButton(e); } diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp index 8873457d..4c554584 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp @@ -155,8 +155,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { } break; case GLFW_KEY_A: { if (context()->window->isModPressed()) { - selection = 0; - cursor = text.size(); + selectAll(); } } break; case GLFW_KEY_ENTER: { @@ -198,6 +197,11 @@ void TextField::setText(std::string text) { onChange(eChange); } +void TextField::selectAll() { + cursor = text.size(); + selection = 0; +} + int TextField::getTextPosition(math::Vec mousePos) { return bndTextFieldTextPosition(context()->window->vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y); }