Browse Source

Revise event and widget API (draft), migrate source files to headers (WIP), separate large headers into small headers in subfolders (WIP)

tags/v1.0.0
Andrew Belt 6 years ago
parent
commit
6475c630b5
100 changed files with 2638 additions and 2539 deletions
  1. +31
    -126
      include/app.hpp
  2. +74
    -0
      include/app/ModuleWidget.hpp
  3. +47
    -0
      include/app/SVGPanel.hpp
  4. +245
    -0
      include/event.hpp
  5. +0
    -108
      include/events.hpp
  6. +81
    -65
      include/helpers.hpp
  7. +39
    -39
      include/math.hpp
  8. +0
    -28
      include/plugin.hpp
  9. +1
    -1
      include/rack.hpp
  10. +0
    -6
      include/rack0.hpp
  11. +21
    -273
      include/ui.hpp
  12. +48
    -0
      include/ui/Button.hpp
  13. +16
    -0
      include/ui/ChoiceButton.hpp
  14. +32
    -0
      include/ui/IconButton.hpp
  15. +49
    -0
      include/ui/Label.hpp
  16. +28
    -0
      include/ui/List.hpp
  17. +81
    -0
      include/ui/Menu.hpp
  18. +16
    -0
      include/ui/MenuEntry.hpp
  19. +75
    -0
      include/ui/MenuItem.hpp
  20. +27
    -0
      include/ui/MenuLabel.hpp
  21. +60
    -0
      include/ui/MenuOverlay.hpp
  22. +27
    -0
      include/ui/MenuSeparator.hpp
  23. +19
    -0
      include/ui/PasswordField.hpp
  24. +22
    -0
      include/ui/ProgressBar.hpp
  25. +43
    -0
      include/ui/RadioButton.hpp
  26. +68
    -0
      include/ui/Scene.hpp
  27. +160
    -0
      include/ui/ScrollWidget.hpp
  28. +58
    -0
      include/ui/SequentialLayout.hpp
  29. +48
    -0
      include/ui/Slider.hpp
  30. +220
    -0
      include/ui/TextField.hpp
  31. +25
    -0
      include/ui/Tooltip.hpp
  32. +13
    -0
      include/ui/TooltipOverlay.hpp
  33. +13
    -0
      include/ui/WindowOverlay.hpp
  34. +24
    -0
      include/ui/WindowWidget.hpp
  35. +12
    -151
      include/widgets.hpp
  36. +86
    -0
      include/widgets/EventWidget.hpp
  37. +37
    -0
      include/widgets/FramebufferWidget.hpp
  38. +30
    -0
      include/widgets/OpaqueWidget.hpp
  39. +49
    -0
      include/widgets/QuantityWidget.hpp
  40. +36
    -0
      include/widgets/SVGWidget.hpp
  41. +47
    -0
      include/widgets/TransformWidget.hpp
  42. +20
    -0
      include/widgets/TransparentWidget.hpp
  43. +21
    -63
      include/widgets/Widget.hpp
  44. +76
    -0
      include/widgets/ZoomWidget.hpp
  45. +6
    -2
      include/window.hpp
  46. +34
    -34
      src/Core/AudioInterface.cpp
  47. +13
    -13
      src/Core/Blank.cpp
  48. +2
    -2
      src/Core/Core.hpp
  49. +37
    -37
      src/Core/MIDICCToCVInterface.cpp
  50. +23
    -23
      src/Core/MIDIToCVInterface.cpp
  51. +32
    -32
      src/Core/MIDITriggerToCVInterface.cpp
  52. +6
    -6
      src/Core/Notes.cpp
  53. +30
    -28
      src/Core/QuadMIDIToCVInterface.cpp
  54. +22
    -21
      src/app/AudioWidget.cpp
  55. +4
    -4
      src/app/Knob.cpp
  56. +5
    -6
      src/app/LedDisplay.cpp
  57. +15
    -14
      src/app/MidiWidget.cpp
  58. +40
    -39
      src/app/ModuleBrowser.cpp
  59. +36
    -44
      src/app/ModuleWidget.cpp
  60. +2
    -4
      src/app/MomentarySwitch.cpp
  61. +7
    -6
      src/app/ParamWidget.cpp
  62. +10
    -9
      src/app/PluginManagerWidget.cpp
  63. +9
    -10
      src/app/Port.cpp
  64. +18
    -18
      src/app/RackScene.cpp
  65. +10
    -13
      src/app/RackWidget.cpp
  66. +4
    -4
      src/app/SVGButton.cpp
  67. +2
    -2
      src/app/SVGKnob.cpp
  68. +0
    -42
      src/app/SVGPanel.cpp
  69. +2
    -2
      src/app/SVGSlider.cpp
  70. +2
    -2
      src/app/SVGSwitch.cpp
  71. +1
    -1
      src/app/ToggleSwitch.cpp
  72. +20
    -19
      src/app/Toolbar.cpp
  73. +2
    -3
      src/event.cpp
  74. +2
    -24
      src/svg.cpp
  75. +0
    -35
      src/ui/Button.cpp
  76. +0
    -11
      src/ui/ChoiceButton.cpp
  77. +0
    -25
      src/ui/IconButton.cpp
  78. +0
    -34
      src/ui/Label.cpp
  79. +0
    -24
      src/ui/List.cpp
  80. +0
    -62
      src/ui/Menu.cpp
  81. +0
    -66
      src/ui/MenuItem.cpp
  82. +0
    -21
      src/ui/MenuLabel.cpp
  83. +0
    -43
      src/ui/MenuOverlay.cpp
  84. +0
    -24
      src/ui/MenuSeparator.cpp
  85. +0
    -15
      src/ui/PasswordField.cpp
  86. +0
    -12
      src/ui/ProgressBar.cpp
  87. +0
    -31
      src/ui/RadioButton.cpp
  88. +0
    -42
      src/ui/Scene.cpp
  89. +0
    -148
      src/ui/ScrollWidget.cpp
  90. +0
    -41
      src/ui/Slider.cpp
  91. +0
    -197
      src/ui/TextField.cpp
  92. +0
    -22
      src/ui/Tooltip.cpp
  93. +0
    -17
      src/ui/WindowWidget.cpp
  94. +0
    -39
      src/ui/layouts.cpp
  95. +1
    -7
      src/widgets/FramebufferWidget.cpp
  96. +0
    -36
      src/widgets/QuantityWidget.cpp
  97. +0
    -49
      src/widgets/TransformWidget.cpp
  98. +11
    -75
      src/widgets/Widget.cpp
  99. +0
    -76
      src/widgets/ZoomWidget.cpp
  100. +105
    -63
      src/window.cpp

+ 31
- 126
include/app.hpp View File

@@ -49,68 +49,14 @@ static const math::Vec RACK_GRID_SIZE = math::Vec(RACK_GRID_WIDTH, RACK_GRID_HEI
static const std::string PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm";
static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv";

} // namespace rack

struct ModuleWidget : OpaqueWidget {
Model *model = NULL;
/** Owns the module pointer */
Module *module = NULL;

SVGPanel *panel = NULL;
std::vector<Port*> inputs;
std::vector<Port*> outputs;
std::vector<ParamWidget*> params;

ModuleWidget(Module *module);
~ModuleWidget();
/** Convenience functions for adding special widgets (calls addChild()) */
void addInput(Port *input);
void addOutput(Port *output);
void addParam(ParamWidget *param);
void setPanel(std::shared_ptr<SVG> svg);

virtual json_t *toJson();
virtual void fromJson(json_t *rootJ);
void copyClipboard();
void pasteClipboard();
void save(std::string filename);
void load(std::string filename);
void loadDialog();
void saveDialog();
#include "app/ModuleWidget.hpp"

virtual void create();
virtual void _delete();
/** Disconnects cables from all ports
Called when the user clicks Disconnect Cables in the context menu.
*/
virtual void disconnect();
/** Resets the parameters of the module and calls the Module's randomize().
Called when the user clicks Initialize in the context menu.
*/
virtual void reset();
/** Deprecated */
virtual void initialize() final {}
/** Randomizes the parameters of the module and calls the Module's randomize().
Called when the user clicks Randomize in the context menu.
*/
virtual void randomize();
/** Do not subclass this to add context menu entries. Use appendContextMenu() instead */
virtual Menu *createContextMenu();
/** Override to add context menu entries to your subclass.
It is recommended to add a blank MenuEntry first for spacing.
*/
virtual void appendContextMenu(Menu *menu) {}

void draw(NVGcontext *vg) override;
void drawShadow(NVGcontext *vg);

math::Vec dragPos;
void onMouseDown(EventMouseDown &e) override;
void onMouseMove(EventMouseMove &e) override;
void onHoverKey(EventHoverKey &e) override;
void onDragStart(EventDragStart &e) override;
void onDragEnd(EventDragEnd &e) override;
void onDragMove(EventDragMove &e) override;
};
namespace rack {


struct WireWidget : OpaqueWidget {
Port *outputPort = NULL;
@@ -189,20 +135,15 @@ struct RackWidget : OpaqueWidget {
void step() override;
void draw(NVGcontext *vg) override;

void onMouseMove(EventMouseMove &e) override;
void onMouseDown(EventMouseDown &e) override;
void onZoom(EventZoom &e) override;
void on(event::Hover &e) override;
void on(event::Button &e) override;
void on(event::Zoom &e) override;
};

struct RackRail : TransparentWidget {
void draw(NVGcontext *vg) override;
};

struct SVGPanel : FramebufferWidget {
void step() override;
void setBackground(std::shared_ptr<SVG> svg);
};

////////////////////
// ParamWidgets and other components
////////////////////
@@ -210,14 +151,6 @@ struct SVGPanel : FramebufferWidget {
/** A Widget that exists on a Panel and interacts with a Module */
struct Component : OpaqueWidget {
Module *module = NULL;

template <typename T = Component>
static T *create(math::Vec pos, Module *module) {
T *o = new T();
o->box.pos = pos;
o->module = module;
return o;
}
};

struct CircularShadow : TransparentWidget {
@@ -241,17 +174,8 @@ struct ParamWidget : Component, QuantityWidget {
void fromJson(json_t *rootJ);
virtual void reset();
virtual void randomize();
void onMouseDown(EventMouseDown &e) override;
void onChange(EventChange &e) override;

template <typename T = ParamWidget>
static T *create(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) {
T *o = Component::create<T>(pos, module);
o->paramId = paramId;
o->setLimits(minValue, maxValue);
o->setDefaultValue(defaultValue);
return o;
}
void on(event::Button &e) override;
void on(event::Change &e) override;
};

/** Implements vertical dragging behavior for ParamWidgets */
@@ -262,9 +186,9 @@ struct Knob : ParamWidget {
float speed = 1.0;
float dragValue;
Knob();
void onDragStart(EventDragStart &e) override;
void onDragMove(EventDragMove &e) override;
void onDragEnd(EventDragEnd &e) override;
void on(event::DragStart &e) override;
void on(event::DragMove &e) override;
void on(event::DragEnd &e) override;
};

/** A knob which rotates an SVG and caches it in a framebuffer */
@@ -278,7 +202,7 @@ struct SVGKnob : Knob, FramebufferWidget {
SVGKnob();
void setSVG(std::shared_ptr<SVG> svg);
void step() override;
void onChange(EventChange &e) override;
void on(event::Change &e) override;
};

/** Behaves like a knob but linearly moves an SVGWidget between two points.
@@ -293,12 +217,9 @@ struct SVGSlider : Knob, FramebufferWidget {
SVGSlider();
void setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> handleSVG);
void step() override;
void onChange(EventChange &e) override;
void on(event::Change &e) override;
};

/** Deprecated name for SVGSlider */
typedef SVGSlider SVGFader;

/** A ParamWidget with multiple frames corresponding to its value */
struct SVGSwitch : virtual ParamWidget, FramebufferWidget {
std::vector<std::shared_ptr<SVG>> frames;
@@ -306,12 +227,12 @@ struct SVGSwitch : virtual ParamWidget, FramebufferWidget {
SVGSwitch();
/** Adds an SVG file to represent the next switch position */
void addFrame(std::shared_ptr<SVG> svg);
void onChange(EventChange &e) override;
void on(event::Change &e) override;
};

/** A switch that cycles through each mechanical position */
struct ToggleSwitch : virtual ParamWidget {
void onDragStart(EventDragStart &e) override;
void on(event::DragStart &e) override;
};

/** A switch that is turned on when held and turned off when released.
@@ -320,8 +241,8 @@ Consider using SVGButton if the switch simply changes the state of your Module w
struct MomentarySwitch : virtual ParamWidget {
/** Don't randomize state */
void randomize() override {}
void onDragStart(EventDragStart &e) override;
void onDragEnd(EventDragEnd &e) override;
void on(event::DragStart &e) override;
void on(event::DragEnd &e) override;
};

/** A Component with a default (up) and active (down) state when clicked.
@@ -335,15 +256,15 @@ struct SVGButton : Component, FramebufferWidget {
SVGButton();
/** If `activeSVG` is NULL, `defaultSVG` is used as the active state instead. */
void setSVGs(std::shared_ptr<SVG> defaultSVG, std::shared_ptr<SVG> activeSVG);
void onDragStart(EventDragStart &e) override;
void onDragEnd(EventDragEnd &e) override;
void on(event::DragStart &e) override;
void on(event::DragEnd &e) override;
};

////////////////////
// IO widgets
////////////////////

struct LedDisplay : VirtualWidget {
struct LedDisplay : virtual EventWidget {
void draw(NVGcontext *vg) override;
};

@@ -359,7 +280,7 @@ struct LedDisplayChoice : TransparentWidget {
NVGcolor color;
LedDisplayChoice();
void draw(NVGcontext *vg) override;
void onMouseDown(EventMouseDown &e) override;
void on(event::Button &e) override;
};

struct LedDisplayTextField : TextField {
@@ -430,14 +351,6 @@ struct ModuleLightWidget : MultiLightWidget {
Module *module = NULL;
int firstLightId;
void step() override;

template <typename T = ModuleLightWidget>
static T *create(math::Vec pos, Module *module, int firstLightId) {
T *o = Widget::create<T>(pos);
o->module = module;
o->firstLightId = firstLightId;
return o;
}
};

////////////////////
@@ -457,20 +370,12 @@ struct Port : Component {
~Port();
void step() override;
void draw(NVGcontext *vg) override;
void onMouseDown(EventMouseDown &e) override;
void onDragStart(EventDragStart &e) override;
void onDragEnd(EventDragEnd &e) override;
void onDragDrop(EventDragDrop &e) override;
void onDragEnter(EventDragEnter &e) override;
void onDragLeave(EventDragEnter &e) override;

template <typename T = Port>
static T *create(math::Vec pos, PortType type, Module *module, int portId) {
T *o = Component::create<T>(pos, module);
o->type = type;
o->portId = portId;
return o;
}
void on(event::Button &e) override;
void on(event::DragStart &e) override;
void on(event::DragEnd &e) override;
void on(event::DragDrop &e) override;
void on(event::DragEnter &e) override;
void on(event::DragLeave &e) override;
};

struct SVGPort : Port, FramebufferWidget {
@@ -503,7 +408,7 @@ struct Toolbar : OpaqueWidget {
void draw(NVGcontext *vg) override;
};

struct PluginManagerWidget : VirtualWidget {
struct PluginManagerWidget : virtual EventWidget {
Widget *loginWidget;
Widget *manageWidget;
Widget *downloadWidget;
@@ -522,8 +427,8 @@ struct RackScene : Scene {
RackScene();
void step() override;
void draw(NVGcontext *vg) override;
void onHoverKey(EventHoverKey &e) override;
void onPathDrop(EventPathDrop &e) override;
void on(event::HoverKey &e) override;
void on(event::PathDrop &e) override;
};

////////////////////


+ 74
- 0
include/app/ModuleWidget.hpp View File

@@ -0,0 +1,74 @@
#pragma once
#include "widgets/OpaqueWidget.hpp"
#include "app/SVGPanel.hpp"
#include "plugin.hpp"
#include "engine.hpp"


namespace rack {


struct ModuleWidget : OpaqueWidget {
Model *model = NULL;
/** Owns the module pointer */
Module *module = NULL;

SVGPanel *panel = NULL;
std::vector<Port*> inputs;
std::vector<Port*> outputs;
std::vector<ParamWidget*> params;

ModuleWidget(Module *module);
~ModuleWidget();
/** Convenience functions for adding special widgets (calls addChild()) */
void addInput(Port *input);
void addOutput(Port *output);
void addParam(ParamWidget *param);
void setPanel(std::shared_ptr<SVG> svg);

virtual json_t *toJson();
virtual void fromJson(json_t *rootJ);
void copyClipboard();
void pasteClipboard();
void save(std::string filename);
void load(std::string filename);
void loadDialog();
void saveDialog();

virtual void create();
virtual void _delete();
/** Disconnects cables from all ports
Called when the user clicks Disconnect Cables in the context menu.
*/
virtual void disconnect();
/** Resets the parameters of the module and calls the Module's randomize().
Called when the user clicks Initialize in the context menu.
*/
virtual void reset();
/** Deprecated */
virtual void initialize() final {}
/** Randomizes the parameters of the module and calls the Module's randomize().
Called when the user clicks Randomize in the context menu.
*/
virtual void randomize();
/** Do not subclass this to add context menu entries. Use appendContextMenu() instead */
virtual Menu *createContextMenu();
/** Override to add context menu entries to your subclass.
It is recommended to add a blank MenuEntry first for spacing.
*/
virtual void appendContextMenu(Menu *menu) {}

void draw(NVGcontext *vg) override;
void drawShadow(NVGcontext *vg);

math::Vec dragPos;
void on(event::Hover &e) override;
void on(event::Button &e) override;
void on(event::HoverKey &e) override;
void on(event::DragStart &e) override;
void on(event::DragEnd &e) override;
void on(event::DragMove &e) override;
};


} // namespace rack

+ 47
- 0
include/app/SVGPanel.hpp View File

@@ -0,0 +1,47 @@
#pragma once
#include "app.hpp"
#include "widgets/FramebufferWidget.hpp"
#include "widgets/SVGWidget.hpp"


namespace rack {


struct PanelBorder : TransparentWidget {
void draw(NVGcontext *vg) override {
NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
nvgBeginPath(vg);
nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
nvgStrokeColor(vg, borderColor);
nvgStrokeWidth(vg, 1.0);
nvgStroke(vg);
}
};


struct SVGPanel : FramebufferWidget {
void step() override {
if (math::isNear(gPixelRatio, 1.0)) {
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
oversample = 2.0;
}
FramebufferWidget::step();
}

void setBackground(std::shared_ptr<SVG> svg) {
SVGWidget *sw = new SVGWidget();
sw->setSVG(svg);
addChild(sw);

// Set size
box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);

PanelBorder *pb = new PanelBorder();
pb->box.size = box.size;
addChild(pb);
}
};



} // namespace rack

+ 245
- 0
include/event.hpp View File

@@ -0,0 +1,245 @@
#pragma once
#include <vector>
#include "widgets/Widget.hpp"


namespace rack {


struct EventWidget;


namespace event {


struct Event {
/** Set this to the Widget that consumes (responds to) the event.
This stops propagation of the event if applicable.
*/
Widget *target = NULL;

virtual ~Event() {}
/** Triggers the event on an EventWidget.
Calls the appropriate `EventWidget::on()` method.
*/
virtual void trigger(EventWidget *w) = 0;
};


struct Position {
/** The pixel coordinate where the event occurred, relative to the Widget it is called on. */
math::Vec pos;
};


struct Key {
/** GLFW_KEY_* */
int key;
/** GLFW_KEY_*. You should usually use `key` instead. */
int scancode;
/** GLFW_RELEASE, GLFW_PRESS, or GLFW_REPEAT */
int action;
/** GLFW_MOD_* */
int mods;
};


struct Text {
/** Unicode code point of the character */
int codepoint;
};


#define EVENT_TRIGGER_DECLARATION() void trigger(EventWidget *w) override
#define EVENT_TRIGGER_DEFINITION(_event) inline void _event::trigger(EventWidget *w) { w->on(*this); }


/** Occurs every frame when the mouse is hovering over a Widget.
Recurses until consumed.
If target is set, other events may occur on that Widget.
*/
struct Hover : Event, Position {
/** Change in mouse position since the last frame. Can be zero. */
math::Vec mouseDelta;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs each mouse button press or release.
Recurses until consumed.
If target is set, other events may occur on that Widget.
*/
struct Button : Event, Position {
/** GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_RIGHT, GLFW_MOUSE_BUTTON_MIDDLE, etc. */
int button;
/** GLFW_PRESS or GLFW_RELEASE */
int action;
/** GLFW_MOD_* */
int mods;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a key is pressed while the mouse is hovering a Widget.
Recurses until consumed.
*/
struct HoverKey : Event, Position, Key {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a character is typed while the mouse is hovering a Widget.
Recurses until consumed.
*/
struct HoverText : Event, Position, Text {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the mouse scroll wheel is moved while the mouse is hovering a Widget.
Recurses until consumed.
*/
struct HoverScroll : Event, Position {
/** Change of scroll wheel position. */
math::Vec scrollDelta;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a Widget begins consuming the Hover event.
*/
struct Enter : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a different Widget is entered.
*/
struct Leave : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a Widget begins consuming the Button press event.
*/
struct Select : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a different Widget is selected.
*/
struct Deselect : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a key is pressed while a Widget is selected.
*/
struct SelectKey : Event, Key {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when text is typed while a Widget is selected.
*/
struct SelectText : Event, Text {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a Widget begins being dragged.
Must consume to allow the drag to occur.
*/
struct DragStart : Event {
int button;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a Widget stops being dragged by releasing the mouse button.
*/
struct DragEnd : Event {
int button;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a dragged Widget is moved.
Called once per frame, even when mouseDelta is zero.
*/
struct DragMove : Event {
int button;
math::Vec mouseDelta;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the mouse enters a Widget while dragging.
*/
struct DragEnter : Event {
Widget *origin = NULL;
int button;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the mouse leaves a Widget while dragging.
*/
struct DragLeave : Event {
Widget *origin = NULL;
int button;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the mouse button is released over a Widget while dragging.
*/
struct DragDrop : Event {
Widget *origin = NULL;
int button;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when a selection of files from the operating system are dropped onto a Widget.
*/
struct PathDrop : Event, Position {
/** List of file paths in the dropped selection */
std::vector<std::string> paths;
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when an certain action is triggered on a Widget.
*/
struct Action : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the value of a Widget changes.
*/
struct Change : Event {
EVENT_TRIGGER_DECLARATION();
};


/** Occurs when the zoom level of a Widget is changed.
Recurses.
*/
struct Zoom : Event {
EVENT_TRIGGER_DECLARATION();
};


} // namespace event


extern Widget *gHoveredWidget;
extern Widget *gSelectedWidget;
extern Widget *gDraggedWidget;
extern Widget *gDragHoveredWidget;


} // namespace rack

+ 0
- 108
include/events.hpp View File

@@ -1,108 +0,0 @@
#pragma once
#include <list>

#include "math.hpp"


namespace rack {


struct Widget;


struct Event {
/** Set this to true to signal that no other widgets should receive the event */
bool consumed = false;
};

struct EventPosition : Event {
math::Vec pos;
};

///////////

struct EventMouseDown : EventPosition {
int button;
/** The widget which responded to the click. Set it to `this` if consumed. */
Widget *target = NULL;
};

struct EventMouseUp : EventPosition {
/** 0 for left mouse button, 1 for right, 2 for middle */
int button;
Widget *target = NULL;
};

struct EventMouseMove : EventPosition {
math::Vec mouseRel;
Widget *target = NULL;
};

struct EventHoverKey : EventPosition {
int key;
Widget *target = NULL;
};

struct EventMouseEnter : Event {
};

struct EventMouseLeave : Event {
};

struct EventFocus : Event {
};

struct EventDefocus : Event {
};

struct EventText : Event {
int codepoint;
};

struct EventKey : Event {
int key;
};

struct EventScroll : EventPosition {
math::Vec scrollRel;
};

/////////////

struct EventDragStart : Event {
};

struct EventDragEnd : Event {
};

struct EventDragMove : Event {
math::Vec mouseRel;
};

struct EventDragEnter : Event {
Widget *origin = NULL;
};

struct EventDragLeave : Event {
Widget *origin = NULL;
};

struct EventDragDrop : Event {
Widget *origin = NULL;
};

struct EventPathDrop : EventPosition {
std::list<std::string> paths;
};

struct EventAction : Event {
};

struct EventChange : Event {
};

struct EventZoom : Event {
};


} // namespace rack

+ 81
- 65
include/helpers.hpp View File

@@ -10,114 +10,130 @@ template <class TModule, class TModuleWidget, typename... Tags>
Model *createModel(std::string author, std::string slug, std::string name, Tags... tags) {
struct TModel : Model {
Module *createModule() override {
TModule *module = new TModule();
return module;
TModule *o = new TModule();
return o;
}
ModuleWidget *createModuleWidget() override {
TModule *module = new TModule();
TModuleWidget *moduleWidget = new TModuleWidget(module);
moduleWidget->model = this;
return moduleWidget;
TModuleWidget *o = new TModuleWidget(module);
o->model = this;
return o;
}
ModuleWidget *createModuleWidgetNull() override {
TModuleWidget *moduleWidget = new TModuleWidget(NULL);
moduleWidget->model = this;
return moduleWidget;
TModuleWidget *o = new TModuleWidget(NULL);
o->model = this;
return o;
}
};
Model *model = new TModel();
model->author = author;
model->slug = slug;
model->name = name;
model->tags = {tags...};
return model;

Model *o = new TModel();
o->author = author;
o->slug = slug;
o->name = name;
o->tags = {tags...};
return o;
}

template <class TWidget>
TWidget *createWidget(math::Vec pos) {
TWidget *w = new TWidget();
w->box.pos = pos;
return w;
TWidget *o = new TWidget();
o->box.pos = pos;
return o;
}

template <class TParamWidget>
TParamWidget *createParam(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) {
TParamWidget *param = new TParamWidget();
param->box.pos = pos;
param->module = module;
param->paramId = paramId;
param->setLimits(minValue, maxValue);
param->setDefaultValue(defaultValue);
return param;
TParamWidget *o = new TParamWidget();
o->box.pos = pos;
o->module = module;
o->paramId = paramId;
o->setLimits(minValue, maxValue);
o->setDefaultValue(defaultValue);
return o;
}

template <class TParamWidget>
TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) {
TParamWidget *param = new TParamWidget();
param->box.pos = pos.minus(param->box.size.div(2));
param->module = module;
param->paramId = paramId;
param->setLimits(minValue, maxValue);
param->setDefaultValue(defaultValue);
return param;
TParamWidget *o = new TParamWidget();
o->box.pos = pos.minus(o->box.size.div(2));
o->module = module;
o->paramId = paramId;
o->setLimits(minValue, maxValue);
o->setDefaultValue(defaultValue);
return o;
}

template <class TPort>
TPort *createInput(math::Vec pos, Module *module, int inputId) {
TPort *port = new TPort();
port->box.pos = pos;
port->module = module;
port->type = Port::INPUT;
port->portId = inputId;
return port;
TPort *o = new TPort();
o->box.pos = pos;
o->module = module;
o->type = Port::INPUT;
o->portId = inputId;
return o;
}

template <class TPort>
TPort *createInputCentered(math::Vec pos, Module *module, int inputId) {
TPort *port = new TPort();
port->box.pos = pos.minus(port->box.size.div(2));
port->module = module;
port->type = Port::INPUT;
port->portId = inputId;
return port;
TPort *o = new TPort();
o->box.pos = pos.minus(o->box.size.div(2));
o->module = module;
o->type = Port::INPUT;
o->portId = inputId;
return o;
}

template <class TPort>
TPort *createOutput(math::Vec pos, Module *module, int outputId) {
TPort *port = new TPort();
port->box.pos = pos;
port->module = module;
port->type = Port::OUTPUT;
port->portId = outputId;
return port;
TPort *o = new TPort();
o->box.pos = pos;
o->module = module;
o->type = Port::OUTPUT;
o->portId = outputId;
return o;
}

template <class TPort>
TPort *createOutputCentered(math::Vec pos, Module *module, int outputId) {
TPort *port = new TPort();
port->box.pos = pos.minus(port->box.size.div(2));
port->module = module;
port->type = Port::OUTPUT;
port->portId = outputId;
return port;
TPort *o = new TPort();
o->box.pos = pos.minus(o->box.size.div(2));
o->module = module;
o->type = Port::OUTPUT;
o->portId = outputId;
return o;
}

template <class TModuleLightWidget>
TModuleLightWidget *createLight(math::Vec pos, Module *module, int firstLightId) {
TModuleLightWidget *light = new TModuleLightWidget();
light->box.pos = pos;
light->module = module;
light->firstLightId = firstLightId;
return light;
TModuleLightWidget *o = new TModuleLightWidget();
o->box.pos = pos;
o->module = module;
o->firstLightId = firstLightId;
return o;
}

template <class TModuleLightWidget>
TModuleLightWidget *createLightCentered(math::Vec pos, Module *module, int firstLightId) {
TModuleLightWidget *light = new TModuleLightWidget();
light->box.pos = pos.minus(light->box.size.div(2));
light->module = module;
light->firstLightId = firstLightId;
return light;
TModuleLightWidget *o = new TModuleLightWidget();
o->box.pos = pos.minus(o->box.size.div(2));
o->module = module;
o->firstLightId = firstLightId;
return o;
}

template <class TMenuLabel = MenuLabel>
TMenuLabel *createMenuLabel(std::string text) {
TMenuLabel *o = new TMenuLabel();
o->text = text;
return o;
}

template <class TMenuItem = MenuItem>
TMenuItem *createMenuItem(std::string text, std::string rightText = "") {
TMenuItem *o = new TMenuItem();
o->text = text;
o->rightText = rightText;
return o;
}




+ 39
- 39
include/math.hpp View File

@@ -156,63 +156,63 @@ struct Vec {
Vec() {}
Vec(float x, float y) : x(x), y(y) {}

Vec neg() {
Vec neg() const {
return Vec(-x, -y);
}
Vec plus(Vec b) {
Vec plus(Vec b) const {
return Vec(x + b.x, y + b.y);
}
Vec minus(Vec b) {
Vec minus(Vec b) const {
return Vec(x - b.x, y - b.y);
}
Vec mult(float s) {
Vec mult(float s) const {
return Vec(x * s, y * s);
}
Vec mult(Vec b) {
Vec mult(Vec b) const {
return Vec(x * b.x, y * b.y);
}
Vec div(float s) {
Vec div(float s) const {
return Vec(x / s, y / s);
}
Vec div(Vec b) {
Vec div(Vec b) const {
return Vec(x / b.x, y / b.y);
}
float dot(Vec b) {
float dot(Vec b) const {
return x * b.x + y * b.y;
}
float norm() {
float norm() const {
return std::hypotf(x, y);
}
Vec flip() {
Vec flip() const {
return Vec(y, x);
}
Vec min(Vec b) {
Vec min(Vec b) const {
return Vec(std::min(x, b.x), std::min(y, b.y));
}
Vec max(Vec b) {
Vec max(Vec b) const {
return Vec(std::max(x, b.x), std::max(y, b.y));
}
Vec round() {
Vec round() const {
return Vec(std::round(x), std::round(y));
}
Vec floor() {
Vec floor() const {
return Vec(std::floor(x), std::floor(y));
}
Vec ceil() {
Vec ceil() const {
return Vec(std::ceil(x), std::ceil(y));
}
bool isEqual(Vec b) {
bool isEqual(Vec b) const {
return x == b.x && y == b.y;
}
bool isZero() {
bool isZero() const {
return x == 0.0f && y == 0.0f;
}
bool isFinite() {
bool isFinite() const {
return std::isfinite(x) && std::isfinite(y);
}
Vec clamp(Rect bound);
Vec clampBetween(Rect bound);
DEPRECATED Vec clamp2(Rect bound);
Vec clamp(Rect bound) const;
Vec clampBetween(Rect bound) const;
DEPRECATED Vec clamp2(Rect bound) const;
};


@@ -228,40 +228,40 @@ struct Rect {
}

/** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right */
bool contains(Vec v) {
bool contains(Vec v) const {
return pos.x <= v.x && v.x < pos.x + size.x
&& pos.y <= v.y && v.y < pos.y + size.y;
}
/** Returns whether this Rect contains an entire Rect */
bool contains(Rect r) {
bool contains(Rect r) const {
return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x
&& pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y;
}
/** Returns whether this Rect overlaps with another Rect */
bool intersects(Rect r) {
bool intersects(Rect r) const {
return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x)
&& (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y);
}
bool isEqual(Rect r) {
bool isEqual(Rect r) const {
return pos.isEqual(r.pos) && size.isEqual(r.size);
}
Vec getCenter() {
Vec getCenter() const {
return pos.plus(size.mult(0.5f));
}
Vec getTopLeft() {
Vec getTopLeft() const {
return pos;
}
Vec getTopRight() {
Vec getTopRight() const {
return pos.plus(Vec(size.x, 0.f));
}
Vec getBottomLeft() {
Vec getBottomLeft() const {
return pos.plus(Vec(0.f, size.y));
}
Vec getBottomRight() {
Vec getBottomRight() const {
return pos.plus(size);
}
/** Clamps the edges of the rectangle to fit within a bound */
Rect clamp(Rect bound) {
Rect clamp(Rect bound) const {
Rect r;
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x);
r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y);
@@ -270,7 +270,7 @@ struct Rect {
return r;
}
/** Nudges the position to fix inside a bounding box */
Rect nudge(Rect bound) {
Rect nudge(Rect bound) const {
Rect r;
r.size = size;
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x);
@@ -278,7 +278,7 @@ struct Rect {
return r;
}
/** Expands this Rect to contain `other` */
Rect expand(Rect other) {
Rect expand(Rect other) const {
Rect r;
r.pos.x = std::min(pos.x, other.pos.x);
r.pos.y = std::min(pos.y, other.pos.y);
@@ -287,16 +287,16 @@ struct Rect {
return r;
}
/** Returns a Rect with its position set to zero */
Rect zeroPos() {
Rect zeroPos() const {
return Rect(Vec(), size);
}
Rect grow(Vec delta) {
Rect grow(Vec delta) const {
Rect r;
r.pos = pos.minus(delta);
r.size = size.plus(delta.mult(2.f));
return r;
}
Rect shrink(Vec delta) {
Rect shrink(Vec delta) const {
Rect r;
r.pos = pos.plus(delta);
r.size = size.minus(delta.mult(2.f));
@@ -305,19 +305,19 @@ struct Rect {
};


inline Vec Vec::clamp(Rect bound) {
inline Vec Vec::clamp(Rect bound) const {
return Vec(
rack::math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x),
rack::math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clampBetween(Rect bound) {
inline Vec Vec::clampBetween(Rect bound) const {
return Vec(
rack::math::clampBetween(x, bound.pos.x, bound.pos.x + bound.size.x),
rack::math::clampBetween(y, bound.pos.y, bound.pos.y + bound.size.y));
}

inline Vec Vec::clamp2(Rect bound) {return clampBetween(bound);}
inline Vec Vec::clamp2(Rect bound) const {return clampBetween(bound);}


} // namespace math


+ 0
- 28
include/plugin.hpp View File

@@ -65,34 +65,6 @@ struct Model {
virtual ModuleWidget *createModuleWidget() { return NULL; }
/** Creates a ModuleWidget with no Module, useful for previews */
virtual ModuleWidget *createModuleWidgetNull() { return NULL; }

/** Create Model subclass which constructs a specific Module and ModuleWidget subclass */
template <typename TModule, typename TModuleWidget, typename... Tags>
static Model *create(std::string author, std::string slug, std::string name, Tags... tags) {
struct TModel : Model {
Module *createModule() override {
TModule *module = new TModule();
return module;
}
ModuleWidget *createModuleWidget() override {
TModule *module = new TModule();
TModuleWidget *moduleWidget = new TModuleWidget(module);
moduleWidget->model = this;
return moduleWidget;
}
ModuleWidget *createModuleWidgetNull() override {
TModuleWidget *moduleWidget = new TModuleWidget(NULL);
moduleWidget->model = this;
return moduleWidget;
}
};
TModel *o = new TModel();
o->author = author;
o->slug = slug;
o->name = name;
o->tags = {tags...};
return o;
}
};




+ 1
- 1
include/rack.hpp View File

@@ -23,7 +23,7 @@ namespace rack {

// Adopt some sub-namespaces into the main namespace for convenience
using namespace math;
using namespace string;
using string::stringf;


} // namespace rack

+ 0
- 6
include/rack0.hpp View File

@@ -56,12 +56,6 @@ DEPRECATED inline float randomUniform() {return random::uniform();}
DEPRECATED inline float randomNormal() {return random::normal();}
DEPRECATED inline float randomf() {return random::uniform();}

////////////////////
// string
////////////////////

using string::stringf;

////////////////////
// logger
////////////////////


+ 21
- 273
include/ui.hpp View File

@@ -1,274 +1,22 @@
#pragma once
#include "widgets.hpp"
#include "blendish.h"


#define CHECKMARK_STRING "✔"
#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "")


namespace rack {

////////////////////
// Layouts (layouts.cpp)
////////////////////

/** Positions children in a row/column based on their widths/heights */
struct SequentialLayout : VirtualWidget {
enum Orientation {
HORIZONTAL_ORIENTATION,
VERTICAL_ORIENTATION,
};
Orientation orientation = HORIZONTAL_ORIENTATION;
enum Alignment {
LEFT_ALIGNMENT,
CENTER_ALIGNMENT,
RIGHT_ALIGNMENT,
};
Alignment alignment = LEFT_ALIGNMENT;
/** Space between adjacent elements */
float spacing = 0.0;
void step() override;
};

////////////////////
// Blendish UI elements
////////////////////

struct Label : VirtualWidget {
std::string text;
float fontSize;
NVGcolor color;
enum Alignment {
LEFT_ALIGNMENT,
CENTER_ALIGNMENT,
RIGHT_ALIGNMENT,
};
Alignment alignment = LEFT_ALIGNMENT;

Label();
void draw(NVGcontext *vg) override;
};

struct List : OpaqueWidget {
void step() override;
};

/** Deletes itself from parent when clicked */
struct MenuOverlay : OpaqueWidget {
void step() override;
void onMouseDown(EventMouseDown &e) override;
void onHoverKey(EventHoverKey &e) override;
};

struct MenuEntry;

struct Menu : OpaqueWidget {
Menu *parentMenu = NULL;
Menu *childMenu = NULL;
/** The entry which created the child menu */
MenuEntry *activeEntry = NULL;

Menu() {
box.size = math::Vec(0, 0);
}
~Menu();
/** Deprecated. Just use addChild(child) instead */
DEPRECATED void pushChild(Widget *child) {
addChild(child);
}
void setChildMenu(Menu *menu);
void step() override;
void draw(NVGcontext *vg) override;
void onScroll(EventScroll &e) override;
};

struct MenuEntry : OpaqueWidget {
MenuEntry() {
box.size = math::Vec(0, BND_WIDGET_HEIGHT);
}
template <typename T = MenuEntry>
static T *create() {
T *o = Widget::create<T>(math::Vec());
return o;
}
};

struct MenuSeparator : MenuEntry {
MenuSeparator();
void draw(NVGcontext *vg) override;
};

struct MenuLabel : MenuEntry {
std::string text;
void draw(NVGcontext *vg) override;
void step() override;

template <typename T = MenuLabel>
static T *create(std::string text) {
T *o = MenuEntry::create<T>();
o->text = text;
return o;
}
};

struct MenuItem : MenuEntry {
std::string text;
std::string rightText;
void draw(NVGcontext *vg) override;
void step() override;
virtual Menu *createChildMenu() {return NULL;}
void onMouseEnter(EventMouseEnter &e) override;
void onDragDrop(EventDragDrop &e) override;

template <typename T = MenuItem>
static T *create(std::string text, std::string rightText = "") {
T *o = MenuEntry::create<T>();
o->text = text;
o->rightText = rightText;
return o;
}
};

struct TooltipOverlay : TransparentWidget {
};

struct WindowOverlay : OpaqueWidget {
};

struct WindowWidget : OpaqueWidget {
std::string title;
void draw(NVGcontext *vg) override;
void onDragMove(EventDragMove &e) override;
};

struct Button : OpaqueWidget {
std::string text;
BNDwidgetState state = BND_DEFAULT;

Button() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg) override;
void onMouseEnter(EventMouseEnter &e) override;
void onMouseLeave(EventMouseLeave &e) override;
void onDragStart(EventDragStart &e) override;
void onDragEnd(EventDragEnd &e) override;
void onDragDrop(EventDragDrop &e) override;
};

struct IconButton : Button {
FramebufferWidget *fw;
SVGWidget *sw;
IconButton();
void setSVG(std::shared_ptr<SVG> svg);
};

struct ChoiceButton : Button {
void draw(NVGcontext *vg) override;
};

struct RadioButton : OpaqueWidget, QuantityWidget {
BNDwidgetState state = BND_DEFAULT;

RadioButton() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg) override;
void onMouseEnter(EventMouseEnter &e) override;
void onMouseLeave(EventMouseLeave &e) override;
void onDragDrop(EventDragDrop &e) override;
};

struct Slider : OpaqueWidget, QuantityWidget {
BNDwidgetState state = BND_DEFAULT;

Slider() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg) override;
void onDragStart(EventDragStart &e) override;
void onDragMove(EventDragMove &e) override;
void onDragEnd(EventDragEnd &e) override;
void onMouseDown(EventMouseDown &e) override;
};

struct ScrollBar;
/** Handles a container with ScrollBar */
struct ScrollWidget : OpaqueWidget {
Widget *container;
ScrollBar *horizontalScrollBar;
ScrollBar *verticalScrollBar;
math::Vec offset;

ScrollWidget();
void scrollTo(math::Rect r);
void draw(NVGcontext *vg) override;
void step() override;
void onMouseMove(EventMouseMove &e) override;
void onScroll(EventScroll &e) override;
void onHoverKey(EventHoverKey &e) override;
};

struct TextField : OpaqueWidget {
std::string text;
std::string placeholder;
bool multiline = false;
/** The index of the text cursor */
int cursor = 0;
/** The index of the other end of the selection.
If nothing is selected, this is equal to `cursor`.
*/
int selection = 0;

TextField() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg) override;
void onMouseDown(EventMouseDown &e) override;
void onMouseMove(EventMouseMove &e) override;
void onFocus(EventFocus &e) override;
void onText(EventText &e) override;
void onKey(EventKey &e) override;
/** Inserts text at the cursor, replacing the selection if necessary */
void insertText(std::string text);
/** Replaces the entire text */
void setText(std::string text);
virtual int getTextPosition(math::Vec mousePos);
virtual void onTextChange() {}
};

struct PasswordField : TextField {
void draw(NVGcontext *vg) override;
};

struct ProgressBar : QuantityWidget {
ProgressBar() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg) override;
};

struct Tooltip : VirtualWidget {
std::string text;
Tooltip();
void draw(NVGcontext *vg) override;
};

struct Scene : OpaqueWidget {
Widget *overlay = NULL;
/** Takes ownership of `w` */
void setOverlay(Widget *w);
Menu *createMenu();
void step() override;
};


////////////////////
// globals
////////////////////

extern Scene *gScene;


} // namespace rack
#include "ui/SequentialLayout.hpp"
#include "ui/Label.hpp"
#include "ui/List.hpp"
#include "ui/MenuOverlay.hpp"
#include "ui/Tooltip.hpp"
#include "ui/Scene.hpp"
#include "ui/TextField.hpp"
#include "ui/PasswordField.hpp"
#include "ui/ScrollWidget.hpp"
#include "ui/Slider.hpp"
#include "ui/Menu.hpp"
#include "ui/MenuEntry.hpp"
#include "ui/MenuSeparator.hpp"
#include "ui/MenuLabel.hpp"
#include "ui/MenuItem.hpp"
#include "ui/Button.hpp"
#include "ui/IconButton.hpp"
#include "ui/ChoiceButton.hpp"
#include "ui/RadioButton.hpp"
#include "ui/WindowWidget.hpp"
#include "ui/ProgressBar.hpp"

+ 48
- 0
include/ui/Button.hpp View File

@@ -0,0 +1,48 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct Button : OpaqueWidget {
std::string text;
BNDwidgetState state = BND_DEFAULT;

Button() {
box.size.y = BND_WIDGET_HEIGHT;
}

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());
Widget::draw(vg);
}

void on(event::Enter &e) override {
state = BND_HOVER;
}

void on(event::Leave &e) override {
state = BND_DEFAULT;
}

void on(event::DragStart &e) override {
state = BND_ACTIVE;
}

void on(event::DragEnd &e) override {
state = BND_HOVER;
}

void on(event::DragDrop &e) override {
if (e.origin == this) {
event::Action eAction;
handleEvent(eAction);
}
}
};


} // namespace rack

+ 16
- 0
include/ui/ChoiceButton.hpp View File

@@ -0,0 +1,16 @@
#pragma once

#include "ui/Button.hpp"


namespace rack {


struct ChoiceButton : Button {
void draw(NVGcontext *vg) override {
bndChoiceButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str());
}
};


} // namespace rack

+ 32
- 0
include/ui/IconButton.hpp View File

@@ -0,0 +1,32 @@
#pragma once

#include "ui/Button.hpp"


namespace rack {


struct IconButton : Button {
FramebufferWidget *fw;
SVGWidget *sw;

IconButton() {
box.size.x = BND_TOOL_WIDTH;

fw = new FramebufferWidget();
fw->oversample = 2;
addChild(fw);

sw = new SVGWidget();
sw->box.pos = math::Vec(2, 2);
fw->addChild(sw);
}

void setSVG(std::shared_ptr<SVG> svg) {
sw->setSVG(svg);
fw->dirty = true;
}
};


} // namespace rack

+ 49
- 0
include/ui/Label.hpp View File

@@ -0,0 +1,49 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct Label : virtual EventWidget {
std::string text;
float fontSize;
NVGcolor color;
enum Alignment {
LEFT_ALIGNMENT,
CENTER_ALIGNMENT,
RIGHT_ALIGNMENT,
};
Alignment alignment = LEFT_ALIGNMENT;

Label() {
box.size.y = BND_WIDGET_HEIGHT;
fontSize = 13;
color = bndGetTheme()->regularTheme.textColor;
}

void draw(NVGcontext *vg) override {
// TODO
// Custom font sizes do not work with right or center alignment
float x;
switch (alignment) {
default:
case LEFT_ALIGNMENT: {
x = 0.0;
} break;
case RIGHT_ALIGNMENT: {
x = box.size.x - bndLabelWidth(vg, -1, text.c_str());
} break;
case CENTER_ALIGNMENT: {
x = (box.size.x - bndLabelWidth(vg, -1, text.c_str())) / 2.0;
} break;
}

bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, color, BND_LEFT, fontSize, text.c_str(), NULL);
}
};


} // namespace rack

+ 28
- 0
include/ui/List.hpp View File

@@ -0,0 +1,28 @@
#pragma once

#include "widgets.hpp"


namespace rack {


struct List : OpaqueWidget {
void step() override {
Widget::step();

// Set positions of children
box.size.y = 0.0;
for (Widget *child : children) {
if (!child->visible)
continue;
// Increment height, set position of child
child->box.pos = math::Vec(0.0, box.size.y);
box.size.y += child->box.size.y;
// Resize width of child
child->box.size.x = box.size.x;
}
}
};


} // namespace rack

+ 81
- 0
include/ui/Menu.hpp View File

@@ -0,0 +1,81 @@
#pragma once

#include "MenuEntry.hpp"
#include "blendish.h"


namespace rack {


struct Menu : OpaqueWidget {
Menu *parentMenu = NULL;
Menu *childMenu = NULL;
/** The entry which created the child menu */
MenuEntry *activeEntry = NULL;

Menu() {
box.size = math::Vec(0, 0);
}

~Menu() {
setChildMenu(NULL);
}

/** Deprecated. Just use addChild(child) instead */
DEPRECATED void pushChild(Widget *child) {
addChild(child);
}

void setChildMenu(Menu *menu) {
if (childMenu) {
if (childMenu->parent)
childMenu->parent->removeChild(childMenu);
delete childMenu;
childMenu = NULL;
}
if (menu) {
childMenu = menu;
assert(parent);
parent->addChild(childMenu);
}
}

void step() override {
Widget::step();

// Set positions of children
box.size = math::Vec(0, 0);
for (Widget *child : children) {
if (!child->visible)
continue;
// Increment height, set position of child
child->box.pos = math::Vec(0, box.size.y);
box.size.y += child->box.size.y;
// Increase width based on maximum width of child
if (child->box.size.x > box.size.x) {
box.size.x = child->box.size.x;
}
}

// Resize widths of children
for (Widget *child : children) {
child->box.size.x = box.size.x;
}
}

void draw(NVGcontext *vg) override {
bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE);
Widget::draw(vg);
}

void on(event::HoverScroll &e) override {
if (!parent)
return;
if (!parent->box.contains(box))
box.pos.y += e.scrollDelta.y;
// e.consumed = true;
}
};


} // namespace rack

+ 16
- 0
include/ui/MenuEntry.hpp View File

@@ -0,0 +1,16 @@
#pragma once

#include "widgets.hpp"


namespace rack {


struct MenuEntry : OpaqueWidget {
MenuEntry() {
box.size = math::Vec(0, BND_WIDGET_HEIGHT);
}
};


} // namespace rack

+ 75
- 0
include/ui/MenuItem.hpp View File

@@ -0,0 +1,75 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


#define BND_LABEL_FONT_SIZE 13


struct MenuItem : MenuEntry {
std::string text;
std::string rightText;

void draw(NVGcontext *vg) override {
// Get state
BNDwidgetState state = (gHoveredWidget == this) ? BND_HOVER : BND_DEFAULT;
Menu *parentMenu = dynamic_cast<Menu*>(parent);
if (parentMenu && parentMenu->activeEntry == this) {
state = BND_ACTIVE;
}

bndMenuItem(vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());

float x = box.size.x - bndLabelWidth(vg, -1, rightText.c_str());
NVGcolor rightColor = (state == BND_DEFAULT) ? bndGetTheme()->menuTheme.textColor : bndGetTheme()->menuTheme.textSelectedColor;
bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, rightColor, BND_LEFT, BND_LABEL_FONT_SIZE, rightText.c_str(), NULL);
}

void step() override {
// Add 10 more pixels because measurements on high-DPI screens are sometimes too small for some reason
const float rightPadding = 10.0;
// HACK use gVg from the window.
// All this does is inspect the font, so it shouldn't modify gVg and should work when called from a FramebufferWidget for example.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + bndLabelWidth(gVg, -1, rightText.c_str()) + rightPadding;
Widget::step();
}

virtual Menu *createChildMenu() {return NULL;}

void on(event::Enter &e) override {
Menu *parentMenu = dynamic_cast<Menu*>(parent);
if (!parentMenu)
return;

parentMenu->activeEntry = NULL;

// Try to create child menu
Menu *childMenu = createChildMenu();
if (childMenu) {
parentMenu->activeEntry = this;
childMenu->box.pos = parent->box.pos.plus(box.getTopRight());
}
parentMenu->setChildMenu(childMenu);
}

void on(event::DragDrop &e) override {
if (e.origin != this)
return;

event::Action eAction;
// Consume event by default, but allow action to un-consume it to prevent the menu from being removed.
eAction.target = this;
handleEvent(eAction);
if (eAction.target) {
// deletes `this`
gScene->setOverlay(NULL);
}
}
};


} // namespace rack

+ 27
- 0
include/ui/MenuLabel.hpp View File

@@ -0,0 +1,27 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct MenuLabel : MenuEntry {
std::string text;

void draw(NVGcontext *vg) override {
bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str());
}

void step() override {
// Add 10 more pixels because Retina measurements are sometimes too small
const float rightPadding = 10.0;
// HACK use gVg from the window.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + rightPadding;
Widget::step();
}
};


} // namespace rack

+ 60
- 0
include/ui/MenuOverlay.hpp View File

@@ -0,0 +1,60 @@
#pragma once

#include "widgets.hpp"


namespace rack {


/** Deletes itself from parent when clicked */
struct MenuOverlay : OpaqueWidget {
void step() override {
Widget::step();

// Fit all children in the box
for (Widget *child : children) {
child->box = child->box.nudge(box.zeroPos());
}
}

void on(event::Button &e) override;
void on(event::HoverKey &e) override;
};


} // namespace rack


#include "ui/Scene.hpp"


namespace rack {


inline void MenuOverlay::on(event::Button &e) {
EventWidget::on(e);
if (!e.target) {
// deletes `this`
gScene->setOverlay(NULL);
e.target = this;
}
}

inline void MenuOverlay::on(event::HoverKey &e) {
switch (e.key) {
case GLFW_KEY_ESCAPE: {
gScene->setOverlay(NULL);
// e.consumed = true;
return;
} break;
}

// if (!e.consumed) {
// // Recurse children but consume the event
// Widget::onHoverKey(e);
// e.consumed = true;
// }
}


} // namespace rack

+ 27
- 0
include/ui/MenuSeparator.hpp View File

@@ -0,0 +1,27 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct MenuSeparator : MenuEntry {
MenuSeparator() {
box.size.y = BND_WIDGET_HEIGHT / 2;
}

void draw(NVGcontext *vg) override {
nvgBeginPath(vg);
const float margin = 8.0;
nvgMoveTo(vg, margin, box.size.y / 2.0);
nvgLineTo(vg, box.size.x - margin, box.size.y / 2.0);
nvgStrokeWidth(vg, 1.0);
nvgStrokeColor(vg, color::alpha(bndGetTheme()->menuTheme.textColor, 0.25));
nvgStroke(vg);
}
};


} // namespace rack

+ 19
- 0
include/ui/PasswordField.hpp View File

@@ -0,0 +1,19 @@
#pragma once

#include "ui/TextField.hpp"


namespace rack {


struct PasswordField : TextField {
void draw(NVGcontext *vg) override {
std::string textTmp = text;
text = std::string(textTmp.size(), '*');
TextField::draw(vg);
text = textTmp;
}
};


} // namespace rack

+ 22
- 0
include/ui/ProgressBar.hpp View File

@@ -0,0 +1,22 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct ProgressBar : QuantityWidget {
ProgressBar() {
box.size.y = BND_WIDGET_HEIGHT;
}

void draw(NVGcontext *vg) override {
float progress = math::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);
}
};


} // namespace rack

+ 43
- 0
include/ui/RadioButton.hpp View File

@@ -0,0 +1,43 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct RadioButton : OpaqueWidget, QuantityWidget {
BNDwidgetState state = BND_DEFAULT;

RadioButton() {
box.size.y = BND_WIDGET_HEIGHT;
}

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());
}

void on(event::Enter &e) override {
state = BND_HOVER;
}

void on(event::Leave &e) override {
state = BND_DEFAULT;
}

void on(event::DragDrop &e) override {
if (e.origin == this) {
if (value)
setValue(0.0);
else
setValue(1.0);

event::Action eAction;
handleEvent(eAction);
}
}
};


} // namespace rack

+ 68
- 0
include/ui/Scene.hpp View File

@@ -0,0 +1,68 @@
#pragma once

#include "widgets.hpp"
#include "ui/Menu.hpp"


namespace rack {


struct Menu;


struct Scene : OpaqueWidget {
Widget *overlay = NULL;

/** Takes ownership of `w` */
void setOverlay(Widget *w) {
if (overlay) {
removeChild(overlay);
delete overlay;
overlay = NULL;
}
if (w) {
addChild(w);
overlay = w;
overlay->box.pos = math::Vec();
}
}

Menu *createMenu();

void step() override {
if (overlay) {
overlay->box.pos = math::Vec(0, 0);
overlay->box.size = box.size;
}

Widget::step();
}
};


extern Scene *gScene;


} // namespace rack


#include "ui/MenuOverlay.hpp"


namespace rack {


inline Menu *Scene::createMenu() {
// Get relative position of the click
MenuOverlay *overlay = new MenuOverlay();
Menu *menu = new Menu();
menu->box.pos = gMousePos;

overlay->addChild(menu);
gScene->setOverlay(overlay);

return menu;
}


} // namespace rack

+ 160
- 0
include/ui/ScrollWidget.hpp View File

@@ -0,0 +1,160 @@
#pragma once

#include "widgets.hpp"


namespace rack {


/** Parent must be a ScrollWidget */
struct ScrollBar : OpaqueWidget {
enum Orientation {
VERTICAL,
HORIZONTAL
};
Orientation orientation;
BNDwidgetState state = BND_DEFAULT;
float offset = 0.0;
float size = 0.0;

ScrollBar() {
box.size = math::Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT);
}

void draw(NVGcontext *vg) override {
bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size);
}

void on(event::DragStart &e) override {
state = BND_ACTIVE;
windowCursorLock();
}

void on(event::DragMove &e) override;

void on(event::DragEnd &e) override {
state = BND_DEFAULT;
windowCursorUnlock();
}
};


/** Handles a container with ScrollBar */
struct ScrollWidget : OpaqueWidget {
Widget *container;
ScrollBar *horizontalScrollBar;
ScrollBar *verticalScrollBar;
math::Vec offset;

ScrollWidget() {
container = new Widget();
addChild(container);

horizontalScrollBar = new ScrollBar();
horizontalScrollBar->orientation = ScrollBar::HORIZONTAL;
horizontalScrollBar->visible = false;
addChild(horizontalScrollBar);

verticalScrollBar = new ScrollBar();
verticalScrollBar->orientation = ScrollBar::VERTICAL;
verticalScrollBar->visible = false;
addChild(verticalScrollBar);
}

void scrollTo(math::Rect r) {
math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos);
offset = offset.clampBetween(bound);
}

void draw(NVGcontext *vg) override {
nvgScissor(vg, 0, 0, box.size.x, box.size.y);
Widget::draw(vg);
nvgResetScissor(vg);
}

void step() override {
Widget::step();

// Clamp scroll offset
math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight();
math::Rect containerBox = math::Rect(math::Vec(0, 0), containerCorner.minus(box.size));
offset = offset.clamp(containerBox);
// Lock offset to top/left if no scrollbar will display
if (containerBox.size.x < 0.0)
offset.x = 0.0;
if (containerBox.size.y < 0.0)
offset.y = 0.0;

// Update the container's positions from the offset
container->box.pos = offset.neg().round();

// Update scrollbar offsets and sizes
math::Vec viewportSize = container->getChildrenBoundingBox().getBottomRight();
math::Vec scrollbarOffset = offset.div(viewportSize.minus(box.size));
math::Vec scrollbarSize = box.size.div(viewportSize);

horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0);
verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0);
horizontalScrollBar->offset = scrollbarOffset.x;
verticalScrollBar->offset = scrollbarOffset.y;
horizontalScrollBar->size = scrollbarSize.x;
verticalScrollBar->size = scrollbarSize.y;

// Resize scroll bars
math::Vec inner = math::Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y);
horizontalScrollBar->box.pos.y = inner.y;
verticalScrollBar->box.pos.x = inner.x;
horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x;
verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y;
}

void on(event::Hover &e) override {
// Scroll with arrow keys
if (!gSelectedWidget) {
float arrowSpeed = 30.0;
if (windowIsShiftPressed() && windowIsModPressed())
arrowSpeed /= 16.0;
else if (windowIsShiftPressed())
arrowSpeed *= 4.0;
else if (windowIsModPressed())
arrowSpeed /= 4.0;

if (glfwGetKey(gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
offset.x -= arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
offset.x += arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
offset.y -= arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
offset.y += arrowSpeed;
}
}

OpaqueWidget::on(e);
}

void on(event::HoverScroll &e) override {
offset = offset.minus(e.scrollDelta);
e.target = this;
}

void on(event::HoverKey &e) override {
OpaqueWidget::on(e);
}
};


inline void ScrollBar::on(event::DragMove &e) {
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent);
assert(scrollWidget);
if (orientation == HORIZONTAL)
scrollWidget->offset.x += e.mouseDelta.x;
else
scrollWidget->offset.y += e.mouseDelta.y;
}


} // namespace rack

+ 58
- 0
include/ui/SequentialLayout.hpp View File

@@ -0,0 +1,58 @@
#pragma once

#include "widgets.hpp"


namespace rack {


/** Positions children in a row/column based on their widths/heights */
struct SequentialLayout : virtual EventWidget {
enum Orientation {
HORIZONTAL_ORIENTATION,
VERTICAL_ORIENTATION,
};
Orientation orientation = HORIZONTAL_ORIENTATION;
enum Alignment {
LEFT_ALIGNMENT,
CENTER_ALIGNMENT,
RIGHT_ALIGNMENT,
};
Alignment alignment = LEFT_ALIGNMENT;
/** Space between adjacent elements */
float spacing = 0.0;

void step() override {
Widget::step();

float offset = 0.0;
for (Widget *child : children) {
if (!child->visible)
continue;
// Set position
(orientation == HORIZONTAL_ORIENTATION ? child->box.pos.x : child->box.pos.y) = offset;
// Increment by size
offset += (orientation == HORIZONTAL_ORIENTATION ? child->box.size.x : child->box.size.y);
offset += spacing;
}

// We're done if left aligned
if (alignment == LEFT_ALIGNMENT)
return;

// Adjust positions based on width of the layout itself
offset -= spacing;
if (alignment == RIGHT_ALIGNMENT)
offset -= (orientation == HORIZONTAL_ORIENTATION ? box.size.x : box.size.y);
else if (alignment == CENTER_ALIGNMENT)
offset -= (orientation == HORIZONTAL_ORIENTATION ? box.size.x : box.size.y) / 2.0;
for (Widget *child : children) {
if (!child->visible)
continue;
(orientation == HORIZONTAL_ORIENTATION ? child->box.pos.x : child->box.pos.y) += offset;
}
}
};


} // namespace rack

+ 48
- 0
include/ui/Slider.hpp View File

@@ -0,0 +1,48 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


static const float SLIDER_SENSITIVITY = 0.001f;


struct Slider : OpaqueWidget, QuantityWidget {
BNDwidgetState state = BND_DEFAULT;

Slider() {
box.size.y = BND_WIDGET_HEIGHT;
}

void draw(NVGcontext *vg) override {
float progress = math::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);
}

void on(event::DragStart &e) override {
state = BND_ACTIVE;
windowCursorLock();
}

void on(event::DragMove &e) override {
setValue(value + SLIDER_SENSITIVITY * (maxValue - minValue) * e.mouseDelta.x);
}

void on(event::DragEnd &e) override {
state = BND_DEFAULT;
windowCursorUnlock();
}

void on(event::Button &e) override {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
setValue(defaultValue);
}
e.target = this;
}
};


} // namespace rack

+ 220
- 0
include/ui/TextField.hpp View File

@@ -0,0 +1,220 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct TextField : OpaqueWidget {
std::string text;
std::string placeholder;
bool multiline = false;
/** The index of the text cursor */
int cursor = 0;
/** The index of the other end of the selection.
If nothing is selected, this is equal to `cursor`.
*/
int selection = 0;

TextField() {
box.size.y = BND_WIDGET_HEIGHT;
}

void draw(NVGcontext *vg) override {
nvgScissor(vg, 0, 0, box.size.x, box.size.y);

BNDwidgetState state;
if (this == gSelectedWidget)
state = BND_ACTIVE;
else if (this == gHoveredWidget)
state = BND_HOVER;
else
state = BND_DEFAULT;

int begin = std::min(cursor, selection);
int end = std::max(cursor, selection);
bndTextField(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str(), begin, end);
// Draw placeholder text
if (text.empty() && state != BND_ACTIVE) {
bndIconLabelCaret(vg, 0.0, 0.0, box.size.x, box.size.y, -1, bndGetTheme()->textFieldTheme.itemColor, 13, placeholder.c_str(), bndGetTheme()->textFieldTheme.itemColor, 0, -1);
}

nvgResetScissor(vg);
}

void on(event::Button &e) override {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
cursor = selection = getTextPosition(e.pos);
}
OpaqueWidget::on(e);
}

void on(event::Hover &e) override {
if (this == gDraggedWidget) {
int pos = getTextPosition(e.pos);
if (pos != selection) {
cursor = pos;
}
}
OpaqueWidget::on(e);
}

void on(event::Enter &e) override {
e.target = this;
}

void on(event::SelectText &e) override {
if (e.codepoint < 128) {
std::string newText(1, (char) e.codepoint);
insertText(newText);
}
e.target = this;
}

void on(event::SelectKey &e) override {
switch (e.key) {
case GLFW_KEY_BACKSPACE: {
if (cursor == selection) {
cursor--;
if (cursor >= 0) {
text.erase(cursor, 1);
event::Change eChange;
handleEvent(eChange);
}
selection = cursor;
}
else {
int begin = std::min(cursor, selection);
text.erase(begin, std::abs(selection - cursor));
event::Change eChange;
handleEvent(eChange);
cursor = selection = begin;
}
} break;
case GLFW_KEY_DELETE: {
if (cursor == selection) {
text.erase(cursor, 1);
event::Change eChange;
handleEvent(eChange);
}
else {
int begin = std::min(cursor, selection);
text.erase(begin, std::abs(selection - cursor));
event::Change eChange;
handleEvent(eChange);
cursor = selection = begin;
}
} break;
case GLFW_KEY_LEFT: {
if (windowIsModPressed()) {
while (--cursor > 0) {
if (text[cursor] == ' ')
break;
}
}
else {
cursor--;
}
if (!windowIsShiftPressed()) {
selection = cursor;
}
} break;
case GLFW_KEY_RIGHT: {
if (windowIsModPressed()) {
while (++cursor < (int) text.size()) {
if (text[cursor] == ' ')
break;
}
}
else {
cursor++;
}
if (!windowIsShiftPressed()) {
selection = cursor;
}
} break;
case GLFW_KEY_HOME: {
selection = cursor = 0;
} break;
case GLFW_KEY_END: {
selection = cursor = text.size();
} break;
case GLFW_KEY_V: {
if (windowIsModPressed()) {
const char *newText = glfwGetClipboardString(gWindow);
if (newText)
insertText(newText);
}
} break;
case GLFW_KEY_X: {
if (windowIsModPressed()) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
glfwSetClipboardString(gWindow, selectedText.c_str());
insertText("");
}
}
} break;
case GLFW_KEY_C: {
if (windowIsModPressed()) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
glfwSetClipboardString(gWindow, selectedText.c_str());
}
}
} break;
case GLFW_KEY_A: {
if (windowIsModPressed()) {
selection = 0;
cursor = text.size();
}
} break;
case GLFW_KEY_ENTER: {
if (multiline) {
insertText("\n");
}
else {
event::Action eAction;
handleEvent(eAction);
}
} break;
}

cursor = math::clamp(cursor, 0, (int) text.size());
selection = math::clamp(selection, 0, (int) text.size());
e.target = this;
}

/** Inserts text at the cursor, replacing the selection if necessary */
void insertText(std::string text) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
this->text.erase(begin, std::abs(selection - cursor));
cursor = selection = begin;
}
this->text.insert(cursor, text);
cursor += text.size();
selection = cursor;
event::Change eChange;
handleEvent(eChange);
}

/** Replaces the entire text */
void setText(std::string text) {
this->text = text;
selection = cursor = text.size();
event::Change eChange;
handleEvent(eChange);
}

virtual int getTextPosition(math::Vec mousePos) {
return bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y);
}
};


} // namespace rack

+ 25
- 0
include/ui/Tooltip.hpp View File

@@ -0,0 +1,25 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct Tooltip : virtual EventWidget {
std::string text;

void draw(NVGcontext *vg) override {
// Wrap size to contents
box.size.x = bndLabelWidth(vg, -1, text.c_str()) + 10.0;
box.size.y = bndLabelHeight(vg, -1, text.c_str(), INFINITY);

bndTooltipBackground(vg, 0.0, 0.0, box.size.x, box.size.y);
bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str());
Widget::draw(vg);
}
};


} // namespace rack

+ 13
- 0
include/ui/TooltipOverlay.hpp View File

@@ -0,0 +1,13 @@
#pragma once

#include "widgets.hpp"


namespace rack {


struct TooltipOverlay : TransparentWidget {
};


} // namespace rack

+ 13
- 0
include/ui/WindowOverlay.hpp View File

@@ -0,0 +1,13 @@
#pragma once

#include "widgets.hpp"


namespace rack {


struct WindowOverlay : OpaqueWidget {
};


} // namespace rack

+ 24
- 0
include/ui/WindowWidget.hpp View File

@@ -0,0 +1,24 @@
#pragma once

#include "widgets.hpp"
#include "blendish.h"


namespace rack {


struct WindowWidget : OpaqueWidget {
std::string title;

void draw(NVGcontext *vg) override {
bndNodeBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_DEFAULT, -1, title.c_str(), bndGetTheme()->backgroundColor);
Widget::draw(vg);
}

void on(event::DragMove &e) override {
box.pos = box.pos.plus(e.mouseDelta);
}
};


} // namespace rack

+ 12
- 151
include/widgets.hpp View File

@@ -1,154 +1,15 @@
#pragma once
#include <list>

#include "common.hpp"
#include "events.hpp"
#include "color.hpp"

#include "widgets/Widget.hpp"


namespace rack {


////////////////////
// Base widget
////////////////////


/** Instead of inheriting from Widget directly, inherit from VirtualWidget to guarantee that only one copy of Widget's member variables are used by each instance of the Widget hierarchy.
*/
struct VirtualWidget : virtual Widget {};

struct TransformWidget : VirtualWidget {
/** The transformation matrix */
float transform[6];
TransformWidget();
math::Rect getChildrenBoundingBox() override;
void identity();
void translate(math::Vec delta);
void rotate(float angle);
void scale(math::Vec s);
void draw(NVGcontext *vg) override;
};

struct ZoomWidget : VirtualWidget {
float zoom = 1.0;
math::Vec getRelativeOffset(math::Vec v, Widget *relative) override;
math::Rect getViewport(math::Rect r) override;
void setZoom(float zoom);
void draw(NVGcontext *vg) override;
void onMouseDown(EventMouseDown &e) override;
void onMouseUp(EventMouseUp &e) override;
void onMouseMove(EventMouseMove &e) override;
void onHoverKey(EventHoverKey &e) override;
void onScroll(EventScroll &e) override;
void onPathDrop(EventPathDrop &e) override;
};

////////////////////
// Trait widgets
////////////////////

/** Widget that does not respond to events */
struct TransparentWidget : VirtualWidget {
void onMouseDown(EventMouseDown &e) override {}
void onMouseUp(EventMouseUp &e) override {}
void onMouseMove(EventMouseMove &e) override {}
void onScroll(EventScroll &e) override {}
};

/** Widget that automatically responds to all mouse events but gives a chance for children to respond instead */
struct OpaqueWidget : VirtualWidget {
void onMouseDown(EventMouseDown &e) override {
Widget::onMouseDown(e);
if (!e.target)
e.target = this;
e.consumed = true;
}
void onMouseUp(EventMouseUp &e) override {
Widget::onMouseUp(e);
if (!e.target)
e.target = this;
e.consumed = true;
}
void onMouseMove(EventMouseMove &e) override {
Widget::onMouseMove(e);
if (!e.target)
e.target = this;
e.consumed = true;
}
void onScroll(EventScroll &e) override {
Widget::onScroll(e);
e.consumed = true;
}
};

struct SVGWidget : VirtualWidget {
std::shared_ptr<SVG> svg;
/** Sets the box size to the svg image size */
void wrap();
/** Sets and wraps the SVG */
void setSVG(std::shared_ptr<SVG> svg);
void draw(NVGcontext *vg) override;
};

/** Caches a widget's draw() result to a framebuffer so it is called less frequently
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.
*/
struct FramebufferWidget : VirtualWidget {
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */
bool dirty = true;
/** A margin in pixels around the children in the framebuffer
This prevents cutting the rendered SVG off on the box edges.
*/
float oversample;
/** The root object in the framebuffer scene
The FramebufferWidget owns the pointer
*/
struct Internal;
Internal *internal;

FramebufferWidget();
~FramebufferWidget();
void draw(NVGcontext *vg) override;
int getImageHandle();
void onZoom(EventZoom &e) override;
};

/** A Widget representing a float value */
struct QuantityWidget : VirtualWidget {
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;

QuantityWidget();
void setValue(float value);
void setLimits(float minValue, float maxValue);
void setDefaultValue(float defaultValue);
/** Generates the display value */
std::string getText();
};


////////////////////
// globals
////////////////////

extern Widget *gHoveredWidget;
extern Widget *gDraggedWidget;
extern Widget *gDragHoveredWidget;
extern Widget *gFocusedWidget;
extern Widget *gTempWidget;


} // namespace rack
#include "widgets/EventWidget.hpp"
#include "widgets/TransparentWidget.hpp"
#include "widgets/OpaqueWidget.hpp"
#include "widgets/TransformWidget.hpp"
#include "widgets/ZoomWidget.hpp"
#include "widgets/SVGWidget.hpp"
#include "widgets/FramebufferWidget.hpp"
#include "widgets/QuantityWidget.hpp"


#define CHECKMARK_STRING "✔"
#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "")

+ 86
- 0
include/widgets/EventWidget.hpp View File

@@ -0,0 +1,86 @@
#pragma once
#include "widgets/Widget.hpp"
#include "event.hpp"


namespace rack {


/** A widget that responds to events */
struct EventWidget : Widget {
void handleEvent(event::Event &e) override {
e.trigger(this);
}

template <class TEvent>
void recurseEvent(TEvent &e) {
for (auto it = children.rbegin(); it != children.rend(); it++) {
Widget *child = *it;
if (!child->visible)
continue;
if (!child->box.contains(e.pos))
continue;
TEvent e2 = e;
e2.pos = e.pos.minus(child->box.pos);
child->handleEvent(e2);
if (e2.target) {
e.target = e.target;
break;
}
}
}

/** Override these event callbacks to respond to events.
See events.hpp for a description of each event.
*/
virtual void on(event::Hover &e) {recurseEvent(e);}
virtual void on(event::Button &e) {recurseEvent(e);}
virtual void on(event::HoverKey &e) {recurseEvent(e);}
virtual void on(event::HoverText &e) {recurseEvent(e);}
virtual void on(event::HoverScroll &e) {recurseEvent(e);}
virtual void on(event::Enter &e) {}
virtual void on(event::Leave &e) {}
virtual void on(event::Select &e) {}
virtual void on(event::Deselect &e) {}
virtual void on(event::SelectKey &e) {}
virtual void on(event::SelectText &e) {}
virtual void on(event::DragStart &e) {}
virtual void on(event::DragEnd &e) {}
virtual void on(event::DragMove &e) {}
virtual void on(event::DragEnter &e) {}
virtual void on(event::DragLeave &e) {}
virtual void on(event::DragDrop &e) {}
virtual void on(event::PathDrop &e) {recurseEvent(e);}
virtual void on(event::Action &e) {}
virtual void on(event::Change &e) {}
virtual void on(event::Zoom &e) {}
};


/** These definitions simply call each `EventWidget::on()` function above.
They need to be defined here because EventWidget is not defined at the time of each event's definition.
*/
EVENT_TRIGGER_DEFINITION(event::Hover)
EVENT_TRIGGER_DEFINITION(event::Button)
EVENT_TRIGGER_DEFINITION(event::HoverKey)
EVENT_TRIGGER_DEFINITION(event::HoverText)
EVENT_TRIGGER_DEFINITION(event::HoverScroll)
EVENT_TRIGGER_DEFINITION(event::Enter)
EVENT_TRIGGER_DEFINITION(event::Leave)
EVENT_TRIGGER_DEFINITION(event::Select)
EVENT_TRIGGER_DEFINITION(event::Deselect)
EVENT_TRIGGER_DEFINITION(event::SelectKey)
EVENT_TRIGGER_DEFINITION(event::SelectText)
EVENT_TRIGGER_DEFINITION(event::DragStart)
EVENT_TRIGGER_DEFINITION(event::DragEnd)
EVENT_TRIGGER_DEFINITION(event::DragMove)
EVENT_TRIGGER_DEFINITION(event::DragEnter)
EVENT_TRIGGER_DEFINITION(event::DragLeave)
EVENT_TRIGGER_DEFINITION(event::DragDrop)
EVENT_TRIGGER_DEFINITION(event::PathDrop)
EVENT_TRIGGER_DEFINITION(event::Action)
EVENT_TRIGGER_DEFINITION(event::Change)
EVENT_TRIGGER_DEFINITION(event::Zoom)


} // namespace rack

+ 37
- 0
include/widgets/FramebufferWidget.hpp View File

@@ -0,0 +1,37 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** Caches a widget's draw() result to a framebuffer so it is called less frequently
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.
*/
struct FramebufferWidget : virtual EventWidget {
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */
bool dirty = true;
/** A margin in pixels around the children in the framebuffer
This prevents cutting the rendered SVG off on the box edges.
*/
float oversample;
/** The root object in the framebuffer scene
The FramebufferWidget owns the pointer
*/
struct Internal;
Internal *internal;

FramebufferWidget();
~FramebufferWidget();
void draw(NVGcontext *vg) override;
int getImageHandle();

void on(event::Zoom &e) override {
dirty = true;
EventWidget::on(e);
}
};


} // namespace rack

+ 30
- 0
include/widgets/OpaqueWidget.hpp View File

@@ -0,0 +1,30 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** Widget that consumes recursing events but gives a chance for children to consume first.
You can of course override the events.
You may also call OpaqueWidget::on() from the overridden method to continue recursing/consuming the event.
*/
struct OpaqueWidget : virtual EventWidget {
template <class TEvent>
void consumeEvent(TEvent &e) {
EventWidget::on(e);
if (!e.target) {
e.target = this;
}
}

void on(event::Hover &e) override {consumeEvent(e);}
void on(event::Button &e) override {consumeEvent(e);}
void on(event::HoverKey &e) override {consumeEvent(e);}
void on(event::HoverText &e) override {consumeEvent(e);}
// void on(event::HoverScroll &e) override {consumeEvent(e);}
void on(event::PathDrop &e) override {consumeEvent(e);}
};


} // namespace rack

+ 49
- 0
include/widgets/QuantityWidget.hpp View File

@@ -0,0 +1,49 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** A Widget representing a float value */
struct QuantityWidget : virtual EventWidget {
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 = math::clampBetween(value, minValue, maxValue);
event::Change e;
on(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::stringf("%s: %.*f%s", label.c_str(), precision, value, unit.c_str());
}
};


} // namespace rack

+ 36
- 0
include/widgets/SVGWidget.hpp View File

@@ -0,0 +1,36 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** Draws an SVG */
struct SVGWidget : virtual EventWidget {
std::shared_ptr<SVG> svg;

/** Sets the box size to the svg image size */
void wrap() {
if (svg && svg->handle) {
box.size = math::Vec(svg->handle->width, svg->handle->height);
}
else {
box.size = math::Vec();
}
}

/** Sets and wraps the SVG */
void setSVG(std::shared_ptr<SVG> svg) {
this->svg = svg;
wrap();
}

void draw(NVGcontext *vg) override {
if (svg && svg->handle) {
svgDraw(vg, svg->handle);
}
}
};


} // namespace rack

+ 47
- 0
include/widgets/TransformWidget.hpp View File

@@ -0,0 +1,47 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** Transforms appearance only, not positions of events */
struct TransformWidget : virtual EventWidget {
/** The transformation matrix */
float transform[6];

TransformWidget() {
identity();
}

void identity() {
nvgTransformIdentity(transform);
}

void translate(math::Vec delta) {
float t[6];
nvgTransformTranslate(t, delta.x, delta.y);
nvgTransformPremultiply(transform, t);
}

void rotate(float angle) {
float t[6];
nvgTransformRotate(t, angle);
nvgTransformPremultiply(transform, t);
}

void scale(math::Vec s) {
float t[6];
nvgTransformScale(t, s.x, s.y);
nvgTransformPremultiply(transform, t);
}

void draw(NVGcontext *vg) override {
// No need to save the state because that is done in the parent
nvgTransform(vg, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);
Widget::draw(vg);
}
};


} // namespace rack

+ 20
- 0
include/widgets/TransparentWidget.hpp View File

@@ -0,0 +1,20 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


/** Widget that does not respond to events and does not pass events to children */
struct TransparentWidget : virtual EventWidget {
/** Override behavior to do nothing instead. */
void on(event::Hover &e) override {}
void on(event::Button &e) override {}
void on(event::HoverKey &e) override {}
void on(event::HoverText &e) override {}
void on(event::HoverScroll &e) override {}
void on(event::PathDrop &e) override {}
};


} // namespace rack

+ 21
- 63
include/widgets/Widget.hpp View File

@@ -1,19 +1,35 @@
#pragma once
#include <list>
#include "common.hpp"
#include "math.hpp"
#include "window.hpp"
#include "color.hpp"


namespace rack {


/** A node in the 2D scene graph
Never inherit from Widget directly. Instead, inherit from VirtualWidget declared below.
*/
namespace event {
struct Event;
} // namespace event


enum PickTarget {
PICK_MOUSE,
PICK_SCROLL,
};


/** A node in the 2D scene graph */
struct Widget {
/** Stores position and size */
math::Rect box = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY));
Widget *parent = NULL;
std::list<Widget*> children;
/** Disable rendering but continue stepping */
bool visible = true;
/** If set to true, parent will delete Widget in the next step() */
bool requestedDelete = false;

virtual ~Widget();

@@ -56,72 +72,14 @@ struct Widget {
void removeChild(Widget *widget);
/** Removes and deletes all children */
void clearChildren();
/** Recursively finalizes event start/end pairs as needed */
void finalizeEvents();

/** Advances the module by one frame */
virtual void step();
/** Draws to NanoVG context */
virtual void draw(NVGcontext *vg);

// Events

/** Called when a mouse button is pressed over this widget */
virtual void onMouseDown(EventMouseDown &e);
/** Called when a mouse button is released over this widget */
virtual void onMouseUp(EventMouseUp &e);
/** Called when the mouse moves over this widget.
Called on every frame, even if `mouseRel = math::Vec(0, 0)`.
*/
virtual void onMouseMove(EventMouseMove &e);
/** Called when a key is pressed while hovering over this widget */
virtual void onHoverKey(EventHoverKey &e);
/** Called when this widget begins responding to `onMouseMove` events */
virtual void onMouseEnter(EventMouseEnter &e) {}
/** Called when this widget no longer responds to `onMouseMove` events */
virtual void onMouseLeave(EventMouseLeave &e) {}
/** Called when this widget gains focus by responding to the `onMouseDown` event */
virtual void onFocus(EventFocus &e) {}
virtual void onDefocus(EventDefocus &e) {}
/** Called when a printable character is received while this widget is focused */
virtual void onText(EventText &e) {}
/** Called when a key is pressed while this widget is focused */
virtual void onKey(EventKey &e) {}
/** Called when the scroll wheel is moved while the mouse is hovering over this widget */
virtual void onScroll(EventScroll &e);

/** Called when a widget responds to `onMouseDown` for a left button press */
virtual void onDragStart(EventDragStart &e) {}
/** Called when the left button is released and this widget is being dragged */
virtual void onDragEnd(EventDragEnd &e) {}
/** Called when a widget responds to `onMouseMove` and is being dragged */
virtual void onDragMove(EventDragMove &e) {}
/** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */
virtual void onDragEnter(EventDragEnter &e) {}
virtual void onDragLeave(EventDragEnter &e) {}
/** Called when a drag action ends while hovering this widget */
virtual void onDragDrop(EventDragDrop &e) {}
/** Called when an OS selection of files is dragged-and-dropped on this widget */
virtual void onPathDrop(EventPathDrop &e);

/** Called when an event triggers an action */
virtual void onAction(EventAction &e) {}
/** For widgets with some concept of values, called when the value is changed */
virtual void onChange(EventChange &e) {}
/** Called when the zoom level is changed of this widget */
virtual void onZoom(EventZoom &e);

/** Helper function for creating and initializing a Widget with certain arguments (in this case just the position).
In this project, you will find this idiom everywhere, as an easier alternative to constructor arguments, for building a Widget (or a subclass) with a one-liner.
Example:
addChild(Widget::create<SVGWidget>(math::Vec(10, 10)))
*/
template <typename T = Widget>
static T *create(math::Vec pos = math::Vec()) {
T *o = new T();
o->box.pos = pos;
return o;
}
/** Trigger an event on this Widget. */
virtual void handleEvent(event::Event &e) {}
};




+ 76
- 0
include/widgets/ZoomWidget.hpp View File

@@ -0,0 +1,76 @@
#pragma once
#include "widgets/EventWidget.hpp"


namespace rack {


struct ZoomWidget : virtual EventWidget {
float zoom = 1.f;

math::Vec getRelativeOffset(math::Vec v, Widget *relative) override {
return Widget::getRelativeOffset(v.mult(zoom), relative);
}

math::Rect getViewport(math::Rect r) override {
r.pos = r.pos.mult(zoom);
r.size = r.size.mult(zoom);
r = Widget::getViewport(r);
r.pos = r.pos.div(zoom);
r.size = r.size.div(zoom);
return r;
}

void setZoom(float zoom) {
if (zoom != this->zoom) {
event::Zoom eZoom;
EventWidget::on(eZoom);
}
this->zoom = zoom;
}

void draw(NVGcontext *vg) override {
// No need to save the state because that is done in the parent
nvgScale(vg, zoom, zoom);
Widget::draw(vg);
}

void on(event::Hover &e) override {
event::Hover e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}

void on(event::Button &e) override {
event::Button e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}

void on(event::HoverKey &e) override {
event::HoverKey e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}

void on(event::HoverText &e) override {
event::HoverText e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}

void on(event::HoverScroll &e) override {
event::HoverScroll e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}

void on(event::PathDrop &e) override {
event::PathDrop e2 = e;
e2.pos = e.pos.div(zoom);
EventWidget::on(e2);
}
};


} // namespace rack

+ 6
- 2
include/window.hpp View File

@@ -1,12 +1,13 @@
#pragma once
#include <memory>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "nanovg.h"
#include "nanosvg.h"

#include "common.hpp"
#include "math.hpp"


#ifdef ARCH_MAC
@@ -76,5 +77,8 @@ void windowSetTheme(NVGcolor bg, NVGcolor fg);
void windowSetFullScreen(bool fullScreen);
bool windowGetFullScreen();

// In svg.cpp
void svgDraw(NVGcontext *vg, NSVGimage *svg);


} // namespace rack

+ 34
- 34
src/Core/AudioInterface.cpp View File

@@ -241,39 +241,39 @@ struct AudioInterfaceWidget : ModuleWidget {
AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/AudioInterface.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0));
addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1));
addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2));
addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3));
addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4));
addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5));
addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6));
addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));
AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), module, AudioInterface::AUDIO_INPUT + 0));
addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), module, AudioInterface::AUDIO_INPUT + 1));
addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), module, AudioInterface::AUDIO_INPUT + 2));
addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), module, AudioInterface::AUDIO_INPUT + 3));
addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), module, AudioInterface::AUDIO_INPUT + 4));
addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), module, AudioInterface::AUDIO_INPUT + 5));
addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), module, AudioInterface::AUDIO_INPUT + 6));
addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), module, AudioInterface::AUDIO_INPUT + 7));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 4));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 5));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 6));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 7));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2));
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3));
AudioWidget *audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339)));
audioWidget->box.size = mm2px(Vec(44, 28));
audioWidget->audioIO = &module->audioIO;
addChild(audioWidget);
@@ -281,4 +281,4 @@ struct AudioInterfaceWidget : ModuleWidget {
};


Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);
Model *modelAudioInterface = createModel<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG);

+ 13
- 13
src/Core/Blank.cpp View File

@@ -3,25 +3,25 @@
using namespace rack;


struct ModuleResizeHandle : Widget {
struct ModuleResizeHandle : EventWidget {
bool right = false;
float dragX;
Rect originalBox;
ModuleResizeHandle() {
box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT);
}
void onMouseDown(EventMouseDown &e) override {
if (e.button == 0) {
e.consumed = true;
e.target = this;
}
void on(event::Hover &e) override {
// TODO
// if (e.button == 0) {
// e.target = this;
// }
}
void onDragStart(EventDragStart &e) override {
void on(event::DragStart &e) override {
dragX = gRackWidget->lastMousePos.x;
ModuleWidget *m = getAncestorOfType<ModuleWidget>();
originalBox = m->box;
}
void onDragMove(EventDragMove &e) override {
void on(event::DragMove &e) override {
ModuleWidget *m = getAncestorOfType<ModuleWidget>();

float newDragX = gRackWidget->lastMousePos.x;
@@ -78,10 +78,10 @@ struct BlankWidget : ModuleWidget {
addChild(leftHandle);
addChild(rightHandle);

addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
topRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0));
bottomRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365));
addChild(createWidget<ScrewSilver>(Vec(15, 0)));
addChild(createWidget<ScrewSilver>(Vec(15, 365)));
topRightScrew = createWidget<ScrewSilver>(Vec(box.size.x - 30, 0));
bottomRightScrew = createWidget<ScrewSilver>(Vec(box.size.x - 30, 365));
addChild(topRightScrew);
addChild(bottomRightScrew);
}
@@ -120,4 +120,4 @@ struct BlankWidget : ModuleWidget {
};


Model *modelBlank = Model::create<Module, BlankWidget>("Core", "Blank", "Blank", BLANK_TAG);
Model *modelBlank = createModel<Module, BlankWidget>("Core", "Blank", "Blank", BLANK_TAG);

+ 2
- 2
src/Core/Core.hpp View File

@@ -28,11 +28,11 @@ struct Grid16MidiWidget : MidiWidget {
void createGridChoices() {
Vec pos = channelChoice->box.getBottomLeft();
for (int x = 1; x < 4; x++) {
vSeparators[x] = Widget::create<LedDisplaySeparator>(pos);
vSeparators[x] = createWidget<LedDisplaySeparator>(pos);
addChild(vSeparators[x]);
}
for (int y = 0; y < 4; y++) {
hSeparators[y] = Widget::create<LedDisplaySeparator>(pos);
hSeparators[y] = createWidget<LedDisplaySeparator>(pos);
addChild(hSeparators[y]);
for (int x = 0; x < 4; x++) {
GridChoice *gridChoice = createGridChoice();


+ 37
- 37
src/Core/MIDICCToCVInterface.cpp View File

@@ -129,41 +129,41 @@ struct MidiCcChoice : GridChoice {
else {
text = string::stringf("%d", module->learnedCcs[id]);
color.a = 1.0;
if (gFocusedWidget == this)
gFocusedWidget = NULL;
if (gSelectedWidget == this)
gSelectedWidget = NULL;
}
}

void onFocus(EventFocus &e) override {
e.consumed = true;
void on(event::Select &e) override {
e.target = this;
module->learningId = id;
focusCc = -1;
}

void onDefocus(EventDefocus &e) override {
void on(event::Deselect &e) override {
if (0 <= focusCc && focusCc < 128) {
module->learnedCcs[id] = focusCc;
}
module->learningId = -1;
}

void onText(EventText &e) override {
void on(event::SelectText &e) override {
char c = e.codepoint;
if ('0' <= c && c <= '9') {
if (focusCc < 0)
focusCc = 0;
focusCc = focusCc * 10 + (c - '0');
}
e.consumed = true;
e.target = this;
}

void onKey(EventKey &e) override {
if (gFocusedWidget == this) {
void on(event::SelectKey &e) override {
if (gSelectedWidget == this) {
if (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) {
EventDefocus eDefocus;
onDefocus(eDefocus);
gFocusedWidget = NULL;
e.consumed = true;
event::Deselect eDeselect;
handleEvent(eDeselect);
gSelectedWidget = NULL;
e.target = this;
}
}
}
@@ -184,29 +184,29 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget {
MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/MIDICCToCVInterface.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 4));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 5));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 6));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 7));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 8));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 9));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 10));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 11));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 12));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 13));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15));
MidiCcWidget *midiWidget = Widget::create<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 4));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 5));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 6));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 7));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 8));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 9));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 10));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 11));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 12));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 13));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 14));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 15));
MidiCcWidget *midiWidget = createWidget<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339)));
midiWidget->module = module;
midiWidget->box.size = mm2px(Vec(44, 54.667));
midiWidget->midiIO = &module->midiInput;
@@ -216,4 +216,4 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget {
};


Model *modelMIDICCToCVInterface = Model::create<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG);
Model *modelMIDICCToCVInterface = createModel<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG);

+ 23
- 23
src/Core/MIDIToCVInterface.cpp View File

@@ -261,25 +261,25 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {
MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/MIDIToCVInterface.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::CV_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::GATE_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::VELOCITY_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::AFTERTOUCH_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::PITCH_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::MOD_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::RETRIGGER_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_1_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_2_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::START_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::STOP_OUTPUT));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CONTINUE_OUTPUT));
MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), module, MIDIToCVInterface::CV_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), module, MIDIToCVInterface::GATE_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), module, MIDIToCVInterface::VELOCITY_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), module, MIDIToCVInterface::AFTERTOUCH_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), module, MIDIToCVInterface::PITCH_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), module, MIDIToCVInterface::MOD_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), module, MIDIToCVInterface::RETRIGGER_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), module, MIDIToCVInterface::CLOCK_1_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), module, MIDIToCVInterface::CLOCK_2_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), module, MIDIToCVInterface::START_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 108.144)), module, MIDIToCVInterface::STOP_OUTPUT));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), module, MIDIToCVInterface::CONTINUE_OUTPUT));
MidiWidget *midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
midiWidget->box.size = mm2px(Vec(33.840, 28));
midiWidget->midiIO = &module->midiInput;
addChild(midiWidget);
@@ -292,7 +292,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {
MIDIToCVInterface *module;
int index;
int division;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
module->divisions[index] = division;
}
};
@@ -305,7 +305,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {
std::vector<int> divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1};
std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
for (size_t i = 0; i < divisions.size(); i++) {
ClockDivisionItem *item = MenuItem::create<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i]));
ClockDivisionItem *item = createMenuItem<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i]));
item->module = module;
item->index = index;
item->division = divisions[i];
@@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {

menu->addChild(construct<MenuLabel>());
for (int i = 0; i < 2; i++) {
ClockItem *item = MenuItem::create<ClockItem>(string::stringf("CLK %d rate", i + 1));
ClockItem *item = createMenuItem<ClockItem>(string::stringf("CLK %d rate", i + 1));
item->module = module;
item->index = i;
menu->addChild(item);
@@ -326,4 +326,4 @@ struct MIDIToCVInterfaceWidget : ModuleWidget {
};


Model *modelMIDIToCVInterface = Model::create<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG);
Model *modelMIDIToCVInterface = createModel<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG);

+ 32
- 32
src/Core/MIDITriggerToCVInterface.cpp View File

@@ -171,17 +171,17 @@ struct MidiTrigChoice : GridChoice {
text = string::stringf("%s%d", noteNames[semi], oct);
color.a = 1.0;

if (gFocusedWidget == this)
gFocusedWidget = NULL;
if (gSelectedWidget == this)
gSelectedWidget = NULL;
}
}

void onFocus(EventFocus &e) override {
e.consumed = true;
void on(event::Select &e) override {
e.target = this;
module->learningId = id;
}

void onDefocus(EventDefocus &e) override {
void on(event::Deselect &e) override {
module->learningId = -1;
}
};
@@ -201,29 +201,29 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget {
MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/MIDITriggerToCVInterface.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 4));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 5));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 6));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 7));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 8));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 9));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 10));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 11));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 12));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 13));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15));
MidiTrigWidget *midiWidget = Widget::create<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 4));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 5));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 6));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 7));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 8));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 9));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 10));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 11));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 12));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 13));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15));
MidiTrigWidget *midiWidget = createWidget<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339)));
midiWidget->module = module;
midiWidget->box.size = mm2px(Vec(44, 54.667));
midiWidget->midiIO = &module->midiInput;
@@ -236,17 +236,17 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget {

struct VelocityItem : MenuItem {
MIDITriggerToCVInterface *module;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
module->velocity ^= true;
}
};

menu->addChild(MenuEntry::create());
VelocityItem *velocityItem = MenuItem::create<VelocityItem>("Velocity", CHECKMARK(module->velocity));
menu->addChild(new MenuEntry());
VelocityItem *velocityItem = createMenuItem<VelocityItem>("Velocity", CHECKMARK(module->velocity));
velocityItem->module = module;
menu->addChild(velocityItem);
}
};


Model *modelMIDITriggerToCVInterface = Model::create<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG);
Model *modelMIDITriggerToCVInterface = createModel<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG);

+ 6
- 6
src/Core/Notes.cpp View File

@@ -10,12 +10,12 @@ struct NotesWidget : ModuleWidget {
NotesWidget(Module *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/Notes.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

textField = Widget::create<LedDisplayTextField>(mm2px(Vec(3.39962, 14.8373)));
textField = createWidget<LedDisplayTextField>(mm2px(Vec(3.39962, 14.8373)));
textField->box.size = mm2px(Vec(74.480, 102.753));
textField->multiline = true;
addChild(textField);
@@ -41,4 +41,4 @@ struct NotesWidget : ModuleWidget {
};


Model *modelNotes = Model::create<Module, NotesWidget>("Core", "Notes", "Notes", BLANK_TAG);
Model *modelNotes = createModel<Module, NotesWidget>("Core", "Notes", "Notes", BLANK_TAG);

+ 30
- 28
src/Core/QuadMIDIToCVInterface.cpp View File

@@ -303,29 +303,29 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) {
setPanel(SVG::load(asset::global("res/Core/QuadMIDIToCVInterface.svg")));

addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3));
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3));
MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.4009969, 14.837336)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), module, QuadMIDIToCVInterface::CV_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), module, QuadMIDIToCVInterface::CV_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), module, QuadMIDIToCVInterface::CV_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), module, QuadMIDIToCVInterface::CV_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3));
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3));
MidiWidget *midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.4009969, 14.837336)));
midiWidget->box.size = mm2px(Vec(44, 28));
midiWidget->midiIO = &module->midiInput;
addChild(midiWidget);
@@ -337,17 +337,19 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
struct PolyphonyItem : MenuItem {
QuadMIDIToCVInterface *module;
QuadMIDIToCVInterface::PolyMode polyMode;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
module->polyMode = polyMode;
module->onReset();
}
};

menu->addChild(MenuEntry::create());
menu->addChild(MenuLabel::create("Polyphony mode"));
menu->addChild(new MenuEntry());
menu->addChild(createMenuLabel("Polyphony mode"));

auto addPolyphonyItem = [&](QuadMIDIToCVInterface::PolyMode polyMode, std::string name) {
PolyphonyItem *item = MenuItem::create<PolyphonyItem>(name, CHECKMARK(module->polyMode == polyMode));
PolyphonyItem *item = new PolyphonyItem();
item->text = name;
item->rightText = CHECKMARK(module->polyMode == polyMode);
item->module = module;
item->polyMode = polyMode;
menu->addChild(item);
@@ -362,5 +364,5 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
};


Model *modelQuadMIDIToCVInterface = Model::create<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG);
Model *modelQuadMIDIToCVInterface = createModel<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG);


+ 22
- 21
src/app/AudioWidget.cpp View File

@@ -1,5 +1,6 @@
#include "app.hpp"
#include "audio.hpp"
#include "helpers.hpp"


namespace rack {
@@ -8,16 +9,16 @@ namespace rack {
struct AudioDriverItem : MenuItem {
AudioIO *audioIO;
int driver;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
audioIO->setDriver(driver);
}
};

struct AudioDriverChoice : LedDisplayChoice {
AudioWidget *audioWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio driver"));
menu->addChild(createMenuLabel("Audio driver"));
for (int driver : audioWidget->audioIO->getDrivers()) {
AudioDriverItem *item = new AudioDriverItem();
item->audioIO = audioWidget->audioIO;
@@ -37,7 +38,7 @@ struct AudioDeviceItem : MenuItem {
AudioIO *audioIO;
int device;
int offset;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
audioIO->setDevice(device, offset);
}
};
@@ -47,9 +48,9 @@ struct AudioDeviceChoice : LedDisplayChoice {
/** Prevents devices with a ridiculous number of channels from being displayed */
int maxTotalChannels = 128;

void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device"));
menu->addChild(createMenuLabel("Audio device"));
int deviceCount = audioWidget->audioIO->getDeviceCount();
{
AudioDeviceItem *item = new AudioDeviceItem();
@@ -88,19 +89,19 @@ struct AudioDeviceChoice : LedDisplayChoice {
struct AudioSampleRateItem : MenuItem {
AudioIO *audioIO;
int sampleRate;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
audioIO->setSampleRate(sampleRate);
}
};

struct AudioSampleRateChoice : LedDisplayChoice {
AudioWidget *audioWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Sample rate"));
menu->addChild(createMenuLabel("Sample rate"));
std::vector<int> sampleRates = audioWidget->audioIO->getSampleRates();
if (sampleRates.empty()) {
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)"));
menu->addChild(createMenuLabel("(Locked by device)"));
}
for (int sampleRate : sampleRates) {
AudioSampleRateItem *item = new AudioSampleRateItem();
@@ -120,19 +121,19 @@ struct AudioSampleRateChoice : LedDisplayChoice {
struct AudioBlockSizeItem : MenuItem {
AudioIO *audioIO;
int blockSize;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
audioIO->setBlockSize(blockSize);
}
};

struct AudioBlockSizeChoice : LedDisplayChoice {
AudioWidget *audioWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Block size"));
menu->addChild(createMenuLabel("Block size"));
std::vector<int> blockSizes = audioWidget->audioIO->getBlockSizes();
if (blockSizes.empty()) {
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)"));
menu->addChild(createMenuLabel("(Locked by device)"));
}
for (int blockSize : blockSizes) {
AudioBlockSizeItem *item = new AudioBlockSizeItem();
@@ -155,34 +156,34 @@ AudioWidget::AudioWidget() {

math::Vec pos = math::Vec();

AudioDriverChoice *driverChoice = Widget::create<AudioDriverChoice>(pos);
AudioDriverChoice *driverChoice = createWidget<AudioDriverChoice>(pos);
driverChoice->audioWidget = this;
addChild(driverChoice);
pos = driverChoice->box.getBottomLeft();
this->driverChoice = driverChoice;

this->driverSeparator = Widget::create<LedDisplaySeparator>(pos);
this->driverSeparator = createWidget<LedDisplaySeparator>(pos);
addChild(this->driverSeparator);

AudioDeviceChoice *deviceChoice = Widget::create<AudioDeviceChoice>(pos);
AudioDeviceChoice *deviceChoice = createWidget<AudioDeviceChoice>(pos);
deviceChoice->audioWidget = this;
addChild(deviceChoice);
pos = deviceChoice->box.getBottomLeft();
this->deviceChoice = deviceChoice;

this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos);
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos);
addChild(this->deviceSeparator);

AudioSampleRateChoice *sampleRateChoice = Widget::create<AudioSampleRateChoice>(pos);
AudioSampleRateChoice *sampleRateChoice = createWidget<AudioSampleRateChoice>(pos);
sampleRateChoice->audioWidget = this;
addChild(sampleRateChoice);
this->sampleRateChoice = sampleRateChoice;

this->sampleRateSeparator = Widget::create<LedDisplaySeparator>(pos);
this->sampleRateSeparator = createWidget<LedDisplaySeparator>(pos);
this->sampleRateSeparator->box.size.y = this->sampleRateChoice->box.size.y;
addChild(this->sampleRateSeparator);

AudioBlockSizeChoice *bufferSizeChoice = Widget::create<AudioBlockSizeChoice>(pos);
AudioBlockSizeChoice *bufferSizeChoice = createWidget<AudioBlockSizeChoice>(pos);
bufferSizeChoice->audioWidget = this;
addChild(bufferSizeChoice);
this->bufferSizeChoice = bufferSizeChoice;


+ 4
- 4
src/app/Knob.cpp View File

@@ -15,13 +15,13 @@ Knob::Knob() {
smooth = true;
}

void Knob::onDragStart(EventDragStart &e) {
void Knob::on(event::DragStart &e) {
windowCursorLock();
dragValue = value;
randomizable = false;
}

void Knob::onDragMove(EventDragMove &e) {
void Knob::on(event::DragMove &e) {
float range;
if (std::isfinite(minValue) && std::isfinite(maxValue)) {
range = maxValue - minValue;
@@ -30,7 +30,7 @@ void Knob::onDragMove(EventDragMove &e) {
// Continuous encoders scale as if their limits are +/-1
range = 1.f - (-1.f);
}
float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed * range;
float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range;

// Drag slower if Mod is held
if (windowIsModPressed())
@@ -43,7 +43,7 @@ void Knob::onDragMove(EventDragMove &e) {
setValue(dragValue);
}

void Knob::onDragEnd(EventDragEnd &e) {
void Knob::on(event::DragEnd &e) {
windowCursorUnlock();
randomizable = true;
}


+ 5
- 6
src/app/LedDisplay.cpp View File

@@ -52,11 +52,10 @@ void LedDisplayChoice::draw(NVGcontext *vg) {
nvgResetScissor(vg);
}

void LedDisplayChoice::onMouseDown(EventMouseDown &e) {
if (e.button == 0 || e.button == 1) {
EventAction eAction;
onAction(eAction);
e.consumed = true;
void LedDisplayChoice::on(event::Button &e) {
if (e.action == GLFW_PRESS && (e.button == GLFW_MOUSE_BUTTON_LEFT || e.button == GLFW_MOUSE_BUTTON_RIGHT)) {
event::Action eAction;
handleEvent(eAction);
e.target = this;
}
}
@@ -85,7 +84,7 @@ void LedDisplayTextField::draw(NVGcontext *vg) {
NVGcolor highlightColor = color;
highlightColor.a = 0.5;
int begin = std::min(cursor, selection);
int end = (this == gFocusedWidget) ? std::max(cursor, selection) : -1;
int end = (this == gSelectedWidget) ? std::max(cursor, selection) : -1;
bndIconLabelCaret(vg, textOffset.x, textOffset.y,
box.size.x - 2*textOffset.x, box.size.y - 2*textOffset.y,
-1, color, 12, text.c_str(), highlightColor, begin, end);


+ 15
- 14
src/app/MidiWidget.cpp View File

@@ -1,5 +1,6 @@
#include "app.hpp"
#include "midi.hpp"
#include "helpers.hpp"


namespace rack {
@@ -8,16 +9,16 @@ namespace rack {
struct MidiDriverItem : MenuItem {
MidiIO *midiIO;
int driverId;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
midiIO->setDriverId(driverId);
}
};

struct MidiDriverChoice : LedDisplayChoice {
MidiWidget *midiWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI driver"));
menu->addChild(createMenuLabel("MIDI driver"));
for (int driverId : midiWidget->midiIO->getDriverIds()) {
MidiDriverItem *item = new MidiDriverItem();
item->midiIO = midiWidget->midiIO;
@@ -42,16 +43,16 @@ struct MidiDriverChoice : LedDisplayChoice {
struct MidiDeviceItem : MenuItem {
MidiIO *midiIO;
int deviceId;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
midiIO->setDeviceId(deviceId);
}
};

struct MidiDeviceChoice : LedDisplayChoice {
MidiWidget *midiWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI device"));
menu->addChild(createMenuLabel("MIDI device"));
{
MidiDeviceItem *item = new MidiDeviceItem();
item->midiIO = midiWidget->midiIO;
@@ -84,16 +85,16 @@ struct MidiDeviceChoice : LedDisplayChoice {
struct MidiChannelItem : MenuItem {
MidiIO *midiIO;
int channel;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
midiIO->channel = channel;
}
};

struct MidiChannelChoice : LedDisplayChoice {
MidiWidget *midiWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI channel"));
menu->addChild(createMenuLabel("MIDI channel"));
for (int channel = -1; channel < 16; channel++) {
MidiChannelItem *item = new MidiChannelItem();
item->midiIO = midiWidget->midiIO;
@@ -114,25 +115,25 @@ MidiWidget::MidiWidget() {

math::Vec pos = math::Vec();

MidiDriverChoice *driverChoice = Widget::create<MidiDriverChoice>(pos);
MidiDriverChoice *driverChoice = createWidget<MidiDriverChoice>(pos);
driverChoice->midiWidget = this;
addChild(driverChoice);
pos = driverChoice->box.getBottomLeft();
this->driverChoice = driverChoice;

this->driverSeparator = Widget::create<LedDisplaySeparator>(pos);
this->driverSeparator = createWidget<LedDisplaySeparator>(pos);
addChild(this->driverSeparator);

MidiDeviceChoice *deviceChoice = Widget::create<MidiDeviceChoice>(pos);
MidiDeviceChoice *deviceChoice = createWidget<MidiDeviceChoice>(pos);
deviceChoice->midiWidget = this;
addChild(deviceChoice);
pos = deviceChoice->box.getBottomLeft();
this->deviceChoice = deviceChoice;

this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos);
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos);
addChild(this->deviceSeparator);

MidiChannelChoice *channelChoice = Widget::create<MidiChannelChoice>(pos);
MidiChannelChoice *channelChoice = createWidget<MidiChannelChoice>(pos);
channelChoice->midiWidget = this;
addChild(channelChoice);
this->channelChoice = channelChoice;


+ 40
- 39
src/app/ModuleBrowser.cpp View File

@@ -1,6 +1,7 @@
#include "app.hpp"
#include "plugin.hpp"
#include "window.hpp"
#include "helpers.hpp"
#include <set>
#include <algorithm>

@@ -45,7 +46,7 @@ static bool isModelMatch(Model *model, std::string search) {
struct FavoriteRadioButton : RadioButton {
Model *model = NULL;

void onAction(EventAction &e) override;
void on(event::Action &e) override;
};


@@ -56,7 +57,7 @@ struct SeparatorItem : OpaqueWidget {

void setText(std::string text) {
clearChildren();
Label *label = Widget::create<Label>(math::Vec(0, 12 + itemMargin));
Label *label = createWidget<Label>(math::Vec(0, 12 + itemMargin));
label->text = text;
label->fontSize = 20;
label->color.a *= 0.5;
@@ -78,19 +79,19 @@ struct BrowserListItem : OpaqueWidget {
Widget::draw(vg);
}

void onDragStart(EventDragStart &e) override;
void on(event::DragStart &e) override;

void onDragDrop(EventDragDrop &e) override {
void on(event::DragDrop &e) override {
if (e.origin != this)
return;
doAction();
}

void doAction() {
EventAction eAction;
eAction.consumed = true;
onAction(eAction);
if (eAction.consumed) {
event::Action eAction;
eAction.target = this;
handleEvent(eAction);
if (eAction.target) {
// deletes `this`
gScene->setOverlay(NULL);
}
@@ -107,7 +108,7 @@ struct ModelItem : BrowserListItem {
assert(model);
this->model = model;

FavoriteRadioButton *favoriteButton = Widget::create<FavoriteRadioButton>(math::Vec(8, itemMargin));
FavoriteRadioButton *favoriteButton = createWidget<FavoriteRadioButton>(math::Vec(8, itemMargin));
favoriteButton->box.size.x = 20;
favoriteButton->label = "★";
addChild(favoriteButton);
@@ -118,11 +119,11 @@ struct ModelItem : BrowserListItem {
favoriteButton->setValue(1);
favoriteButton->model = model;

Label *nameLabel = Widget::create<Label>(favoriteButton->box.getTopRight());
Label *nameLabel = createWidget<Label>(favoriteButton->box.getTopRight());
nameLabel->text = model->name;
addChild(nameLabel);

pluginLabel = Widget::create<Label>(math::Vec(0, itemMargin));
pluginLabel = createWidget<Label>(math::Vec(0, itemMargin));
pluginLabel->alignment = Label::RIGHT_ALIGNMENT;
pluginLabel->text = model->plugin->slug + " " + model->plugin->version;
pluginLabel->color.a = 0.5;
@@ -135,7 +136,7 @@ struct ModelItem : BrowserListItem {
pluginLabel->box.size.x = box.size.x - BND_SCROLLBAR_WIDTH;
}

void onAction(EventAction &e) override {
void on(event::Action &e) override {
ModuleWidget *moduleWidget = model->createModuleWidget();
if (!moduleWidget)
return;
@@ -153,7 +154,7 @@ struct AuthorItem : BrowserListItem {
void setAuthor(std::string author) {
clearChildren();
this->author = author;
Label *authorLabel = Widget::create<Label>(math::Vec(0, 0 + itemMargin));
Label *authorLabel = createWidget<Label>(math::Vec(0, 0 + itemMargin));
if (author.empty())
authorLabel->text = "Show all modules";
else
@@ -161,7 +162,7 @@ struct AuthorItem : BrowserListItem {
addChild(authorLabel);
}

void onAction(EventAction &e) override;
void on(event::Action &e) override;
};


@@ -171,7 +172,7 @@ struct TagItem : BrowserListItem {
void setTag(ModelTag tag) {
clearChildren();
this->tag = tag;
Label *tagLabel = Widget::create<Label>(math::Vec(0, 0 + itemMargin));
Label *tagLabel = createWidget<Label>(math::Vec(0, 0 + itemMargin));
if (tag == NO_TAG)
tagLabel->text = "Show all tags";
else
@@ -179,18 +180,18 @@ struct TagItem : BrowserListItem {
addChild(tagLabel);
}

void onAction(EventAction &e) override;
void on(event::Action &e) override;
};


struct ClearFilterItem : BrowserListItem {
ClearFilterItem() {
Label *label = Widget::create<Label>(math::Vec(0, 0 + itemMargin));
Label *label = createWidget<Label>(math::Vec(0, 0 + itemMargin));
label->text = "Back";
addChild(label);
}

void onAction(EventAction &e) override;
void on(event::Action &e) override;
};


@@ -270,8 +271,8 @@ struct ModuleBrowser;

struct SearchModuleField : TextField {
ModuleBrowser *moduleBrowser;
void onTextChange() override;
void onKey(EventKey &e) override;
void on(event::Change &e) override;
void on(event::SelectKey &e) override;
};


@@ -428,7 +429,7 @@ struct ModuleBrowser : OpaqueWidget {
moduleScroll->box.size.y = std::min(box.size.y - moduleScroll->box.pos.y, moduleList->box.size.y);
box.size.y = std::min(box.size.y, moduleScroll->box.getBottomRight().y);

gFocusedWidget = searchField;
gSelectedWidget = searchField;
Widget::step();
}
};
@@ -436,31 +437,31 @@ struct ModuleBrowser : OpaqueWidget {

// Implementations of inline methods above

void AuthorItem::onAction(EventAction &e) {
void AuthorItem::on(event::Action &e) {
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>();
sAuthorFilter = author;
moduleBrowser->clearSearch();
moduleBrowser->refreshSearch();
e.consumed = false;
e.target = this;
}

void TagItem::onAction(EventAction &e) {
void TagItem::on(event::Action &e) {
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>();
sTagFilter = tag;
moduleBrowser->clearSearch();
moduleBrowser->refreshSearch();
e.consumed = false;
e.target = this;
}

void ClearFilterItem::onAction(EventAction &e) {
void ClearFilterItem::on(event::Action &e) {
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>();
sAuthorFilter = "";
sTagFilter = NO_TAG;
moduleBrowser->refreshSearch();
e.consumed = false;
e.target = this;
}

void FavoriteRadioButton::onAction(EventAction &e) {
void FavoriteRadioButton::on(event::Action &e) {
if (!model)
return;
if (value) {
@@ -477,56 +478,56 @@ void FavoriteRadioButton::onAction(EventAction &e) {
moduleBrowser->refreshSearch();
}

void BrowserListItem::onDragStart(EventDragStart &e) {
void BrowserListItem::on(event::DragStart &e) {
BrowserList *list = dynamic_cast<BrowserList*>(parent);
if (list) {
list->selectItem(this);
}
}

void SearchModuleField::onTextChange() {
void SearchModuleField::on(event::Change &e) {
moduleBrowser->refreshSearch();
}

void SearchModuleField::onKey(EventKey &e) {
void SearchModuleField::on(event::SelectKey &e) {
switch (e.key) {
case GLFW_KEY_ESCAPE: {
gScene->setOverlay(NULL);
e.consumed = true;
e.target = this;
return;
} break;
case GLFW_KEY_UP: {
moduleBrowser->moduleList->incrementSelection(-1);
moduleBrowser->moduleList->scrollSelected();
e.consumed = true;
e.target = this;
} break;
case GLFW_KEY_DOWN: {
moduleBrowser->moduleList->incrementSelection(1);
moduleBrowser->moduleList->scrollSelected();
e.consumed = true;
e.target = this;
} break;
case GLFW_KEY_PAGE_UP: {
moduleBrowser->moduleList->incrementSelection(-5);
moduleBrowser->moduleList->scrollSelected();
e.consumed = true;
e.target = this;
} break;
case GLFW_KEY_PAGE_DOWN: {
moduleBrowser->moduleList->incrementSelection(5);
moduleBrowser->moduleList->scrollSelected();
e.consumed = true;
e.target = this;
} break;
case GLFW_KEY_ENTER: {
BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem();
if (item) {
item->doAction();
e.consumed = true;
e.target = this;
return;
}
} break;
}

if (!e.consumed) {
TextField::onKey(e);
if (!e.target) {
TextField::on(e);
}
}



+ 36
- 44
src/app/ModuleWidget.cpp View File

@@ -330,93 +330,86 @@ void ModuleWidget::drawShadow(NVGcontext *vg) {
nvgFill(vg);
}

void ModuleWidget::onMouseDown(EventMouseDown &e) {
Widget::onMouseDown(e);
if (e.consumed)
return;

if (e.button == 1) {
createContextMenu();
void ModuleWidget::on(event::Hover &e) {
OpaqueWidget::on(e);

// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget.
if (glfwGetKey(gWindow, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
if (!windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->deleteModule(this);
delete this;
// e.target = this;
return;
}
}
e.consumed = true;
e.target = this;
}

void ModuleWidget::onMouseMove(EventMouseMove &e) {
OpaqueWidget::onMouseMove(e);

// Don't delete the ModuleWidget if a TextField is focused
if (!gFocusedWidget) {
// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget.
if (glfwGetKey(gWindow, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
if (!windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->deleteModule(this);
this->finalizeEvents();
delete this;
e.consumed = true;
return;
}
void ModuleWidget::on(event::Button &e) {
OpaqueWidget::on(e);
if (e.target == this) {
if (e.button == 1) {
createContextMenu();
}
}
}

void ModuleWidget::onHoverKey(EventHoverKey &e) {
void ModuleWidget::on(event::HoverKey &e) {
switch (e.key) {
case GLFW_KEY_I: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
reset();
e.consumed = true;
e.target = this;
return;
}
} break;
case GLFW_KEY_R: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
randomize();
e.consumed = true;
e.target = this;
return;
}
} break;
case GLFW_KEY_C: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
copyClipboard();
e.consumed = true;
e.target = this;
return;
}
} break;
case GLFW_KEY_V: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
pasteClipboard();
e.consumed = true;
e.target = this;
return;
}
} break;
case GLFW_KEY_D: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->cloneModule(this);
e.consumed = true;
e.target = this;
return;
}
} break;
case GLFW_KEY_U: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
disconnect();
e.consumed = true;
e.target = this;
return;
}
} break;
}

Widget::onHoverKey(e);
OpaqueWidget::on(e);
}

void ModuleWidget::onDragStart(EventDragStart &e) {
void ModuleWidget::on(event::DragStart &e) {
dragPos = gRackWidget->lastMousePos.minus(box.pos);
}

void ModuleWidget::onDragEnd(EventDragEnd &e) {
void ModuleWidget::on(event::DragEnd &e) {
}

void ModuleWidget::onDragMove(EventDragMove &e) {
void ModuleWidget::on(event::DragMove &e) {
if (!gRackWidget->lockModules) {
math::Rect newBox = box;
newBox.pos = gRackWidget->lastMousePos.minus(dragPos);
@@ -427,65 +420,64 @@ void ModuleWidget::onDragMove(EventDragMove &e) {

struct ModuleDisconnectItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->disconnect();
}
};

struct ModuleResetItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->reset();
}
};

struct ModuleRandomizeItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->randomize();
}
};

struct ModuleCopyItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->copyClipboard();
}
};

struct ModulePasteItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->pasteClipboard();
}
};

struct ModuleSaveItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->saveDialog();
}
};

struct ModuleLoadItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
moduleWidget->loadDialog();
}
};

struct ModuleCloneItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->cloneModule(moduleWidget);
}
};

struct ModuleDeleteItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->deleteModule(moduleWidget);
moduleWidget->finalizeEvents();
delete moduleWidget;
}
};


+ 2
- 4
src/app/MomentarySwitch.cpp View File

@@ -4,13 +4,11 @@
namespace rack {


void MomentarySwitch::onDragStart(EventDragStart &e) {
void MomentarySwitch::on(event::DragStart &e) {
setValue(maxValue);
EventAction eAction;
onAction(eAction);
}

void MomentarySwitch::onDragEnd(EventDragEnd &e) {
void MomentarySwitch::on(event::DragEnd &e) {
setValue(minValue);
}



+ 7
- 6
src/app/ParamWidget.cpp View File

@@ -36,15 +36,16 @@ void ParamWidget::randomize() {
}
}

void ParamWidget::onMouseDown(EventMouseDown &e) {
if (e.button == 1) {
reset();
void ParamWidget::on(event::Button &e) {
OpaqueWidget::on(e);
if (e.target == this) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
reset();
}
}
e.consumed = true;
e.target = this;
}

void ParamWidget::onChange(EventChange &e) {
void ParamWidget::on(event::Change &e) {
if (!module)
return;



+ 10
- 9
src/app/PluginManagerWidget.cpp View File

@@ -3,6 +3,7 @@
#include "app.hpp"
#include "plugin.hpp"
#include "window.hpp"
#include "helpers.hpp"
#include "osdialog.h"


@@ -10,7 +11,7 @@ namespace rack {


struct RegisterButton : Button {
void onAction(EventAction &e) override {
void on(event::Action &e) override {
std::thread t([&]() {
system::openBrowser("https://vcvrack.com/");
});
@@ -22,7 +23,7 @@ struct RegisterButton : Button {
struct LogInButton : Button {
TextField *emailField;
TextField *passwordField;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
std::thread t(pluginLogIn, emailField->text, passwordField->text);
t.detach();
passwordField->text = "";
@@ -38,7 +39,7 @@ struct StatusLabel : Label {


struct ManageButton : Button {
void onAction(EventAction &e) override {
void on(event::Action &e) override {
std::thread t([&]() {
system::openBrowser("https://vcvrack.com/plugins.html");
});
@@ -84,7 +85,7 @@ struct SyncButton : Button {
nvgStroke(vg);
}
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
available = false;
std::thread t([this]() {
if (pluginSync(false))
@@ -96,7 +97,7 @@ struct SyncButton : Button {


struct LogOutButton : Button {
void onAction(EventAction &e) override {
void on(event::Action &e) override {
pluginLogOut();
}
};
@@ -114,7 +115,7 @@ struct DownloadProgressBar : ProgressBar {


struct CancelButton : Button {
void onAction(EventAction &e) override {
void on(event::Action &e) override {
pluginCancelDownload();
}
};
@@ -124,7 +125,7 @@ PluginManagerWidget::PluginManagerWidget() {
box.size.y = BND_WIDGET_HEIGHT;

{
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0));
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0));
layout->spacing = 5;
loginWidget = layout;

@@ -157,7 +158,7 @@ PluginManagerWidget::PluginManagerWidget() {
}

{
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0));
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0));
layout->spacing = 5;
manageWidget = layout;

@@ -180,7 +181,7 @@ PluginManagerWidget::PluginManagerWidget() {
}

{
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0));
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0));
layout->spacing = 5;
downloadWidget = layout;



+ 9
- 10
src/app/Port.cpp View File

@@ -49,20 +49,19 @@ void Port::draw(NVGcontext *vg) {
}
}

void Port::onMouseDown(EventMouseDown &e) {
if (e.button == 1) {
void Port::on(event::Button &e) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
gRackWidget->wireContainer->removeTopWire(this);

// HACK
// Update hovered*Port of active wire if applicable
EventDragEnter e;
onDragEnter(e);
event::DragEnter eDragEnter;
on(eDragEnter);
}
e.consumed = true;
e.target = this;
}

void Port::onDragStart(EventDragStart &e) {
void Port::on(event::DragStart &e) {
// Try to grab wire on top of stack
WireWidget *wire = gRackWidget->wireContainer->getTopWire(this);
if (type == OUTPUT && windowIsModPressed()) {
@@ -88,16 +87,16 @@ void Port::onDragStart(EventDragStart &e) {
gRackWidget->wireContainer->setActiveWire(wire);
}

void Port::onDragEnd(EventDragEnd &e) {
void Port::on(event::DragEnd &e) {
// FIXME
// If the source Port is deleted, this will be called, removing the cable
gRackWidget->wireContainer->commitActiveWire();
}

void Port::onDragDrop(EventDragDrop &e) {
void Port::on(event::DragDrop &e) {
}

void Port::onDragEnter(EventDragEnter &e) {
void Port::on(event::DragEnter &e) {
// Reject ports if this is an input port and something is already plugged into it
if (type == INPUT) {
WireWidget *topWire = gRackWidget->wireContainer->getTopWire(this);
@@ -114,7 +113,7 @@ void Port::onDragEnter(EventDragEnter &e) {
}
}

void Port::onDragLeave(EventDragEnter &e) {
void Port::on(event::DragLeave &e) {
WireWidget *activeWire = gRackWidget->wireContainer->activeWire;
if (activeWire) {
if (type == INPUT)


+ 18
- 18
src/app/RackScene.cpp View File

@@ -55,53 +55,53 @@ void RackScene::draw(NVGcontext *vg) {
Scene::draw(vg);
}

void RackScene::onHoverKey(EventHoverKey &e) {
Widget::onHoverKey(e);
void RackScene::on(event::HoverKey &e) {
Scene::on(e);

if (!e.consumed) {
if (!e.target) {
switch (e.key) {
case GLFW_KEY_N: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->reset();
e.consumed = true;
e.target = this;
}
} break;
case GLFW_KEY_Q: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
windowClose();
e.consumed = true;
e.target = this;
}
} break;
case GLFW_KEY_O: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->loadDialog();
e.consumed = true;
e.target = this;
}
if (windowIsModPressed() && windowIsShiftPressed()) {
gRackWidget->revert();
e.consumed = true;
e.target = this;
}
} break;
case GLFW_KEY_S: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->saveDialog();
e.consumed = true;
e.target = this;
}
if (windowIsModPressed() && windowIsShiftPressed()) {
gRackWidget->saveAsDialog();
e.consumed = true;
e.target = this;
}
} break;
case GLFW_KEY_V: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->pastePresetClipboard();
e.consumed = true;
e.target = this;
}
} break;
case GLFW_KEY_ENTER:
case GLFW_KEY_KP_ENTER: {
appModuleBrowserCreate();
e.consumed = true;
e.target = this;
} break;
case GLFW_KEY_F11: {
windowSetFullScreen(!windowGetFullScreen());
@@ -110,17 +110,17 @@ void RackScene::onHoverKey(EventHoverKey &e) {
}
}

void RackScene::onPathDrop(EventPathDrop &e) {
void RackScene::on(event::PathDrop &e) {
if (e.paths.size() >= 1) {
const std::string &firstPath = e.paths.front();
if (string::extension(firstPath) == "vcv") {
gRackWidget->load(firstPath);
e.consumed = true;
const std::string &path = e.paths[0];
if (string::extension(path) == "vcv") {
gRackWidget->load(path);
e.target = this;
}
}

if (!e.consumed)
Scene::onPathDrop(e);
if (!e.target)
Scene::on(e);
}




+ 10
- 13
src/app/RackWidget.cpp View File

@@ -497,26 +497,23 @@ void RackWidget::draw(NVGcontext *vg) {
Widget::draw(vg);
}

void RackWidget::onMouseMove(EventMouseMove &e) {
OpaqueWidget::onMouseMove(e);
void RackWidget::on(event::Hover &e) {
OpaqueWidget::on(e);
lastMousePos = e.pos;
}

void RackWidget::onMouseDown(EventMouseDown &e) {
Widget::onMouseDown(e);
if (e.consumed)
return;

if (e.button == 1) {
appModuleBrowserCreate();
void RackWidget::on(event::Button &e) {
OpaqueWidget::on(e);
if (e.target == this) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
appModuleBrowserCreate();
}
}
e.consumed = true;
e.target = this;
}

void RackWidget::onZoom(EventZoom &e) {
void RackWidget::on(event::Zoom &e) {
rails->box.size = math::Vec();
Widget::onZoom(e);
EventWidget::on(e);
}




+ 4
- 4
src/app/SVGButton.cpp View File

@@ -16,14 +16,14 @@ void SVGButton::setSVGs(std::shared_ptr<SVG> defaultSVG, std::shared_ptr<SVG> ac
this->activeSVG = activeSVG ? activeSVG : defaultSVG;
}

void SVGButton::onDragStart(EventDragStart &e) {
EventAction eAction;
onAction(eAction);
void SVGButton::on(event::DragStart &e) {
event::Action eAction;
handleEvent(eAction);
sw->setSVG(activeSVG);
dirty = true;
}

void SVGButton::onDragEnd(EventDragEnd &e) {
void SVGButton::on(event::DragEnd &e) {
sw->setSVG(defaultSVG);
dirty = true;
}


+ 2
- 2
src/app/SVGKnob.cpp View File

@@ -46,9 +46,9 @@ void SVGKnob::step() {
FramebufferWidget::step();
}

void SVGKnob::onChange(EventChange &e) {
void SVGKnob::on(event::Change &e) {
dirty = true;
Knob::onChange(e);
ParamWidget::on(e);
}




+ 0
- 42
src/app/SVGPanel.cpp View File

@@ -1,42 +0,0 @@
#include "app.hpp"
#include "window.hpp"


namespace rack {


struct PanelBorder : TransparentWidget {
void draw(NVGcontext *vg) override {
NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
nvgBeginPath(vg);
nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
nvgStrokeColor(vg, borderColor);
nvgStrokeWidth(vg, 1.0);
nvgStroke(vg);
}
};


void SVGPanel::step() {
if (math::isNear(gPixelRatio, 1.0)) {
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
oversample = 2.0;
}
FramebufferWidget::step();
}

void SVGPanel::setBackground(std::shared_ptr<SVG> svg) {
SVGWidget *sw = new SVGWidget();
sw->setSVG(svg);
addChild(sw);

// Set size
box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);

PanelBorder *pb = new PanelBorder();
pb->box.size = box.size;
addChild(pb);
}


} // namespace rack

+ 2
- 2
src/app/SVGSlider.cpp View File

@@ -30,9 +30,9 @@ void SVGSlider::step() {
FramebufferWidget::step();
}

void SVGSlider::onChange(EventChange &e) {
void SVGSlider::on(event::Change &e) {
dirty = true;
Knob::onChange(e);
ParamWidget::on(e);
}




+ 2
- 2
src/app/SVGSwitch.cpp View File

@@ -18,13 +18,13 @@ void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) {
}
}

void SVGSwitch::onChange(EventChange &e) {
void SVGSwitch::on(event::Change &e) {
assert(frames.size() > 0);
float valueScaled = math::rescale(value, minValue, maxValue, 0, frames.size() - 1);
int index = math::clamp((int) roundf(valueScaled), 0, (int) frames.size() - 1);
sw->setSVG(frames[index]);
dirty = true;
ParamWidget::onChange(e);
ParamWidget::on(e);
}




+ 1
- 1
src/app/ToggleSwitch.cpp View File

@@ -4,7 +4,7 @@
namespace rack {


void ToggleSwitch::onDragStart(EventDragStart &e) {
void ToggleSwitch::on(event::DragStart &e) {
// Cycle through values
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3.
if (value >= maxValue)


+ 20
- 19
src/app/Toolbar.cpp View File

@@ -2,6 +2,7 @@
#include "window.hpp"
#include "engine.hpp"
#include "asset.hpp"
#include "helpers.hpp"


namespace rack {
@@ -10,22 +11,22 @@ namespace rack {
struct TooltipIconButton : IconButton {
Tooltip *tooltip = NULL;
std::string tooltipText;
void onMouseEnter(EventMouseEnter &e) override {
void on(event::Enter &e) override {
if (!tooltip) {
tooltip = new Tooltip();
tooltip->box.pos = getAbsoluteOffset(math::Vec(0, BND_WIDGET_HEIGHT));
tooltip->text = tooltipText;
gScene->addChild(tooltip);
}
IconButton::onMouseEnter(e);
IconButton::on(e);
}
void onMouseLeave(EventMouseLeave &e) override {
void on(event::Leave &e) override {
if (tooltip) {
gScene->removeChild(tooltip);
delete tooltip;
tooltip = NULL;
}
IconButton::onMouseLeave(e);
IconButton::on(e);
}
};

@@ -34,7 +35,7 @@ struct NewButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_146097_cc.svg")));
tooltipText = "New patch (" WINDOW_MOD_KEY_NAME "+N)";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->reset();
}
};
@@ -44,7 +45,7 @@ struct OpenButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg")));
tooltipText = "Open patch (" WINDOW_MOD_KEY_NAME "+O)";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->loadDialog();
}
};
@@ -54,7 +55,7 @@ struct SaveButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg")));
tooltipText = "Save patch (" WINDOW_MOD_KEY_NAME "+S)";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->saveDialog();
}
};
@@ -64,7 +65,7 @@ struct SaveAsButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg")));
tooltipText = "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->saveAsDialog();
}
};
@@ -74,7 +75,7 @@ struct RevertButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg")));
tooltipText = "Revert patch";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->revert();
}
};
@@ -84,7 +85,7 @@ struct DisconnectCablesButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg")));
tooltipText = "Disconnect cables";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->disconnect();
}
};
@@ -94,20 +95,20 @@ struct PowerMeterButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg")));
tooltipText = "Toggle power meter (see manual for explanation)";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gPowerMeter ^= true;
}
};

struct EnginePauseItem : MenuItem {
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gPaused ^= true;
}
};

struct SampleRateItem : MenuItem {
float sampleRate;
void onAction(EventAction &e) override {
void on(event::Action &e) override {
engineSetSampleRate(sampleRate);
gPaused = false;
}
@@ -118,12 +119,12 @@ struct SampleRateButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg")));
tooltipText = "Engine sample rate";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
Menu *menu = gScene->createMenu();
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
menu->box.size.x = box.size.x;

menu->addChild(MenuLabel::create("Engine sample rate"));
menu->addChild(createMenuLabel("Engine sample rate"));

EnginePauseItem *pauseItem = new EnginePauseItem();
pauseItem->text = gPaused ? "Resume engine" : "Pause engine";
@@ -145,15 +146,15 @@ struct RackLockButton : TooltipIconButton {
setSVG(SVG::load(asset::global("res/icons/noun_468341_cc.svg")));
tooltipText = "Lock modules";
}
void onAction(EventAction &e) override {
void on(event::Action &e) override {
gRackWidget->lockModules ^= true;
}
};

struct ZoomSlider : Slider {
void onAction(EventAction &e) override {
Slider::onAction(e);
gRackScene->zoomWidget->setZoom(roundf(value) / 100.0);
void on(event::Action &e) override {
EventWidget::on(e);
gRackScene->zoomWidget->setZoom(std::round(value) / 100.0);
}
};



src/widgets/widgets.cpp → src/event.cpp View File

@@ -1,4 +1,4 @@
#include "widgets.hpp"
#include "event.hpp"

namespace rack {

@@ -6,8 +6,7 @@ namespace rack {
Widget *gHoveredWidget = NULL;
Widget *gDraggedWidget = NULL;
Widget *gDragHoveredWidget = NULL;
Widget *gFocusedWidget = NULL;
Widget *gTempWidget = NULL;
Widget *gSelectedWidget = NULL;


} // namespace rack

src/widgets/SVGWidget.cpp → src/svg.cpp View File

@@ -1,4 +1,4 @@
#include "widgets.hpp"
#include "window.hpp"


// #define DEBUG_ONLY(x) x
@@ -52,7 +52,7 @@ static float getLineCrossing(math::Vec p0, math::Vec p1, math::Vec p2, math::Vec
return -(d.x * b.y - d.y * b.x) / m;
}

static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
void svgDraw(NVGcontext *vg, NSVGimage *svg) {
DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);)
int shapeIndex = 0;
// Iterate shape linked list
@@ -195,26 +195,4 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
}


void SVGWidget::wrap() {
if (svg && svg->handle) {
box.size = math::Vec(svg->handle->width, svg->handle->height);
}
else {
box.size = math::Vec();
}
}

void SVGWidget::setSVG(std::shared_ptr<SVG> svg) {
this->svg = svg;
wrap();
}

void SVGWidget::draw(NVGcontext *vg) {
if (svg && svg->handle) {
// printf("drawing svg %f %f\n", box.size.x, box.size.y);
drawSVG(vg, svg->handle);
}
}


} // namespace rack

+ 0
- 35
src/ui/Button.cpp View File

@@ -1,35 +0,0 @@
#include "ui.hpp"


namespace rack {

void Button::draw(NVGcontext *vg) {
bndToolButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str());
Widget::draw(vg);
}

void Button::onMouseEnter(EventMouseEnter &e) {
state = BND_HOVER;
}

void Button::onMouseLeave(EventMouseLeave &e) {
state = BND_DEFAULT;
}

void Button::onDragStart(EventDragStart &e) {
state = BND_ACTIVE;
}

void Button::onDragEnd(EventDragEnd &e) {
state = BND_HOVER;
}

void Button::onDragDrop(EventDragDrop &e) {
if (e.origin == this) {
EventAction eAction;
onAction(eAction);
}
}


} // namespace rack

+ 0
- 11
src/ui/ChoiceButton.cpp View File

@@ -1,11 +0,0 @@
#include "ui.hpp"


namespace rack {

void ChoiceButton::draw(NVGcontext *vg) {
bndChoiceButton(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str());
}


} // namespace rack

+ 0
- 25
src/ui/IconButton.cpp View File

@@ -1,25 +0,0 @@
#include "ui.hpp"


namespace rack {


IconButton::IconButton() {
box.size.x = BND_TOOL_WIDTH;

fw = new FramebufferWidget();
fw->oversample = 2;
addChild(fw);

sw = new SVGWidget();
sw->box.pos = math::Vec(2, 2);
fw->addChild(sw);
}

void IconButton::setSVG(std::shared_ptr<SVG> svg) {
sw->setSVG(svg);
fw->dirty = true;
}


} // namespace rack

+ 0
- 34
src/ui/Label.cpp View File

@@ -1,34 +0,0 @@
#include "ui.hpp"


namespace rack {


Label::Label() {
box.size.y = BND_WIDGET_HEIGHT;
fontSize = 13;
color = bndGetTheme()->regularTheme.textColor;
}

void Label::draw(NVGcontext *vg) {
// TODO
// Custom font sizes do not work with right or center alignment
float x;
switch (alignment) {
default:
case LEFT_ALIGNMENT: {
x = 0.0;
} break;
case RIGHT_ALIGNMENT: {
x = box.size.x - bndLabelWidth(vg, -1, text.c_str());
} break;
case CENTER_ALIGNMENT: {
x = (box.size.x - bndLabelWidth(vg, -1, text.c_str())) / 2.0;
} break;
}

bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, color, BND_LEFT, fontSize, text.c_str(), NULL);
}


} // namespace rack

+ 0
- 24
src/ui/List.cpp View File

@@ -1,24 +0,0 @@
#include "ui.hpp"


namespace rack {


void List::step() {
Widget::step();

// Set positions of children
box.size.y = 0.0;
for (Widget *child : children) {
if (!child->visible)
continue;
// Increment height, set position of child
child->box.pos = math::Vec(0.0, box.size.y);
box.size.y += child->box.size.y;
// Resize width of child
child->box.size.x = box.size.x;
}
}


} // namespace rack

+ 0
- 62
src/ui/Menu.cpp View File

@@ -1,62 +0,0 @@
#include "ui.hpp"


namespace rack {

Menu::~Menu() {
setChildMenu(NULL);
}

void Menu::setChildMenu(Menu *menu) {
if (childMenu) {
if (childMenu->parent)
childMenu->parent->removeChild(childMenu);
delete childMenu;
childMenu = NULL;
}
if (menu) {
childMenu = menu;
assert(parent);
parent->addChild(childMenu);
}
}

void Menu::step() {
Widget::step();

// Set positions of children
box.size = math::Vec(0, 0);
for (Widget *child : children) {
if (!child->visible)
continue;
// Increment height, set position of child
child->box.pos = math::Vec(0, box.size.y);
box.size.y += child->box.size.y;
// Increase width based on maximum width of child
if (child->box.size.x > box.size.x) {
box.size.x = child->box.size.x;
}
}

// Resize widths of children
for (Widget *child : children) {
child->box.size.x = box.size.x;
}
}

void Menu::draw(NVGcontext *vg) {
bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE);
Widget::draw(vg);
}


void Menu::onScroll(EventScroll &e) {
if (!parent)
return;
if (!parent->box.contains(box))
box.pos.y += e.scrollRel.y;
e.consumed = true;
}


} // namespace rack

+ 0
- 66
src/ui/MenuItem.cpp View File

@@ -1,66 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {


#define BND_LABEL_FONT_SIZE 13


void MenuItem::draw(NVGcontext *vg) {
// Get state
BNDwidgetState state = (gHoveredWidget == this) ? BND_HOVER : BND_DEFAULT;
Menu *parentMenu = dynamic_cast<Menu*>(parent);
if (parentMenu && parentMenu->activeEntry == this) {
state = BND_ACTIVE;
}

bndMenuItem(vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());

float x = box.size.x - bndLabelWidth(vg, -1, rightText.c_str());
NVGcolor rightColor = (state == BND_DEFAULT) ? bndGetTheme()->menuTheme.textColor : bndGetTheme()->menuTheme.textSelectedColor;
bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, rightColor, BND_LEFT, BND_LABEL_FONT_SIZE, rightText.c_str(), NULL);
}

void MenuItem::step() {
// Add 10 more pixels because measurements on high-DPI screens are sometimes too small for some reason
const float rightPadding = 10.0;
// HACK use gVg from the window.
// All this does is inspect the font, so it shouldn't modify gVg and should work when called from a FramebufferWidget for example.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + bndLabelWidth(gVg, -1, rightText.c_str()) + rightPadding;
Widget::step();
}

void MenuItem::onMouseEnter(EventMouseEnter &e) {
Menu *parentMenu = dynamic_cast<Menu*>(parent);
if (!parentMenu)
return;

parentMenu->activeEntry = NULL;

// Try to create child menu
Menu *childMenu = createChildMenu();
if (childMenu) {
parentMenu->activeEntry = this;
childMenu->box.pos = parent->box.pos.plus(box.getTopRight());
}
parentMenu->setChildMenu(childMenu);
}

void MenuItem::onDragDrop(EventDragDrop &e) {
if (e.origin != this)
return;

EventAction eAction;
// Consume event by default, but allow action to un-consume it to prevent the menu from being removed.
eAction.consumed = true;
onAction(eAction);
if (eAction.consumed) {
// deletes `this`
gScene->setOverlay(NULL);
}
}


} // namespace rack

+ 0
- 21
src/ui/MenuLabel.cpp View File

@@ -1,21 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {


void MenuLabel::draw(NVGcontext *vg) {
bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str());
}

void MenuLabel::step() {
// Add 10 more pixels because Retina measurements are sometimes too small
const float rightPadding = 10.0;
// HACK use gVg from the window.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + rightPadding;
Widget::step();
}


} // namespace rack

+ 0
- 43
src/ui/MenuOverlay.cpp View File

@@ -1,43 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {


void MenuOverlay::step() {
Widget::step();

// Fit all children in the box
for (Widget *child : children) {
child->box = child->box.nudge(box.zeroPos());
}
}

void MenuOverlay::onMouseDown(EventMouseDown &e) {
Widget::onMouseDown(e);
if (!e.consumed) {
// deletes `this`
gScene->setOverlay(NULL);
e.consumed = true;
}
}

void MenuOverlay::onHoverKey(EventHoverKey &e) {
switch (e.key) {
case GLFW_KEY_ESCAPE: {
gScene->setOverlay(NULL);
e.consumed = true;
return;
} break;
}

if (!e.consumed) {
// Recurse children but consume the event
Widget::onHoverKey(e);
e.consumed = true;
}
}


} // namespace rack

+ 0
- 24
src/ui/MenuSeparator.cpp View File

@@ -1,24 +0,0 @@
#include "ui.hpp"
#include "window.hpp"
#include "color.hpp"


namespace rack {


MenuSeparator::MenuSeparator() {
box.size.y = BND_WIDGET_HEIGHT / 2;
}

void MenuSeparator::draw(NVGcontext *vg) {
nvgBeginPath(vg);
const float margin = 8.0;
nvgMoveTo(vg, margin, box.size.y / 2.0);
nvgLineTo(vg, box.size.x - margin, box.size.y / 2.0);
nvgStrokeWidth(vg, 1.0);
nvgStrokeColor(vg, color::alpha(bndGetTheme()->menuTheme.textColor, 0.25));
nvgStroke(vg);
}


} // namespace rack

+ 0
- 15
src/ui/PasswordField.cpp View File

@@ -1,15 +0,0 @@
#include "ui.hpp"


namespace rack {


void PasswordField::draw(NVGcontext *vg) {
std::string textTmp = text;
text = std::string(textTmp.size(), '*');
TextField::draw(vg);
text = textTmp;
}


} // namespace rack

+ 0
- 12
src/ui/ProgressBar.cpp View File

@@ -1,12 +0,0 @@
#include "ui.hpp"


namespace rack {

void ProgressBar::draw(NVGcontext *vg) {
float progress = math::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);
}


} // namespace rack

+ 0
- 31
src/ui/RadioButton.cpp View File

@@ -1,31 +0,0 @@
#include "ui.hpp"


namespace rack {

void RadioButton::draw(NVGcontext *vg) {
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());
}

void RadioButton::onMouseEnter(EventMouseEnter &e) {
state = BND_HOVER;
}

void RadioButton::onMouseLeave(EventMouseLeave &e) {
state = BND_DEFAULT;
}

void RadioButton::onDragDrop(EventDragDrop &e) {
if (e.origin == this) {
if (value)
setValue(0.0);
else
setValue(1.0);

EventAction eAction;
onAction(eAction);
}
}


} // namespace rack

+ 0
- 42
src/ui/Scene.cpp View File

@@ -1,42 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {

void Scene::setOverlay(Widget *w) {
if (overlay) {
removeChild(overlay);
delete overlay;
overlay = NULL;
}
if (w) {
addChild(w);
overlay = w;
overlay->box.pos = math::Vec();
}
}

Menu *Scene::createMenu() {
// Get relative position of the click
MenuOverlay *overlay = new MenuOverlay();
Menu *menu = new Menu();
menu->box.pos = gMousePos;

overlay->addChild(menu);
gScene->setOverlay(overlay);

return menu;
}

void Scene::step() {
if (overlay) {
overlay->box.pos = math::Vec(0, 0);
overlay->box.size = box.size;
}

Widget::step();
}


} // namespace rack

+ 0
- 148
src/ui/ScrollWidget.cpp View File

@@ -1,148 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {


/** Parent must be a ScrollWidget */
struct ScrollBar : OpaqueWidget {
enum Orientation {
VERTICAL,
HORIZONTAL
};
Orientation orientation;
BNDwidgetState state = BND_DEFAULT;
float offset = 0.0;
float size = 0.0;

ScrollBar() {
box.size = math::Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT);
}

void draw(NVGcontext *vg) override {
bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size);
}

void onDragStart(EventDragStart &e) override {
state = BND_ACTIVE;
windowCursorLock();
}

void onDragMove(EventDragMove &e) override {
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent);
assert(scrollWidget);
if (orientation == HORIZONTAL)
scrollWidget->offset.x += e.mouseRel.x;
else
scrollWidget->offset.y += e.mouseRel.y;
}

void onDragEnd(EventDragEnd &e) override {
state = BND_DEFAULT;
windowCursorUnlock();
}
};


ScrollWidget::ScrollWidget() {
container = new Widget();
addChild(container);

horizontalScrollBar = new ScrollBar();
horizontalScrollBar->orientation = ScrollBar::HORIZONTAL;
horizontalScrollBar->visible = false;
addChild(horizontalScrollBar);

verticalScrollBar = new ScrollBar();
verticalScrollBar->orientation = ScrollBar::VERTICAL;
verticalScrollBar->visible = false;
addChild(verticalScrollBar);
}

void ScrollWidget::scrollTo(math::Rect r) {
math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos);
offset = offset.clampBetween(bound);
}

void ScrollWidget::draw(NVGcontext *vg) {
nvgScissor(vg, 0, 0, box.size.x, box.size.y);
Widget::draw(vg);
nvgResetScissor(vg);
}

void ScrollWidget::step() {
Widget::step();

// Clamp scroll offset
math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight();
math::Rect containerBox = math::Rect(math::Vec(0, 0), containerCorner.minus(box.size));
offset = offset.clamp(containerBox);
// Lock offset to top/left if no scrollbar will display
if (containerBox.size.x < 0.0)
offset.x = 0.0;
if (containerBox.size.y < 0.0)
offset.y = 0.0;

// Update the container's positions from the offset
container->box.pos = offset.neg().round();

// Update scrollbar offsets and sizes
math::Vec viewportSize = container->getChildrenBoundingBox().getBottomRight();
math::Vec scrollbarOffset = offset.div(viewportSize.minus(box.size));
math::Vec scrollbarSize = box.size.div(viewportSize);

horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0);
verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0);
horizontalScrollBar->offset = scrollbarOffset.x;
verticalScrollBar->offset = scrollbarOffset.y;
horizontalScrollBar->size = scrollbarSize.x;
verticalScrollBar->size = scrollbarSize.y;

// Resize scroll bars
math::Vec inner = math::Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y);
horizontalScrollBar->box.pos.y = inner.y;
verticalScrollBar->box.pos.x = inner.x;
horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x;
verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y;
}

void ScrollWidget::onMouseMove(EventMouseMove &e) {
// Scroll with arrow keys
if (!gFocusedWidget) {
float arrowSpeed = 30.0;
if (windowIsShiftPressed() && windowIsModPressed())
arrowSpeed /= 16.0;
else if (windowIsShiftPressed())
arrowSpeed *= 4.0;
else if (windowIsModPressed())
arrowSpeed /= 4.0;

if (glfwGetKey(gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
offset.x -= arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
offset.x += arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
offset.y -= arrowSpeed;
}
if (glfwGetKey(gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
offset.y += arrowSpeed;
}
}

Widget::onMouseMove(e);
}

void ScrollWidget::onScroll(EventScroll &e) {
offset = offset.minus(e.scrollRel);
e.consumed = true;
}

void ScrollWidget::onHoverKey(EventHoverKey &e) {
Widget::onHoverKey(e);
}


} // namespace rack

+ 0
- 41
src/ui/Slider.cpp View File

@@ -1,41 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {

#define SLIDER_SENSITIVITY 0.001

void Slider::draw(NVGcontext *vg) {
float progress = math::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);
}

void Slider::onDragStart(EventDragStart &e) {
state = BND_ACTIVE;
windowCursorLock();
}

void Slider::onDragMove(EventDragMove &e) {
setValue(value + SLIDER_SENSITIVITY * (maxValue - minValue) * e.mouseRel.x);
}

void Slider::onDragEnd(EventDragEnd &e) {
state = BND_DEFAULT;
windowCursorUnlock();
EventAction eAction;
onAction(eAction);
}

void Slider::onMouseDown(EventMouseDown &e) {
if (e.button == 1) {
setValue(defaultValue);
EventAction eAction;
onAction(eAction);
}
e.consumed = true;
e.target = this;
}


} // namespace rack

+ 0
- 197
src/ui/TextField.cpp View File

@@ -1,197 +0,0 @@
#include "ui.hpp"
// for gVg
#include "window.hpp"
// for key codes
#include <GLFW/glfw3.h>


namespace rack {


void TextField::draw(NVGcontext *vg) {
nvgScissor(vg, 0, 0, box.size.x, box.size.y);

BNDwidgetState state;
if (this == gFocusedWidget)
state = BND_ACTIVE;
else if (this == gHoveredWidget)
state = BND_HOVER;
else
state = BND_DEFAULT;

int begin = std::min(cursor, selection);
int end = std::max(cursor, selection);
bndTextField(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str(), begin, end);
// Draw placeholder text
if (text.empty() && state != BND_ACTIVE) {
bndIconLabelCaret(vg, 0.0, 0.0, box.size.x, box.size.y, -1, bndGetTheme()->textFieldTheme.itemColor, 13, placeholder.c_str(), bndGetTheme()->textFieldTheme.itemColor, 0, -1);
}

nvgResetScissor(vg);
}

void TextField::onMouseDown(EventMouseDown &e) {
if (e.button == 0) {
cursor = selection = getTextPosition(e.pos);
}
OpaqueWidget::onMouseDown(e);
}

void TextField::onMouseMove(EventMouseMove &e) {
if (this == gDraggedWidget) {
int pos = getTextPosition(e.pos);
if (pos != selection) {
cursor = pos;
}
}
OpaqueWidget::onMouseMove(e);
}

void TextField::onFocus(EventFocus &e) {
e.consumed = true;
}

void TextField::onText(EventText &e) {
if (e.codepoint < 128) {
std::string newText(1, (char) e.codepoint);
insertText(newText);
}
e.consumed = true;
}

void TextField::onKey(EventKey &e) {
switch (e.key) {
case GLFW_KEY_BACKSPACE: {
if (cursor == selection) {
cursor--;
if (cursor >= 0) {
text.erase(cursor, 1);
onTextChange();
}
selection = cursor;
}
else {
int begin = std::min(cursor, selection);
text.erase(begin, std::abs(selection - cursor));
onTextChange();
cursor = selection = begin;
}
} break;
case GLFW_KEY_DELETE: {
if (cursor == selection) {
text.erase(cursor, 1);
onTextChange();
}
else {
int begin = std::min(cursor, selection);
text.erase(begin, std::abs(selection - cursor));
onTextChange();
cursor = selection = begin;
}
} break;
case GLFW_KEY_LEFT: {
if (windowIsModPressed()) {
while (--cursor > 0) {
if (text[cursor] == ' ')
break;
}
}
else {
cursor--;
}
if (!windowIsShiftPressed()) {
selection = cursor;
}
} break;
case GLFW_KEY_RIGHT: {
if (windowIsModPressed()) {
while (++cursor < (int) text.size()) {
if (text[cursor] == ' ')
break;
}
}
else {
cursor++;
}
if (!windowIsShiftPressed()) {
selection = cursor;
}
} break;
case GLFW_KEY_HOME: {
selection = cursor = 0;
} break;
case GLFW_KEY_END: {
selection = cursor = text.size();
} break;
case GLFW_KEY_V: {
if (windowIsModPressed()) {
const char *newText = glfwGetClipboardString(gWindow);
if (newText)
insertText(newText);
}
} break;
case GLFW_KEY_X: {
if (windowIsModPressed()) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
glfwSetClipboardString(gWindow, selectedText.c_str());
insertText("");
}
}
} break;
case GLFW_KEY_C: {
if (windowIsModPressed()) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
glfwSetClipboardString(gWindow, selectedText.c_str());
}
}
} break;
case GLFW_KEY_A: {
if (windowIsModPressed()) {
selection = 0;
cursor = text.size();
}
} break;
case GLFW_KEY_ENTER: {
if (multiline) {
insertText("\n");
}
else {
EventAction e;
onAction(e);
}
} break;
}

cursor = math::clamp(cursor, 0, (int) text.size());
selection = math::clamp(selection, 0, (int) text.size());
e.consumed = true;
}

void TextField::insertText(std::string text) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
this->text.erase(begin, std::abs(selection - cursor));
cursor = selection = begin;
}
this->text.insert(cursor, text);
cursor += text.size();
selection = cursor;
onTextChange();
}

void TextField::setText(std::string text) {
this->text = text;
selection = cursor = text.size();
onTextChange();
}

int TextField::getTextPosition(math::Vec mousePos) {
return bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y);
}


} // namespace rack

+ 0
- 22
src/ui/Tooltip.cpp View File

@@ -1,22 +0,0 @@
#include "ui.hpp"
#include "window.hpp"


namespace rack {


Tooltip::Tooltip() {
}

void Tooltip::draw(NVGcontext *vg) {
// Wrap size to contents
box.size.x = bndLabelWidth(vg, -1, text.c_str()) + 10.0;
box.size.y = bndLabelHeight(vg, -1, text.c_str(), INFINITY);

bndTooltipBackground(vg, 0.0, 0.0, box.size.x, box.size.y);
bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str());
Widget::draw(vg);
}


} // namespace rack

+ 0
- 17
src/ui/WindowWidget.cpp View File

@@ -1,17 +0,0 @@
#include "ui.hpp"


namespace rack {


void WindowWidget::draw(NVGcontext *vg) {
bndNodeBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_DEFAULT, -1, title.c_str(), bndGetTheme()->backgroundColor);
Widget::draw(vg);
}

void WindowWidget::onDragMove(EventDragMove &e) {
box.pos = box.pos.plus(e.mouseRel);
}


} // namespace rack

+ 0
- 39
src/ui/layouts.cpp View File

@@ -1,39 +0,0 @@
#include "ui.hpp"


namespace rack {


void SequentialLayout::step() {
Widget::step();

float offset = 0.0;
for (Widget *child : children) {
if (!child->visible)
continue;
// Set position
(orientation == HORIZONTAL_ORIENTATION ? child->box.pos.x : child->box.pos.y) = offset;
// Increment by size
offset += (orientation == HORIZONTAL_ORIENTATION ? child->box.size.x : child->box.size.y);
offset += spacing;
}

// We're done if left aligned
if (alignment == LEFT_ALIGNMENT)
return;

// Adjust positions based on width of the layout itself
offset -= spacing;
if (alignment == RIGHT_ALIGNMENT)
offset -= (orientation == HORIZONTAL_ORIENTATION ? box.size.x : box.size.y);
else if (alignment == CENTER_ALIGNMENT)
offset -= (orientation == HORIZONTAL_ORIENTATION ? box.size.x : box.size.y) / 2.0;
for (Widget *child : children) {
if (!child->visible)
continue;
(orientation == HORIZONTAL_ORIENTATION ? child->box.pos.x : child->box.pos.y) += offset;
}
}


} // namespace rack

+ 1
- 7
src/widgets/FramebufferWidget.cpp View File

@@ -1,5 +1,4 @@
#include "widgets.hpp"
#include "window.hpp"
#include "widgets/FramebufferWidget.hpp"
#include "nanovg_gl.h"
#include "nanovg_gl_utils.h"

@@ -118,10 +117,5 @@ int FramebufferWidget::getImageHandle() {
return internal->fb->image;
}

void FramebufferWidget::onZoom(EventZoom &e) {
dirty = true;
Widget::onZoom(e);
}


} // namespace rack

+ 0
- 36
src/widgets/QuantityWidget.cpp View File

@@ -1,36 +0,0 @@
#include "widgets.hpp"


namespace rack {

QuantityWidget::QuantityWidget() {
EventChange e;
onChange(e);
}

void QuantityWidget::setValue(float value) {
this->value = math::clamp(value, fminf(minValue, maxValue), fmaxf(minValue, maxValue));
EventChange e;
onChange(e);
}

void QuantityWidget::setLimits(float minValue, float maxValue) {
this->minValue = minValue;
this->maxValue = maxValue;
}

void QuantityWidget::setDefaultValue(float defaultValue) {
this->defaultValue = defaultValue;
setValue(defaultValue);
}

std::string QuantityWidget::getText() {
std::string text = label;
text += ": ";
text += string::stringf("%.*f", precision, value);
text += unit;
return text;
}


} // namespace rack

+ 0
- 49
src/widgets/TransformWidget.cpp View File

@@ -1,49 +0,0 @@
#include "widgets.hpp"


namespace rack {


TransformWidget::TransformWidget() {
identity();
}

math::Rect TransformWidget::getChildrenBoundingBox() {
math::Rect bound = Widget::getChildrenBoundingBox();
math::Vec topLeft = bound.pos;
math::Vec bottomRight = bound.getBottomRight();
nvgTransformPoint(&topLeft.x, &topLeft.y, transform, topLeft.x, topLeft.y);
nvgTransformPoint(&bottomRight.x, &bottomRight.y, transform, bottomRight.x, bottomRight.y);
return math::Rect(topLeft, bottomRight.minus(topLeft));
}

void TransformWidget::identity() {
nvgTransformIdentity(transform);
}

void TransformWidget::translate(math::Vec delta) {
float t[6];
nvgTransformTranslate(t, delta.x, delta.y);
nvgTransformPremultiply(transform, t);
}

void TransformWidget::rotate(float angle) {
float t[6];
nvgTransformRotate(t, angle);
nvgTransformPremultiply(transform, t);
}

void TransformWidget::scale(math::Vec s) {
float t[6];
nvgTransformScale(t, s.x, s.y);
nvgTransformPremultiply(transform, t);
}

void TransformWidget::draw(NVGcontext *vg) {
// No need to save the state because that is done in the parent
nvgTransform(vg, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);
Widget::draw(vg);
}


} // namespace rack

+ 11
- 75
src/widgets/Widget.cpp View File

@@ -12,8 +12,7 @@ Widget::~Widget() {
if (gHoveredWidget == this) gHoveredWidget = NULL;
if (gDraggedWidget == this) gDraggedWidget = NULL;
if (gDragHoveredWidget == this) gDragHoveredWidget = NULL;
if (gFocusedWidget == this) gFocusedWidget = NULL;
if (gTempWidget == this) gTempWidget = NULL;
if (gSelectedWidget == this) gSelectedWidget = NULL;
clearChildren();
}

@@ -75,34 +74,18 @@ void Widget::clearChildren() {
children.clear();
}

void Widget::finalizeEvents() {
// Stop dragging and hovering this widget
if (gHoveredWidget == this) {
EventMouseLeave e;
gHoveredWidget->onMouseLeave(e);
gHoveredWidget = NULL;
}
if (gDraggedWidget == this) {
EventDragEnd e;
gDraggedWidget->onDragEnd(e);
gDraggedWidget = NULL;
}
if (gDragHoveredWidget == this) {
gDragHoveredWidget = NULL;
}
if (gFocusedWidget == this) {
EventDefocus e;
gFocusedWidget->onDefocus(e);
gFocusedWidget = NULL;
}
for (Widget *child : children) {
child->finalizeEvents();
}
}

void Widget::step() {
for (Widget *child : children) {
for (auto it = children.begin(); it != children.end();) {
Widget *child = *it;
// Delete children if a delete is requested
if (child->requestedDelete) {
it = children.erase(it);
delete child;
continue;
}

child->step();
it++;
}
}

@@ -117,52 +100,5 @@ void Widget::draw(NVGcontext *vg) {
}
}

#define RECURSE_EVENT_POSITION(_method) { \
math::Vec pos = e.pos; \
for (auto it = children.rbegin(); it != children.rend(); it++) { \
Widget *child = *it; \
if (!child->visible) \
continue; \
if (child->box.contains(pos)) { \
e.pos = pos.minus(child->box.pos); \
child->_method(e); \
if (e.consumed) \
break; \
} \
} \
e.pos = pos; \
}


void Widget::onMouseDown(EventMouseDown &e) {
RECURSE_EVENT_POSITION(onMouseDown);
}

void Widget::onMouseUp(EventMouseUp &e) {
RECURSE_EVENT_POSITION(onMouseUp);
}

void Widget::onMouseMove(EventMouseMove &e) {
RECURSE_EVENT_POSITION(onMouseMove);
}

void Widget::onHoverKey(EventHoverKey &e) {
RECURSE_EVENT_POSITION(onHoverKey);
}

void Widget::onScroll(EventScroll &e) {
RECURSE_EVENT_POSITION(onScroll);
}

void Widget::onPathDrop(EventPathDrop &e) {
RECURSE_EVENT_POSITION(onPathDrop);
}

void Widget::onZoom(EventZoom &e) {
for (auto it = children.rbegin(); it != children.rend(); it++) {
Widget *child = *it;
child->onZoom(e);
}
}

} // namespace rack

+ 0
- 76
src/widgets/ZoomWidget.cpp View File

@@ -1,76 +0,0 @@
#include "widgets.hpp"


namespace rack {


math::Vec ZoomWidget::getRelativeOffset(math::Vec v, Widget *relative) {
return Widget::getRelativeOffset(v.mult(zoom), relative);
}

math::Rect ZoomWidget::getViewport(math::Rect r) {
r.pos = r.pos.mult(zoom);
r.size = r.size.mult(zoom);
r = Widget::getViewport(r);
r.pos = r.pos.div(zoom);
r.size = r.size.div(zoom);
return r;
}

void ZoomWidget::setZoom(float zoom) {
if (zoom != this->zoom) {
EventZoom e;
onZoom(e);
}
this->zoom = zoom;
}

void ZoomWidget::draw(NVGcontext *vg) {
nvgScale(vg, zoom, zoom);
Widget::draw(vg);
}

void ZoomWidget::onMouseDown(EventMouseDown &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onMouseDown(e);
e.pos = pos;
}

void ZoomWidget::onMouseUp(EventMouseUp &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onMouseUp(e);
e.pos = pos;
}

void ZoomWidget::onMouseMove(EventMouseMove &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onMouseMove(e);
e.pos = pos;
}

void ZoomWidget::onHoverKey(EventHoverKey &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onHoverKey(e);
e.pos = pos;
}

void ZoomWidget::onScroll(EventScroll &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onScroll(e);
e.pos = pos;
}

void ZoomWidget::onPathDrop(EventPathDrop &e) {
math::Vec pos = e.pos;
e.pos = e.pos.div(zoom);
Widget::onPathDrop(e);
e.pos = pos;
}


} // namespace rack

+ 105
- 63
src/window.cpp View File

@@ -45,30 +45,64 @@ math::Vec gMousePos;
std::string lastWindowTitle;


void windowSizeCallback(GLFWwindow* window, int width, int height) {
}
static void windowSizeCallback(GLFWwindow* window, int width, int height) {}

void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
static void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
#ifdef ARCH_MAC
// Ctrl-left click --> right click
// Remap Ctrl-left click to right click on Mac
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
if (mods & GLFW_MOD_CONTROL) {
button = GLFW_MOUSE_BUTTON_RIGHT;
}
}
#endif

if (action == GLFW_PRESS) {
gTempWidget = NULL;
// onMouseDown
{
EventMouseDown e;
e.pos = gMousePos;
e.button = button;
gScene->onMouseDown(e);
gTempWidget = e.target;
// event::Button
event::Button eButton;
eButton.button = button;
eButton.action = action;
eButton.mods = mods;
gScene->handleEvent(eButton);
Widget *clickedWidget = eButton.target;

// Dragging
if (clickedWidget) {
// TODO keep track of dragged mouse button
if (action == GLFW_PRESS) {
event::DragStart eDragStart;
eDragStart.button = button;
clickedWidget->handleEvent(eDragStart);
gDraggedWidget = eDragStart.target;
}

if (action == GLFW_RELEASE) {
event::DragEnd eDragEnd;
// TODO Use dragged button
eDragEnd.button = button;
clickedWidget->handleEvent(eDragEnd);
gDraggedWidget = eDragEnd.target;
}
}

// Selection
if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) {
if (clickedWidget != gSelectedWidget) {
if (gSelectedWidget) {
event::Deselect eDeselect;
gSelectedWidget->handleEvent(eDeselect);
}

gSelectedWidget = clickedWidget;

if (gSelectedWidget) {
event::Select eSelect;
gSelectedWidget->handleEvent(eSelect);
}
}
}

/*
if (action == GLFW_PRESS) {
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (gTempWidget) {
// onDragStart
@@ -77,19 +111,19 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
}
gDraggedWidget = gTempWidget;

if (gTempWidget != gFocusedWidget) {
if (gFocusedWidget) {
if (gTempWidget != gSelectedWidget) {
if (gSelectedWidget) {
// onDefocus
EventDefocus e;
gFocusedWidget->onDefocus(e);
gSelectedWidget->onDefocus(e);
}
gFocusedWidget = NULL;
gSelectedWidget = NULL;
if (gTempWidget) {
// onFocus
EventFocus e;
gTempWidget->onFocus(e);
if (e.consumed) {
gFocusedWidget = gTempWidget;
gSelectedWidget = gTempWidget;
}
}
}
@@ -125,6 +159,7 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
}
gTempWidget = NULL;
}
*/
}

struct MouseButtonArguments {
@@ -151,7 +186,7 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m

void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
math::Vec mousePos = math::Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round();
math::Vec mouseRel = mousePos.minus(gMousePos);
math::Vec mouseDelta = mousePos.minus(gMousePos);

int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR);
(void) cursorMode;
@@ -171,20 +206,24 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {

gMousePos = mousePos;

gTempWidget = NULL;
// onMouseMove
{
EventMouseMove e;
e.pos = mousePos;
e.mouseRel = mouseRel;
gScene->onMouseMove(e);
gTempWidget = e.target;
event::Hover eHover;
eHover.pos = mousePos;
eHover.mouseDelta = mouseDelta;
gScene->handleEvent(eHover);

if (gDraggedWidget) {
event::DragMove eDragMove;
// TODO
eDragMove.button = 0;
eDragMove.mouseDelta = mouseDelta;
gDraggedWidget->handleEvent(eDragMove);
}

/*
if (gDraggedWidget) {
// onDragMove
EventDragMove e;
e.mouseRel = mouseRel;
e.mouseDelta = mouseDelta;
gDraggedWidget->onDragMove(e);

if (gTempWidget != gDragHoveredWidget) {
@@ -222,59 +261,63 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
// Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed
EventScroll e;
e.pos = mousePos;
e.scrollRel = mouseRel;
e.scrollRel = mouseDelta;
gScene->onScroll(e);
}
*/
}

void cursorEnterCallback(GLFWwindow* window, int entered) {
if (!entered) {
if (gHoveredWidget) {
// onMouseLeave
EventMouseLeave e;
gHoveredWidget->onMouseLeave(e);
event::Leave eLeave;
gHoveredWidget->handleEvent(eLeave);
}
gHoveredWidget = NULL;
}
}

void scrollCallback(GLFWwindow *window, double x, double y) {
math::Vec scrollRel = math::Vec(x, y);
math::Vec scrollDelta = math::Vec(x, y);
#if ARCH_LIN || ARCH_WIN
if (windowIsShiftPressed())
scrollRel = math::Vec(y, x);
scrollDelta = math::Vec(y, x);
#endif
// onScroll
EventScroll e;
e.pos = gMousePos;
e.scrollRel = scrollRel.mult(50.0);
gScene->onScroll(e);
scrollDelta = scrollDelta.mult(50.0);
event::HoverScroll eHoverScroll;
eHoverScroll.scrollDelta = scrollDelta;
gScene->handleEvent(eHoverScroll);
}

void charCallback(GLFWwindow *window, unsigned int codepoint) {
if (gFocusedWidget) {
// onText
EventText e;
e.codepoint = codepoint;
gFocusedWidget->onText(e);
if (gSelectedWidget) {
event::SelectText eSelectText;
eSelectText.codepoint = codepoint;
gSelectedWidget->handleEvent(eSelectText);
}
}

void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
if (gFocusedWidget) {
// onKey
EventKey e;
e.key = key;
gFocusedWidget->onKey(e);
if (e.consumed)
if (gSelectedWidget) {
event::SelectKey eSelectKey;
eSelectKey.key = key;
eSelectKey.scancode = scancode;
eSelectKey.action = action;
eSelectKey.mods = mods;
gSelectedWidget->handleEvent(eSelectKey);
if (eSelectKey.target)
return;
}
// onHoverKey
EventHoverKey e;
e.pos = gMousePos;
e.key = key;
gScene->onHoverKey(e);

event::HoverKey eHoverKey;
eHoverKey.key = key;
eHoverKey.scancode = scancode;
eHoverKey.action = action;
eHoverKey.mods = mods;
eHoverKey.pos = gMousePos;
gScene->handleEvent(eHoverKey);
}

// Keyboard MIDI driver
@@ -289,13 +332,12 @@ void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods
}

void dropCallback(GLFWwindow *window, int count, const char **paths) {
// onPathDrop
EventPathDrop e;
e.pos = gMousePos;
event::PathDrop ePathDrop;
ePathDrop.pos = gMousePos;
for (int i = 0; i < count; i++) {
e.paths.push_back(paths[i]);
ePathDrop.paths.push_back(paths[i]);
}
gScene->onPathDrop(e);
gScene->handleEvent(ePathDrop);
}

void errorCallback(int error, const char *description) {
@@ -464,8 +506,8 @@ void windowRun() {
glfwGetWindowContentScale(gWindow, &pixelRatio, NULL);
pixelRatio = roundf(pixelRatio);
if (pixelRatio != gPixelRatio) {
EventZoom eZoom;
gScene->onZoom(eZoom);
event::Zoom eZoom;
gScene->handleEvent(eZoom);
gPixelRatio = pixelRatio;
}



Loading…
Cancel
Save