@@ -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 PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm"; | ||||
static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; | static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; | ||||
} // 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 { | struct WireWidget : OpaqueWidget { | ||||
Port *outputPort = NULL; | Port *outputPort = NULL; | ||||
@@ -189,20 +135,15 @@ struct RackWidget : OpaqueWidget { | |||||
void step() override; | void step() override; | ||||
void draw(NVGcontext *vg) 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 { | struct RackRail : TransparentWidget { | ||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
}; | }; | ||||
struct SVGPanel : FramebufferWidget { | |||||
void step() override; | |||||
void setBackground(std::shared_ptr<SVG> svg); | |||||
}; | |||||
//////////////////// | //////////////////// | ||||
// ParamWidgets and other components | // ParamWidgets and other components | ||||
//////////////////// | //////////////////// | ||||
@@ -210,14 +151,6 @@ struct SVGPanel : FramebufferWidget { | |||||
/** A Widget that exists on a Panel and interacts with a Module */ | /** A Widget that exists on a Panel and interacts with a Module */ | ||||
struct Component : OpaqueWidget { | struct Component : OpaqueWidget { | ||||
Module *module = NULL; | 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 { | struct CircularShadow : TransparentWidget { | ||||
@@ -241,17 +174,8 @@ struct ParamWidget : Component, QuantityWidget { | |||||
void fromJson(json_t *rootJ); | void fromJson(json_t *rootJ); | ||||
virtual void reset(); | virtual void reset(); | ||||
virtual void randomize(); | 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 */ | /** Implements vertical dragging behavior for ParamWidgets */ | ||||
@@ -262,9 +186,9 @@ struct Knob : ParamWidget { | |||||
float speed = 1.0; | float speed = 1.0; | ||||
float dragValue; | float dragValue; | ||||
Knob(); | 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 */ | /** A knob which rotates an SVG and caches it in a framebuffer */ | ||||
@@ -278,7 +202,7 @@ struct SVGKnob : Knob, FramebufferWidget { | |||||
SVGKnob(); | SVGKnob(); | ||||
void setSVG(std::shared_ptr<SVG> svg); | void setSVG(std::shared_ptr<SVG> svg); | ||||
void step() override; | 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. | /** Behaves like a knob but linearly moves an SVGWidget between two points. | ||||
@@ -293,12 +217,9 @@ struct SVGSlider : Knob, FramebufferWidget { | |||||
SVGSlider(); | SVGSlider(); | ||||
void setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> handleSVG); | void setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> handleSVG); | ||||
void step() override; | 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 */ | /** A ParamWidget with multiple frames corresponding to its value */ | ||||
struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | ||||
std::vector<std::shared_ptr<SVG>> frames; | std::vector<std::shared_ptr<SVG>> frames; | ||||
@@ -306,12 +227,12 @@ struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | |||||
SVGSwitch(); | SVGSwitch(); | ||||
/** Adds an SVG file to represent the next switch position */ | /** Adds an SVG file to represent the next switch position */ | ||||
void addFrame(std::shared_ptr<SVG> svg); | 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 */ | /** A switch that cycles through each mechanical position */ | ||||
struct ToggleSwitch : virtual ParamWidget { | 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. | /** 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 { | struct MomentarySwitch : virtual ParamWidget { | ||||
/** Don't randomize state */ | /** Don't randomize state */ | ||||
void randomize() override {} | 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. | /** A Component with a default (up) and active (down) state when clicked. | ||||
@@ -335,15 +256,15 @@ struct SVGButton : Component, FramebufferWidget { | |||||
SVGButton(); | SVGButton(); | ||||
/** If `activeSVG` is NULL, `defaultSVG` is used as the active state instead. */ | /** 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 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 | // IO widgets | ||||
//////////////////// | //////////////////// | ||||
struct LedDisplay : VirtualWidget { | |||||
struct LedDisplay : virtual EventWidget { | |||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
}; | }; | ||||
@@ -359,7 +280,7 @@ struct LedDisplayChoice : TransparentWidget { | |||||
NVGcolor color; | NVGcolor color; | ||||
LedDisplayChoice(); | LedDisplayChoice(); | ||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
void onMouseDown(EventMouseDown &e) override; | |||||
void on(event::Button &e) override; | |||||
}; | }; | ||||
struct LedDisplayTextField : TextField { | struct LedDisplayTextField : TextField { | ||||
@@ -430,14 +351,6 @@ struct ModuleLightWidget : MultiLightWidget { | |||||
Module *module = NULL; | Module *module = NULL; | ||||
int firstLightId; | int firstLightId; | ||||
void step() override; | 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(); | ~Port(); | ||||
void step() override; | void step() override; | ||||
void draw(NVGcontext *vg) 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 { | struct SVGPort : Port, FramebufferWidget { | ||||
@@ -503,7 +408,7 @@ struct Toolbar : OpaqueWidget { | |||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
}; | }; | ||||
struct PluginManagerWidget : VirtualWidget { | |||||
struct PluginManagerWidget : virtual EventWidget { | |||||
Widget *loginWidget; | Widget *loginWidget; | ||||
Widget *manageWidget; | Widget *manageWidget; | ||||
Widget *downloadWidget; | Widget *downloadWidget; | ||||
@@ -522,8 +427,8 @@ struct RackScene : Scene { | |||||
RackScene(); | RackScene(); | ||||
void step() override; | void step() override; | ||||
void draw(NVGcontext *vg) 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; | |||||
}; | }; | ||||
//////////////////// | //////////////////// | ||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -10,114 +10,130 @@ template <class TModule, class TModuleWidget, typename... Tags> | |||||
Model *createModel(std::string author, std::string slug, std::string name, Tags... tags) { | Model *createModel(std::string author, std::string slug, std::string name, Tags... tags) { | ||||
struct TModel : Model { | struct TModel : Model { | ||||
Module *createModule() override { | Module *createModule() override { | ||||
TModule *module = new TModule(); | |||||
return module; | |||||
TModule *o = new TModule(); | |||||
return o; | |||||
} | } | ||||
ModuleWidget *createModuleWidget() override { | ModuleWidget *createModuleWidget() override { | ||||
TModule *module = new TModule(); | 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 { | 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> | template <class TWidget> | ||||
TWidget *createWidget(math::Vec pos) { | 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> | template <class TParamWidget> | ||||
TParamWidget *createParam(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | 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> | template <class TParamWidget> | ||||
TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | 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> | template <class TPort> | ||||
TPort *createInput(math::Vec pos, Module *module, int inputId) { | 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> | template <class TPort> | ||||
TPort *createInputCentered(math::Vec pos, Module *module, int inputId) { | 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> | template <class TPort> | ||||
TPort *createOutput(math::Vec pos, Module *module, int outputId) { | 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> | template <class TPort> | ||||
TPort *createOutputCentered(math::Vec pos, Module *module, int outputId) { | 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> | template <class TModuleLightWidget> | ||||
TModuleLightWidget *createLight(math::Vec pos, Module *module, int firstLightId) { | 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> | template <class TModuleLightWidget> | ||||
TModuleLightWidget *createLightCentered(math::Vec pos, Module *module, int firstLightId) { | 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; | |||||
} | } | ||||
@@ -156,63 +156,63 @@ struct Vec { | |||||
Vec() {} | Vec() {} | ||||
Vec(float x, float y) : x(x), y(y) {} | Vec(float x, float y) : x(x), y(y) {} | ||||
Vec neg() { | |||||
Vec neg() const { | |||||
return Vec(-x, -y); | return Vec(-x, -y); | ||||
} | } | ||||
Vec plus(Vec b) { | |||||
Vec plus(Vec b) const { | |||||
return Vec(x + b.x, y + b.y); | 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); | return Vec(x - b.x, y - b.y); | ||||
} | } | ||||
Vec mult(float s) { | |||||
Vec mult(float s) const { | |||||
return Vec(x * s, y * s); | return Vec(x * s, y * s); | ||||
} | } | ||||
Vec mult(Vec b) { | |||||
Vec mult(Vec b) const { | |||||
return Vec(x * b.x, y * b.y); | return Vec(x * b.x, y * b.y); | ||||
} | } | ||||
Vec div(float s) { | |||||
Vec div(float s) const { | |||||
return Vec(x / s, y / s); | return Vec(x / s, y / s); | ||||
} | } | ||||
Vec div(Vec b) { | |||||
Vec div(Vec b) const { | |||||
return Vec(x / b.x, y / b.y); | return Vec(x / b.x, y / b.y); | ||||
} | } | ||||
float dot(Vec b) { | |||||
float dot(Vec b) const { | |||||
return x * b.x + y * b.y; | return x * b.x + y * b.y; | ||||
} | } | ||||
float norm() { | |||||
float norm() const { | |||||
return std::hypotf(x, y); | return std::hypotf(x, y); | ||||
} | } | ||||
Vec flip() { | |||||
Vec flip() const { | |||||
return Vec(y, x); | 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)); | 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)); | 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)); | return Vec(std::round(x), std::round(y)); | ||||
} | } | ||||
Vec floor() { | |||||
Vec floor() const { | |||||
return Vec(std::floor(x), std::floor(y)); | return Vec(std::floor(x), std::floor(y)); | ||||
} | } | ||||
Vec ceil() { | |||||
Vec ceil() const { | |||||
return Vec(std::ceil(x), std::ceil(y)); | return Vec(std::ceil(x), std::ceil(y)); | ||||
} | } | ||||
bool isEqual(Vec b) { | |||||
bool isEqual(Vec b) const { | |||||
return x == b.x && y == b.y; | return x == b.x && y == b.y; | ||||
} | } | ||||
bool isZero() { | |||||
bool isZero() const { | |||||
return x == 0.0f && y == 0.0f; | return x == 0.0f && y == 0.0f; | ||||
} | } | ||||
bool isFinite() { | |||||
bool isFinite() const { | |||||
return std::isfinite(x) && std::isfinite(y); | 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 */ | /** 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 | return pos.x <= v.x && v.x < pos.x + size.x | ||||
&& pos.y <= v.y && v.y < pos.y + size.y; | && pos.y <= v.y && v.y < pos.y + size.y; | ||||
} | } | ||||
/** Returns whether this Rect contains an entire Rect */ | /** 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 | 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; | && pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y; | ||||
} | } | ||||
/** Returns whether this Rect overlaps with another Rect */ | /** 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) | 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); | && (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); | return pos.isEqual(r.pos) && size.isEqual(r.size); | ||||
} | } | ||||
Vec getCenter() { | |||||
Vec getCenter() const { | |||||
return pos.plus(size.mult(0.5f)); | return pos.plus(size.mult(0.5f)); | ||||
} | } | ||||
Vec getTopLeft() { | |||||
Vec getTopLeft() const { | |||||
return pos; | return pos; | ||||
} | } | ||||
Vec getTopRight() { | |||||
Vec getTopRight() const { | |||||
return pos.plus(Vec(size.x, 0.f)); | return pos.plus(Vec(size.x, 0.f)); | ||||
} | } | ||||
Vec getBottomLeft() { | |||||
Vec getBottomLeft() const { | |||||
return pos.plus(Vec(0.f, size.y)); | return pos.plus(Vec(0.f, size.y)); | ||||
} | } | ||||
Vec getBottomRight() { | |||||
Vec getBottomRight() const { | |||||
return pos.plus(size); | return pos.plus(size); | ||||
} | } | ||||
/** Clamps the edges of the rectangle to fit within a bound */ | /** Clamps the edges of the rectangle to fit within a bound */ | ||||
Rect clamp(Rect bound) { | |||||
Rect clamp(Rect bound) const { | |||||
Rect r; | Rect r; | ||||
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x); | 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); | r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y); | ||||
@@ -270,7 +270,7 @@ struct Rect { | |||||
return r; | return r; | ||||
} | } | ||||
/** Nudges the position to fix inside a bounding box */ | /** Nudges the position to fix inside a bounding box */ | ||||
Rect nudge(Rect bound) { | |||||
Rect nudge(Rect bound) const { | |||||
Rect r; | Rect r; | ||||
r.size = size; | r.size = size; | ||||
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x); | 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; | return r; | ||||
} | } | ||||
/** Expands this Rect to contain `other` */ | /** Expands this Rect to contain `other` */ | ||||
Rect expand(Rect other) { | |||||
Rect expand(Rect other) const { | |||||
Rect r; | Rect r; | ||||
r.pos.x = std::min(pos.x, other.pos.x); | r.pos.x = std::min(pos.x, other.pos.x); | ||||
r.pos.y = std::min(pos.y, other.pos.y); | r.pos.y = std::min(pos.y, other.pos.y); | ||||
@@ -287,16 +287,16 @@ struct Rect { | |||||
return r; | return r; | ||||
} | } | ||||
/** Returns a Rect with its position set to zero */ | /** Returns a Rect with its position set to zero */ | ||||
Rect zeroPos() { | |||||
Rect zeroPos() const { | |||||
return Rect(Vec(), size); | return Rect(Vec(), size); | ||||
} | } | ||||
Rect grow(Vec delta) { | |||||
Rect grow(Vec delta) const { | |||||
Rect r; | Rect r; | ||||
r.pos = pos.minus(delta); | r.pos = pos.minus(delta); | ||||
r.size = size.plus(delta.mult(2.f)); | r.size = size.plus(delta.mult(2.f)); | ||||
return r; | return r; | ||||
} | } | ||||
Rect shrink(Vec delta) { | |||||
Rect shrink(Vec delta) const { | |||||
Rect r; | Rect r; | ||||
r.pos = pos.plus(delta); | r.pos = pos.plus(delta); | ||||
r.size = size.minus(delta.mult(2.f)); | 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( | return Vec( | ||||
rack::math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), | 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)); | 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( | return Vec( | ||||
rack::math::clampBetween(x, bound.pos.x, bound.pos.x + bound.size.x), | 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)); | 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 | } // namespace math | ||||
@@ -65,34 +65,6 @@ struct Model { | |||||
virtual ModuleWidget *createModuleWidget() { return NULL; } | virtual ModuleWidget *createModuleWidget() { return NULL; } | ||||
/** Creates a ModuleWidget with no Module, useful for previews */ | /** Creates a ModuleWidget with no Module, useful for previews */ | ||||
virtual ModuleWidget *createModuleWidgetNull() { return NULL; } | 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; | |||||
} | |||||
}; | }; | ||||
@@ -23,7 +23,7 @@ namespace rack { | |||||
// Adopt some sub-namespaces into the main namespace for convenience | // Adopt some sub-namespaces into the main namespace for convenience | ||||
using namespace math; | using namespace math; | ||||
using namespace string; | |||||
using string::stringf; | |||||
} // namespace rack | } // namespace rack |
@@ -56,12 +56,6 @@ DEPRECATED inline float randomUniform() {return random::uniform();} | |||||
DEPRECATED inline float randomNormal() {return random::normal();} | DEPRECATED inline float randomNormal() {return random::normal();} | ||||
DEPRECATED inline float randomf() {return random::uniform();} | DEPRECATED inline float randomf() {return random::uniform();} | ||||
//////////////////// | |||||
// string | |||||
//////////////////// | |||||
using string::stringf; | |||||
//////////////////// | //////////////////// | ||||
// logger | // logger | ||||
//////////////////// | //////////////////// | ||||
@@ -1,274 +1,22 @@ | |||||
#pragma once | #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" |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -0,0 +1,13 @@ | |||||
#pragma once | |||||
#include "widgets.hpp" | |||||
namespace rack { | |||||
struct TooltipOverlay : TransparentWidget { | |||||
}; | |||||
} // namespace rack |
@@ -0,0 +1,13 @@ | |||||
#pragma once | |||||
#include "widgets.hpp" | |||||
namespace rack { | |||||
struct WindowOverlay : OpaqueWidget { | |||||
}; | |||||
} // namespace rack |
@@ -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 |
@@ -1,154 +1,15 @@ | |||||
#pragma once | #pragma once | ||||
#include <list> | |||||
#include "common.hpp" | |||||
#include "events.hpp" | |||||
#include "color.hpp" | |||||
#include "widgets/Widget.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 : "") |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -1,19 +1,35 @@ | |||||
#pragma once | #pragma once | ||||
#include <list> | |||||
#include "common.hpp" | |||||
#include "math.hpp" | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "color.hpp" | |||||
namespace rack { | 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 { | struct Widget { | ||||
/** Stores position and size */ | /** Stores position and size */ | ||||
math::Rect box = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY)); | math::Rect box = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY)); | ||||
Widget *parent = NULL; | Widget *parent = NULL; | ||||
std::list<Widget*> children; | std::list<Widget*> children; | ||||
/** Disable rendering but continue stepping */ | |||||
bool visible = true; | bool visible = true; | ||||
/** If set to true, parent will delete Widget in the next step() */ | |||||
bool requestedDelete = false; | |||||
virtual ~Widget(); | virtual ~Widget(); | ||||
@@ -56,72 +72,14 @@ struct Widget { | |||||
void removeChild(Widget *widget); | void removeChild(Widget *widget); | ||||
/** Removes and deletes all children */ | /** Removes and deletes all children */ | ||||
void clearChildren(); | void clearChildren(); | ||||
/** Recursively finalizes event start/end pairs as needed */ | |||||
void finalizeEvents(); | |||||
/** Advances the module by one frame */ | /** Advances the module by one frame */ | ||||
virtual void step(); | virtual void step(); | ||||
/** Draws to NanoVG context */ | /** Draws to NanoVG context */ | ||||
virtual void draw(NVGcontext *vg); | 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) {} | |||||
}; | }; | ||||
@@ -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 |
@@ -1,12 +1,13 @@ | |||||
#pragma once | #pragma once | ||||
#include <memory> | #include <memory> | ||||
#define GLEW_STATIC | #define GLEW_STATIC | ||||
#include <GL/glew.h> | |||||
#include <GLFW/glfw3.h> | |||||
#include "GL/glew.h" | |||||
#include "GLFW/glfw3.h" | |||||
#include "nanovg.h" | #include "nanovg.h" | ||||
#include "nanosvg.h" | #include "nanosvg.h" | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "math.hpp" | |||||
#ifdef ARCH_MAC | #ifdef ARCH_MAC | ||||
@@ -76,5 +77,8 @@ void windowSetTheme(NVGcolor bg, NVGcolor fg); | |||||
void windowSetFullScreen(bool fullScreen); | void windowSetFullScreen(bool fullScreen); | ||||
bool windowGetFullScreen(); | bool windowGetFullScreen(); | ||||
// In svg.cpp | |||||
void svgDraw(NVGcontext *vg, NSVGimage *svg); | |||||
} // namespace rack | } // namespace rack |
@@ -241,39 +241,39 @@ struct AudioInterfaceWidget : ModuleWidget { | |||||
AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) { | AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/AudioInterface.svg"))); | 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->box.size = mm2px(Vec(44, 28)); | ||||
audioWidget->audioIO = &module->audioIO; | audioWidget->audioIO = &module->audioIO; | ||||
addChild(audioWidget); | 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); |
@@ -3,25 +3,25 @@ | |||||
using namespace rack; | using namespace rack; | ||||
struct ModuleResizeHandle : Widget { | |||||
struct ModuleResizeHandle : EventWidget { | |||||
bool right = false; | bool right = false; | ||||
float dragX; | float dragX; | ||||
Rect originalBox; | Rect originalBox; | ||||
ModuleResizeHandle() { | ModuleResizeHandle() { | ||||
box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT); | 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; | dragX = gRackWidget->lastMousePos.x; | ||||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | ||||
originalBox = m->box; | originalBox = m->box; | ||||
} | } | ||||
void onDragMove(EventDragMove &e) override { | |||||
void on(event::DragMove &e) override { | |||||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | ||||
float newDragX = gRackWidget->lastMousePos.x; | float newDragX = gRackWidget->lastMousePos.x; | ||||
@@ -78,10 +78,10 @@ struct BlankWidget : ModuleWidget { | |||||
addChild(leftHandle); | addChild(leftHandle); | ||||
addChild(rightHandle); | 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(topRightScrew); | ||||
addChild(bottomRightScrew); | 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); |
@@ -28,11 +28,11 @@ struct Grid16MidiWidget : MidiWidget { | |||||
void createGridChoices() { | void createGridChoices() { | ||||
Vec pos = channelChoice->box.getBottomLeft(); | Vec pos = channelChoice->box.getBottomLeft(); | ||||
for (int x = 1; x < 4; x++) { | for (int x = 1; x < 4; x++) { | ||||
vSeparators[x] = Widget::create<LedDisplaySeparator>(pos); | |||||
vSeparators[x] = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(vSeparators[x]); | addChild(vSeparators[x]); | ||||
} | } | ||||
for (int y = 0; y < 4; y++) { | for (int y = 0; y < 4; y++) { | ||||
hSeparators[y] = Widget::create<LedDisplaySeparator>(pos); | |||||
hSeparators[y] = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(hSeparators[y]); | addChild(hSeparators[y]); | ||||
for (int x = 0; x < 4; x++) { | for (int x = 0; x < 4; x++) { | ||||
GridChoice *gridChoice = createGridChoice(); | GridChoice *gridChoice = createGridChoice(); | ||||
@@ -129,41 +129,41 @@ struct MidiCcChoice : GridChoice { | |||||
else { | else { | ||||
text = string::stringf("%d", module->learnedCcs[id]); | text = string::stringf("%d", module->learnedCcs[id]); | ||||
color.a = 1.0; | 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; | module->learningId = id; | ||||
focusCc = -1; | focusCc = -1; | ||||
} | } | ||||
void onDefocus(EventDefocus &e) override { | |||||
void on(event::Deselect &e) override { | |||||
if (0 <= focusCc && focusCc < 128) { | if (0 <= focusCc && focusCc < 128) { | ||||
module->learnedCcs[id] = focusCc; | module->learnedCcs[id] = focusCc; | ||||
} | } | ||||
module->learningId = -1; | module->learningId = -1; | ||||
} | } | ||||
void onText(EventText &e) override { | |||||
void on(event::SelectText &e) override { | |||||
char c = e.codepoint; | char c = e.codepoint; | ||||
if ('0' <= c && c <= '9') { | if ('0' <= c && c <= '9') { | ||||
if (focusCc < 0) | if (focusCc < 0) | ||||
focusCc = 0; | focusCc = 0; | ||||
focusCc = focusCc * 10 + (c - '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) { | 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) { | MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/MIDICCToCVInterface.svg"))); | 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->module = module; | ||||
midiWidget->box.size = mm2px(Vec(44, 54.667)); | midiWidget->box.size = mm2px(Vec(44, 54.667)); | ||||
midiWidget->midiIO = &module->midiInput; | 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); |
@@ -261,25 +261,25 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||||
MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) { | MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/MIDIToCVInterface.svg"))); | 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->box.size = mm2px(Vec(33.840, 28)); | ||||
midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
addChild(midiWidget); | addChild(midiWidget); | ||||
@@ -292,7 +292,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||||
MIDIToCVInterface *module; | MIDIToCVInterface *module; | ||||
int index; | int index; | ||||
int division; | int division; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
module->divisions[index] = division; | 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<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"}; | std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"}; | ||||
for (size_t i = 0; i < divisions.size(); i++) { | 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->module = module; | ||||
item->index = index; | item->index = index; | ||||
item->division = divisions[i]; | item->division = divisions[i]; | ||||
@@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||||
menu->addChild(construct<MenuLabel>()); | menu->addChild(construct<MenuLabel>()); | ||||
for (int i = 0; i < 2; i++) { | 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->module = module; | ||||
item->index = i; | item->index = i; | ||||
menu->addChild(item); | 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); |
@@ -171,17 +171,17 @@ struct MidiTrigChoice : GridChoice { | |||||
text = string::stringf("%s%d", noteNames[semi], oct); | text = string::stringf("%s%d", noteNames[semi], oct); | ||||
color.a = 1.0; | 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; | module->learningId = id; | ||||
} | } | ||||
void onDefocus(EventDefocus &e) override { | |||||
void on(event::Deselect &e) override { | |||||
module->learningId = -1; | module->learningId = -1; | ||||
} | } | ||||
}; | }; | ||||
@@ -201,29 +201,29 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||||
MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) { | MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/MIDITriggerToCVInterface.svg"))); | 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->module = module; | ||||
midiWidget->box.size = mm2px(Vec(44, 54.667)); | midiWidget->box.size = mm2px(Vec(44, 54.667)); | ||||
midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
@@ -236,17 +236,17 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||||
struct VelocityItem : MenuItem { | struct VelocityItem : MenuItem { | ||||
MIDITriggerToCVInterface *module; | MIDITriggerToCVInterface *module; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
module->velocity ^= true; | 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; | velocityItem->module = module; | ||||
menu->addChild(velocityItem); | 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); |
@@ -10,12 +10,12 @@ struct NotesWidget : ModuleWidget { | |||||
NotesWidget(Module *module) : ModuleWidget(module) { | NotesWidget(Module *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/Notes.svg"))); | 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->box.size = mm2px(Vec(74.480, 102.753)); | ||||
textField->multiline = true; | textField->multiline = true; | ||||
addChild(textField); | 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); |
@@ -303,29 +303,29 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { | |||||
QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) { | QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) { | ||||
setPanel(SVG::load(asset::global("res/Core/QuadMIDIToCVInterface.svg"))); | 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->box.size = mm2px(Vec(44, 28)); | ||||
midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
addChild(midiWidget); | addChild(midiWidget); | ||||
@@ -337,17 +337,19 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { | |||||
struct PolyphonyItem : MenuItem { | struct PolyphonyItem : MenuItem { | ||||
QuadMIDIToCVInterface *module; | QuadMIDIToCVInterface *module; | ||||
QuadMIDIToCVInterface::PolyMode polyMode; | QuadMIDIToCVInterface::PolyMode polyMode; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
module->polyMode = polyMode; | module->polyMode = polyMode; | ||||
module->onReset(); | 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) { | 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->module = module; | ||||
item->polyMode = polyMode; | item->polyMode = polyMode; | ||||
menu->addChild(item); | 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); | |||||
@@ -1,5 +1,6 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "audio.hpp" | #include "audio.hpp" | ||||
#include "helpers.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -8,16 +9,16 @@ namespace rack { | |||||
struct AudioDriverItem : MenuItem { | struct AudioDriverItem : MenuItem { | ||||
AudioIO *audioIO; | AudioIO *audioIO; | ||||
int driver; | int driver; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
audioIO->setDriver(driver); | audioIO->setDriver(driver); | ||||
} | } | ||||
}; | }; | ||||
struct AudioDriverChoice : LedDisplayChoice { | struct AudioDriverChoice : LedDisplayChoice { | ||||
AudioWidget *audioWidget; | AudioWidget *audioWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio driver")); | |||||
menu->addChild(createMenuLabel("Audio driver")); | |||||
for (int driver : audioWidget->audioIO->getDrivers()) { | for (int driver : audioWidget->audioIO->getDrivers()) { | ||||
AudioDriverItem *item = new AudioDriverItem(); | AudioDriverItem *item = new AudioDriverItem(); | ||||
item->audioIO = audioWidget->audioIO; | item->audioIO = audioWidget->audioIO; | ||||
@@ -37,7 +38,7 @@ struct AudioDeviceItem : MenuItem { | |||||
AudioIO *audioIO; | AudioIO *audioIO; | ||||
int device; | int device; | ||||
int offset; | int offset; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
audioIO->setDevice(device, offset); | audioIO->setDevice(device, offset); | ||||
} | } | ||||
}; | }; | ||||
@@ -47,9 +48,9 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||||
/** Prevents devices with a ridiculous number of channels from being displayed */ | /** Prevents devices with a ridiculous number of channels from being displayed */ | ||||
int maxTotalChannels = 128; | int maxTotalChannels = 128; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device")); | |||||
menu->addChild(createMenuLabel("Audio device")); | |||||
int deviceCount = audioWidget->audioIO->getDeviceCount(); | int deviceCount = audioWidget->audioIO->getDeviceCount(); | ||||
{ | { | ||||
AudioDeviceItem *item = new AudioDeviceItem(); | AudioDeviceItem *item = new AudioDeviceItem(); | ||||
@@ -88,19 +89,19 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||||
struct AudioSampleRateItem : MenuItem { | struct AudioSampleRateItem : MenuItem { | ||||
AudioIO *audioIO; | AudioIO *audioIO; | ||||
int sampleRate; | int sampleRate; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
audioIO->setSampleRate(sampleRate); | audioIO->setSampleRate(sampleRate); | ||||
} | } | ||||
}; | }; | ||||
struct AudioSampleRateChoice : LedDisplayChoice { | struct AudioSampleRateChoice : LedDisplayChoice { | ||||
AudioWidget *audioWidget; | AudioWidget *audioWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Sample rate")); | |||||
menu->addChild(createMenuLabel("Sample rate")); | |||||
std::vector<int> sampleRates = audioWidget->audioIO->getSampleRates(); | std::vector<int> sampleRates = audioWidget->audioIO->getSampleRates(); | ||||
if (sampleRates.empty()) { | if (sampleRates.empty()) { | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)")); | |||||
menu->addChild(createMenuLabel("(Locked by device)")); | |||||
} | } | ||||
for (int sampleRate : sampleRates) { | for (int sampleRate : sampleRates) { | ||||
AudioSampleRateItem *item = new AudioSampleRateItem(); | AudioSampleRateItem *item = new AudioSampleRateItem(); | ||||
@@ -120,19 +121,19 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||||
struct AudioBlockSizeItem : MenuItem { | struct AudioBlockSizeItem : MenuItem { | ||||
AudioIO *audioIO; | AudioIO *audioIO; | ||||
int blockSize; | int blockSize; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
audioIO->setBlockSize(blockSize); | audioIO->setBlockSize(blockSize); | ||||
} | } | ||||
}; | }; | ||||
struct AudioBlockSizeChoice : LedDisplayChoice { | struct AudioBlockSizeChoice : LedDisplayChoice { | ||||
AudioWidget *audioWidget; | AudioWidget *audioWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Block size")); | |||||
menu->addChild(createMenuLabel("Block size")); | |||||
std::vector<int> blockSizes = audioWidget->audioIO->getBlockSizes(); | std::vector<int> blockSizes = audioWidget->audioIO->getBlockSizes(); | ||||
if (blockSizes.empty()) { | if (blockSizes.empty()) { | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)")); | |||||
menu->addChild(createMenuLabel("(Locked by device)")); | |||||
} | } | ||||
for (int blockSize : blockSizes) { | for (int blockSize : blockSizes) { | ||||
AudioBlockSizeItem *item = new AudioBlockSizeItem(); | AudioBlockSizeItem *item = new AudioBlockSizeItem(); | ||||
@@ -155,34 +156,34 @@ AudioWidget::AudioWidget() { | |||||
math::Vec pos = math::Vec(); | math::Vec pos = math::Vec(); | ||||
AudioDriverChoice *driverChoice = Widget::create<AudioDriverChoice>(pos); | |||||
AudioDriverChoice *driverChoice = createWidget<AudioDriverChoice>(pos); | |||||
driverChoice->audioWidget = this; | driverChoice->audioWidget = this; | ||||
addChild(driverChoice); | addChild(driverChoice); | ||||
pos = driverChoice->box.getBottomLeft(); | pos = driverChoice->box.getBottomLeft(); | ||||
this->driverChoice = driverChoice; | this->driverChoice = driverChoice; | ||||
this->driverSeparator = Widget::create<LedDisplaySeparator>(pos); | |||||
this->driverSeparator = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(this->driverSeparator); | addChild(this->driverSeparator); | ||||
AudioDeviceChoice *deviceChoice = Widget::create<AudioDeviceChoice>(pos); | |||||
AudioDeviceChoice *deviceChoice = createWidget<AudioDeviceChoice>(pos); | |||||
deviceChoice->audioWidget = this; | deviceChoice->audioWidget = this; | ||||
addChild(deviceChoice); | addChild(deviceChoice); | ||||
pos = deviceChoice->box.getBottomLeft(); | pos = deviceChoice->box.getBottomLeft(); | ||||
this->deviceChoice = deviceChoice; | this->deviceChoice = deviceChoice; | ||||
this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos); | |||||
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(this->deviceSeparator); | addChild(this->deviceSeparator); | ||||
AudioSampleRateChoice *sampleRateChoice = Widget::create<AudioSampleRateChoice>(pos); | |||||
AudioSampleRateChoice *sampleRateChoice = createWidget<AudioSampleRateChoice>(pos); | |||||
sampleRateChoice->audioWidget = this; | sampleRateChoice->audioWidget = this; | ||||
addChild(sampleRateChoice); | addChild(sampleRateChoice); | ||||
this->sampleRateChoice = 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; | this->sampleRateSeparator->box.size.y = this->sampleRateChoice->box.size.y; | ||||
addChild(this->sampleRateSeparator); | addChild(this->sampleRateSeparator); | ||||
AudioBlockSizeChoice *bufferSizeChoice = Widget::create<AudioBlockSizeChoice>(pos); | |||||
AudioBlockSizeChoice *bufferSizeChoice = createWidget<AudioBlockSizeChoice>(pos); | |||||
bufferSizeChoice->audioWidget = this; | bufferSizeChoice->audioWidget = this; | ||||
addChild(bufferSizeChoice); | addChild(bufferSizeChoice); | ||||
this->bufferSizeChoice = bufferSizeChoice; | this->bufferSizeChoice = bufferSizeChoice; | ||||
@@ -15,13 +15,13 @@ Knob::Knob() { | |||||
smooth = true; | smooth = true; | ||||
} | } | ||||
void Knob::onDragStart(EventDragStart &e) { | |||||
void Knob::on(event::DragStart &e) { | |||||
windowCursorLock(); | windowCursorLock(); | ||||
dragValue = value; | dragValue = value; | ||||
randomizable = false; | randomizable = false; | ||||
} | } | ||||
void Knob::onDragMove(EventDragMove &e) { | |||||
void Knob::on(event::DragMove &e) { | |||||
float range; | float range; | ||||
if (std::isfinite(minValue) && std::isfinite(maxValue)) { | if (std::isfinite(minValue) && std::isfinite(maxValue)) { | ||||
range = maxValue - minValue; | range = maxValue - minValue; | ||||
@@ -30,7 +30,7 @@ void Knob::onDragMove(EventDragMove &e) { | |||||
// Continuous encoders scale as if their limits are +/-1 | // Continuous encoders scale as if their limits are +/-1 | ||||
range = 1.f - (-1.f); | 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 | // Drag slower if Mod is held | ||||
if (windowIsModPressed()) | if (windowIsModPressed()) | ||||
@@ -43,7 +43,7 @@ void Knob::onDragMove(EventDragMove &e) { | |||||
setValue(dragValue); | setValue(dragValue); | ||||
} | } | ||||
void Knob::onDragEnd(EventDragEnd &e) { | |||||
void Knob::on(event::DragEnd &e) { | |||||
windowCursorUnlock(); | windowCursorUnlock(); | ||||
randomizable = true; | randomizable = true; | ||||
} | } | ||||
@@ -52,11 +52,10 @@ void LedDisplayChoice::draw(NVGcontext *vg) { | |||||
nvgResetScissor(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; | e.target = this; | ||||
} | } | ||||
} | } | ||||
@@ -85,7 +84,7 @@ void LedDisplayTextField::draw(NVGcontext *vg) { | |||||
NVGcolor highlightColor = color; | NVGcolor highlightColor = color; | ||||
highlightColor.a = 0.5; | highlightColor.a = 0.5; | ||||
int begin = std::min(cursor, selection); | 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, | bndIconLabelCaret(vg, textOffset.x, textOffset.y, | ||||
box.size.x - 2*textOffset.x, box.size.y - 2*textOffset.y, | box.size.x - 2*textOffset.x, box.size.y - 2*textOffset.y, | ||||
-1, color, 12, text.c_str(), highlightColor, begin, end); | -1, color, 12, text.c_str(), highlightColor, begin, end); | ||||
@@ -1,5 +1,6 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "midi.hpp" | #include "midi.hpp" | ||||
#include "helpers.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -8,16 +9,16 @@ namespace rack { | |||||
struct MidiDriverItem : MenuItem { | struct MidiDriverItem : MenuItem { | ||||
MidiIO *midiIO; | MidiIO *midiIO; | ||||
int driverId; | int driverId; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
midiIO->setDriverId(driverId); | midiIO->setDriverId(driverId); | ||||
} | } | ||||
}; | }; | ||||
struct MidiDriverChoice : LedDisplayChoice { | struct MidiDriverChoice : LedDisplayChoice { | ||||
MidiWidget *midiWidget; | MidiWidget *midiWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI driver")); | |||||
menu->addChild(createMenuLabel("MIDI driver")); | |||||
for (int driverId : midiWidget->midiIO->getDriverIds()) { | for (int driverId : midiWidget->midiIO->getDriverIds()) { | ||||
MidiDriverItem *item = new MidiDriverItem(); | MidiDriverItem *item = new MidiDriverItem(); | ||||
item->midiIO = midiWidget->midiIO; | item->midiIO = midiWidget->midiIO; | ||||
@@ -42,16 +43,16 @@ struct MidiDriverChoice : LedDisplayChoice { | |||||
struct MidiDeviceItem : MenuItem { | struct MidiDeviceItem : MenuItem { | ||||
MidiIO *midiIO; | MidiIO *midiIO; | ||||
int deviceId; | int deviceId; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
midiIO->setDeviceId(deviceId); | midiIO->setDeviceId(deviceId); | ||||
} | } | ||||
}; | }; | ||||
struct MidiDeviceChoice : LedDisplayChoice { | struct MidiDeviceChoice : LedDisplayChoice { | ||||
MidiWidget *midiWidget; | MidiWidget *midiWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI device")); | |||||
menu->addChild(createMenuLabel("MIDI device")); | |||||
{ | { | ||||
MidiDeviceItem *item = new MidiDeviceItem(); | MidiDeviceItem *item = new MidiDeviceItem(); | ||||
item->midiIO = midiWidget->midiIO; | item->midiIO = midiWidget->midiIO; | ||||
@@ -84,16 +85,16 @@ struct MidiDeviceChoice : LedDisplayChoice { | |||||
struct MidiChannelItem : MenuItem { | struct MidiChannelItem : MenuItem { | ||||
MidiIO *midiIO; | MidiIO *midiIO; | ||||
int channel; | int channel; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
midiIO->channel = channel; | midiIO->channel = channel; | ||||
} | } | ||||
}; | }; | ||||
struct MidiChannelChoice : LedDisplayChoice { | struct MidiChannelChoice : LedDisplayChoice { | ||||
MidiWidget *midiWidget; | MidiWidget *midiWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI channel")); | |||||
menu->addChild(createMenuLabel("MIDI channel")); | |||||
for (int channel = -1; channel < 16; channel++) { | for (int channel = -1; channel < 16; channel++) { | ||||
MidiChannelItem *item = new MidiChannelItem(); | MidiChannelItem *item = new MidiChannelItem(); | ||||
item->midiIO = midiWidget->midiIO; | item->midiIO = midiWidget->midiIO; | ||||
@@ -114,25 +115,25 @@ MidiWidget::MidiWidget() { | |||||
math::Vec pos = math::Vec(); | math::Vec pos = math::Vec(); | ||||
MidiDriverChoice *driverChoice = Widget::create<MidiDriverChoice>(pos); | |||||
MidiDriverChoice *driverChoice = createWidget<MidiDriverChoice>(pos); | |||||
driverChoice->midiWidget = this; | driverChoice->midiWidget = this; | ||||
addChild(driverChoice); | addChild(driverChoice); | ||||
pos = driverChoice->box.getBottomLeft(); | pos = driverChoice->box.getBottomLeft(); | ||||
this->driverChoice = driverChoice; | this->driverChoice = driverChoice; | ||||
this->driverSeparator = Widget::create<LedDisplaySeparator>(pos); | |||||
this->driverSeparator = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(this->driverSeparator); | addChild(this->driverSeparator); | ||||
MidiDeviceChoice *deviceChoice = Widget::create<MidiDeviceChoice>(pos); | |||||
MidiDeviceChoice *deviceChoice = createWidget<MidiDeviceChoice>(pos); | |||||
deviceChoice->midiWidget = this; | deviceChoice->midiWidget = this; | ||||
addChild(deviceChoice); | addChild(deviceChoice); | ||||
pos = deviceChoice->box.getBottomLeft(); | pos = deviceChoice->box.getBottomLeft(); | ||||
this->deviceChoice = deviceChoice; | this->deviceChoice = deviceChoice; | ||||
this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos); | |||||
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos); | |||||
addChild(this->deviceSeparator); | addChild(this->deviceSeparator); | ||||
MidiChannelChoice *channelChoice = Widget::create<MidiChannelChoice>(pos); | |||||
MidiChannelChoice *channelChoice = createWidget<MidiChannelChoice>(pos); | |||||
channelChoice->midiWidget = this; | channelChoice->midiWidget = this; | ||||
addChild(channelChoice); | addChild(channelChoice); | ||||
this->channelChoice = channelChoice; | this->channelChoice = channelChoice; | ||||
@@ -1,6 +1,7 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "plugin.hpp" | #include "plugin.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "helpers.hpp" | |||||
#include <set> | #include <set> | ||||
#include <algorithm> | #include <algorithm> | ||||
@@ -45,7 +46,7 @@ static bool isModelMatch(Model *model, std::string search) { | |||||
struct FavoriteRadioButton : RadioButton { | struct FavoriteRadioButton : RadioButton { | ||||
Model *model = NULL; | 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) { | void setText(std::string text) { | ||||
clearChildren(); | clearChildren(); | ||||
Label *label = Widget::create<Label>(math::Vec(0, 12 + itemMargin)); | |||||
Label *label = createWidget<Label>(math::Vec(0, 12 + itemMargin)); | |||||
label->text = text; | label->text = text; | ||||
label->fontSize = 20; | label->fontSize = 20; | ||||
label->color.a *= 0.5; | label->color.a *= 0.5; | ||||
@@ -78,19 +79,19 @@ struct BrowserListItem : OpaqueWidget { | |||||
Widget::draw(vg); | 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) | if (e.origin != this) | ||||
return; | return; | ||||
doAction(); | doAction(); | ||||
} | } | ||||
void 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` | // deletes `this` | ||||
gScene->setOverlay(NULL); | gScene->setOverlay(NULL); | ||||
} | } | ||||
@@ -107,7 +108,7 @@ struct ModelItem : BrowserListItem { | |||||
assert(model); | assert(model); | ||||
this->model = 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->box.size.x = 20; | ||||
favoriteButton->label = "★"; | favoriteButton->label = "★"; | ||||
addChild(favoriteButton); | addChild(favoriteButton); | ||||
@@ -118,11 +119,11 @@ struct ModelItem : BrowserListItem { | |||||
favoriteButton->setValue(1); | favoriteButton->setValue(1); | ||||
favoriteButton->model = model; | favoriteButton->model = model; | ||||
Label *nameLabel = Widget::create<Label>(favoriteButton->box.getTopRight()); | |||||
Label *nameLabel = createWidget<Label>(favoriteButton->box.getTopRight()); | |||||
nameLabel->text = model->name; | nameLabel->text = model->name; | ||||
addChild(nameLabel); | addChild(nameLabel); | ||||
pluginLabel = Widget::create<Label>(math::Vec(0, itemMargin)); | |||||
pluginLabel = createWidget<Label>(math::Vec(0, itemMargin)); | |||||
pluginLabel->alignment = Label::RIGHT_ALIGNMENT; | pluginLabel->alignment = Label::RIGHT_ALIGNMENT; | ||||
pluginLabel->text = model->plugin->slug + " " + model->plugin->version; | pluginLabel->text = model->plugin->slug + " " + model->plugin->version; | ||||
pluginLabel->color.a = 0.5; | pluginLabel->color.a = 0.5; | ||||
@@ -135,7 +136,7 @@ struct ModelItem : BrowserListItem { | |||||
pluginLabel->box.size.x = box.size.x - BND_SCROLLBAR_WIDTH; | 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(); | ModuleWidget *moduleWidget = model->createModuleWidget(); | ||||
if (!moduleWidget) | if (!moduleWidget) | ||||
return; | return; | ||||
@@ -153,7 +154,7 @@ struct AuthorItem : BrowserListItem { | |||||
void setAuthor(std::string author) { | void setAuthor(std::string author) { | ||||
clearChildren(); | clearChildren(); | ||||
this->author = author; | 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()) | if (author.empty()) | ||||
authorLabel->text = "Show all modules"; | authorLabel->text = "Show all modules"; | ||||
else | else | ||||
@@ -161,7 +162,7 @@ struct AuthorItem : BrowserListItem { | |||||
addChild(authorLabel); | addChild(authorLabel); | ||||
} | } | ||||
void onAction(EventAction &e) override; | |||||
void on(event::Action &e) override; | |||||
}; | }; | ||||
@@ -171,7 +172,7 @@ struct TagItem : BrowserListItem { | |||||
void setTag(ModelTag tag) { | void setTag(ModelTag tag) { | ||||
clearChildren(); | clearChildren(); | ||||
this->tag = tag; | 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) | if (tag == NO_TAG) | ||||
tagLabel->text = "Show all tags"; | tagLabel->text = "Show all tags"; | ||||
else | else | ||||
@@ -179,18 +180,18 @@ struct TagItem : BrowserListItem { | |||||
addChild(tagLabel); | addChild(tagLabel); | ||||
} | } | ||||
void onAction(EventAction &e) override; | |||||
void on(event::Action &e) override; | |||||
}; | }; | ||||
struct ClearFilterItem : BrowserListItem { | struct ClearFilterItem : BrowserListItem { | ||||
ClearFilterItem() { | ClearFilterItem() { | ||||
Label *label = Widget::create<Label>(math::Vec(0, 0 + itemMargin)); | |||||
Label *label = createWidget<Label>(math::Vec(0, 0 + itemMargin)); | |||||
label->text = "Back"; | label->text = "Back"; | ||||
addChild(label); | addChild(label); | ||||
} | } | ||||
void onAction(EventAction &e) override; | |||||
void on(event::Action &e) override; | |||||
}; | }; | ||||
@@ -270,8 +271,8 @@ struct ModuleBrowser; | |||||
struct SearchModuleField : TextField { | struct SearchModuleField : TextField { | ||||
ModuleBrowser *moduleBrowser; | 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); | 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); | box.size.y = std::min(box.size.y, moduleScroll->box.getBottomRight().y); | ||||
gFocusedWidget = searchField; | |||||
gSelectedWidget = searchField; | |||||
Widget::step(); | Widget::step(); | ||||
} | } | ||||
}; | }; | ||||
@@ -436,31 +437,31 @@ struct ModuleBrowser : OpaqueWidget { | |||||
// Implementations of inline methods above | // Implementations of inline methods above | ||||
void AuthorItem::onAction(EventAction &e) { | |||||
void AuthorItem::on(event::Action &e) { | |||||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ||||
sAuthorFilter = author; | sAuthorFilter = author; | ||||
moduleBrowser->clearSearch(); | moduleBrowser->clearSearch(); | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.consumed = false; | |||||
e.target = this; | |||||
} | } | ||||
void TagItem::onAction(EventAction &e) { | |||||
void TagItem::on(event::Action &e) { | |||||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ||||
sTagFilter = tag; | sTagFilter = tag; | ||||
moduleBrowser->clearSearch(); | moduleBrowser->clearSearch(); | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.consumed = false; | |||||
e.target = this; | |||||
} | } | ||||
void ClearFilterItem::onAction(EventAction &e) { | |||||
void ClearFilterItem::on(event::Action &e) { | |||||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | ||||
sAuthorFilter = ""; | sAuthorFilter = ""; | ||||
sTagFilter = NO_TAG; | sTagFilter = NO_TAG; | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.consumed = false; | |||||
e.target = this; | |||||
} | } | ||||
void FavoriteRadioButton::onAction(EventAction &e) { | |||||
void FavoriteRadioButton::on(event::Action &e) { | |||||
if (!model) | if (!model) | ||||
return; | return; | ||||
if (value) { | if (value) { | ||||
@@ -477,56 +478,56 @@ void FavoriteRadioButton::onAction(EventAction &e) { | |||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
} | } | ||||
void BrowserListItem::onDragStart(EventDragStart &e) { | |||||
void BrowserListItem::on(event::DragStart &e) { | |||||
BrowserList *list = dynamic_cast<BrowserList*>(parent); | BrowserList *list = dynamic_cast<BrowserList*>(parent); | ||||
if (list) { | if (list) { | ||||
list->selectItem(this); | list->selectItem(this); | ||||
} | } | ||||
} | } | ||||
void SearchModuleField::onTextChange() { | |||||
void SearchModuleField::on(event::Change &e) { | |||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
} | } | ||||
void SearchModuleField::onKey(EventKey &e) { | |||||
void SearchModuleField::on(event::SelectKey &e) { | |||||
switch (e.key) { | switch (e.key) { | ||||
case GLFW_KEY_ESCAPE: { | case GLFW_KEY_ESCAPE: { | ||||
gScene->setOverlay(NULL); | gScene->setOverlay(NULL); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} break; | } break; | ||||
case GLFW_KEY_UP: { | case GLFW_KEY_UP: { | ||||
moduleBrowser->moduleList->incrementSelection(-1); | moduleBrowser->moduleList->incrementSelection(-1); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} break; | } break; | ||||
case GLFW_KEY_DOWN: { | case GLFW_KEY_DOWN: { | ||||
moduleBrowser->moduleList->incrementSelection(1); | moduleBrowser->moduleList->incrementSelection(1); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} break; | } break; | ||||
case GLFW_KEY_PAGE_UP: { | case GLFW_KEY_PAGE_UP: { | ||||
moduleBrowser->moduleList->incrementSelection(-5); | moduleBrowser->moduleList->incrementSelection(-5); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} break; | } break; | ||||
case GLFW_KEY_PAGE_DOWN: { | case GLFW_KEY_PAGE_DOWN: { | ||||
moduleBrowser->moduleList->incrementSelection(5); | moduleBrowser->moduleList->incrementSelection(5); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} break; | } break; | ||||
case GLFW_KEY_ENTER: { | case GLFW_KEY_ENTER: { | ||||
BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | ||||
if (item) { | if (item) { | ||||
item->doAction(); | item->doAction(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
} | } | ||||
if (!e.consumed) { | |||||
TextField::onKey(e); | |||||
if (!e.target) { | |||||
TextField::on(e); | |||||
} | } | ||||
} | } | ||||
@@ -330,93 +330,86 @@ void ModuleWidget::drawShadow(NVGcontext *vg) { | |||||
nvgFill(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) { | switch (e.key) { | ||||
case GLFW_KEY_I: { | case GLFW_KEY_I: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
reset(); | reset(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_R: { | case GLFW_KEY_R: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
randomize(); | randomize(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_C: { | case GLFW_KEY_C: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
copyClipboard(); | copyClipboard(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_V: { | case GLFW_KEY_V: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
pasteClipboard(); | pasteClipboard(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_D: { | case GLFW_KEY_D: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
gRackWidget->cloneModule(this); | gRackWidget->cloneModule(this); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_U: { | case GLFW_KEY_U: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
disconnect(); | disconnect(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
return; | return; | ||||
} | } | ||||
} break; | } break; | ||||
} | } | ||||
Widget::onHoverKey(e); | |||||
OpaqueWidget::on(e); | |||||
} | } | ||||
void ModuleWidget::onDragStart(EventDragStart &e) { | |||||
void ModuleWidget::on(event::DragStart &e) { | |||||
dragPos = gRackWidget->lastMousePos.minus(box.pos); | 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) { | if (!gRackWidget->lockModules) { | ||||
math::Rect newBox = box; | math::Rect newBox = box; | ||||
newBox.pos = gRackWidget->lastMousePos.minus(dragPos); | newBox.pos = gRackWidget->lastMousePos.minus(dragPos); | ||||
@@ -427,65 +420,64 @@ void ModuleWidget::onDragMove(EventDragMove &e) { | |||||
struct ModuleDisconnectItem : MenuItem { | struct ModuleDisconnectItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->disconnect(); | moduleWidget->disconnect(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleResetItem : MenuItem { | struct ModuleResetItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->reset(); | moduleWidget->reset(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleRandomizeItem : MenuItem { | struct ModuleRandomizeItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->randomize(); | moduleWidget->randomize(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleCopyItem : MenuItem { | struct ModuleCopyItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->copyClipboard(); | moduleWidget->copyClipboard(); | ||||
} | } | ||||
}; | }; | ||||
struct ModulePasteItem : MenuItem { | struct ModulePasteItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->pasteClipboard(); | moduleWidget->pasteClipboard(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleSaveItem : MenuItem { | struct ModuleSaveItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->saveDialog(); | moduleWidget->saveDialog(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleLoadItem : MenuItem { | struct ModuleLoadItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
moduleWidget->loadDialog(); | moduleWidget->loadDialog(); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleCloneItem : MenuItem { | struct ModuleCloneItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->cloneModule(moduleWidget); | gRackWidget->cloneModule(moduleWidget); | ||||
} | } | ||||
}; | }; | ||||
struct ModuleDeleteItem : MenuItem { | struct ModuleDeleteItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->deleteModule(moduleWidget); | gRackWidget->deleteModule(moduleWidget); | ||||
moduleWidget->finalizeEvents(); | |||||
delete moduleWidget; | delete moduleWidget; | ||||
} | } | ||||
}; | }; | ||||
@@ -4,13 +4,11 @@ | |||||
namespace rack { | namespace rack { | ||||
void MomentarySwitch::onDragStart(EventDragStart &e) { | |||||
void MomentarySwitch::on(event::DragStart &e) { | |||||
setValue(maxValue); | setValue(maxValue); | ||||
EventAction eAction; | |||||
onAction(eAction); | |||||
} | } | ||||
void MomentarySwitch::onDragEnd(EventDragEnd &e) { | |||||
void MomentarySwitch::on(event::DragEnd &e) { | |||||
setValue(minValue); | setValue(minValue); | ||||
} | } | ||||
@@ -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) | if (!module) | ||||
return; | return; | ||||
@@ -3,6 +3,7 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "plugin.hpp" | #include "plugin.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "helpers.hpp" | |||||
#include "osdialog.h" | #include "osdialog.h" | ||||
@@ -10,7 +11,7 @@ namespace rack { | |||||
struct RegisterButton : Button { | struct RegisterButton : Button { | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
std::thread t([&]() { | std::thread t([&]() { | ||||
system::openBrowser("https://vcvrack.com/"); | system::openBrowser("https://vcvrack.com/"); | ||||
}); | }); | ||||
@@ -22,7 +23,7 @@ struct RegisterButton : Button { | |||||
struct LogInButton : Button { | struct LogInButton : Button { | ||||
TextField *emailField; | TextField *emailField; | ||||
TextField *passwordField; | TextField *passwordField; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
std::thread t(pluginLogIn, emailField->text, passwordField->text); | std::thread t(pluginLogIn, emailField->text, passwordField->text); | ||||
t.detach(); | t.detach(); | ||||
passwordField->text = ""; | passwordField->text = ""; | ||||
@@ -38,7 +39,7 @@ struct StatusLabel : Label { | |||||
struct ManageButton : Button { | struct ManageButton : Button { | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
std::thread t([&]() { | std::thread t([&]() { | ||||
system::openBrowser("https://vcvrack.com/plugins.html"); | system::openBrowser("https://vcvrack.com/plugins.html"); | ||||
}); | }); | ||||
@@ -84,7 +85,7 @@ struct SyncButton : Button { | |||||
nvgStroke(vg); | nvgStroke(vg); | ||||
} | } | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
available = false; | available = false; | ||||
std::thread t([this]() { | std::thread t([this]() { | ||||
if (pluginSync(false)) | if (pluginSync(false)) | ||||
@@ -96,7 +97,7 @@ struct SyncButton : Button { | |||||
struct LogOutButton : Button { | struct LogOutButton : Button { | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
pluginLogOut(); | pluginLogOut(); | ||||
} | } | ||||
}; | }; | ||||
@@ -114,7 +115,7 @@ struct DownloadProgressBar : ProgressBar { | |||||
struct CancelButton : Button { | struct CancelButton : Button { | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
pluginCancelDownload(); | pluginCancelDownload(); | ||||
} | } | ||||
}; | }; | ||||
@@ -124,7 +125,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||||
box.size.y = BND_WIDGET_HEIGHT; | 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; | layout->spacing = 5; | ||||
loginWidget = layout; | 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; | layout->spacing = 5; | ||||
manageWidget = layout; | 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; | layout->spacing = 5; | ||||
downloadWidget = layout; | downloadWidget = layout; | ||||
@@ -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); | gRackWidget->wireContainer->removeTopWire(this); | ||||
// HACK | // HACK | ||||
// Update hovered*Port of active wire if applicable | // Update hovered*Port of active wire if applicable | ||||
EventDragEnter e; | |||||
onDragEnter(e); | |||||
event::DragEnter eDragEnter; | |||||
on(eDragEnter); | |||||
} | } | ||||
e.consumed = true; | |||||
e.target = this; | e.target = this; | ||||
} | } | ||||
void Port::onDragStart(EventDragStart &e) { | |||||
void Port::on(event::DragStart &e) { | |||||
// Try to grab wire on top of stack | // Try to grab wire on top of stack | ||||
WireWidget *wire = gRackWidget->wireContainer->getTopWire(this); | WireWidget *wire = gRackWidget->wireContainer->getTopWire(this); | ||||
if (type == OUTPUT && windowIsModPressed()) { | if (type == OUTPUT && windowIsModPressed()) { | ||||
@@ -88,16 +87,16 @@ void Port::onDragStart(EventDragStart &e) { | |||||
gRackWidget->wireContainer->setActiveWire(wire); | gRackWidget->wireContainer->setActiveWire(wire); | ||||
} | } | ||||
void Port::onDragEnd(EventDragEnd &e) { | |||||
void Port::on(event::DragEnd &e) { | |||||
// FIXME | // FIXME | ||||
// If the source Port is deleted, this will be called, removing the cable | // If the source Port is deleted, this will be called, removing the cable | ||||
gRackWidget->wireContainer->commitActiveWire(); | 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 | // Reject ports if this is an input port and something is already plugged into it | ||||
if (type == INPUT) { | if (type == INPUT) { | ||||
WireWidget *topWire = gRackWidget->wireContainer->getTopWire(this); | 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; | WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | ||||
if (activeWire) { | if (activeWire) { | ||||
if (type == INPUT) | if (type == INPUT) | ||||
@@ -55,53 +55,53 @@ void RackScene::draw(NVGcontext *vg) { | |||||
Scene::draw(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) { | switch (e.key) { | ||||
case GLFW_KEY_N: { | case GLFW_KEY_N: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
gRackWidget->reset(); | gRackWidget->reset(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_Q: { | case GLFW_KEY_Q: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
windowClose(); | windowClose(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_O: { | case GLFW_KEY_O: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
gRackWidget->loadDialog(); | gRackWidget->loadDialog(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
if (windowIsModPressed() && windowIsShiftPressed()) { | if (windowIsModPressed() && windowIsShiftPressed()) { | ||||
gRackWidget->revert(); | gRackWidget->revert(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_S: { | case GLFW_KEY_S: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
gRackWidget->saveDialog(); | gRackWidget->saveDialog(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
if (windowIsModPressed() && windowIsShiftPressed()) { | if (windowIsModPressed() && windowIsShiftPressed()) { | ||||
gRackWidget->saveAsDialog(); | gRackWidget->saveAsDialog(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_V: { | case GLFW_KEY_V: { | ||||
if (windowIsModPressed() && !windowIsShiftPressed()) { | if (windowIsModPressed() && !windowIsShiftPressed()) { | ||||
gRackWidget->pastePresetClipboard(); | gRackWidget->pastePresetClipboard(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_ENTER: | case GLFW_KEY_ENTER: | ||||
case GLFW_KEY_KP_ENTER: { | case GLFW_KEY_KP_ENTER: { | ||||
appModuleBrowserCreate(); | appModuleBrowserCreate(); | ||||
e.consumed = true; | |||||
e.target = this; | |||||
} break; | } break; | ||||
case GLFW_KEY_F11: { | case GLFW_KEY_F11: { | ||||
windowSetFullScreen(!windowGetFullScreen()); | 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) { | 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); | |||||
} | } | ||||
@@ -497,26 +497,23 @@ void RackWidget::draw(NVGcontext *vg) { | |||||
Widget::draw(vg); | Widget::draw(vg); | ||||
} | } | ||||
void RackWidget::onMouseMove(EventMouseMove &e) { | |||||
OpaqueWidget::onMouseMove(e); | |||||
void RackWidget::on(event::Hover &e) { | |||||
OpaqueWidget::on(e); | |||||
lastMousePos = e.pos; | 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(); | rails->box.size = math::Vec(); | ||||
Widget::onZoom(e); | |||||
EventWidget::on(e); | |||||
} | } | ||||
@@ -16,14 +16,14 @@ void SVGButton::setSVGs(std::shared_ptr<SVG> defaultSVG, std::shared_ptr<SVG> ac | |||||
this->activeSVG = activeSVG ? activeSVG : defaultSVG; | 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); | sw->setSVG(activeSVG); | ||||
dirty = true; | dirty = true; | ||||
} | } | ||||
void SVGButton::onDragEnd(EventDragEnd &e) { | |||||
void SVGButton::on(event::DragEnd &e) { | |||||
sw->setSVG(defaultSVG); | sw->setSVG(defaultSVG); | ||||
dirty = true; | dirty = true; | ||||
} | } | ||||
@@ -46,9 +46,9 @@ void SVGKnob::step() { | |||||
FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
} | } | ||||
void SVGKnob::onChange(EventChange &e) { | |||||
void SVGKnob::on(event::Change &e) { | |||||
dirty = true; | dirty = true; | ||||
Knob::onChange(e); | |||||
ParamWidget::on(e); | |||||
} | } | ||||
@@ -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 |
@@ -30,9 +30,9 @@ void SVGSlider::step() { | |||||
FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
} | } | ||||
void SVGSlider::onChange(EventChange &e) { | |||||
void SVGSlider::on(event::Change &e) { | |||||
dirty = true; | dirty = true; | ||||
Knob::onChange(e); | |||||
ParamWidget::on(e); | |||||
} | } | ||||
@@ -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); | assert(frames.size() > 0); | ||||
float valueScaled = math::rescale(value, minValue, maxValue, 0, frames.size() - 1); | float valueScaled = math::rescale(value, minValue, maxValue, 0, frames.size() - 1); | ||||
int index = math::clamp((int) roundf(valueScaled), 0, (int) frames.size() - 1); | int index = math::clamp((int) roundf(valueScaled), 0, (int) frames.size() - 1); | ||||
sw->setSVG(frames[index]); | sw->setSVG(frames[index]); | ||||
dirty = true; | dirty = true; | ||||
ParamWidget::onChange(e); | |||||
ParamWidget::on(e); | |||||
} | } | ||||
@@ -4,7 +4,7 @@ | |||||
namespace rack { | namespace rack { | ||||
void ToggleSwitch::onDragStart(EventDragStart &e) { | |||||
void ToggleSwitch::on(event::DragStart &e) { | |||||
// Cycle through values | // Cycle through values | ||||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | ||||
if (value >= maxValue) | if (value >= maxValue) | ||||
@@ -2,6 +2,7 @@ | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "engine.hpp" | #include "engine.hpp" | ||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "helpers.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -10,22 +11,22 @@ namespace rack { | |||||
struct TooltipIconButton : IconButton { | struct TooltipIconButton : IconButton { | ||||
Tooltip *tooltip = NULL; | Tooltip *tooltip = NULL; | ||||
std::string tooltipText; | std::string tooltipText; | ||||
void onMouseEnter(EventMouseEnter &e) override { | |||||
void on(event::Enter &e) override { | |||||
if (!tooltip) { | if (!tooltip) { | ||||
tooltip = new Tooltip(); | tooltip = new Tooltip(); | ||||
tooltip->box.pos = getAbsoluteOffset(math::Vec(0, BND_WIDGET_HEIGHT)); | tooltip->box.pos = getAbsoluteOffset(math::Vec(0, BND_WIDGET_HEIGHT)); | ||||
tooltip->text = tooltipText; | tooltip->text = tooltipText; | ||||
gScene->addChild(tooltip); | gScene->addChild(tooltip); | ||||
} | } | ||||
IconButton::onMouseEnter(e); | |||||
IconButton::on(e); | |||||
} | } | ||||
void onMouseLeave(EventMouseLeave &e) override { | |||||
void on(event::Leave &e) override { | |||||
if (tooltip) { | if (tooltip) { | ||||
gScene->removeChild(tooltip); | gScene->removeChild(tooltip); | ||||
delete tooltip; | delete tooltip; | ||||
tooltip = NULL; | 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"))); | setSVG(SVG::load(asset::global("res/icons/noun_146097_cc.svg"))); | ||||
tooltipText = "New patch (" WINDOW_MOD_KEY_NAME "+N)"; | tooltipText = "New patch (" WINDOW_MOD_KEY_NAME "+N)"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->reset(); | gRackWidget->reset(); | ||||
} | } | ||||
}; | }; | ||||
@@ -44,7 +45,7 @@ struct OpenButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg"))); | ||||
tooltipText = "Open patch (" WINDOW_MOD_KEY_NAME "+O)"; | tooltipText = "Open patch (" WINDOW_MOD_KEY_NAME "+O)"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->loadDialog(); | gRackWidget->loadDialog(); | ||||
} | } | ||||
}; | }; | ||||
@@ -54,7 +55,7 @@ struct SaveButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg"))); | ||||
tooltipText = "Save patch (" WINDOW_MOD_KEY_NAME "+S)"; | tooltipText = "Save patch (" WINDOW_MOD_KEY_NAME "+S)"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->saveDialog(); | gRackWidget->saveDialog(); | ||||
} | } | ||||
}; | }; | ||||
@@ -64,7 +65,7 @@ struct SaveAsButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg"))); | ||||
tooltipText = "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)"; | tooltipText = "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->saveAsDialog(); | gRackWidget->saveAsDialog(); | ||||
} | } | ||||
}; | }; | ||||
@@ -74,7 +75,7 @@ struct RevertButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg"))); | ||||
tooltipText = "Revert patch"; | tooltipText = "Revert patch"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->revert(); | gRackWidget->revert(); | ||||
} | } | ||||
}; | }; | ||||
@@ -84,7 +85,7 @@ struct DisconnectCablesButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg"))); | ||||
tooltipText = "Disconnect cables"; | tooltipText = "Disconnect cables"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->disconnect(); | gRackWidget->disconnect(); | ||||
} | } | ||||
}; | }; | ||||
@@ -94,20 +95,20 @@ struct PowerMeterButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg"))); | ||||
tooltipText = "Toggle power meter (see manual for explanation)"; | tooltipText = "Toggle power meter (see manual for explanation)"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gPowerMeter ^= true; | gPowerMeter ^= true; | ||||
} | } | ||||
}; | }; | ||||
struct EnginePauseItem : MenuItem { | struct EnginePauseItem : MenuItem { | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gPaused ^= true; | gPaused ^= true; | ||||
} | } | ||||
}; | }; | ||||
struct SampleRateItem : MenuItem { | struct SampleRateItem : MenuItem { | ||||
float sampleRate; | float sampleRate; | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
engineSetSampleRate(sampleRate); | engineSetSampleRate(sampleRate); | ||||
gPaused = false; | gPaused = false; | ||||
} | } | ||||
@@ -118,12 +119,12 @@ struct SampleRateButton : TooltipIconButton { | |||||
setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg"))); | setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg"))); | ||||
tooltipText = "Engine sample rate"; | tooltipText = "Engine sample rate"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | ||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
menu->addChild(MenuLabel::create("Engine sample rate")); | |||||
menu->addChild(createMenuLabel("Engine sample rate")); | |||||
EnginePauseItem *pauseItem = new EnginePauseItem(); | EnginePauseItem *pauseItem = new EnginePauseItem(); | ||||
pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; | 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"))); | setSVG(SVG::load(asset::global("res/icons/noun_468341_cc.svg"))); | ||||
tooltipText = "Lock modules"; | tooltipText = "Lock modules"; | ||||
} | } | ||||
void onAction(EventAction &e) override { | |||||
void on(event::Action &e) override { | |||||
gRackWidget->lockModules ^= true; | gRackWidget->lockModules ^= true; | ||||
} | } | ||||
}; | }; | ||||
struct ZoomSlider : Slider { | 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); | |||||
} | } | ||||
}; | }; | ||||
@@ -1,4 +1,4 @@ | |||||
#include "widgets.hpp" | |||||
#include "event.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -6,8 +6,7 @@ namespace rack { | |||||
Widget *gHoveredWidget = NULL; | Widget *gHoveredWidget = NULL; | ||||
Widget *gDraggedWidget = NULL; | Widget *gDraggedWidget = NULL; | ||||
Widget *gDragHoveredWidget = NULL; | Widget *gDragHoveredWidget = NULL; | ||||
Widget *gFocusedWidget = NULL; | |||||
Widget *gTempWidget = NULL; | |||||
Widget *gSelectedWidget = NULL; | |||||
} // namespace rack | } // namespace rack |
@@ -1,4 +1,4 @@ | |||||
#include "widgets.hpp" | |||||
#include "window.hpp" | |||||
// #define DEBUG_ONLY(x) x | // #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; | 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);) | DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | ||||
int shapeIndex = 0; | int shapeIndex = 0; | ||||
// Iterate shape linked list | // 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 | } // namespace rack |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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,5 +1,4 @@ | |||||
#include "widgets.hpp" | |||||
#include "window.hpp" | |||||
#include "widgets/FramebufferWidget.hpp" | |||||
#include "nanovg_gl.h" | #include "nanovg_gl.h" | ||||
#include "nanovg_gl_utils.h" | #include "nanovg_gl_utils.h" | ||||
@@ -118,10 +117,5 @@ int FramebufferWidget::getImageHandle() { | |||||
return internal->fb->image; | return internal->fb->image; | ||||
} | } | ||||
void FramebufferWidget::onZoom(EventZoom &e) { | |||||
dirty = true; | |||||
Widget::onZoom(e); | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -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 |
@@ -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 |
@@ -12,8 +12,7 @@ Widget::~Widget() { | |||||
if (gHoveredWidget == this) gHoveredWidget = NULL; | if (gHoveredWidget == this) gHoveredWidget = NULL; | ||||
if (gDraggedWidget == this) gDraggedWidget = NULL; | if (gDraggedWidget == this) gDraggedWidget = NULL; | ||||
if (gDragHoveredWidget == this) gDragHoveredWidget = NULL; | if (gDragHoveredWidget == this) gDragHoveredWidget = NULL; | ||||
if (gFocusedWidget == this) gFocusedWidget = NULL; | |||||
if (gTempWidget == this) gTempWidget = NULL; | |||||
if (gSelectedWidget == this) gSelectedWidget = NULL; | |||||
clearChildren(); | clearChildren(); | ||||
} | } | ||||
@@ -75,34 +74,18 @@ void Widget::clearChildren() { | |||||
children.clear(); | 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() { | 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(); | 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 | } // namespace rack |
@@ -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 |
@@ -45,30 +45,64 @@ math::Vec gMousePos; | |||||
std::string lastWindowTitle; | 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 | #ifdef ARCH_MAC | ||||
// Ctrl-left click --> right click | |||||
// Remap Ctrl-left click to right click on Mac | |||||
if (button == GLFW_MOUSE_BUTTON_LEFT) { | 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; | button = GLFW_MOUSE_BUTTON_RIGHT; | ||||
} | } | ||||
} | } | ||||
#endif | #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 (button == GLFW_MOUSE_BUTTON_LEFT) { | ||||
if (gTempWidget) { | if (gTempWidget) { | ||||
// onDragStart | // onDragStart | ||||
@@ -77,19 +111,19 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||||
} | } | ||||
gDraggedWidget = gTempWidget; | gDraggedWidget = gTempWidget; | ||||
if (gTempWidget != gFocusedWidget) { | |||||
if (gFocusedWidget) { | |||||
if (gTempWidget != gSelectedWidget) { | |||||
if (gSelectedWidget) { | |||||
// onDefocus | // onDefocus | ||||
EventDefocus e; | EventDefocus e; | ||||
gFocusedWidget->onDefocus(e); | |||||
gSelectedWidget->onDefocus(e); | |||||
} | } | ||||
gFocusedWidget = NULL; | |||||
gSelectedWidget = NULL; | |||||
if (gTempWidget) { | if (gTempWidget) { | ||||
// onFocus | // onFocus | ||||
EventFocus e; | EventFocus e; | ||||
gTempWidget->onFocus(e); | gTempWidget->onFocus(e); | ||||
if (e.consumed) { | if (e.consumed) { | ||||
gFocusedWidget = gTempWidget; | |||||
gSelectedWidget = gTempWidget; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -125,6 +159,7 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||||
} | } | ||||
gTempWidget = NULL; | gTempWidget = NULL; | ||||
} | } | ||||
*/ | |||||
} | } | ||||
struct MouseButtonArguments { | struct MouseButtonArguments { | ||||
@@ -151,7 +186,7 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m | |||||
void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | ||||
math::Vec mousePos = math::Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round(); | 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); | int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR); | ||||
(void) cursorMode; | (void) cursorMode; | ||||
@@ -171,20 +206,24 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||||
gMousePos = mousePos; | 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) { | if (gDraggedWidget) { | ||||
// onDragMove | // onDragMove | ||||
EventDragMove e; | EventDragMove e; | ||||
e.mouseRel = mouseRel; | |||||
e.mouseDelta = mouseDelta; | |||||
gDraggedWidget->onDragMove(e); | gDraggedWidget->onDragMove(e); | ||||
if (gTempWidget != gDragHoveredWidget) { | 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 | // Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed | ||||
EventScroll e; | EventScroll e; | ||||
e.pos = mousePos; | e.pos = mousePos; | ||||
e.scrollRel = mouseRel; | |||||
e.scrollRel = mouseDelta; | |||||
gScene->onScroll(e); | gScene->onScroll(e); | ||||
} | } | ||||
*/ | |||||
} | } | ||||
void cursorEnterCallback(GLFWwindow* window, int entered) { | void cursorEnterCallback(GLFWwindow* window, int entered) { | ||||
if (!entered) { | if (!entered) { | ||||
if (gHoveredWidget) { | if (gHoveredWidget) { | ||||
// onMouseLeave | |||||
EventMouseLeave e; | |||||
gHoveredWidget->onMouseLeave(e); | |||||
event::Leave eLeave; | |||||
gHoveredWidget->handleEvent(eLeave); | |||||
} | } | ||||
gHoveredWidget = NULL; | gHoveredWidget = NULL; | ||||
} | } | ||||
} | } | ||||
void scrollCallback(GLFWwindow *window, double x, double y) { | 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 ARCH_LIN || ARCH_WIN | ||||
if (windowIsShiftPressed()) | if (windowIsShiftPressed()) | ||||
scrollRel = math::Vec(y, x); | |||||
scrollDelta = math::Vec(y, x); | |||||
#endif | #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) { | 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) { | void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { | ||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) { | 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; | 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 | // 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) { | 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++) { | 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) { | void errorCallback(int error, const char *description) { | ||||
@@ -464,8 +506,8 @@ void windowRun() { | |||||
glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); | glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); | ||||
pixelRatio = roundf(pixelRatio); | pixelRatio = roundf(pixelRatio); | ||||
if (pixelRatio != gPixelRatio) { | if (pixelRatio != gPixelRatio) { | ||||
EventZoom eZoom; | |||||
gScene->onZoom(eZoom); | |||||
event::Zoom eZoom; | |||||
gScene->handleEvent(eZoom); | |||||
gPixelRatio = pixelRatio; | gPixelRatio = pixelRatio; | ||||
} | } | ||||