@@ -16,18 +16,16 @@ struct Param { | |||||
/** Set to 0 for linear, nonzero for exponential */ | /** Set to 0 for linear, nonzero for exponential */ | ||||
float displayBase = 0.f; | float displayBase = 0.f; | ||||
float displayMultiplier = 1.f; | float displayMultiplier = 1.f; | ||||
int displayPrecision = 2; | |||||
std::string label; | std::string label; | ||||
std::string unit; | 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->value = defaultValue; | ||||
this->minValue = minValue; | this->minValue = minValue; | ||||
this->maxValue = maxValue; | this->maxValue = maxValue; | ||||
this->defaultValue = defaultValue; | this->defaultValue = defaultValue; | ||||
this->label = label; | this->label = label; | ||||
this->unit = unit; | this->unit = unit; | ||||
this->displayPrecision = displayPrecision; | |||||
this->displayBase = displayBase; | this->displayBase = displayBase; | ||||
this->displayMultiplier = displayMultiplier; | this->displayMultiplier = displayMultiplier; | ||||
} | } | ||||
@@ -8,33 +8,9 @@ namespace rack { | |||||
/** Deletes itself from parent when clicked */ | /** Deletes itself from parent when clicked */ | ||||
struct MenuOverlay : OpaqueWidget { | 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; | |||||
}; | }; | ||||
@@ -50,6 +50,13 @@ struct Quantity { | |||||
return string::f("%.*f", getDisplayPrecision(), getDisplayValue()); | 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 */ | /** The name of the quantity */ | ||||
virtual std::string getLabel() {return "";} | virtual std::string getLabel() {return "";} | ||||
@@ -32,6 +32,7 @@ struct TextField : OpaqueWidget { | |||||
/** Replaces the entire text */ | /** Replaces the entire text */ | ||||
void setText(std::string text); | void setText(std::string text); | ||||
void selectAll(); | |||||
virtual int getTextPosition(math::Vec mousePos); | virtual int getTextPosition(math::Vec mousePos); | ||||
}; | }; | ||||
@@ -51,7 +51,7 @@ float ParamQuantity::getDisplayValue() { | |||||
return Quantity::getDisplayValue(); | return Quantity::getDisplayValue(); | ||||
if (getParam()->displayBase == 0.f) { | if (getParam()->displayBase == 0.f) { | ||||
// Linear | // Linear | ||||
return getParam()->value * getParam()->displayMultiplier; | |||||
return getValue() * getParam()->displayMultiplier; | |||||
} | } | ||||
else if (getParam()->displayBase == 1.f) { | else if (getParam()->displayBase == 1.f) { | ||||
// Fixed (special case of exponential) | // Fixed (special case of exponential) | ||||
@@ -59,7 +59,7 @@ float ParamQuantity::getDisplayValue() { | |||||
} | } | ||||
else { | else { | ||||
// Exponential | // 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; | return; | ||||
if (getParam()->displayBase == 0.f) { | if (getParam()->displayBase == 0.f) { | ||||
// Linear | // Linear | ||||
getParam()->value = displayValue / getParam()->displayMultiplier; | |||||
setValue(displayValue / getParam()->displayMultiplier); | |||||
} | } | ||||
else if (getParam()->displayBase == 1.f) { | else if (getParam()->displayBase == 1.f) { | ||||
// Fixed | // Fixed | ||||
getParam()->value = getParam()->displayMultiplier; | |||||
setValue(getParam()->displayMultiplier); | |||||
} | } | ||||
else { | else { | ||||
// Exponential | // 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() { | int ParamQuantity::getDisplayPrecision() { | ||||
if (!module) | if (!module) | ||||
return Quantity::getDisplayPrecision(); | 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() { | std::string ParamQuantity::getLabel() { | ||||
@@ -1,4 +1,6 @@ | |||||
#include "app/ParamWidget.hpp" | #include "app/ParamWidget.hpp" | ||||
#include "ui/MenuOverlay.hpp" | |||||
#include "ui/TextField.hpp" | |||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "context.hpp" | #include "context.hpp" | ||||
#include "settings.hpp" | #include "settings.hpp" | ||||
@@ -8,6 +10,43 @@ | |||||
namespace rack { | 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<MenuOverlay>(); | |||||
overlay->requestedDelete = true; | |||||
e.consume(this); | |||||
} | |||||
if (e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { | |||||
MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | |||||
overlay->requestedDelete = true; | |||||
e.consume(this); | |||||
} | |||||
if (!e.getConsumed()) | |||||
TextField::onSelectKey(e); | |||||
} | |||||
}; | |||||
ParamWidget::~ParamWidget() { | ParamWidget::~ParamWidget() { | ||||
if (quantity) | if (quantity) | ||||
delete quantity; | delete quantity; | ||||
@@ -27,7 +66,7 @@ void ParamWidget::step() { | |||||
if (tooltip) { | if (tooltip) { | ||||
if (quantity) | if (quantity) | ||||
tooltip->text = quantity->getString(); | tooltip->text = quantity->getString(); | ||||
tooltip->box.pos = getAbsoluteOffset(box.size); | |||||
tooltip->box.pos = getAbsoluteOffset(box.size).round(); | |||||
} | } | ||||
OpaqueWidget::step(); | OpaqueWidget::step(); | ||||
@@ -50,6 +89,19 @@ void ParamWidget::onButton(const event::Button &e) { | |||||
// dynamic_cast<ParamQuantity*>(quantity)->getParam()->reset(); | // dynamic_cast<ParamQuantity*>(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); | OpaqueWidget::onButton(e); | ||||
} | } | ||||
@@ -155,8 +155,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { | |||||
} break; | } break; | ||||
case GLFW_KEY_A: { | case GLFW_KEY_A: { | ||||
if (context()->window->isModPressed()) { | if (context()->window->isModPressed()) { | ||||
selection = 0; | |||||
cursor = text.size(); | |||||
selectAll(); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_ENTER: { | case GLFW_KEY_ENTER: { | ||||
@@ -198,6 +197,11 @@ void TextField::setText(std::string text) { | |||||
onChange(eChange); | onChange(eChange); | ||||
} | } | ||||
void TextField::selectAll() { | |||||
cursor = text.size(); | |||||
selection = 0; | |||||
} | |||||
int TextField::getTextPosition(math::Vec mousePos) { | 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); | return bndTextFieldTextPosition(context()->window->vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y); | ||||
} | } | ||||