@@ -15,32 +15,9 @@ struct Knob : ParamWidget { | |||||
/** Multiplier for mouse movement to adjust knob value */ | /** Multiplier for mouse movement to adjust knob value */ | ||||
float speed = 1.0; | float speed = 1.0; | ||||
void onDragStart(event::DragStart &e) override { | |||||
context()->window->cursorLock(); | |||||
} | |||||
void onDragEnd(event::DragEnd &e) override { | |||||
context()->window->cursorUnlock(); | |||||
} | |||||
void onDragMove(event::DragMove &e) override { | |||||
if (quantity) { | |||||
float range; | |||||
if (quantity->isBounded()) { | |||||
range = quantity->getRange(); | |||||
} | |||||
else { | |||||
// Continuous encoders scale as if their limits are +/-1 | |||||
range = 2.f; | |||||
} | |||||
float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range; | |||||
// Drag slower if Mod is held | |||||
if (context()->window->isModPressed()) | |||||
delta /= 16.f; | |||||
quantity->moveValue(delta); | |||||
} | |||||
} | |||||
void onDragStart(event::DragStart &e) override; | |||||
void onDragEnd(event::DragEnd &e) override; | |||||
void onDragMove(event::DragMove &e) override; | |||||
}; | }; | ||||
@@ -8,7 +8,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct LedDisplay : VirtualWidget { | |||||
struct LedDisplay : virtual Widget { | |||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
}; | }; | ||||
@@ -1,6 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/Quantity.hpp" | #include "ui/Quantity.hpp" | ||||
#include "engine/Module.hpp" | #include "engine/Module.hpp" | ||||
#include "engine/Param.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -16,70 +17,19 @@ struct ParamQuantity : Quantity { | |||||
bool snap = false; | bool snap = false; | ||||
float snapValue = 0.f; | float snapValue = 0.f; | ||||
Param *getParam() { | |||||
assert(module); | |||||
return &module->params[paramId]; | |||||
} | |||||
void commitSnap() { | |||||
// TODO | |||||
} | |||||
void setValue(float value) override { | |||||
// TODO Smooth | |||||
// TODO Snap | |||||
getParam()->value = value; | |||||
} | |||||
float getValue() override { | |||||
return getParam()->value; | |||||
} | |||||
float getMinValue() override { | |||||
return getParam()->minValue; | |||||
} | |||||
float getMaxValue() override { | |||||
return getParam()->maxValue; | |||||
} | |||||
float getDefaultValue() override { | |||||
return getParam()->defaultValue; | |||||
} | |||||
float getDisplayValue() override { | |||||
if (getParam()->displayBase == 0.f) { | |||||
// Linear | |||||
return getParam()->value * getParam()->displayMultiplier; | |||||
} | |||||
else { | |||||
// Exponential | |||||
return std::pow(getParam()->displayBase, getParam()->value) * getParam()->displayMultiplier; | |||||
} | |||||
} | |||||
void setDisplayValue(float displayValue) override { | |||||
if (getParam()->displayBase == 0.f) { | |||||
// Linear | |||||
getParam()->value = displayValue / getParam()->displayMultiplier; | |||||
} | |||||
else { | |||||
// Exponential | |||||
getParam()->value = std::log(displayValue / getParam()->displayMultiplier) / std::log(getParam()->displayBase); | |||||
} | |||||
} | |||||
int getDisplayPrecision() override { | |||||
return getParam()->displayPrecision; | |||||
} | |||||
std::string getLabel() override { | |||||
return getParam()->label; | |||||
} | |||||
std::string getUnit() override { | |||||
return getParam()->unit; | |||||
} | |||||
Param *getParam(); | |||||
void commitSnap(); | |||||
void setValue(float value) override; | |||||
float getValue() override; | |||||
float getMinValue() override; | |||||
float getMaxValue() override; | |||||
float getDefaultValue() override; | |||||
float getDisplayValue() override; | |||||
void setDisplayValue(float displayValue) override ; | |||||
int getDisplayPrecision() override; | |||||
std::string getLabel() override; | |||||
std::string getUnit() override; | |||||
}; | }; | ||||
@@ -1,22 +1,18 @@ | |||||
#pragma once | #pragma once | ||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "widgets/OpaqueWidget.hpp" | #include "widgets/OpaqueWidget.hpp" | ||||
#include "app/ParamQuantity.hpp" | |||||
#include "ui/Quantity.hpp" | |||||
namespace rack { | namespace rack { | ||||
/** Controls a ParamQuantity */ | |||||
struct ParamWidget : OpaqueWidget { | struct ParamWidget : OpaqueWidget { | ||||
ParamQuantity *quantity; | |||||
ParamWidget() { | |||||
quantity = new ParamQuantity; | |||||
} | |||||
Quantity *quantity = NULL; | |||||
~ParamWidget() { | ~ParamWidget() { | ||||
delete quantity; | |||||
if (quantity) | |||||
delete quantity; | |||||
} | } | ||||
/** For legacy patch loading */ | /** For legacy patch loading */ | ||||
@@ -6,7 +6,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct PluginManagerWidget : VirtualWidget { | |||||
struct PluginManagerWidget : virtual Widget { | |||||
Widget *loginWidget; | Widget *loginWidget; | ||||
Widget *manageWidget; | Widget *manageWidget; | ||||
Widget *downloadWidget; | Widget *downloadWidget; | ||||
@@ -1,11 +1,13 @@ | |||||
#include "plugin/Model.hpp" | #include "plugin/Model.hpp" | ||||
#include "event.hpp" | |||||
#include "ui/MenuLabel.hpp" | #include "ui/MenuLabel.hpp" | ||||
#include "ui/MenuItem.hpp" | #include "ui/MenuItem.hpp" | ||||
#include "ui/Menu.hpp" | #include "ui/Menu.hpp" | ||||
#include "app/Port.hpp" | #include "app/Port.hpp" | ||||
#include "app/ParamQuantity.hpp" | |||||
#include "app/ParamWidget.hpp" | |||||
#include "engine/Module.hpp" | #include "engine/Module.hpp" | ||||
#include "context.hpp" | #include "context.hpp" | ||||
#include "window.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -54,8 +56,10 @@ template <class TParamWidget> | |||||
TParamWidget *createParam(math::Vec pos, Module *module, int paramId) { | TParamWidget *createParam(math::Vec pos, Module *module, int paramId) { | ||||
TParamWidget *o = new TParamWidget; | TParamWidget *o = new TParamWidget; | ||||
o->box.pos = pos; | o->box.pos = pos; | ||||
o->quantity->module = module; | |||||
o->quantity->paramId = paramId; | |||||
ParamQuantity *q = new ParamQuantity; | |||||
q->module = module; | |||||
q->paramId = paramId; | |||||
o->quantity = q; | |||||
return o; | return o; | ||||
} | } | ||||
@@ -63,8 +67,10 @@ template <class TParamWidget> | |||||
TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId) { | TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId) { | ||||
TParamWidget *o = new TParamWidget; | TParamWidget *o = new TParamWidget; | ||||
o->box.pos = pos.minus(o->box.size.div(2)); | o->box.pos = pos.minus(o->box.size.div(2)); | ||||
o->quantity->module = module; | |||||
o->quantity->paramId = paramId; | |||||
ParamQuantity *q = new ParamQuantity; | |||||
q->module = module; | |||||
q->paramId = paramId; | |||||
o->quantity = q; | |||||
return o; | return o; | ||||
} | } | ||||
@@ -6,7 +6,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct Label : VirtualWidget { | |||||
struct Label : virtual Widget { | |||||
std::string text; | std::string text; | ||||
float fontSize; | float fontSize; | ||||
NVGcolor color; | NVGcolor color; | ||||
@@ -5,7 +5,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct ProgressBar : VirtualWidget { | |||||
struct ProgressBar : virtual Widget { | |||||
Quantity *quantity = NULL; | Quantity *quantity = NULL; | ||||
ProgressBar() { | ProgressBar() { | ||||
@@ -1,4 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | |||||
#include "math.hpp" | |||||
#include "string.hpp" | #include "string.hpp" | ||||
@@ -7,7 +7,7 @@ namespace rack { | |||||
/** Positions children in a row/column based on their widths/heights */ | /** Positions children in a row/column based on their widths/heights */ | ||||
struct SequentialLayout : VirtualWidget { | |||||
struct SequentialLayout : virtual Widget { | |||||
enum Orientation { | enum Orientation { | ||||
HORIZONTAL_ORIENTATION, | HORIZONTAL_ORIENTATION, | ||||
VERTICAL_ORIENTATION, | VERTICAL_ORIENTATION, | ||||
@@ -6,7 +6,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct Tooltip : VirtualWidget { | |||||
struct Tooltip : virtual Widget { | |||||
std::string text; | std::string text; | ||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
@@ -9,7 +9,7 @@ namespace rack { | |||||
When `dirty` is true, its children will be re-rendered on the next call to step() override. | When `dirty` is true, its children will be re-rendered on the next call to step() override. | ||||
Events are not passed to the underlying scene. | Events are not passed to the underlying scene. | ||||
*/ | */ | ||||
struct FramebufferWidget : VirtualWidget { | |||||
struct FramebufferWidget : virtual Widget { | |||||
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */ | /** Set this to true to re-render the children to the framebuffer the next time it is drawn */ | ||||
bool dirty = true; | bool dirty = true; | ||||
/** A margin in pixels around the children in the framebuffer | /** A margin in pixels around the children in the framebuffer | ||||
@@ -9,7 +9,7 @@ namespace rack { | |||||
You can of course override the events. | You can of course override the events. | ||||
You may also call OpaqueWidget::on*() from the overridden method to continue recursing/consuming the event. | You may also call OpaqueWidget::on*() from the overridden method to continue recursing/consuming the event. | ||||
*/ | */ | ||||
struct OpaqueWidget : VirtualWidget { | |||||
struct OpaqueWidget : virtual Widget { | |||||
void onHover(event::Hover &e) override { | void onHover(event::Hover &e) override { | ||||
Widget::onHover(e); | Widget::onHover(e); | ||||
if (!e.target) | if (!e.target) | ||||
@@ -7,7 +7,7 @@ namespace rack { | |||||
/** Draws an SVG */ | /** Draws an SVG */ | ||||
struct SVGWidget : VirtualWidget { | |||||
struct SVGWidget : virtual Widget { | |||||
std::shared_ptr<SVG> svg; | std::shared_ptr<SVG> svg; | ||||
/** Sets the box size to the svg image size */ | /** Sets the box size to the svg image size */ | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
/** Transforms appearance only, not positions of events */ | /** Transforms appearance only, not positions of events */ | ||||
struct TransformWidget : VirtualWidget { | |||||
struct TransformWidget : virtual Widget { | |||||
/** The transformation matrix */ | /** The transformation matrix */ | ||||
float transform[6]; | float transform[6]; | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
/** Widget that does not respond to events and does not pass events to children */ | /** Widget that does not respond to events and does not pass events to children */ | ||||
struct TransparentWidget : VirtualWidget { | |||||
struct TransparentWidget : virtual Widget { | |||||
/** Override behavior to do nothing instead. */ | /** Override behavior to do nothing instead. */ | ||||
void onHover(event::Hover &e) override {} | void onHover(event::Hover &e) override {} | ||||
void onButton(event::Button &e) override {} | void onButton(event::Button &e) override {} | ||||
@@ -12,7 +12,7 @@ namespace rack { | |||||
/** A node in the 2D scene graph | /** A node in the 2D scene graph | ||||
It is recommended to inherit virtually from Widget instead of directly. | It is recommended to inherit virtually from Widget instead of directly. | ||||
e.g. `struct MyWidget : VirtualWidget {}` | |||||
e.g. `struct MyWidget : virtual Widget {}` | |||||
*/ | */ | ||||
struct Widget { | struct Widget { | ||||
/** Stores position and size */ | /** Stores position and size */ | ||||
@@ -125,10 +125,4 @@ struct Widget { | |||||
}; | }; | ||||
/** Inherit from this class instead of inheriting from Widget directly. | |||||
Allows multiple inheritance in the class hierarchy. | |||||
*/ | |||||
struct VirtualWidget : virtual Widget {}; | |||||
} // namespace rack | } // namespace rack |
@@ -5,7 +5,7 @@ | |||||
namespace rack { | namespace rack { | ||||
struct ZoomWidget : VirtualWidget { | |||||
struct ZoomWidget : virtual Widget { | |||||
float zoom = 1.f; | float zoom = 1.f; | ||||
math::Vec getRelativeOffset(math::Vec v, Widget *relative) override { | math::Vec getRelativeOffset(math::Vec v, Widget *relative) override { | ||||
@@ -36,39 +36,32 @@ struct ZoomWidget : VirtualWidget { | |||||
} | } | ||||
void onHover(event::Hover &e) override { | void onHover(event::Hover &e) override { | ||||
event::Hover e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onHover(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onHover(e); | |||||
} | } | ||||
void onButton(event::Button &e) override { | void onButton(event::Button &e) override { | ||||
event::Button e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onButton(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onButton(e); | |||||
} | } | ||||
void onHoverKey(event::HoverKey &e) override { | void onHoverKey(event::HoverKey &e) override { | ||||
event::HoverKey e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onHoverKey(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onHoverKey(e); | |||||
} | } | ||||
void onHoverText(event::HoverText &e) override { | void onHoverText(event::HoverText &e) override { | ||||
event::HoverText e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onHoverText(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onHoverText(e); | |||||
} | } | ||||
void onHoverScroll(event::HoverScroll &e) override { | void onHoverScroll(event::HoverScroll &e) override { | ||||
event::HoverScroll e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onHoverScroll(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onHoverScroll(e); | |||||
} | } | ||||
void onDragHover(event::DragHover &e) override { | void onDragHover(event::DragHover &e) override { | ||||
event::DragHover e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onDragHover(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onDragHover(e); | |||||
} | } | ||||
void onPathDrop(event::PathDrop &e) override { | void onPathDrop(event::PathDrop &e) override { | ||||
event::PathDrop e2 = e; | |||||
e2.pos = e.pos.div(zoom); | |||||
Widget::onPathDrop(e2); | |||||
e.pos = e.pos.div(zoom); | |||||
Widget::onPathDrop(e); | |||||
} | } | ||||
}; | }; | ||||
@@ -0,0 +1,39 @@ | |||||
#include "app/Knob.hpp" | |||||
namespace rack { | |||||
void Knob::onDragStart(event::DragStart &e) { | |||||
context()->window->cursorLock(); | |||||
e.target = this; | |||||
} | |||||
void Knob::onDragEnd(event::DragEnd &e) { | |||||
context()->window->cursorUnlock(); | |||||
} | |||||
void Knob::onDragMove(event::DragMove &e) { | |||||
if (quantity) { | |||||
float range; | |||||
if (quantity->isBounded()) { | |||||
range = quantity->getRange(); | |||||
} | |||||
else { | |||||
// Continuous encoders scale as if their limits are +/-1 | |||||
range = 2.f; | |||||
} | |||||
float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range; | |||||
// Drag slower if Mod is held | |||||
if (context()->window->isModPressed()) | |||||
delta /= 16.f; | |||||
quantity->moveValue(delta); | |||||
event::Change eChange; | |||||
onChange(eChange); | |||||
} | |||||
} | |||||
} // namespace rack |
@@ -12,6 +12,7 @@ | |||||
#include "ui/TextField.hpp" | #include "ui/TextField.hpp" | ||||
#include "plugin.hpp" | #include "plugin.hpp" | ||||
#include "context.hpp" | #include "context.hpp" | ||||
#include "logger.hpp" | |||||
static const float itemMargin = 2.0; | static const float itemMargin = 2.0; | ||||
@@ -365,6 +365,7 @@ void ModuleWidget::onHoverKey(event::HoverKey &e) { | |||||
void ModuleWidget::onDragStart(event::DragStart &e) { | void ModuleWidget::onDragStart(event::DragStart &e) { | ||||
dragPos = context()->scene->rackWidget->lastMousePos.minus(box.pos); | dragPos = context()->scene->rackWidget->lastMousePos.minus(box.pos); | ||||
e.target = this; | |||||
} | } | ||||
void ModuleWidget::onDragEnd(event::DragEnd &e) { | void ModuleWidget::onDragEnd(event::DragEnd &e) { | ||||
@@ -0,0 +1,74 @@ | |||||
#include "app/ParamQuantity.hpp" | |||||
namespace rack { | |||||
Param *ParamQuantity::getParam() { | |||||
assert(module); | |||||
return &module->params[paramId]; | |||||
} | |||||
void ParamQuantity::commitSnap() { | |||||
// TODO | |||||
} | |||||
void ParamQuantity::setValue(float value) { | |||||
value = math::clamp(value, getMinValue(), getMaxValue()); | |||||
// TODO Smooth | |||||
// TODO Snap | |||||
getParam()->value = value; | |||||
} | |||||
float ParamQuantity::getValue() { | |||||
return getParam()->value; | |||||
} | |||||
float ParamQuantity::getMinValue() { | |||||
return getParam()->minValue; | |||||
} | |||||
float ParamQuantity::getMaxValue() { | |||||
return getParam()->maxValue; | |||||
} | |||||
float ParamQuantity::getDefaultValue() { | |||||
return getParam()->defaultValue; | |||||
} | |||||
float ParamQuantity::getDisplayValue() { | |||||
if (getParam()->displayBase == 0.f) { | |||||
// Linear | |||||
return getParam()->value * getParam()->displayMultiplier; | |||||
} | |||||
else { | |||||
// Exponential | |||||
return std::pow(getParam()->displayBase, getParam()->value) * getParam()->displayMultiplier; | |||||
} | |||||
} | |||||
void ParamQuantity::setDisplayValue(float displayValue) { | |||||
if (getParam()->displayBase == 0.f) { | |||||
// Linear | |||||
getParam()->value = displayValue / getParam()->displayMultiplier; | |||||
} | |||||
else { | |||||
// Exponential | |||||
getParam()->value = std::log(displayValue / getParam()->displayMultiplier) / std::log(getParam()->displayBase); | |||||
} | |||||
} | |||||
int ParamQuantity::getDisplayPrecision() { | |||||
return getParam()->displayPrecision; | |||||
} | |||||
std::string ParamQuantity::getLabel() { | |||||
return getParam()->label; | |||||
} | |||||
std::string ParamQuantity::getUnit() { | |||||
return getParam()->unit; | |||||
} | |||||
} // namespace rack |
@@ -511,6 +511,7 @@ void RackWidget::onHover(event::Hover &e) { | |||||
} | } | ||||
void RackWidget::onButton(event::Button &e) { | void RackWidget::onButton(event::Button &e) { | ||||
DEBUG("what"); | |||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
if (e.target == this) { | if (e.target == this) { | ||||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | ||||
@@ -30,11 +30,11 @@ void SVGKnob::step() { | |||||
if (dirty && quantity) { | if (dirty && quantity) { | ||||
float angle; | float angle; | ||||
if (quantity->isBounded()) { | if (quantity->isBounded()) { | ||||
angle = math::rescale(quantity->getValue(), -1.f, 1.f, minAngle, maxAngle); | |||||
angle = math::rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | |||||
angle = std::fmod(angle, 2*M_PI); | angle = std::fmod(angle, 2*M_PI); | ||||
} | } | ||||
else { | else { | ||||
angle = math::rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | |||||
angle = math::rescale(quantity->getValue(), 0.f, 1.f, minAngle, maxAngle); | |||||
} | } | ||||
tw->identity(); | tw->identity(); | ||||
// Rotate SVG | // Rotate SVG | ||||
@@ -48,7 +48,7 @@ void SVGKnob::step() { | |||||
void SVGKnob::onChange(event::Change &e) { | void SVGKnob::onChange(event::Change &e) { | ||||
dirty = true; | dirty = true; | ||||
ParamWidget::onChange(e); | |||||
Knob::onChange(e); | |||||
} | } | ||||
@@ -22,7 +22,7 @@ void Context::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
// event::DragStart | // event::DragStart | ||||
event::DragStart eDragStart; | event::DragStart eDragStart; | ||||
clickedWidget->onDragStart(eDragStart); | clickedWidget->onDragStart(eDragStart); | ||||
draggedWidget = clickedWidget; | |||||
draggedWidget = eDragStart.target; | |||||
} | } | ||||
if (action == GLFW_RELEASE && draggedWidget) { | if (action == GLFW_RELEASE && draggedWidget) { | ||||
@@ -63,14 +63,14 @@ void Context::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
} | } | ||||
} | } | ||||
if (button == GLFW_MOUSE_BUTTON_MIDDLE) { | |||||
if (action == GLFW_PRESS) { | |||||
scrollWidget = clickedWidget; | |||||
} | |||||
if (action == GLFW_RELEASE) { | |||||
scrollWidget = NULL; | |||||
} | |||||
} | |||||
// if (button == GLFW_MOUSE_BUTTON_MIDDLE) { | |||||
// if (action == GLFW_PRESS) { | |||||
// scrollWidget = clickedWidget; | |||||
// } | |||||
// if (action == GLFW_RELEASE) { | |||||
// scrollWidget = NULL; | |||||
// } | |||||
// } | |||||
} | } | ||||
@@ -99,6 +99,15 @@ void Widget::draw(NVGcontext *vg) { | |||||
nvgSave(vg); | nvgSave(vg); | ||||
nvgTranslate(vg, child->box.pos.x, child->box.pos.y); | nvgTranslate(vg, child->box.pos.x, child->box.pos.y); | ||||
child->draw(vg); | child->draw(vg); | ||||
// Draw red hitboxes | |||||
// if (context()->event->hoveredWidget == child) { | |||||
// nvgBeginPath(vg); | |||||
// nvgRect(vg, 0, 0, child->box.size.x, child->box.size.y); | |||||
// nvgFillColor(vg, nvgRGBAf(1, 0, 0, 0.5)); | |||||
// nvgFill(vg); | |||||
// } | |||||
nvgRestore(vg); | nvgRestore(vg); | ||||
} | } | ||||
} | } | ||||