@@ -8,7 +8,7 @@ Licenses of **third-party libraries** are listed in [LICENSE-dist.txt](LICENSE-d | |||||
The **Component Library graphics** in the `res/ComponentLibrary` directory are copyright © 2018 [Grayscale](http://grayscale.info/) and licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/). Commercial plugins must request a commercial license to use Component Library graphics by emailing contact@vcvrack.com. | The **Component Library graphics** in the `res/ComponentLibrary` directory are copyright © 2018 [Grayscale](http://grayscale.info/) and licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/). Commercial plugins must request a commercial license to use Component Library graphics by emailing contact@vcvrack.com. | ||||
The **Core panel graphics** in the `res/Core` directory are copyright © 2018 [Grayscale](http://grayscale.info/) and licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). You may not distribute modified adaptations. | |||||
The **Core panel graphics** in the `res/Core` directory are copyright © 2018 [Grayscale](http://grayscale.info/) and licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). You may not create derivative works. | |||||
The **VCV logo and icon** are copyright © 2017 Andrew Belt and may not be used in derivative works. | The **VCV logo and icon** are copyright © 2017 Andrew Belt and may not be used in derivative works. | ||||
@@ -1,8 +1,8 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui.hpp" | |||||
#include "app/AudioWidget.hpp" | #include "app/AudioWidget.hpp" | ||||
#include "app/CircularShadow.hpp" | #include "app/CircularShadow.hpp" | ||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "app/Component.hpp" | |||||
#include "app/Knob.hpp" | #include "app/Knob.hpp" | ||||
#include "app/LedDisplay.hpp" | #include "app/LedDisplay.hpp" | ||||
#include "app/LightWidget.hpp" | #include "app/LightWidget.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/TransparentWidget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
@@ -1,17 +0,0 @@ | |||||
#pragma once | |||||
#include "app/common.hpp" | |||||
namespace rack { | |||||
struct Module; | |||||
/** A Widget that exists on a Panel and interacts with a Module */ | |||||
struct Component : OpaqueWidget { | |||||
Module *module = NULL; | |||||
}; | |||||
} // namespace rack |
@@ -6,17 +6,40 @@ | |||||
namespace rack { | namespace rack { | ||||
static const float KNOB_SENSITIVITY = 0.0015f; | |||||
/** Implements vertical dragging behavior for ParamWidgets */ | /** Implements vertical dragging behavior for ParamWidgets */ | ||||
struct Knob : ParamWidget { | struct Knob : ParamWidget { | ||||
/** Snap to nearest integer while dragging */ | |||||
bool snap = false; | |||||
/** Multiplier for mouse movement to adjust knob value */ | /** Multiplier for mouse movement to adjust knob value */ | ||||
float speed = 1.0; | float speed = 1.0; | ||||
float dragValue; | |||||
Knob(); | |||||
void onDragStart(event::DragStart &e) override; | |||||
void onDragMove(event::DragMove &e) override; | |||||
void onDragEnd(event::DragEnd &e) override; | |||||
void onDragStart(event::DragStart &e) override { | |||||
windowCursorLock(); | |||||
} | |||||
void onDragEnd(event::DragEnd &e) override { | |||||
windowCursorUnlock(); | |||||
} | |||||
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 (windowIsModPressed()) | |||||
delta /= 16.f; | |||||
quantity->moveValue(delta); | |||||
} | |||||
} | |||||
}; | }; | ||||
@@ -1,11 +1,14 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/Widget.hpp" | |||||
#include "widgets/TransparentWidget.hpp" | |||||
#include "ui/TextField.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct LedDisplay : virtual Widget { | |||||
struct LedDisplay : VirtualWidget { | |||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
}; | }; | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/TransparentWidget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
@@ -1,5 +1,10 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/Menu.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "app/SVGPanel.hpp" | |||||
#include "app/Port.hpp" | |||||
#include "app/ParamWidget.hpp" | |||||
#include "plugin.hpp" | #include "plugin.hpp" | ||||
#include "engine.hpp" | #include "engine.hpp" | ||||
@@ -0,0 +1,57 @@ | |||||
#pragma once | |||||
#include "ui/Quantity.hpp" | |||||
#include "engine.hpp" | |||||
namespace rack { | |||||
/** A Quantity that wraps an engine Param */ | |||||
struct ParamQuantity : Quantity { | |||||
Module *module = NULL; | |||||
int paramId = 0; | |||||
/** Use engine smoothing of Param values */ | |||||
bool smooth = false; | |||||
/** Snap to the nearest integer */ | |||||
bool snap = false; | |||||
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; | |||||
} | |||||
std::string getLabel() override { | |||||
return getParam()->label; | |||||
} | |||||
std::string getUnit() override { | |||||
return getParam()->unit; | |||||
} | |||||
int getPrecision() override { | |||||
return getParam()->precision; | |||||
} | |||||
}; | |||||
} // namespace rack |
@@ -1,4 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "app/ParamQuantity.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "engine.hpp" | #include "engine.hpp" | ||||
@@ -6,22 +8,23 @@ | |||||
namespace rack { | namespace rack { | ||||
/** A Component which has control over a Param */ | |||||
struct ParamWidget : Component, QuantityWidget { | |||||
int paramId; | |||||
/** Used to momentarily disable value randomization | |||||
To permanently disable or change randomization behavior, override the randomize() method instead of changing this. | |||||
*/ | |||||
bool randomizable = true; | |||||
/** Apply per-sample smoothing in the engine */ | |||||
bool smooth = false; | |||||
/** Controls a ParamQuantity */ | |||||
struct ParamWidget : OpaqueWidget { | |||||
ParamQuantity *quantity; | |||||
json_t *toJson(); | |||||
ParamWidget() { | |||||
quantity = new ParamQuantity; | |||||
} | |||||
~ParamWidget() { | |||||
delete quantity; | |||||
} | |||||
/** For legacy patch loading */ | |||||
void fromJson(json_t *rootJ); | void fromJson(json_t *rootJ); | ||||
virtual void reset(); | virtual void reset(); | ||||
virtual void randomize(); | virtual void randomize(); | ||||
void onButton(event::Button &e) override; | void onButton(event::Button &e) override; | ||||
void onChange(event::Change &e) override; | |||||
}; | }; | ||||
@@ -1,11 +1,12 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/Widget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct PluginManagerWidget : virtual Widget { | |||||
struct PluginManagerWidget : VirtualWidget { | |||||
Widget *loginWidget; | Widget *loginWidget; | ||||
Widget *manageWidget; | Widget *manageWidget; | ||||
Widget *downloadWidget; | Widget *downloadWidget; | ||||
@@ -1,17 +1,21 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "app/MultiLightWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
struct Port : Component { | |||||
struct Port : OpaqueWidget { | |||||
Module *module = NULL; | |||||
int portId; | |||||
enum PortType { | enum PortType { | ||||
INPUT, | INPUT, | ||||
OUTPUT | OUTPUT | ||||
}; | }; | ||||
PortType type = INPUT; | PortType type = INPUT; | ||||
int portId; | |||||
MultiLightWidget *plugLight; | MultiLightWidget *plugLight; | ||||
Port(); | Port(); | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/TransparentWidget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
@@ -1,10 +1,15 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/Scene.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct ScrollWidget; | |||||
struct ZoomWidget; | |||||
struct RackScene : Scene { | struct RackScene : Scene { | ||||
ScrollWidget *scrollWidget; | ScrollWidget *scrollWidget; | ||||
ZoomWidget *zoomWidget; | ZoomWidget *zoomWidget; | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/ScrollWidget.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
@@ -9,7 +9,7 @@ namespace rack { | |||||
/** A Component with a default (up) and active (down) state when clicked. | /** A Component with a default (up) and active (down) state when clicked. | ||||
Does not modify a Param, simply calls onAction() of a subclass. | Does not modify a Param, simply calls onAction() of a subclass. | ||||
*/ | */ | ||||
struct SVGButton : Component, FramebufferWidget { | |||||
struct SVGButton : FramebufferWidget { | |||||
std::shared_ptr<SVG> defaultSVG; | std::shared_ptr<SVG> defaultSVG; | ||||
std::shared_ptr<SVG> activeSVG; | std::shared_ptr<SVG> activeSVG; | ||||
SVGWidget *sw; | SVGWidget *sw; | ||||
@@ -1,6 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "app/CircularShadow.hpp" | #include "app/CircularShadow.hpp" | ||||
#include "widgets/TransformWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,5 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "widgets/FramebufferWidget.hpp" | |||||
#include "widgets/SVGWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,4 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/Slider.hpp" | |||||
#include "ui/RadioButton.hpp" | |||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
@@ -6,10 +9,9 @@ namespace rack { | |||||
struct Toolbar : OpaqueWidget { | struct Toolbar : OpaqueWidget { | ||||
Slider *wireOpacitySlider; | |||||
Slider *wireTensionSlider; | |||||
Slider *zoomSlider; | |||||
RadioButton *cpuUsageButton; | |||||
// TODO Move these to future Rack app state | |||||
float wireOpacity = 0.5; | |||||
float wireTension = 0.5; | |||||
Toolbar(); | Toolbar(); | ||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
@@ -1,5 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui.hpp" | |||||
#include "math.hpp" | |||||
#include <string> | |||||
#include <jansson.h> | #include <jansson.h> | ||||
@@ -37,11 +38,4 @@ static const std::string PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm"; | |||||
static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; | static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; | ||||
/** Deprecated. Will be removed in v1 */ | |||||
json_t *colorToJson(NVGcolor color); | |||||
/** Deprecated. Will be removed in v1 */ | |||||
NVGcolor jsonToColor(json_t *colorJ); | |||||
} // namespace rack | } // namespace rack |
@@ -59,8 +59,9 @@ struct RoundHugeBlackKnob : RoundKnob { | |||||
struct RoundBlackSnapKnob : RoundBlackKnob { | struct RoundBlackSnapKnob : RoundBlackKnob { | ||||
RoundBlackSnapKnob() { | RoundBlackSnapKnob() { | ||||
snap = true; | |||||
smooth = false; | |||||
// TODO | |||||
// quantity.snap = true; | |||||
// quantity.smooth = false; | |||||
} | } | ||||
}; | }; | ||||
@@ -308,8 +309,9 @@ struct BefacoBigKnob : SVGKnob { | |||||
struct BefacoBigSnapKnob : BefacoBigKnob { | struct BefacoBigSnapKnob : BefacoBigKnob { | ||||
BefacoBigSnapKnob() { | BefacoBigSnapKnob() { | ||||
snap = true; | |||||
smooth = false; | |||||
// TODO | |||||
// quantity.snap = true; | |||||
// quantity.smooth = false; | |||||
} | } | ||||
}; | }; | ||||
@@ -8,7 +8,27 @@ namespace rack { | |||||
struct Param { | struct Param { | ||||
float value = 0.0; | |||||
float value = 0.f; | |||||
float minValue = 0.f; | |||||
float maxValue = 1.f; | |||||
float defaultValue = 0.f; | |||||
std::string label; | |||||
std::string unit; | |||||
int precision = 2; | |||||
// TODO Change this horrible method name | |||||
void setup(float minValue, float maxValue, float defaultValue, std::string label = "", std::string unit = "", int precision = 2) { | |||||
this->value = defaultValue; | |||||
this->minValue = minValue; | |||||
this->maxValue = maxValue; | |||||
this->defaultValue = defaultValue; | |||||
this->label = label; | |||||
this->unit = unit; | |||||
this->precision = precision; | |||||
} | |||||
json_t *toJson(); | |||||
void fromJson(json_t *rootJ); | |||||
}; | }; | ||||
@@ -46,8 +46,8 @@ template <class TParamWidget> | |||||
TParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | TParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | ||||
TParamWidget *o = new TParamWidget; | TParamWidget *o = new TParamWidget; | ||||
o->box.pos = pos; | o->box.pos = pos; | ||||
o->module = module; | |||||
o->paramId = paramId; | |||||
o->quantity->module = module; | |||||
o->quantity->paramId = paramId; | |||||
o->setLimits(minValue, maxValue); | o->setLimits(minValue, maxValue); | ||||
o->setDefaultValue(defaultValue); | o->setDefaultValue(defaultValue); | ||||
return o; | return o; | ||||
@@ -57,8 +57,8 @@ template <class TParamWidget> | |||||
TParamWidget *createParamCentered(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | TParamWidget *createParamCentered(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | ||||
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->module = module; | |||||
o->paramId = paramId; | |||||
o->quantity->module = module; | |||||
o->quantity->paramId = paramId; | |||||
o->setLimits(minValue, maxValue); | o->setLimits(minValue, maxValue); | ||||
o->setDefaultValue(defaultValue); | o->setDefaultValue(defaultValue); | ||||
return o; | return o; | ||||
@@ -244,6 +244,12 @@ struct Rect { | |||||
bool isEqual(Rect r) const { | bool isEqual(Rect r) const { | ||||
return pos.isEqual(r.pos) && size.isEqual(r.size); | return pos.isEqual(r.pos) && size.isEqual(r.size); | ||||
} | } | ||||
float getLeft() const { | |||||
return pos.x + size.x; | |||||
} | |||||
float getBottom() const { | |||||
return pos.y + size.y; | |||||
} | |||||
Vec getCenter() const { | Vec getCenter() const { | ||||
return pos.plus(size.mult(0.5f)); | return pos.plus(size.mult(0.5f)); | ||||
} | } | ||||
@@ -22,6 +22,9 @@ bool endsWith(std::string str, std::string suffix); | |||||
/** Extracts portions of a path */ | /** Extracts portions of a path */ | ||||
std::string directory(std::string path); | std::string directory(std::string path); | ||||
std::string filename(std::string path); | std::string filename(std::string path); | ||||
/** Extracts the portion of a path without the extension */ | |||||
std::string basename(std::string path); | |||||
/** Extracts the extension of a path */ | |||||
std::string extension(std::string path); | std::string extension(std::string path); | ||||
struct CaseInsensitiveCompare { | struct CaseInsensitiveCompare { | ||||
@@ -1,5 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include <vector> | |||||
#include <list> | |||||
#include "common.hpp" | #include "common.hpp" | ||||
@@ -7,7 +7,7 @@ namespace rack { | |||||
namespace system { | namespace system { | ||||
std::vector<std::string> listEntries(std::string path); | |||||
std::list<std::string> listEntries(std::string path); | |||||
bool isFile(std::string path); | bool isFile(std::string path); | ||||
bool isDirectory(std::string path); | bool isDirectory(std::string path); | ||||
void copyFile(std::string srcPath, std::string destPath); | void copyFile(std::string srcPath, std::string destPath); | ||||
@@ -1,6 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/QuantityWidget.hpp" | |||||
#include "ui/SequentialLayout.hpp" | #include "ui/SequentialLayout.hpp" | ||||
#include "ui/Label.hpp" | #include "ui/Label.hpp" | ||||
#include "ui/List.hpp" | #include "ui/List.hpp" | ||||
@@ -1,5 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/Quantity.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -8,11 +10,18 @@ namespace rack { | |||||
struct Button : OpaqueWidget { | struct Button : OpaqueWidget { | ||||
std::string text; | std::string text; | ||||
BNDwidgetState state = BND_DEFAULT; | BNDwidgetState state = BND_DEFAULT; | ||||
/** Optional, owned. Tracks the pressed state of the button.*/ | |||||
Quantity *quantity = NULL; | |||||
Button() { | Button() { | ||||
box.size.y = BND_WIDGET_HEIGHT; | box.size.y = BND_WIDGET_HEIGHT; | ||||
} | } | ||||
~Button() { | |||||
if (quantity) | |||||
delete quantity; | |||||
} | |||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
bndToolButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str()); | bndToolButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str()); | ||||
Widget::draw(vg); | Widget::draw(vg); | ||||
@@ -28,10 +37,14 @@ struct Button : OpaqueWidget { | |||||
void onDragStart(event::DragStart &e) override { | void onDragStart(event::DragStart &e) override { | ||||
state = BND_ACTIVE; | state = BND_ACTIVE; | ||||
if (quantity) | |||||
quantity->setMax(); | |||||
} | } | ||||
void onDragEnd(event::DragEnd &e) override { | void onDragEnd(event::DragEnd &e) override { | ||||
state = BND_HOVER; | state = BND_HOVER; | ||||
if (quantity) | |||||
quantity->setMin(); | |||||
} | } | ||||
void onDragDrop(event::DragDrop &e) override { | void onDragDrop(event::DragDrop &e) override { | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | |||||
#include "ui/Button.hpp" | #include "ui/Button.hpp" | ||||
@@ -1,5 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/Button.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,11 +1,12 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/Widget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct Label : virtual Widget { | |||||
struct Label : VirtualWidget { | |||||
std::string text; | std::string text; | ||||
float fontSize; | float fontSize; | ||||
NVGcolor color; | NVGcolor color; | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,4 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | |||||
#include "ui/MenuEntry.hpp" | #include "ui/MenuEntry.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,6 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/MenuOverlay.hpp" | |||||
#include "ui/MenuEntry.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,5 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/MenuEntry.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,5 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/MenuEntry.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,19 +1,26 @@ | |||||
#pragma once | #pragma once | ||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/QuantityWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
struct ProgressBar : QuantityWidget { | |||||
struct ProgressBar : VirtualWidget { | |||||
Quantity *quantity = NULL; | |||||
ProgressBar() { | ProgressBar() { | ||||
box.size.y = BND_WIDGET_HEIGHT; | box.size.y = BND_WIDGET_HEIGHT; | ||||
} | } | ||||
~ProgressBar() { | |||||
if (quantity) | |||||
delete quantity; | |||||
} | |||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
float progress = rescale(value, minValue, maxValue, 0.0, 1.0); | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL, BND_DEFAULT, progress, getText().c_str(), NULL); | |||||
float progress = quantity ? quantity->getScaledValue() : 0.f; | |||||
std::string text = quantity ? quantity->getString() : ""; | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL, BND_DEFAULT, progress, text.c_str(), NULL); | |||||
} | } | ||||
}; | }; | ||||
@@ -0,0 +1,128 @@ | |||||
#pragma once | |||||
#include "string.hpp" | |||||
namespace rack { | |||||
/** A controller for manipulating a float value (which subclasses must store somehow) with limits and labels | |||||
Often used as a decorator component for Widgets that read or write a quantity. | |||||
*/ | |||||
struct Quantity { | |||||
virtual ~Quantity() {} | |||||
/** Sets the value directly | |||||
Override this to change the state of your subclass to represent the new value. | |||||
*/ | |||||
virtual void setValue(float value) {} | |||||
/** Returns the value | |||||
Override this to return the state of your subclass. | |||||
*/ | |||||
virtual float getValue() {return 0.f;} | |||||
/** Returns the minimum allowed value */ | |||||
virtual float getMinValue() {return 0.f;} | |||||
/** Returns the maximum allowed value */ | |||||
virtual float getMaxValue() {return 1.f;} | |||||
/** Returns the default value, for resetting */ | |||||
virtual float getDefaultValue() {return 0.f;} | |||||
/** Returns the value, possibly transformed | |||||
Useful for logarithmic scaling, multiplying by 100 for percentages, etc. | |||||
*/ | |||||
virtual float getDisplayValue() {return getValue();} | |||||
/** Sets the value by the transformed display value */ | |||||
virtual void setDisplayValue(float displayValue) {setValue(displayValue);} | |||||
/** The number of decimal places for display | |||||
A precision of 2 will display as "1.00" for example. | |||||
*/ | |||||
virtual int getPrecision() {return 2;} | |||||
/** Returns a string representation of the display value */ | |||||
virtual std::string getDisplayValueString() { | |||||
return string::f("%.*f", getPrecision(), getDisplayValue()); | |||||
} | |||||
/** The name of the quantity */ | |||||
virtual std::string getLabel() {return "";} | |||||
/** The unit abbreviation of the quantity | |||||
Include an initial space character if you want a space after the number, e.g. "440 Hz". This allows space-less units, like "100%". | |||||
*/ | |||||
virtual std::string getUnit() {return "";} | |||||
/** Returns a string representation of the quantity */ | |||||
virtual std::string getString() { | |||||
std::string s; | |||||
std::string label = getLabel(); | |||||
if (!label.empty()) | |||||
s += label + ": "; | |||||
s += getDisplayValueString() + getUnit(); | |||||
return s; | |||||
} | |||||
// Helper methods | |||||
/** Resets the value to the default value */ | |||||
void reset() { | |||||
setValue(getDefaultValue()); | |||||
} | |||||
/** Checks whether the value is at the min value */ | |||||
bool isMin() { | |||||
return getValue() <= getMinValue(); | |||||
} | |||||
/** Checks whether the value is at the max value */ | |||||
bool isMax() { | |||||
return getValue() >= getMaxValue(); | |||||
} | |||||
/** Sets the value to the min value */ | |||||
void setMin() { | |||||
setValue(getMinValue()); | |||||
} | |||||
/** Sets the value to the max value */ | |||||
void setMax() { | |||||
setValue(getMaxValue()); | |||||
} | |||||
/** Sets value from the range 0 to 1 */ | |||||
void setScaledValue(float scaledValue) { | |||||
setValue(rescale(scaledValue, 0.f, 1.f, getMinValue(), getMaxValue())); | |||||
} | |||||
/** Returns the value rescaled to the range 0 to 1 */ | |||||
float getScaledValue() { | |||||
return rescale(getValue(), getMinValue(), getMaxValue(), 0.f, 1.f); | |||||
} | |||||
/** The difference between the max and min values */ | |||||
float getRange() { | |||||
return getMaxValue() - getMinValue(); | |||||
} | |||||
/** Checks whether the bounds are finite */ | |||||
bool isBounded() { | |||||
return std::isfinite(getMinValue()) && std::isfinite(getMaxValue()); | |||||
} | |||||
/** Adds an amount to the value */ | |||||
void moveValue(float deltaValue) { | |||||
setValue(getValue() + deltaValue); | |||||
} | |||||
/** Adds an amount to the value scaled to the range 0 to 1 */ | |||||
void moveScaledValue(float deltaScaledValue) { | |||||
moveValue(deltaScaledValue * getRange()); | |||||
} | |||||
}; | |||||
} // namespace rack |
@@ -1,49 +0,0 @@ | |||||
#pragma once | |||||
#include "ui/common.hpp" | |||||
namespace rack { | |||||
/** A Widget representing a float value */ | |||||
struct QuantityWidget : virtual Widget { | |||||
float value = 0.0; | |||||
float minValue = 0.0; | |||||
float maxValue = 1.0; | |||||
float defaultValue = 0.0; | |||||
std::string label; | |||||
/** Include a space character if you want a space after the number, e.g. " Hz" */ | |||||
std::string unit; | |||||
/** The decimal place to round for displaying values. | |||||
A precision of 2 will display as "1.00" for example. | |||||
*/ | |||||
int precision = 2; | |||||
void reset() { | |||||
setValue(defaultValue); | |||||
} | |||||
void setValue(float value) { | |||||
this->value = clampBetween(value, minValue, maxValue); | |||||
event::Change e; | |||||
onChange(e); | |||||
} | |||||
void setLimits(float minValue, float maxValue) { | |||||
this->minValue = minValue; | |||||
this->maxValue = maxValue; | |||||
setValue(value); | |||||
} | |||||
void setDefaultValue(float defaultValue) { | |||||
this->defaultValue = defaultValue; | |||||
} | |||||
/** Generates the display value */ | |||||
std::string getText() { | |||||
return string::f("%s: %.*f%s", label.c_str(), precision, value, unit.c_str()); | |||||
} | |||||
}; | |||||
} // namespace rack |
@@ -1,36 +1,53 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/QuantityWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
struct RadioButton : OpaqueWidget, QuantityWidget { | |||||
struct RadioButton : OpaqueWidget { | |||||
BNDwidgetState state = BND_DEFAULT; | BNDwidgetState state = BND_DEFAULT; | ||||
Quantity *quantity = NULL; | |||||
RadioButton() { | RadioButton() { | ||||
box.size.y = BND_WIDGET_HEIGHT; | box.size.y = BND_WIDGET_HEIGHT; | ||||
} | } | ||||
~RadioButton() { | |||||
if (quantity) | |||||
delete quantity; | |||||
} | |||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
bndRadioButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, value == 0.0 ? state : BND_ACTIVE, -1, label.c_str()); | |||||
std::string label; | |||||
if (quantity) | |||||
label = quantity->getLabel(); | |||||
bndRadioButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, label.c_str()); | |||||
} | } | ||||
void onEnter(event::Enter &e) override { | void onEnter(event::Enter &e) override { | ||||
state = BND_HOVER; | |||||
if (state != BND_ACTIVE) | |||||
state = BND_HOVER; | |||||
} | } | ||||
void onLeave(event::Leave &e) override { | void onLeave(event::Leave &e) override { | ||||
state = BND_DEFAULT; | |||||
if (state != BND_ACTIVE) | |||||
state = BND_DEFAULT; | |||||
} | } | ||||
void onDragDrop(event::DragDrop &e) override { | void onDragDrop(event::DragDrop &e) override { | ||||
if (e.origin == this) { | if (e.origin == this) { | ||||
if (value) | |||||
setValue(0.0); | |||||
else | |||||
setValue(1.0); | |||||
if (state == BND_ACTIVE) { | |||||
state = BND_HOVER; | |||||
if (quantity) | |||||
quantity->setMin(); | |||||
} | |||||
else { | |||||
state = BND_ACTIVE; | |||||
if (quantity) | |||||
quantity->setMax(); | |||||
} | |||||
event::Action eAction; | event::Action eAction; | ||||
onAction(eAction); | onAction(eAction); | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "event.hpp" | #include "event.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/Widget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -6,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 : virtual Widget { | |||||
struct SequentialLayout : VirtualWidget { | |||||
enum Orientation { | enum Orientation { | ||||
HORIZONTAL_ORIENTATION, | HORIZONTAL_ORIENTATION, | ||||
VERTICAL_ORIENTATION, | VERTICAL_ORIENTATION, | ||||
@@ -1,6 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/Quantity.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "ui/QuantityWidget.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -9,16 +10,23 @@ namespace rack { | |||||
static const float SLIDER_SENSITIVITY = 0.001f; | static const float SLIDER_SENSITIVITY = 0.001f; | ||||
struct Slider : OpaqueWidget, QuantityWidget { | |||||
struct Slider : OpaqueWidget { | |||||
BNDwidgetState state = BND_DEFAULT; | BNDwidgetState state = BND_DEFAULT; | ||||
Quantity *quantity = NULL; | |||||
Slider() { | Slider() { | ||||
box.size.y = BND_WIDGET_HEIGHT; | box.size.y = BND_WIDGET_HEIGHT; | ||||
} | } | ||||
~Slider() { | |||||
if (quantity) | |||||
delete quantity; | |||||
} | |||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
float progress = rescale(value, minValue, maxValue, 0.0, 1.0); | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL); | |||||
float progress = quantity ? quantity->getScaledValue() : 0.f; | |||||
std::string text = quantity ? quantity->getString() : ""; | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, text.c_str(), NULL); | |||||
} | } | ||||
void onDragStart(event::DragStart &e) override { | void onDragStart(event::DragStart &e) override { | ||||
@@ -27,7 +35,9 @@ struct Slider : OpaqueWidget, QuantityWidget { | |||||
} | } | ||||
void onDragMove(event::DragMove &e) override { | void onDragMove(event::DragMove &e) override { | ||||
setValue(value + SLIDER_SENSITIVITY * (maxValue - minValue) * e.mouseDelta.x); | |||||
if (quantity) { | |||||
quantity->moveScaledValue(SLIDER_SENSITIVITY * e.mouseDelta.x); | |||||
} | |||||
} | } | ||||
void onDragEnd(event::DragEnd &e) override { | void onDragEnd(event::DragEnd &e) override { | ||||
@@ -37,7 +47,8 @@ struct Slider : OpaqueWidget, QuantityWidget { | |||||
void onButton(event::Button &e) override { | void onButton(event::Button &e) override { | ||||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | ||||
setValue(defaultValue); | |||||
if (quantity) | |||||
quantity->reset(); | |||||
} | } | ||||
e.target = this; | e.target = this; | ||||
} | } | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
#include "event.hpp" | #include "event.hpp" | ||||
@@ -1,11 +1,12 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/Widget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct Tooltip : virtual Widget { | |||||
struct Tooltip : VirtualWidget { | |||||
std::string text; | std::string text; | ||||
void draw(NVGcontext *vg) override { | void draw(NVGcontext *vg) override { | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/TransparentWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets/OpaqueWidget.hpp" | |||||
#include "ui/common.hpp" | #include "ui/common.hpp" | ||||
@@ -1,5 +1,4 @@ | |||||
#pragma once | #pragma once | ||||
#include "widgets.hpp" | |||||
#include "blendish.h" | #include "blendish.h" | ||||
#define CHECKMARK_STRING "✔" | #define CHECKMARK_STRING "✔" | ||||
@@ -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 : virtual Widget { | |||||
struct FramebufferWidget : VirtualWidget { | |||||
/** 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 : virtual Widget { | |||||
struct OpaqueWidget : VirtualWidget { | |||||
void onHover(event::Hover &e) override { | void onHover(event::Hover &e) override { | ||||
Widget::onHover(e); | Widget::onHover(e); | ||||
if (!e.target) | if (!e.target) | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
/** Draws an SVG */ | /** Draws an SVG */ | ||||
struct SVGWidget : virtual Widget { | |||||
struct SVGWidget : VirtualWidget { | |||||
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 : virtual Widget { | |||||
struct TransformWidget : VirtualWidget { | |||||
/** 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 : virtual Widget { | |||||
struct TransparentWidget : VirtualWidget { | |||||
/** 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 : virtual Widget {}` | |||||
e.g. `struct MyWidget : VirtualWidget {}` | |||||
*/ | */ | ||||
struct Widget { | struct Widget { | ||||
/** Stores position and size */ | /** Stores position and size */ | ||||
@@ -125,4 +125,10 @@ 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 : virtual Widget { | |||||
struct ZoomWidget : VirtualWidget { | |||||
float zoom = 1.f; | float zoom = 1.f; | ||||
Vec getRelativeOffset(Vec v, Widget *relative) override { | Vec getRelativeOffset(Vec v, Widget *relative) override { | ||||
@@ -1,4 +1,4 @@ | |||||
#include "app.hpp" | |||||
#include "app/AudioWidget.hpp" | |||||
#include "audio.hpp" | #include "audio.hpp" | ||||
#include "helpers.hpp" | #include "helpers.hpp" | ||||
@@ -1,52 +0,0 @@ | |||||
#include "app.hpp" | |||||
#include "window.hpp" | |||||
#include "engine.hpp" | |||||
// For GLFW_KEY_LEFT_CONTROL, etc. | |||||
#include <GLFW/glfw3.h> | |||||
namespace rack { | |||||
static const float KNOB_SENSITIVITY = 0.0015f; | |||||
Knob::Knob() { | |||||
smooth = true; | |||||
} | |||||
void Knob::onDragStart(event::DragStart &e) { | |||||
windowCursorLock(); | |||||
dragValue = value; | |||||
randomizable = false; | |||||
} | |||||
void Knob::onDragMove(event::DragMove &e) { | |||||
float range; | |||||
if (std::isfinite(minValue) && std::isfinite(maxValue)) { | |||||
range = maxValue - minValue; | |||||
} | |||||
else { | |||||
// Continuous encoders scale as if their limits are +/-1 | |||||
range = 1.f - (-1.f); | |||||
} | |||||
float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range; | |||||
// Drag slower if Mod is held | |||||
if (windowIsModPressed()) | |||||
delta /= 16.f; | |||||
dragValue += delta; | |||||
dragValue = clampBetween(dragValue, minValue, maxValue); | |||||
if (snap) | |||||
setValue(std::round(dragValue)); | |||||
else | |||||
setValue(dragValue); | |||||
} | |||||
void Knob::onDragEnd(event::DragEnd &e) { | |||||
windowCursorUnlock(); | |||||
randomizable = true; | |||||
} | |||||
} // namespace rack |
@@ -44,9 +44,20 @@ static bool isModelMatch(Model *model, std::string search) { | |||||
} | } | ||||
struct FavoriteQuantity : Quantity { | |||||
std::string getString() override { | |||||
return "★"; | |||||
} | |||||
}; | |||||
struct FavoriteRadioButton : RadioButton { | struct FavoriteRadioButton : RadioButton { | ||||
Model *model = NULL; | Model *model = NULL; | ||||
FavoriteRadioButton() { | |||||
quantity = new FavoriteQuantity; | |||||
} | |||||
void onAction(event::Action &e) override; | void onAction(event::Action &e) override; | ||||
}; | }; | ||||
@@ -111,13 +122,12 @@ struct ModelItem : BrowserListItem { | |||||
FavoriteRadioButton *favoriteButton = createWidget<FavoriteRadioButton>(Vec(8, itemMargin)); | FavoriteRadioButton *favoriteButton = createWidget<FavoriteRadioButton>(Vec(8, itemMargin)); | ||||
favoriteButton->box.size.x = 20; | favoriteButton->box.size.x = 20; | ||||
favoriteButton->label = "★"; | |||||
addChild(favoriteButton); | addChild(favoriteButton); | ||||
// Set favorite button initial state | // Set favorite button initial state | ||||
auto it = sFavoriteModels.find(model); | auto it = sFavoriteModels.find(model); | ||||
if (it != sFavoriteModels.end()) | if (it != sFavoriteModels.end()) | ||||
favoriteButton->setValue(1); | |||||
favoriteButton->quantity->setValue(1); | |||||
favoriteButton->model = model; | favoriteButton->model = model; | ||||
Label *nameLabel = createWidget<Label>(favoriteButton->box.getTopRight()); | Label *nameLabel = createWidget<Label>(favoriteButton->box.getTopRight()); | ||||
@@ -465,7 +475,7 @@ void ClearFilterItem::onAction(event::Action &e) { | |||||
void FavoriteRadioButton::onAction(event::Action &e) { | void FavoriteRadioButton::onAction(event::Action &e) { | ||||
if (!model) | if (!model) | ||||
return; | return; | ||||
if (value) { | |||||
if (quantity->isMax()) { | |||||
sFavoriteModels.insert(model); | sFavoriteModels.insert(model); | ||||
} | } | ||||
else { | else { | ||||
@@ -61,18 +61,20 @@ json_t *ModuleWidget::toJson() { | |||||
// plugin | // plugin | ||||
json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); | json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); | ||||
// version (of plugin) | |||||
// version of plugin | |||||
if (!model->plugin->version.empty()) | if (!model->plugin->version.empty()) | ||||
json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); | json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); | ||||
// model | // model | ||||
json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | ||||
// params | // params | ||||
json_t *paramsJ = json_array(); | |||||
for (ParamWidget *paramWidget : params) { | |||||
json_t *paramJ = paramWidget->toJson(); | |||||
json_array_append_new(paramsJ, paramJ); | |||||
if (module) { | |||||
json_t *paramsJ = json_array(); | |||||
for (Param ¶m : module->params) { | |||||
json_t *paramJ = param.toJson(); | |||||
json_array_append_new(paramsJ, paramJ); | |||||
} | |||||
json_object_set_new(rootJ, "params", paramsJ); | |||||
} | } | ||||
json_object_set_new(rootJ, "params", paramsJ); | |||||
// data | // data | ||||
if (module) { | if (module) { | ||||
json_t *dataJ = module->toJson(); | json_t *dataJ = module->toJson(); | ||||
@@ -126,28 +128,15 @@ void ModuleWidget::fromJson(json_t *rootJ) { | |||||
size_t i; | size_t i; | ||||
json_t *paramJ; | json_t *paramJ; | ||||
json_array_foreach(paramsJ, i, paramJ) { | json_array_foreach(paramsJ, i, paramJ) { | ||||
if (legacy && legacy <= 1) { | |||||
// Legacy 1 mode | |||||
// The index in the array we're iterating is the index of the ParamWidget in the params vector. | |||||
if (i < params.size()) { | |||||
// Create upgraded version of param JSON object | |||||
json_t *newParamJ = json_object(); | |||||
json_object_set(newParamJ, "value", paramJ); | |||||
params[i]->fromJson(newParamJ); | |||||
json_decref(newParamJ); | |||||
} | |||||
uint32_t paramId = i; | |||||
// Get paramId | |||||
json_t *paramIdJ = json_object_get(paramJ, "paramId"); | |||||
if (paramIdJ) { | |||||
// Legacy v0.6.0 to <v1.0 | |||||
paramId = json_integer_value(paramIdJ); | |||||
} | } | ||||
else { | |||||
// Get paramId | |||||
json_t *paramIdJ = json_object_get(paramJ, "paramId"); | |||||
if (!paramIdJ) | |||||
continue; | |||||
int paramId = json_integer_value(paramIdJ); | |||||
// Find ParamWidget(s) with paramId | |||||
for (ParamWidget *paramWidget : params) { | |||||
if (paramWidget->paramId == paramId) | |||||
paramWidget->fromJson(paramJ); | |||||
} | |||||
if (paramId < module->params.size()) { | |||||
module->params[paramId].fromJson(paramJ); | |||||
} | } | ||||
} | } | ||||
@@ -5,11 +5,15 @@ namespace rack { | |||||
void MomentarySwitch::onDragStart(event::DragStart &e) { | void MomentarySwitch::onDragStart(event::DragStart &e) { | ||||
setValue(maxValue); | |||||
if (quantity) { | |||||
quantity->setMax(); | |||||
} | |||||
} | } | ||||
void MomentarySwitch::onDragEnd(event::DragEnd &e) { | void MomentarySwitch::onDragEnd(event::DragEnd &e) { | ||||
setValue(minValue); | |||||
if (quantity) { | |||||
quantity->setMin(); | |||||
} | |||||
} | } | ||||
@@ -1,4 +1,4 @@ | |||||
#include "app.hpp" | |||||
#include "app/ParamWidget.hpp" | |||||
#include "engine.hpp" | #include "engine.hpp" | ||||
#include "random.hpp" | #include "random.hpp" | ||||
@@ -6,33 +6,28 @@ | |||||
namespace rack { | namespace rack { | ||||
json_t *ParamWidget::toJson() { | |||||
json_t *rootJ = json_object(); | |||||
json_object_set_new(rootJ, "paramId", json_integer(paramId)); | |||||
// Infinite params should serialize to 0 | |||||
float v = (std::isfinite(minValue) && std::isfinite(maxValue)) ? value : 0.f; | |||||
json_object_set_new(rootJ, "value", json_real(v)); | |||||
return rootJ; | |||||
} | |||||
void ParamWidget::fromJson(json_t *rootJ) { | void ParamWidget::fromJson(json_t *rootJ) { | ||||
json_t *valueJ = json_object_get(rootJ, "value"); | json_t *valueJ = json_object_get(rootJ, "value"); | ||||
if (valueJ) | |||||
setValue(json_number_value(valueJ)); | |||||
if (valueJ) { | |||||
if (quantity) | |||||
quantity->setValue(json_number_value(valueJ)); | |||||
} | |||||
} | } | ||||
void ParamWidget::reset() { | void ParamWidget::reset() { | ||||
// Infinite params should not be reset | |||||
if (std::isfinite(minValue) && std::isfinite(maxValue)) { | |||||
setValue(defaultValue); | |||||
if (quantity) { | |||||
// Infinite params should not be reset | |||||
if (quantity->isBounded()) | |||||
quantity->reset(); | |||||
} | } | ||||
} | } | ||||
void ParamWidget::randomize() { | void ParamWidget::randomize() { | ||||
// Infinite params should not be randomized | |||||
if (randomizable && std::isfinite(minValue) && std::isfinite(maxValue)) { | |||||
setValue(rescale(random::uniform(), 0.f, 1.f, minValue, maxValue)); | |||||
if (quantity) { | |||||
// Infinite params should not be randomized | |||||
if (quantity->isBounded()) { | |||||
quantity->setScaledValue(random::uniform()); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -40,20 +35,11 @@ void ParamWidget::onButton(event::Button &e) { | |||||
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) { | ||||
reset(); | |||||
if (quantity) | |||||
quantity->reset(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
void ParamWidget::onChange(event::Change &e) { | |||||
if (!module) | |||||
return; | |||||
if (smooth) | |||||
engineSetParamSmooth(module, paramId, value); | |||||
else | |||||
engineSetParam(module, paramId, value); | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -103,13 +103,28 @@ struct LogOutButton : Button { | |||||
}; | }; | ||||
struct DownloadQuantity : Quantity { | |||||
float getValue() override { | |||||
return pluginGetDownloadProgress(); | |||||
} | |||||
float getDisplayValue() override { | |||||
return getValue() * 100.f; | |||||
} | |||||
int getPrecision() override {return 0;} | |||||
std::string getLabel() override { | |||||
return "Downloading " + pluginGetDownloadName(); | |||||
} | |||||
std::string getUnit() override {return "%";} | |||||
}; | |||||
struct DownloadProgressBar : ProgressBar { | struct DownloadProgressBar : ProgressBar { | ||||
void step() override { | |||||
label = "Downloading"; | |||||
std::string name = pluginGetDownloadName(); | |||||
if (name != "") | |||||
label += " " + name; | |||||
setValue(100.0 * pluginGetDownloadProgress()); | |||||
DownloadProgressBar() { | |||||
quantity = new DownloadQuantity; | |||||
} | } | ||||
}; | }; | ||||
@@ -187,8 +202,6 @@ PluginManagerWidget::PluginManagerWidget() { | |||||
ProgressBar *downloadProgress = new DownloadProgressBar; | ProgressBar *downloadProgress = new DownloadProgressBar; | ||||
downloadProgress->box.size.x = 300; | downloadProgress->box.size.x = 300; | ||||
downloadProgress->setLimits(0, 100); | |||||
downloadProgress->unit = "%"; | |||||
downloadWidget->addChild(downloadProgress); | downloadWidget->addChild(downloadProgress); | ||||
// Button *cancelButton = new CancelButton; | // Button *cancelButton = new CancelButton; | ||||
@@ -27,14 +27,14 @@ void SVGKnob::setSVG(std::shared_ptr<SVG> svg) { | |||||
void SVGKnob::step() { | void SVGKnob::step() { | ||||
// Re-transform TransformWidget if dirty | // Re-transform TransformWidget if dirty | ||||
if (dirty) { | |||||
if (dirty && quantity) { | |||||
float angle; | float angle; | ||||
if (std::isfinite(minValue) && std::isfinite(maxValue)) { | |||||
angle = rescale(value, minValue, maxValue, minAngle, maxAngle); | |||||
if (quantity->isBounded()) { | |||||
angle = rescale(quantity->getValue(), -1.f, 1.f, minAngle, maxAngle); | |||||
angle = std::fmod(angle, 2*M_PI); | |||||
} | } | ||||
else { | else { | ||||
angle = rescale(value, -1.0, 1.0, minAngle, maxAngle); | |||||
angle = std::fmod(angle, 2*M_PI); | |||||
angle = rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | |||||
} | } | ||||
tw->identity(); | tw->identity(); | ||||
// Rotate SVG | // Rotate SVG | ||||
@@ -23,9 +23,12 @@ void SVGSlider::setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> | |||||
} | } | ||||
void SVGSlider::step() { | void SVGSlider::step() { | ||||
if (dirty) { | |||||
if (dirty && quantity) { | |||||
// Interpolate handle position | // Interpolate handle position | ||||
handle->box.pos = Vec(rescale(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescale(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | |||||
float v = quantity->getScaledValue(); | |||||
handle->box.pos = Vec( | |||||
rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x), | |||||
rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y)); | |||||
} | } | ||||
FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
} | } | ||||
@@ -20,10 +20,12 @@ void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | |||||
void SVGSwitch::onChange(event::Change &e) { | void SVGSwitch::onChange(event::Change &e) { | ||||
assert(frames.size() > 0); | assert(frames.size() > 0); | ||||
float valueScaled = rescale(value, minValue, maxValue, 0, frames.size() - 1); | |||||
int index = clamp((int) roundf(valueScaled), 0, (int) frames.size() - 1); | |||||
sw->setSVG(frames[index]); | |||||
dirty = true; | |||||
if (quantity) { | |||||
int index = quantity->getScaledValue() * (frames.size() - 1); | |||||
index = clamp(index, 0, (int) frames.size() - 1); | |||||
sw->setSVG(frames[index]); | |||||
dirty = true; | |||||
} | |||||
ParamWidget::onChange(e); | ParamWidget::onChange(e); | ||||
} | } | ||||
@@ -7,10 +7,12 @@ namespace rack { | |||||
void ToggleSwitch::onDragStart(event::DragStart &e) { | void ToggleSwitch::onDragStart(event::DragStart &e) { | ||||
// Cycle through values | // Cycle through values | ||||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | ||||
if (value >= maxValue) | |||||
setValue(minValue); | |||||
else | |||||
setValue(value + 1.0); | |||||
if (quantity) { | |||||
if (quantity->isMax()) | |||||
quantity->setMin(); | |||||
else | |||||
quantity->moveValue(1.f); | |||||
} | |||||
} | } | ||||
@@ -10,12 +10,11 @@ namespace rack { | |||||
struct TooltipIconButton : IconButton { | struct TooltipIconButton : IconButton { | ||||
Tooltip *tooltip = NULL; | Tooltip *tooltip = NULL; | ||||
std::string tooltipText; | |||||
void onEnter(event::Enter &e) override { | void onEnter(event::Enter &e) override { | ||||
if (!tooltip) { | if (!tooltip) { | ||||
tooltip = new Tooltip; | tooltip = new Tooltip; | ||||
tooltip->box.pos = getAbsoluteOffset(Vec(0, BND_WIDGET_HEIGHT)); | tooltip->box.pos = getAbsoluteOffset(Vec(0, BND_WIDGET_HEIGHT)); | ||||
tooltip->text = tooltipText; | |||||
tooltip->text = getTooltipText(); | |||||
gRackScene->addChild(tooltip); | gRackScene->addChild(tooltip); | ||||
} | } | ||||
IconButton::onEnter(e); | IconButton::onEnter(e); | ||||
@@ -28,13 +27,14 @@ struct TooltipIconButton : IconButton { | |||||
} | } | ||||
IconButton::onLeave(e); | IconButton::onLeave(e); | ||||
} | } | ||||
virtual std::string getTooltipText() {return "";} | |||||
}; | }; | ||||
struct NewButton : TooltipIconButton { | struct NewButton : TooltipIconButton { | ||||
NewButton() { | NewButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_146097_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_146097_cc.svg"))); | ||||
tooltipText = "New patch (" WINDOW_MOD_KEY_NAME "+N)"; | |||||
} | } | ||||
std::string getTooltipText() override {return "New patch (" WINDOW_MOD_KEY_NAME "+N)";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->reset(); | gRackWidget->reset(); | ||||
} | } | ||||
@@ -43,8 +43,8 @@ struct NewButton : TooltipIconButton { | |||||
struct OpenButton : TooltipIconButton { | struct OpenButton : TooltipIconButton { | ||||
OpenButton() { | OpenButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg"))); | ||||
tooltipText = "Open patch (" WINDOW_MOD_KEY_NAME "+O)"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Open patch (" WINDOW_MOD_KEY_NAME "+O)";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->loadDialog(); | gRackWidget->loadDialog(); | ||||
} | } | ||||
@@ -53,8 +53,8 @@ struct OpenButton : TooltipIconButton { | |||||
struct SaveButton : TooltipIconButton { | struct SaveButton : TooltipIconButton { | ||||
SaveButton() { | SaveButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg"))); | ||||
tooltipText = "Save patch (" WINDOW_MOD_KEY_NAME "+S)"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Save patch (" WINDOW_MOD_KEY_NAME "+S)";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->saveDialog(); | gRackWidget->saveDialog(); | ||||
} | } | ||||
@@ -63,8 +63,8 @@ struct SaveButton : TooltipIconButton { | |||||
struct SaveAsButton : TooltipIconButton { | struct SaveAsButton : TooltipIconButton { | ||||
SaveAsButton() { | SaveAsButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg"))); | ||||
tooltipText = "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->saveAsDialog(); | gRackWidget->saveAsDialog(); | ||||
} | } | ||||
@@ -73,8 +73,8 @@ struct SaveAsButton : TooltipIconButton { | |||||
struct RevertButton : TooltipIconButton { | struct RevertButton : TooltipIconButton { | ||||
RevertButton() { | RevertButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg"))); | ||||
tooltipText = "Revert patch"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Revert patch";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->revert(); | gRackWidget->revert(); | ||||
} | } | ||||
@@ -83,8 +83,8 @@ struct RevertButton : TooltipIconButton { | |||||
struct DisconnectCablesButton : TooltipIconButton { | struct DisconnectCablesButton : TooltipIconButton { | ||||
DisconnectCablesButton() { | DisconnectCablesButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg"))); | ||||
tooltipText = "Disconnect cables"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Disconnect cables";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->disconnect(); | gRackWidget->disconnect(); | ||||
} | } | ||||
@@ -93,8 +93,8 @@ struct DisconnectCablesButton : TooltipIconButton { | |||||
struct PowerMeterButton : TooltipIconButton { | struct PowerMeterButton : TooltipIconButton { | ||||
PowerMeterButton() { | PowerMeterButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg"))); | ||||
tooltipText = "Toggle power meter (see manual for explanation)"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Toggle power meter (see manual for explanation)";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gPowerMeter ^= true; | gPowerMeter ^= true; | ||||
} | } | ||||
@@ -117,8 +117,8 @@ struct SampleRateItem : MenuItem { | |||||
struct SampleRateButton : TooltipIconButton { | struct SampleRateButton : TooltipIconButton { | ||||
SampleRateButton() { | SampleRateButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg"))); | ||||
tooltipText = "Engine sample rate"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Engine sample rate";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
Menu *menu = createMenu(); | Menu *menu = createMenu(); | ||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | ||||
@@ -144,18 +144,52 @@ struct SampleRateButton : TooltipIconButton { | |||||
struct RackLockButton : TooltipIconButton { | struct RackLockButton : TooltipIconButton { | ||||
RackLockButton() { | RackLockButton() { | ||||
setSVG(SVG::load(asset::global("res/icons/noun_468341_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_468341_cc.svg"))); | ||||
tooltipText = "Lock modules"; | |||||
} | } | ||||
std::string getTooltipText() override {return "Lock modules";} | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
gRackWidget->lockModules ^= true; | gRackWidget->lockModules ^= true; | ||||
} | } | ||||
}; | }; | ||||
struct ZoomSlider : Slider { | |||||
void onAction(event::Action &e) override { | |||||
Slider::onAction(e); | |||||
gRackScene->zoomWidget->setZoom(std::round(value) / 100.0); | |||||
struct WireOpacityQuantity : Quantity { | |||||
void setValue(float value) override { | |||||
// TODO | |||||
} | |||||
float getValue() override { | |||||
return 0; | |||||
} | |||||
float getDefaultValue() override {return 0.5;} | |||||
std::string getLabel() override {return "Cable opacity";} | |||||
int getPrecision() override {return 0;} | |||||
}; | |||||
struct WireTensionQuantity : Quantity { | |||||
void setValue(float value) override { | |||||
// TODO | |||||
} | |||||
float getValue() override { | |||||
return 0; | |||||
} | |||||
float getDefaultValue() override {return 0.5;} | |||||
std::string getLabel() override {return "Cable tension";} | |||||
int getPrecision() override {return 0;} | |||||
}; | |||||
struct ZoomQuantity : Quantity { | |||||
void setValue(float value) override { | |||||
gRackScene->zoomWidget->setZoom(std::round(value) / 100); | |||||
} | |||||
float getValue() override { | |||||
return gRackScene->zoomWidget->zoom * 100; | |||||
} | } | ||||
float getMinValue() override {return 25;} | |||||
float getMaxValue() override {return 200;} | |||||
float getDefaultValue() override {return 100;} | |||||
std::string getLabel() override {return "Zoom";} | |||||
std::string getUnit() override {return "%";} | |||||
int getPrecision() override {return 0;} | |||||
}; | }; | ||||
@@ -178,30 +212,22 @@ Toolbar::Toolbar() { | |||||
layout->addChild(new PowerMeterButton); | layout->addChild(new PowerMeterButton); | ||||
layout->addChild(new RackLockButton); | layout->addChild(new RackLockButton); | ||||
wireOpacitySlider = new Slider; | |||||
Slider *wireOpacitySlider = new Slider; | |||||
WireOpacityQuantity *wireOpacityQuantity = new WireOpacityQuantity; | |||||
wireOpacitySlider->quantity = wireOpacityQuantity; | |||||
wireOpacitySlider->box.size.x = 150; | wireOpacitySlider->box.size.x = 150; | ||||
wireOpacitySlider->label = "Cable opacity"; | |||||
wireOpacitySlider->precision = 0; | |||||
wireOpacitySlider->unit = "%"; | |||||
wireOpacitySlider->setLimits(0.0, 100.0); | |||||
wireOpacitySlider->setDefaultValue(50.0); | |||||
layout->addChild(wireOpacitySlider); | layout->addChild(wireOpacitySlider); | ||||
wireTensionSlider = new Slider; | |||||
Slider *wireTensionSlider = new Slider; | |||||
WireTensionQuantity *wireTensionQuantity = new WireTensionQuantity; | |||||
wireTensionSlider->quantity = wireTensionQuantity; | |||||
wireTensionSlider->box.size.x = 150; | wireTensionSlider->box.size.x = 150; | ||||
wireTensionSlider->label = "Cable tension"; | |||||
wireTensionSlider->unit = ""; | |||||
wireTensionSlider->setLimits(0.0, 1.0); | |||||
wireTensionSlider->setDefaultValue(0.5); | |||||
layout->addChild(wireTensionSlider); | layout->addChild(wireTensionSlider); | ||||
zoomSlider = new ZoomSlider; | |||||
Slider *zoomSlider = new Slider; | |||||
ZoomQuantity *zoomQuantity = new ZoomQuantity; | |||||
zoomSlider->quantity = zoomQuantity; | |||||
zoomSlider->box.size.x = 150; | zoomSlider->box.size.x = 150; | ||||
zoomSlider->precision = 0; | |||||
zoomSlider->label = "Zoom"; | |||||
zoomSlider->unit = "%"; | |||||
zoomSlider->setLimits(25.0, 200.0); | |||||
zoomSlider->setDefaultValue(100.0); | |||||
layout->addChild(zoomSlider); | layout->addChild(zoomSlider); | ||||
// Kind of hacky, but display the PluginManagerWidget only if the local directory is not the development directory | // Kind of hacky, but display the PluginManagerWidget only if the local directory is not the development directory | ||||
@@ -153,17 +153,17 @@ json_t *WireWidget::toJson() { | |||||
void WireWidget::fromJson(json_t *rootJ) { | void WireWidget::fromJson(json_t *rootJ) { | ||||
json_t *colorJ = json_object_get(rootJ, "color"); | json_t *colorJ = json_object_get(rootJ, "color"); | ||||
if (colorJ) { | if (colorJ) { | ||||
// Legacy v0.6.0 and earlier | |||||
if (json_is_object(colorJ)) | |||||
color = jsonToColor(colorJ); | |||||
else | |||||
// v0.6.0 and earlier patches use JSON objects. Just ignore them if so and use the existing wire color. | |||||
if (json_is_string(colorJ)) | |||||
color = color::fromHexString(json_string_value(colorJ)); | color = color::fromHexString(json_string_value(colorJ)); | ||||
} | } | ||||
} | } | ||||
void WireWidget::draw(NVGcontext *vg) { | void WireWidget::draw(NVGcontext *vg) { | ||||
float opacity = gToolbar->wireOpacitySlider->value / 100.0; | |||||
float tension = gToolbar->wireTensionSlider->value; | |||||
// float opacity = gToolbar->wireOpacitySlider->value / 100.0; | |||||
// float tension = gToolbar->wireTensionSlider->value; | |||||
float opacity = 0.5; | |||||
float tension = 0.5; | |||||
WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | ||||
if (activeWire) { | if (activeWire) { | ||||
@@ -69,10 +69,27 @@ static int smoothParamId; | |||||
static float smoothValue; | static float smoothValue; | ||||
json_t *Param::toJson() { | |||||
json_t *rootJ = json_object(); | |||||
// Infinite params should serialize to 0 | |||||
float v = (std::isfinite(minValue) && std::isfinite(maxValue)) ? value : 0.f; | |||||
json_object_set_new(rootJ, "value", json_real(v)); | |||||
return rootJ; | |||||
} | |||||
void Param::fromJson(json_t *rootJ) { | |||||
json_t *valueJ = json_object_get(rootJ, "value"); | |||||
if (valueJ) | |||||
value = json_number_value(valueJ); | |||||
} | |||||
float Light::getBrightness() { | float Light::getBrightness() { | ||||
// LEDs are diodes, so don't allow reverse current. | // LEDs are diodes, so don't allow reverse current. | ||||
// For some reason, instead of the RMS, the sqrt of RMS looks better | // For some reason, instead of the RMS, the sqrt of RMS looks better | ||||
return powf(fmaxf(0.f, value), 0.25f); | |||||
return std::pow(std::fmaxf(0.f, value), 0.25f); | |||||
} | } | ||||
void Light::setBrightnessSmooth(float brightness, float frames) { | void Light::setBrightnessSmooth(float brightness, float frames) { | ||||
@@ -31,12 +31,12 @@ static json_t *settingsToJson() { | |||||
} | } | ||||
// opacity | // opacity | ||||
float opacity = gToolbar->wireOpacitySlider->value; | |||||
float opacity = gToolbar->wireOpacity; | |||||
json_t *opacityJ = json_real(opacity); | json_t *opacityJ = json_real(opacity); | ||||
json_object_set_new(rootJ, "wireOpacity", opacityJ); | json_object_set_new(rootJ, "wireOpacity", opacityJ); | ||||
// tension | // tension | ||||
float tension = gToolbar->wireTensionSlider->value; | |||||
float tension = gToolbar->wireTension; | |||||
json_t *tensionJ = json_real(tension); | json_t *tensionJ = json_real(tension); | ||||
json_object_set_new(rootJ, "wireTension", tensionJ); | json_object_set_new(rootJ, "wireTension", tensionJ); | ||||
@@ -99,18 +99,17 @@ static void settingsFromJson(json_t *rootJ) { | |||||
// opacity | // opacity | ||||
json_t *opacityJ = json_object_get(rootJ, "wireOpacity"); | json_t *opacityJ = json_object_get(rootJ, "wireOpacity"); | ||||
if (opacityJ) | if (opacityJ) | ||||
gToolbar->wireOpacitySlider->value = json_number_value(opacityJ); | |||||
gToolbar->wireOpacity = json_number_value(opacityJ); | |||||
// tension | // tension | ||||
json_t *tensionJ = json_object_get(rootJ, "wireTension"); | json_t *tensionJ = json_object_get(rootJ, "wireTension"); | ||||
if (tensionJ) | if (tensionJ) | ||||
gToolbar->wireTensionSlider->value = json_number_value(tensionJ); | |||||
gToolbar->wireTension = json_number_value(tensionJ); | |||||
// zoom | // zoom | ||||
json_t *zoomJ = json_object_get(rootJ, "zoom"); | json_t *zoomJ = json_object_get(rootJ, "zoom"); | ||||
if (zoomJ) { | if (zoomJ) { | ||||
gRackScene->zoomWidget->setZoom(clamp((float) json_number_value(zoomJ), 0.25f, 4.0f)); | gRackScene->zoomWidget->setZoom(clamp((float) json_number_value(zoomJ), 0.25f, 4.0f)); | ||||
gToolbar->zoomSlider->setValue(json_number_value(zoomJ) * 100.0); | |||||
} | } | ||||
// allowCursorLock | // allowCursorLock | ||||
@@ -63,13 +63,16 @@ std::string filename(std::string path) { | |||||
return filename; | return filename; | ||||
} | } | ||||
std::string basename(std::string path) { | |||||
size_t pos = path.rfind('.'); | |||||
return std::string(path, 0, pos); | |||||
} | |||||
std::string extension(std::string path) { | std::string extension(std::string path) { | ||||
const char *ext = strrchr(filename(path).c_str(), '.'); | |||||
if (!ext) | |||||
return ""; | |||||
return ext + 1; | |||||
size_t pos = path.rfind('.'); | |||||
return std::string(path, pos); | |||||
} | } | ||||
} // namespace network | |||||
} // namespace string | |||||
} // namespace rack | } // namespace rack |
@@ -13,8 +13,8 @@ namespace rack { | |||||
namespace system { | namespace system { | ||||
std::vector<std::string> listEntries(std::string path) { | |||||
std::vector<std::string> filenames; | |||||
std::list<std::string> listEntries(std::string path) { | |||||
std::list<std::string> filenames; | |||||
DIR *dir = opendir(path.c_str()); | DIR *dir = opendir(path.c_str()); | ||||
if (dir) { | if (dir) { | ||||
struct dirent *d; | struct dirent *d; | ||||