@@ -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; | |||
} | |||
@@ -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; | |||
}; | |||
@@ -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 "";} | |||
@@ -32,6 +32,7 @@ struct TextField : OpaqueWidget { | |||
/** Replaces the entire text */ | |||
void setText(std::string text); | |||
void selectAll(); | |||
virtual int getTextPosition(math::Vec mousePos); | |||
}; | |||
@@ -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() { | |||
@@ -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<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() { | |||
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<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); | |||
} | |||
@@ -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); | |||
} | |||