@@ -49,68 +49,14 @@ static const math::Vec RACK_GRID_SIZE = math::Vec(RACK_GRID_WIDTH, RACK_GRID_HEI | |||
static const std::string PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm"; | |||
static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; | |||
} // namespace rack | |||
struct ModuleWidget : OpaqueWidget { | |||
Model *model = NULL; | |||
/** Owns the module pointer */ | |||
Module *module = NULL; | |||
SVGPanel *panel = NULL; | |||
std::vector<Port*> inputs; | |||
std::vector<Port*> outputs; | |||
std::vector<ParamWidget*> params; | |||
ModuleWidget(Module *module); | |||
~ModuleWidget(); | |||
/** Convenience functions for adding special widgets (calls addChild()) */ | |||
void addInput(Port *input); | |||
void addOutput(Port *output); | |||
void addParam(ParamWidget *param); | |||
void setPanel(std::shared_ptr<SVG> svg); | |||
virtual json_t *toJson(); | |||
virtual void fromJson(json_t *rootJ); | |||
void copyClipboard(); | |||
void pasteClipboard(); | |||
void save(std::string filename); | |||
void load(std::string filename); | |||
void loadDialog(); | |||
void saveDialog(); | |||
#include "app/ModuleWidget.hpp" | |||
virtual void create(); | |||
virtual void _delete(); | |||
/** Disconnects cables from all ports | |||
Called when the user clicks Disconnect Cables in the context menu. | |||
*/ | |||
virtual void disconnect(); | |||
/** Resets the parameters of the module and calls the Module's randomize(). | |||
Called when the user clicks Initialize in the context menu. | |||
*/ | |||
virtual void reset(); | |||
/** Deprecated */ | |||
virtual void initialize() final {} | |||
/** Randomizes the parameters of the module and calls the Module's randomize(). | |||
Called when the user clicks Randomize in the context menu. | |||
*/ | |||
virtual void randomize(); | |||
/** Do not subclass this to add context menu entries. Use appendContextMenu() instead */ | |||
virtual Menu *createContextMenu(); | |||
/** Override to add context menu entries to your subclass. | |||
It is recommended to add a blank MenuEntry first for spacing. | |||
*/ | |||
virtual void appendContextMenu(Menu *menu) {} | |||
void draw(NVGcontext *vg) override; | |||
void drawShadow(NVGcontext *vg); | |||
math::Vec dragPos; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onMouseMove(EventMouseMove &e) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void onDragMove(EventDragMove &e) override; | |||
}; | |||
namespace rack { | |||
struct WireWidget : OpaqueWidget { | |||
Port *outputPort = NULL; | |||
@@ -189,20 +135,15 @@ struct RackWidget : OpaqueWidget { | |||
void step() override; | |||
void draw(NVGcontext *vg) override; | |||
void onMouseMove(EventMouseMove &e) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onZoom(EventZoom &e) override; | |||
void on(event::Hover &e) override; | |||
void on(event::Button &e) override; | |||
void on(event::Zoom &e) override; | |||
}; | |||
struct RackRail : TransparentWidget { | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct SVGPanel : FramebufferWidget { | |||
void step() override; | |||
void setBackground(std::shared_ptr<SVG> svg); | |||
}; | |||
//////////////////// | |||
// ParamWidgets and other components | |||
//////////////////// | |||
@@ -210,14 +151,6 @@ struct SVGPanel : FramebufferWidget { | |||
/** A Widget that exists on a Panel and interacts with a Module */ | |||
struct Component : OpaqueWidget { | |||
Module *module = NULL; | |||
template <typename T = Component> | |||
static T *create(math::Vec pos, Module *module) { | |||
T *o = new T(); | |||
o->box.pos = pos; | |||
o->module = module; | |||
return o; | |||
} | |||
}; | |||
struct CircularShadow : TransparentWidget { | |||
@@ -241,17 +174,8 @@ struct ParamWidget : Component, QuantityWidget { | |||
void fromJson(json_t *rootJ); | |||
virtual void reset(); | |||
virtual void randomize(); | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onChange(EventChange &e) override; | |||
template <typename T = ParamWidget> | |||
static T *create(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
T *o = Component::create<T>(pos, module); | |||
o->paramId = paramId; | |||
o->setLimits(minValue, maxValue); | |||
o->setDefaultValue(defaultValue); | |||
return o; | |||
} | |||
void on(event::Button &e) override; | |||
void on(event::Change &e) override; | |||
}; | |||
/** Implements vertical dragging behavior for ParamWidgets */ | |||
@@ -262,9 +186,9 @@ struct Knob : ParamWidget { | |||
float speed = 1.0; | |||
float dragValue; | |||
Knob(); | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragMove(EventDragMove &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void on(event::DragStart &e) override; | |||
void on(event::DragMove &e) override; | |||
void on(event::DragEnd &e) override; | |||
}; | |||
/** A knob which rotates an SVG and caches it in a framebuffer */ | |||
@@ -278,7 +202,7 @@ struct SVGKnob : Knob, FramebufferWidget { | |||
SVGKnob(); | |||
void setSVG(std::shared_ptr<SVG> svg); | |||
void step() override; | |||
void onChange(EventChange &e) override; | |||
void on(event::Change &e) override; | |||
}; | |||
/** Behaves like a knob but linearly moves an SVGWidget between two points. | |||
@@ -293,12 +217,9 @@ struct SVGSlider : Knob, FramebufferWidget { | |||
SVGSlider(); | |||
void setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> handleSVG); | |||
void step() override; | |||
void onChange(EventChange &e) override; | |||
void on(event::Change &e) override; | |||
}; | |||
/** Deprecated name for SVGSlider */ | |||
typedef SVGSlider SVGFader; | |||
/** A ParamWidget with multiple frames corresponding to its value */ | |||
struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | |||
std::vector<std::shared_ptr<SVG>> frames; | |||
@@ -306,12 +227,12 @@ struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | |||
SVGSwitch(); | |||
/** Adds an SVG file to represent the next switch position */ | |||
void addFrame(std::shared_ptr<SVG> svg); | |||
void onChange(EventChange &e) override; | |||
void on(event::Change &e) override; | |||
}; | |||
/** A switch that cycles through each mechanical position */ | |||
struct ToggleSwitch : virtual ParamWidget { | |||
void onDragStart(EventDragStart &e) override; | |||
void on(event::DragStart &e) override; | |||
}; | |||
/** A switch that is turned on when held and turned off when released. | |||
@@ -320,8 +241,8 @@ Consider using SVGButton if the switch simply changes the state of your Module w | |||
struct MomentarySwitch : virtual ParamWidget { | |||
/** Don't randomize state */ | |||
void randomize() override {} | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void on(event::DragStart &e) override; | |||
void on(event::DragEnd &e) override; | |||
}; | |||
/** A Component with a default (up) and active (down) state when clicked. | |||
@@ -335,15 +256,15 @@ struct SVGButton : Component, FramebufferWidget { | |||
SVGButton(); | |||
/** If `activeSVG` is NULL, `defaultSVG` is used as the active state instead. */ | |||
void setSVGs(std::shared_ptr<SVG> defaultSVG, std::shared_ptr<SVG> activeSVG); | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void on(event::DragStart &e) override; | |||
void on(event::DragEnd &e) override; | |||
}; | |||
//////////////////// | |||
// IO widgets | |||
//////////////////// | |||
struct LedDisplay : VirtualWidget { | |||
struct LedDisplay : virtual EventWidget { | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
@@ -359,7 +280,7 @@ struct LedDisplayChoice : TransparentWidget { | |||
NVGcolor color; | |||
LedDisplayChoice(); | |||
void draw(NVGcontext *vg) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void on(event::Button &e) override; | |||
}; | |||
struct LedDisplayTextField : TextField { | |||
@@ -430,14 +351,6 @@ struct ModuleLightWidget : MultiLightWidget { | |||
Module *module = NULL; | |||
int firstLightId; | |||
void step() override; | |||
template <typename T = ModuleLightWidget> | |||
static T *create(math::Vec pos, Module *module, int firstLightId) { | |||
T *o = Widget::create<T>(pos); | |||
o->module = module; | |||
o->firstLightId = firstLightId; | |||
return o; | |||
} | |||
}; | |||
//////////////////// | |||
@@ -457,20 +370,12 @@ struct Port : Component { | |||
~Port(); | |||
void step() override; | |||
void draw(NVGcontext *vg) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void onDragDrop(EventDragDrop &e) override; | |||
void onDragEnter(EventDragEnter &e) override; | |||
void onDragLeave(EventDragEnter &e) override; | |||
template <typename T = Port> | |||
static T *create(math::Vec pos, PortType type, Module *module, int portId) { | |||
T *o = Component::create<T>(pos, module); | |||
o->type = type; | |||
o->portId = portId; | |||
return o; | |||
} | |||
void on(event::Button &e) override; | |||
void on(event::DragStart &e) override; | |||
void on(event::DragEnd &e) override; | |||
void on(event::DragDrop &e) override; | |||
void on(event::DragEnter &e) override; | |||
void on(event::DragLeave &e) override; | |||
}; | |||
struct SVGPort : Port, FramebufferWidget { | |||
@@ -503,7 +408,7 @@ struct Toolbar : OpaqueWidget { | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct PluginManagerWidget : VirtualWidget { | |||
struct PluginManagerWidget : virtual EventWidget { | |||
Widget *loginWidget; | |||
Widget *manageWidget; | |||
Widget *downloadWidget; | |||
@@ -522,8 +427,8 @@ struct RackScene : Scene { | |||
RackScene(); | |||
void step() override; | |||
void draw(NVGcontext *vg) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
void onPathDrop(EventPathDrop &e) override; | |||
void on(event::HoverKey &e) override; | |||
void on(event::PathDrop &e) override; | |||
}; | |||
//////////////////// | |||
@@ -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) { | |||
struct TModel : Model { | |||
Module *createModule() override { | |||
TModule *module = new TModule(); | |||
return module; | |||
TModule *o = new TModule(); | |||
return o; | |||
} | |||
ModuleWidget *createModuleWidget() override { | |||
TModule *module = new TModule(); | |||
TModuleWidget *moduleWidget = new TModuleWidget(module); | |||
moduleWidget->model = this; | |||
return moduleWidget; | |||
TModuleWidget *o = new TModuleWidget(module); | |||
o->model = this; | |||
return o; | |||
} | |||
ModuleWidget *createModuleWidgetNull() override { | |||
TModuleWidget *moduleWidget = new TModuleWidget(NULL); | |||
moduleWidget->model = this; | |||
return moduleWidget; | |||
TModuleWidget *o = new TModuleWidget(NULL); | |||
o->model = this; | |||
return o; | |||
} | |||
}; | |||
Model *model = new TModel(); | |||
model->author = author; | |||
model->slug = slug; | |||
model->name = name; | |||
model->tags = {tags...}; | |||
return model; | |||
Model *o = new TModel(); | |||
o->author = author; | |||
o->slug = slug; | |||
o->name = name; | |||
o->tags = {tags...}; | |||
return o; | |||
} | |||
template <class TWidget> | |||
TWidget *createWidget(math::Vec pos) { | |||
TWidget *w = new TWidget(); | |||
w->box.pos = pos; | |||
return w; | |||
TWidget *o = new TWidget(); | |||
o->box.pos = pos; | |||
return o; | |||
} | |||
template <class TParamWidget> | |||
TParamWidget *createParam(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
TParamWidget *param = new TParamWidget(); | |||
param->box.pos = pos; | |||
param->module = module; | |||
param->paramId = paramId; | |||
param->setLimits(minValue, maxValue); | |||
param->setDefaultValue(defaultValue); | |||
return param; | |||
TParamWidget *o = new TParamWidget(); | |||
o->box.pos = pos; | |||
o->module = module; | |||
o->paramId = paramId; | |||
o->setLimits(minValue, maxValue); | |||
o->setDefaultValue(defaultValue); | |||
return o; | |||
} | |||
template <class TParamWidget> | |||
TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
TParamWidget *param = new TParamWidget(); | |||
param->box.pos = pos.minus(param->box.size.div(2)); | |||
param->module = module; | |||
param->paramId = paramId; | |||
param->setLimits(minValue, maxValue); | |||
param->setDefaultValue(defaultValue); | |||
return param; | |||
TParamWidget *o = new TParamWidget(); | |||
o->box.pos = pos.minus(o->box.size.div(2)); | |||
o->module = module; | |||
o->paramId = paramId; | |||
o->setLimits(minValue, maxValue); | |||
o->setDefaultValue(defaultValue); | |||
return o; | |||
} | |||
template <class TPort> | |||
TPort *createInput(math::Vec pos, Module *module, int inputId) { | |||
TPort *port = new TPort(); | |||
port->box.pos = pos; | |||
port->module = module; | |||
port->type = Port::INPUT; | |||
port->portId = inputId; | |||
return port; | |||
TPort *o = new TPort(); | |||
o->box.pos = pos; | |||
o->module = module; | |||
o->type = Port::INPUT; | |||
o->portId = inputId; | |||
return o; | |||
} | |||
template <class TPort> | |||
TPort *createInputCentered(math::Vec pos, Module *module, int inputId) { | |||
TPort *port = new TPort(); | |||
port->box.pos = pos.minus(port->box.size.div(2)); | |||
port->module = module; | |||
port->type = Port::INPUT; | |||
port->portId = inputId; | |||
return port; | |||
TPort *o = new TPort(); | |||
o->box.pos = pos.minus(o->box.size.div(2)); | |||
o->module = module; | |||
o->type = Port::INPUT; | |||
o->portId = inputId; | |||
return o; | |||
} | |||
template <class TPort> | |||
TPort *createOutput(math::Vec pos, Module *module, int outputId) { | |||
TPort *port = new TPort(); | |||
port->box.pos = pos; | |||
port->module = module; | |||
port->type = Port::OUTPUT; | |||
port->portId = outputId; | |||
return port; | |||
TPort *o = new TPort(); | |||
o->box.pos = pos; | |||
o->module = module; | |||
o->type = Port::OUTPUT; | |||
o->portId = outputId; | |||
return o; | |||
} | |||
template <class TPort> | |||
TPort *createOutputCentered(math::Vec pos, Module *module, int outputId) { | |||
TPort *port = new TPort(); | |||
port->box.pos = pos.minus(port->box.size.div(2)); | |||
port->module = module; | |||
port->type = Port::OUTPUT; | |||
port->portId = outputId; | |||
return port; | |||
TPort *o = new TPort(); | |||
o->box.pos = pos.minus(o->box.size.div(2)); | |||
o->module = module; | |||
o->type = Port::OUTPUT; | |||
o->portId = outputId; | |||
return o; | |||
} | |||
template <class TModuleLightWidget> | |||
TModuleLightWidget *createLight(math::Vec pos, Module *module, int firstLightId) { | |||
TModuleLightWidget *light = new TModuleLightWidget(); | |||
light->box.pos = pos; | |||
light->module = module; | |||
light->firstLightId = firstLightId; | |||
return light; | |||
TModuleLightWidget *o = new TModuleLightWidget(); | |||
o->box.pos = pos; | |||
o->module = module; | |||
o->firstLightId = firstLightId; | |||
return o; | |||
} | |||
template <class TModuleLightWidget> | |||
TModuleLightWidget *createLightCentered(math::Vec pos, Module *module, int firstLightId) { | |||
TModuleLightWidget *light = new TModuleLightWidget(); | |||
light->box.pos = pos.minus(light->box.size.div(2)); | |||
light->module = module; | |||
light->firstLightId = firstLightId; | |||
return light; | |||
TModuleLightWidget *o = new TModuleLightWidget(); | |||
o->box.pos = pos.minus(o->box.size.div(2)); | |||
o->module = module; | |||
o->firstLightId = firstLightId; | |||
return o; | |||
} | |||
template <class TMenuLabel = MenuLabel> | |||
TMenuLabel *createMenuLabel(std::string text) { | |||
TMenuLabel *o = new TMenuLabel(); | |||
o->text = text; | |||
return o; | |||
} | |||
template <class TMenuItem = MenuItem> | |||
TMenuItem *createMenuItem(std::string text, std::string rightText = "") { | |||
TMenuItem *o = new TMenuItem(); | |||
o->text = text; | |||
o->rightText = rightText; | |||
return o; | |||
} | |||
@@ -156,63 +156,63 @@ struct Vec { | |||
Vec() {} | |||
Vec(float x, float y) : x(x), y(y) {} | |||
Vec neg() { | |||
Vec neg() const { | |||
return Vec(-x, -y); | |||
} | |||
Vec plus(Vec b) { | |||
Vec plus(Vec b) const { | |||
return Vec(x + b.x, y + b.y); | |||
} | |||
Vec minus(Vec b) { | |||
Vec minus(Vec b) const { | |||
return Vec(x - b.x, y - b.y); | |||
} | |||
Vec mult(float s) { | |||
Vec mult(float s) const { | |||
return Vec(x * s, y * s); | |||
} | |||
Vec mult(Vec b) { | |||
Vec mult(Vec b) const { | |||
return Vec(x * b.x, y * b.y); | |||
} | |||
Vec div(float s) { | |||
Vec div(float s) const { | |||
return Vec(x / s, y / s); | |||
} | |||
Vec div(Vec b) { | |||
Vec div(Vec b) const { | |||
return Vec(x / b.x, y / b.y); | |||
} | |||
float dot(Vec b) { | |||
float dot(Vec b) const { | |||
return x * b.x + y * b.y; | |||
} | |||
float norm() { | |||
float norm() const { | |||
return std::hypotf(x, y); | |||
} | |||
Vec flip() { | |||
Vec flip() const { | |||
return Vec(y, x); | |||
} | |||
Vec min(Vec b) { | |||
Vec min(Vec b) const { | |||
return Vec(std::min(x, b.x), std::min(y, b.y)); | |||
} | |||
Vec max(Vec b) { | |||
Vec max(Vec b) const { | |||
return Vec(std::max(x, b.x), std::max(y, b.y)); | |||
} | |||
Vec round() { | |||
Vec round() const { | |||
return Vec(std::round(x), std::round(y)); | |||
} | |||
Vec floor() { | |||
Vec floor() const { | |||
return Vec(std::floor(x), std::floor(y)); | |||
} | |||
Vec ceil() { | |||
Vec ceil() const { | |||
return Vec(std::ceil(x), std::ceil(y)); | |||
} | |||
bool isEqual(Vec b) { | |||
bool isEqual(Vec b) const { | |||
return x == b.x && y == b.y; | |||
} | |||
bool isZero() { | |||
bool isZero() const { | |||
return x == 0.0f && y == 0.0f; | |||
} | |||
bool isFinite() { | |||
bool isFinite() const { | |||
return std::isfinite(x) && std::isfinite(y); | |||
} | |||
Vec clamp(Rect bound); | |||
Vec clampBetween(Rect bound); | |||
DEPRECATED Vec clamp2(Rect bound); | |||
Vec clamp(Rect bound) const; | |||
Vec clampBetween(Rect bound) const; | |||
DEPRECATED Vec clamp2(Rect bound) const; | |||
}; | |||
@@ -228,40 +228,40 @@ struct Rect { | |||
} | |||
/** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right */ | |||
bool contains(Vec v) { | |||
bool contains(Vec v) const { | |||
return pos.x <= v.x && v.x < pos.x + size.x | |||
&& pos.y <= v.y && v.y < pos.y + size.y; | |||
} | |||
/** Returns whether this Rect contains an entire Rect */ | |||
bool contains(Rect r) { | |||
bool contains(Rect r) const { | |||
return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x | |||
&& pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y; | |||
} | |||
/** Returns whether this Rect overlaps with another Rect */ | |||
bool intersects(Rect r) { | |||
bool intersects(Rect r) const { | |||
return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x) | |||
&& (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y); | |||
} | |||
bool isEqual(Rect r) { | |||
bool isEqual(Rect r) const { | |||
return pos.isEqual(r.pos) && size.isEqual(r.size); | |||
} | |||
Vec getCenter() { | |||
Vec getCenter() const { | |||
return pos.plus(size.mult(0.5f)); | |||
} | |||
Vec getTopLeft() { | |||
Vec getTopLeft() const { | |||
return pos; | |||
} | |||
Vec getTopRight() { | |||
Vec getTopRight() const { | |||
return pos.plus(Vec(size.x, 0.f)); | |||
} | |||
Vec getBottomLeft() { | |||
Vec getBottomLeft() const { | |||
return pos.plus(Vec(0.f, size.y)); | |||
} | |||
Vec getBottomRight() { | |||
Vec getBottomRight() const { | |||
return pos.plus(size); | |||
} | |||
/** Clamps the edges of the rectangle to fit within a bound */ | |||
Rect clamp(Rect bound) { | |||
Rect clamp(Rect bound) const { | |||
Rect r; | |||
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x); | |||
r.pos.y = clampBetween(pos.y, bound.pos.y, bound.pos.y + bound.size.y); | |||
@@ -270,7 +270,7 @@ struct Rect { | |||
return r; | |||
} | |||
/** Nudges the position to fix inside a bounding box */ | |||
Rect nudge(Rect bound) { | |||
Rect nudge(Rect bound) const { | |||
Rect r; | |||
r.size = size; | |||
r.pos.x = clampBetween(pos.x, bound.pos.x, bound.pos.x + bound.size.x - size.x); | |||
@@ -278,7 +278,7 @@ struct Rect { | |||
return r; | |||
} | |||
/** Expands this Rect to contain `other` */ | |||
Rect expand(Rect other) { | |||
Rect expand(Rect other) const { | |||
Rect r; | |||
r.pos.x = std::min(pos.x, other.pos.x); | |||
r.pos.y = std::min(pos.y, other.pos.y); | |||
@@ -287,16 +287,16 @@ struct Rect { | |||
return r; | |||
} | |||
/** Returns a Rect with its position set to zero */ | |||
Rect zeroPos() { | |||
Rect zeroPos() const { | |||
return Rect(Vec(), size); | |||
} | |||
Rect grow(Vec delta) { | |||
Rect grow(Vec delta) const { | |||
Rect r; | |||
r.pos = pos.minus(delta); | |||
r.size = size.plus(delta.mult(2.f)); | |||
return r; | |||
} | |||
Rect shrink(Vec delta) { | |||
Rect shrink(Vec delta) const { | |||
Rect r; | |||
r.pos = pos.plus(delta); | |||
r.size = size.minus(delta.mult(2.f)); | |||
@@ -305,19 +305,19 @@ struct Rect { | |||
}; | |||
inline Vec Vec::clamp(Rect bound) { | |||
inline Vec Vec::clamp(Rect bound) const { | |||
return Vec( | |||
rack::math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
rack::math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
} | |||
inline Vec Vec::clampBetween(Rect bound) { | |||
inline Vec Vec::clampBetween(Rect bound) const { | |||
return Vec( | |||
rack::math::clampBetween(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
rack::math::clampBetween(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
} | |||
inline Vec Vec::clamp2(Rect bound) {return clampBetween(bound);} | |||
inline Vec Vec::clamp2(Rect bound) const {return clampBetween(bound);} | |||
} // namespace math | |||
@@ -65,34 +65,6 @@ struct Model { | |||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||
/** Creates a ModuleWidget with no Module, useful for previews */ | |||
virtual ModuleWidget *createModuleWidgetNull() { return NULL; } | |||
/** Create Model subclass which constructs a specific Module and ModuleWidget subclass */ | |||
template <typename TModule, typename TModuleWidget, typename... Tags> | |||
static Model *create(std::string author, std::string slug, std::string name, Tags... tags) { | |||
struct TModel : Model { | |||
Module *createModule() override { | |||
TModule *module = new TModule(); | |||
return module; | |||
} | |||
ModuleWidget *createModuleWidget() override { | |||
TModule *module = new TModule(); | |||
TModuleWidget *moduleWidget = new TModuleWidget(module); | |||
moduleWidget->model = this; | |||
return moduleWidget; | |||
} | |||
ModuleWidget *createModuleWidgetNull() override { | |||
TModuleWidget *moduleWidget = new TModuleWidget(NULL); | |||
moduleWidget->model = this; | |||
return moduleWidget; | |||
} | |||
}; | |||
TModel *o = new TModel(); | |||
o->author = author; | |||
o->slug = slug; | |||
o->name = name; | |||
o->tags = {tags...}; | |||
return o; | |||
} | |||
}; | |||
@@ -23,7 +23,7 @@ namespace rack { | |||
// Adopt some sub-namespaces into the main namespace for convenience | |||
using namespace math; | |||
using namespace string; | |||
using string::stringf; | |||
} // namespace rack |
@@ -56,12 +56,6 @@ DEPRECATED inline float randomUniform() {return random::uniform();} | |||
DEPRECATED inline float randomNormal() {return random::normal();} | |||
DEPRECATED inline float randomf() {return random::uniform();} | |||
//////////////////// | |||
// string | |||
//////////////////// | |||
using string::stringf; | |||
//////////////////// | |||
// logger | |||
//////////////////// | |||
@@ -1,274 +1,22 @@ | |||
#pragma once | |||
#include "widgets.hpp" | |||
#include "blendish.h" | |||
#define CHECKMARK_STRING "✔" | |||
#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") | |||
namespace rack { | |||
//////////////////// | |||
// Layouts (layouts.cpp) | |||
//////////////////// | |||
/** Positions children in a row/column based on their widths/heights */ | |||
struct SequentialLayout : VirtualWidget { | |||
enum Orientation { | |||
HORIZONTAL_ORIENTATION, | |||
VERTICAL_ORIENTATION, | |||
}; | |||
Orientation orientation = HORIZONTAL_ORIENTATION; | |||
enum Alignment { | |||
LEFT_ALIGNMENT, | |||
CENTER_ALIGNMENT, | |||
RIGHT_ALIGNMENT, | |||
}; | |||
Alignment alignment = LEFT_ALIGNMENT; | |||
/** Space between adjacent elements */ | |||
float spacing = 0.0; | |||
void step() override; | |||
}; | |||
//////////////////// | |||
// Blendish UI elements | |||
//////////////////// | |||
struct Label : VirtualWidget { | |||
std::string text; | |||
float fontSize; | |||
NVGcolor color; | |||
enum Alignment { | |||
LEFT_ALIGNMENT, | |||
CENTER_ALIGNMENT, | |||
RIGHT_ALIGNMENT, | |||
}; | |||
Alignment alignment = LEFT_ALIGNMENT; | |||
Label(); | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct List : OpaqueWidget { | |||
void step() override; | |||
}; | |||
/** Deletes itself from parent when clicked */ | |||
struct MenuOverlay : OpaqueWidget { | |||
void step() override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
}; | |||
struct MenuEntry; | |||
struct Menu : OpaqueWidget { | |||
Menu *parentMenu = NULL; | |||
Menu *childMenu = NULL; | |||
/** The entry which created the child menu */ | |||
MenuEntry *activeEntry = NULL; | |||
Menu() { | |||
box.size = math::Vec(0, 0); | |||
} | |||
~Menu(); | |||
/** Deprecated. Just use addChild(child) instead */ | |||
DEPRECATED void pushChild(Widget *child) { | |||
addChild(child); | |||
} | |||
void setChildMenu(Menu *menu); | |||
void step() override; | |||
void draw(NVGcontext *vg) override; | |||
void onScroll(EventScroll &e) override; | |||
}; | |||
struct MenuEntry : OpaqueWidget { | |||
MenuEntry() { | |||
box.size = math::Vec(0, BND_WIDGET_HEIGHT); | |||
} | |||
template <typename T = MenuEntry> | |||
static T *create() { | |||
T *o = Widget::create<T>(math::Vec()); | |||
return o; | |||
} | |||
}; | |||
struct MenuSeparator : MenuEntry { | |||
MenuSeparator(); | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct MenuLabel : MenuEntry { | |||
std::string text; | |||
void draw(NVGcontext *vg) override; | |||
void step() override; | |||
template <typename T = MenuLabel> | |||
static T *create(std::string text) { | |||
T *o = MenuEntry::create<T>(); | |||
o->text = text; | |||
return o; | |||
} | |||
}; | |||
struct MenuItem : MenuEntry { | |||
std::string text; | |||
std::string rightText; | |||
void draw(NVGcontext *vg) override; | |||
void step() override; | |||
virtual Menu *createChildMenu() {return NULL;} | |||
void onMouseEnter(EventMouseEnter &e) override; | |||
void onDragDrop(EventDragDrop &e) override; | |||
template <typename T = MenuItem> | |||
static T *create(std::string text, std::string rightText = "") { | |||
T *o = MenuEntry::create<T>(); | |||
o->text = text; | |||
o->rightText = rightText; | |||
return o; | |||
} | |||
}; | |||
struct TooltipOverlay : TransparentWidget { | |||
}; | |||
struct WindowOverlay : OpaqueWidget { | |||
}; | |||
struct WindowWidget : OpaqueWidget { | |||
std::string title; | |||
void draw(NVGcontext *vg) override; | |||
void onDragMove(EventDragMove &e) override; | |||
}; | |||
struct Button : OpaqueWidget { | |||
std::string text; | |||
BNDwidgetState state = BND_DEFAULT; | |||
Button() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg) override; | |||
void onMouseEnter(EventMouseEnter &e) override; | |||
void onMouseLeave(EventMouseLeave &e) override; | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void onDragDrop(EventDragDrop &e) override; | |||
}; | |||
struct IconButton : Button { | |||
FramebufferWidget *fw; | |||
SVGWidget *sw; | |||
IconButton(); | |||
void setSVG(std::shared_ptr<SVG> svg); | |||
}; | |||
struct ChoiceButton : Button { | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct RadioButton : OpaqueWidget, QuantityWidget { | |||
BNDwidgetState state = BND_DEFAULT; | |||
RadioButton() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg) override; | |||
void onMouseEnter(EventMouseEnter &e) override; | |||
void onMouseLeave(EventMouseLeave &e) override; | |||
void onDragDrop(EventDragDrop &e) override; | |||
}; | |||
struct Slider : OpaqueWidget, QuantityWidget { | |||
BNDwidgetState state = BND_DEFAULT; | |||
Slider() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg) override; | |||
void onDragStart(EventDragStart &e) override; | |||
void onDragMove(EventDragMove &e) override; | |||
void onDragEnd(EventDragEnd &e) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
}; | |||
struct ScrollBar; | |||
/** Handles a container with ScrollBar */ | |||
struct ScrollWidget : OpaqueWidget { | |||
Widget *container; | |||
ScrollBar *horizontalScrollBar; | |||
ScrollBar *verticalScrollBar; | |||
math::Vec offset; | |||
ScrollWidget(); | |||
void scrollTo(math::Rect r); | |||
void draw(NVGcontext *vg) override; | |||
void step() override; | |||
void onMouseMove(EventMouseMove &e) override; | |||
void onScroll(EventScroll &e) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
}; | |||
struct TextField : OpaqueWidget { | |||
std::string text; | |||
std::string placeholder; | |||
bool multiline = false; | |||
/** The index of the text cursor */ | |||
int cursor = 0; | |||
/** The index of the other end of the selection. | |||
If nothing is selected, this is equal to `cursor`. | |||
*/ | |||
int selection = 0; | |||
TextField() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onMouseMove(EventMouseMove &e) override; | |||
void onFocus(EventFocus &e) override; | |||
void onText(EventText &e) override; | |||
void onKey(EventKey &e) override; | |||
/** Inserts text at the cursor, replacing the selection if necessary */ | |||
void insertText(std::string text); | |||
/** Replaces the entire text */ | |||
void setText(std::string text); | |||
virtual int getTextPosition(math::Vec mousePos); | |||
virtual void onTextChange() {} | |||
}; | |||
struct PasswordField : TextField { | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct ProgressBar : QuantityWidget { | |||
ProgressBar() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct Tooltip : VirtualWidget { | |||
std::string text; | |||
Tooltip(); | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct Scene : OpaqueWidget { | |||
Widget *overlay = NULL; | |||
/** Takes ownership of `w` */ | |||
void setOverlay(Widget *w); | |||
Menu *createMenu(); | |||
void step() override; | |||
}; | |||
//////////////////// | |||
// globals | |||
//////////////////// | |||
extern Scene *gScene; | |||
} // namespace rack | |||
#include "ui/SequentialLayout.hpp" | |||
#include "ui/Label.hpp" | |||
#include "ui/List.hpp" | |||
#include "ui/MenuOverlay.hpp" | |||
#include "ui/Tooltip.hpp" | |||
#include "ui/Scene.hpp" | |||
#include "ui/TextField.hpp" | |||
#include "ui/PasswordField.hpp" | |||
#include "ui/ScrollWidget.hpp" | |||
#include "ui/Slider.hpp" | |||
#include "ui/Menu.hpp" | |||
#include "ui/MenuEntry.hpp" | |||
#include "ui/MenuSeparator.hpp" | |||
#include "ui/MenuLabel.hpp" | |||
#include "ui/MenuItem.hpp" | |||
#include "ui/Button.hpp" | |||
#include "ui/IconButton.hpp" | |||
#include "ui/ChoiceButton.hpp" | |||
#include "ui/RadioButton.hpp" | |||
#include "ui/WindowWidget.hpp" | |||
#include "ui/ProgressBar.hpp" |
@@ -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 | |||
#include <list> | |||
#include "common.hpp" | |||
#include "events.hpp" | |||
#include "color.hpp" | |||
#include "widgets/Widget.hpp" | |||
namespace rack { | |||
//////////////////// | |||
// Base widget | |||
//////////////////// | |||
/** Instead of inheriting from Widget directly, inherit from VirtualWidget to guarantee that only one copy of Widget's member variables are used by each instance of the Widget hierarchy. | |||
*/ | |||
struct VirtualWidget : virtual Widget {}; | |||
struct TransformWidget : VirtualWidget { | |||
/** The transformation matrix */ | |||
float transform[6]; | |||
TransformWidget(); | |||
math::Rect getChildrenBoundingBox() override; | |||
void identity(); | |||
void translate(math::Vec delta); | |||
void rotate(float angle); | |||
void scale(math::Vec s); | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
struct ZoomWidget : VirtualWidget { | |||
float zoom = 1.0; | |||
math::Vec getRelativeOffset(math::Vec v, Widget *relative) override; | |||
math::Rect getViewport(math::Rect r) override; | |||
void setZoom(float zoom); | |||
void draw(NVGcontext *vg) override; | |||
void onMouseDown(EventMouseDown &e) override; | |||
void onMouseUp(EventMouseUp &e) override; | |||
void onMouseMove(EventMouseMove &e) override; | |||
void onHoverKey(EventHoverKey &e) override; | |||
void onScroll(EventScroll &e) override; | |||
void onPathDrop(EventPathDrop &e) override; | |||
}; | |||
//////////////////// | |||
// Trait widgets | |||
//////////////////// | |||
/** Widget that does not respond to events */ | |||
struct TransparentWidget : VirtualWidget { | |||
void onMouseDown(EventMouseDown &e) override {} | |||
void onMouseUp(EventMouseUp &e) override {} | |||
void onMouseMove(EventMouseMove &e) override {} | |||
void onScroll(EventScroll &e) override {} | |||
}; | |||
/** Widget that automatically responds to all mouse events but gives a chance for children to respond instead */ | |||
struct OpaqueWidget : VirtualWidget { | |||
void onMouseDown(EventMouseDown &e) override { | |||
Widget::onMouseDown(e); | |||
if (!e.target) | |||
e.target = this; | |||
e.consumed = true; | |||
} | |||
void onMouseUp(EventMouseUp &e) override { | |||
Widget::onMouseUp(e); | |||
if (!e.target) | |||
e.target = this; | |||
e.consumed = true; | |||
} | |||
void onMouseMove(EventMouseMove &e) override { | |||
Widget::onMouseMove(e); | |||
if (!e.target) | |||
e.target = this; | |||
e.consumed = true; | |||
} | |||
void onScroll(EventScroll &e) override { | |||
Widget::onScroll(e); | |||
e.consumed = true; | |||
} | |||
}; | |||
struct SVGWidget : VirtualWidget { | |||
std::shared_ptr<SVG> svg; | |||
/** Sets the box size to the svg image size */ | |||
void wrap(); | |||
/** Sets and wraps the SVG */ | |||
void setSVG(std::shared_ptr<SVG> svg); | |||
void draw(NVGcontext *vg) override; | |||
}; | |||
/** Caches a widget's draw() result to a framebuffer so it is called less frequently | |||
When `dirty` is true, its children will be re-rendered on the next call to step() override. | |||
Events are not passed to the underlying scene. | |||
*/ | |||
struct FramebufferWidget : VirtualWidget { | |||
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */ | |||
bool dirty = true; | |||
/** A margin in pixels around the children in the framebuffer | |||
This prevents cutting the rendered SVG off on the box edges. | |||
*/ | |||
float oversample; | |||
/** The root object in the framebuffer scene | |||
The FramebufferWidget owns the pointer | |||
*/ | |||
struct Internal; | |||
Internal *internal; | |||
FramebufferWidget(); | |||
~FramebufferWidget(); | |||
void draw(NVGcontext *vg) override; | |||
int getImageHandle(); | |||
void onZoom(EventZoom &e) override; | |||
}; | |||
/** A Widget representing a float value */ | |||
struct QuantityWidget : VirtualWidget { | |||
float value = 0.0; | |||
float minValue = 0.0; | |||
float maxValue = 1.0; | |||
float defaultValue = 0.0; | |||
std::string label; | |||
/** Include a space character if you want a space after the number, e.g. " Hz" */ | |||
std::string unit; | |||
/** The decimal place to round for displaying values. | |||
A precision of 2 will display as "1.00" for example. | |||
*/ | |||
int precision = 2; | |||
QuantityWidget(); | |||
void setValue(float value); | |||
void setLimits(float minValue, float maxValue); | |||
void setDefaultValue(float defaultValue); | |||
/** Generates the display value */ | |||
std::string getText(); | |||
}; | |||
//////////////////// | |||
// globals | |||
//////////////////// | |||
extern Widget *gHoveredWidget; | |||
extern Widget *gDraggedWidget; | |||
extern Widget *gDragHoveredWidget; | |||
extern Widget *gFocusedWidget; | |||
extern Widget *gTempWidget; | |||
} // namespace rack | |||
#include "widgets/EventWidget.hpp" | |||
#include "widgets/TransparentWidget.hpp" | |||
#include "widgets/OpaqueWidget.hpp" | |||
#include "widgets/TransformWidget.hpp" | |||
#include "widgets/ZoomWidget.hpp" | |||
#include "widgets/SVGWidget.hpp" | |||
#include "widgets/FramebufferWidget.hpp" | |||
#include "widgets/QuantityWidget.hpp" | |||
#define CHECKMARK_STRING "✔" | |||
#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") |
@@ -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 | |||
#include <list> | |||
#include "common.hpp" | |||
#include "math.hpp" | |||
#include "window.hpp" | |||
#include "color.hpp" | |||
namespace rack { | |||
/** A node in the 2D scene graph | |||
Never inherit from Widget directly. Instead, inherit from VirtualWidget declared below. | |||
*/ | |||
namespace event { | |||
struct Event; | |||
} // namespace event | |||
enum PickTarget { | |||
PICK_MOUSE, | |||
PICK_SCROLL, | |||
}; | |||
/** A node in the 2D scene graph */ | |||
struct Widget { | |||
/** Stores position and size */ | |||
math::Rect box = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY)); | |||
Widget *parent = NULL; | |||
std::list<Widget*> children; | |||
/** Disable rendering but continue stepping */ | |||
bool visible = true; | |||
/** If set to true, parent will delete Widget in the next step() */ | |||
bool requestedDelete = false; | |||
virtual ~Widget(); | |||
@@ -56,72 +72,14 @@ struct Widget { | |||
void removeChild(Widget *widget); | |||
/** Removes and deletes all children */ | |||
void clearChildren(); | |||
/** Recursively finalizes event start/end pairs as needed */ | |||
void finalizeEvents(); | |||
/** Advances the module by one frame */ | |||
virtual void step(); | |||
/** Draws to NanoVG context */ | |||
virtual void draw(NVGcontext *vg); | |||
// Events | |||
/** Called when a mouse button is pressed over this widget */ | |||
virtual void onMouseDown(EventMouseDown &e); | |||
/** Called when a mouse button is released over this widget */ | |||
virtual void onMouseUp(EventMouseUp &e); | |||
/** Called when the mouse moves over this widget. | |||
Called on every frame, even if `mouseRel = math::Vec(0, 0)`. | |||
*/ | |||
virtual void onMouseMove(EventMouseMove &e); | |||
/** Called when a key is pressed while hovering over this widget */ | |||
virtual void onHoverKey(EventHoverKey &e); | |||
/** Called when this widget begins responding to `onMouseMove` events */ | |||
virtual void onMouseEnter(EventMouseEnter &e) {} | |||
/** Called when this widget no longer responds to `onMouseMove` events */ | |||
virtual void onMouseLeave(EventMouseLeave &e) {} | |||
/** Called when this widget gains focus by responding to the `onMouseDown` event */ | |||
virtual void onFocus(EventFocus &e) {} | |||
virtual void onDefocus(EventDefocus &e) {} | |||
/** Called when a printable character is received while this widget is focused */ | |||
virtual void onText(EventText &e) {} | |||
/** Called when a key is pressed while this widget is focused */ | |||
virtual void onKey(EventKey &e) {} | |||
/** Called when the scroll wheel is moved while the mouse is hovering over this widget */ | |||
virtual void onScroll(EventScroll &e); | |||
/** Called when a widget responds to `onMouseDown` for a left button press */ | |||
virtual void onDragStart(EventDragStart &e) {} | |||
/** Called when the left button is released and this widget is being dragged */ | |||
virtual void onDragEnd(EventDragEnd &e) {} | |||
/** Called when a widget responds to `onMouseMove` and is being dragged */ | |||
virtual void onDragMove(EventDragMove &e) {} | |||
/** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */ | |||
virtual void onDragEnter(EventDragEnter &e) {} | |||
virtual void onDragLeave(EventDragEnter &e) {} | |||
/** Called when a drag action ends while hovering this widget */ | |||
virtual void onDragDrop(EventDragDrop &e) {} | |||
/** Called when an OS selection of files is dragged-and-dropped on this widget */ | |||
virtual void onPathDrop(EventPathDrop &e); | |||
/** Called when an event triggers an action */ | |||
virtual void onAction(EventAction &e) {} | |||
/** For widgets with some concept of values, called when the value is changed */ | |||
virtual void onChange(EventChange &e) {} | |||
/** Called when the zoom level is changed of this widget */ | |||
virtual void onZoom(EventZoom &e); | |||
/** Helper function for creating and initializing a Widget with certain arguments (in this case just the position). | |||
In this project, you will find this idiom everywhere, as an easier alternative to constructor arguments, for building a Widget (or a subclass) with a one-liner. | |||
Example: | |||
addChild(Widget::create<SVGWidget>(math::Vec(10, 10))) | |||
*/ | |||
template <typename T = Widget> | |||
static T *create(math::Vec pos = math::Vec()) { | |||
T *o = new T(); | |||
o->box.pos = pos; | |||
return o; | |||
} | |||
/** Trigger an event on this Widget. */ | |||
virtual void handleEvent(event::Event &e) {} | |||
}; | |||
@@ -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 | |||
#include <memory> | |||
#define GLEW_STATIC | |||
#include <GL/glew.h> | |||
#include <GLFW/glfw3.h> | |||
#include "GL/glew.h" | |||
#include "GLFW/glfw3.h" | |||
#include "nanovg.h" | |||
#include "nanosvg.h" | |||
#include "common.hpp" | |||
#include "math.hpp" | |||
#ifdef ARCH_MAC | |||
@@ -76,5 +77,8 @@ void windowSetTheme(NVGcolor bg, NVGcolor fg); | |||
void windowSetFullScreen(bool fullScreen); | |||
bool windowGetFullScreen(); | |||
// In svg.cpp | |||
void svgDraw(NVGcontext *vg, NSVGimage *svg); | |||
} // namespace rack |
@@ -241,39 +241,39 @@ struct AudioInterfaceWidget : ModuleWidget { | |||
AudioInterfaceWidget(AudioInterface *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/AudioInterface.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 0)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 1)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 2)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 3)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 4)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 5)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 6)); | |||
addInput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), Port::INPUT, module, AudioInterface::AUDIO_INPUT + 7)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 4)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 5)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 6)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), Port::OUTPUT, module, AudioInterface::AUDIO_OUTPUT + 7)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2)); | |||
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3)); | |||
AudioWidget *audioWidget = Widget::create<AudioWidget>(mm2px(Vec(3.2122073, 14.837339))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069211, 55.530807)), module, AudioInterface::AUDIO_INPUT + 0)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 55.530807)), module, AudioInterface::AUDIO_INPUT + 1)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 55.530807)), module, AudioInterface::AUDIO_INPUT + 2)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 55.530807)), module, AudioInterface::AUDIO_INPUT + 3)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(3.7069209, 70.144905)), module, AudioInterface::AUDIO_INPUT + 4)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(15.307249, 70.144905)), module, AudioInterface::AUDIO_INPUT + 5)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(26.906193, 70.144905)), module, AudioInterface::AUDIO_INPUT + 6)); | |||
addInput(createInput<PJ301MPort>(mm2px(Vec(38.506519, 70.144905)), module, AudioInterface::AUDIO_INPUT + 7)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506519, 92.143906)), module, AudioInterface::AUDIO_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.7069209, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 4)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.307249, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 5)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(26.906193, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 6)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.506523, 108.1443)), module, AudioInterface::AUDIO_OUTPUT + 7)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 54.577202)), module, AudioInterface::INPUT_LIGHT + 0)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 54.577202)), module, AudioInterface::INPUT_LIGHT + 1)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 69.158226)), module, AudioInterface::INPUT_LIGHT + 2)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 69.158226)), module, AudioInterface::INPUT_LIGHT + 3)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 0)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 91.147583)), module, AudioInterface::OUTPUT_LIGHT + 1)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(12.524985, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 2)); | |||
addChild(createLight<SmallLight<GreenLight>>(mm2px(Vec(35.725647, 107.17003)), module, AudioInterface::OUTPUT_LIGHT + 3)); | |||
AudioWidget *audioWidget = createWidget<AudioWidget>(mm2px(Vec(3.2122073, 14.837339))); | |||
audioWidget->box.size = mm2px(Vec(44, 28)); | |||
audioWidget->audioIO = &module->audioIO; | |||
addChild(audioWidget); | |||
@@ -281,4 +281,4 @@ struct AudioInterfaceWidget : ModuleWidget { | |||
}; | |||
Model *modelAudioInterface = Model::create<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG); | |||
Model *modelAudioInterface = createModel<AudioInterface, AudioInterfaceWidget>("Core", "AudioInterface", "Audio", EXTERNAL_TAG); |
@@ -3,25 +3,25 @@ | |||
using namespace rack; | |||
struct ModuleResizeHandle : Widget { | |||
struct ModuleResizeHandle : EventWidget { | |||
bool right = false; | |||
float dragX; | |||
Rect originalBox; | |||
ModuleResizeHandle() { | |||
box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT); | |||
} | |||
void onMouseDown(EventMouseDown &e) override { | |||
if (e.button == 0) { | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void on(event::Hover &e) override { | |||
// TODO | |||
// if (e.button == 0) { | |||
// e.target = this; | |||
// } | |||
} | |||
void onDragStart(EventDragStart &e) override { | |||
void on(event::DragStart &e) override { | |||
dragX = gRackWidget->lastMousePos.x; | |||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | |||
originalBox = m->box; | |||
} | |||
void onDragMove(EventDragMove &e) override { | |||
void on(event::DragMove &e) override { | |||
ModuleWidget *m = getAncestorOfType<ModuleWidget>(); | |||
float newDragX = gRackWidget->lastMousePos.x; | |||
@@ -78,10 +78,10 @@ struct BlankWidget : ModuleWidget { | |||
addChild(leftHandle); | |||
addChild(rightHandle); | |||
addChild(Widget::create<ScrewSilver>(Vec(15, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(15, 365))); | |||
topRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)); | |||
bottomRightScrew = Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)); | |||
addChild(createWidget<ScrewSilver>(Vec(15, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(15, 365))); | |||
topRightScrew = createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)); | |||
bottomRightScrew = createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)); | |||
addChild(topRightScrew); | |||
addChild(bottomRightScrew); | |||
} | |||
@@ -120,4 +120,4 @@ struct BlankWidget : ModuleWidget { | |||
}; | |||
Model *modelBlank = Model::create<Module, BlankWidget>("Core", "Blank", "Blank", BLANK_TAG); | |||
Model *modelBlank = createModel<Module, BlankWidget>("Core", "Blank", "Blank", BLANK_TAG); |
@@ -28,11 +28,11 @@ struct Grid16MidiWidget : MidiWidget { | |||
void createGridChoices() { | |||
Vec pos = channelChoice->box.getBottomLeft(); | |||
for (int x = 1; x < 4; x++) { | |||
vSeparators[x] = Widget::create<LedDisplaySeparator>(pos); | |||
vSeparators[x] = createWidget<LedDisplaySeparator>(pos); | |||
addChild(vSeparators[x]); | |||
} | |||
for (int y = 0; y < 4; y++) { | |||
hSeparators[y] = Widget::create<LedDisplaySeparator>(pos); | |||
hSeparators[y] = createWidget<LedDisplaySeparator>(pos); | |||
addChild(hSeparators[y]); | |||
for (int x = 0; x < 4; x++) { | |||
GridChoice *gridChoice = createGridChoice(); | |||
@@ -129,41 +129,41 @@ struct MidiCcChoice : GridChoice { | |||
else { | |||
text = string::stringf("%d", module->learnedCcs[id]); | |||
color.a = 1.0; | |||
if (gFocusedWidget == this) | |||
gFocusedWidget = NULL; | |||
if (gSelectedWidget == this) | |||
gSelectedWidget = NULL; | |||
} | |||
} | |||
void onFocus(EventFocus &e) override { | |||
e.consumed = true; | |||
void on(event::Select &e) override { | |||
e.target = this; | |||
module->learningId = id; | |||
focusCc = -1; | |||
} | |||
void onDefocus(EventDefocus &e) override { | |||
void on(event::Deselect &e) override { | |||
if (0 <= focusCc && focusCc < 128) { | |||
module->learnedCcs[id] = focusCc; | |||
} | |||
module->learningId = -1; | |||
} | |||
void onText(EventText &e) override { | |||
void on(event::SelectText &e) override { | |||
char c = e.codepoint; | |||
if ('0' <= c && c <= '9') { | |||
if (focusCc < 0) | |||
focusCc = 0; | |||
focusCc = focusCc * 10 + (c - '0'); | |||
} | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void onKey(EventKey &e) override { | |||
if (gFocusedWidget == this) { | |||
void on(event::SelectKey &e) override { | |||
if (gSelectedWidget == this) { | |||
if (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) { | |||
EventDefocus eDefocus; | |||
onDefocus(eDefocus); | |||
gFocusedWidget = NULL; | |||
e.consumed = true; | |||
event::Deselect eDeselect; | |||
handleEvent(eDeselect); | |||
gSelectedWidget = NULL; | |||
e.target = this; | |||
} | |||
} | |||
} | |||
@@ -184,29 +184,29 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget { | |||
MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/MIDICCToCVInterface.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 4)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 5)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 6)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 7)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 8)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 9)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 10)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 11)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 12)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 13)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15)); | |||
MidiCcWidget *midiWidget = Widget::create<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 4)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 5)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 6)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 7)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 8)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 9)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 10)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 11)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 12)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 13)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 14)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 15)); | |||
MidiCcWidget *midiWidget = createWidget<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339))); | |||
midiWidget->module = module; | |||
midiWidget->box.size = mm2px(Vec(44, 54.667)); | |||
midiWidget->midiIO = &module->midiInput; | |||
@@ -216,4 +216,4 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget { | |||
}; | |||
Model *modelMIDICCToCVInterface = Model::create<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG); | |||
Model *modelMIDICCToCVInterface = createModel<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG); |
@@ -261,25 +261,25 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/MIDIToCVInterface.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::CV_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::GATE_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::VELOCITY_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::AFTERTOUCH_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::PITCH_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::MOD_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::RETRIGGER_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_1_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_2_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::START_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::STOP_OUTPUT)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CONTINUE_OUTPUT)); | |||
MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.41891, 14.8373))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), module, MIDIToCVInterface::CV_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), module, MIDIToCVInterface::GATE_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), module, MIDIToCVInterface::VELOCITY_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), module, MIDIToCVInterface::AFTERTOUCH_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), module, MIDIToCVInterface::PITCH_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), module, MIDIToCVInterface::MOD_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), module, MIDIToCVInterface::RETRIGGER_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), module, MIDIToCVInterface::CLOCK_1_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), module, MIDIToCVInterface::CLOCK_2_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), module, MIDIToCVInterface::START_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 108.144)), module, MIDIToCVInterface::STOP_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), module, MIDIToCVInterface::CONTINUE_OUTPUT)); | |||
MidiWidget *midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.41891, 14.8373))); | |||
midiWidget->box.size = mm2px(Vec(33.840, 28)); | |||
midiWidget->midiIO = &module->midiInput; | |||
addChild(midiWidget); | |||
@@ -292,7 +292,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
MIDIToCVInterface *module; | |||
int index; | |||
int division; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
module->divisions[index] = division; | |||
} | |||
}; | |||
@@ -305,7 +305,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
std::vector<int> divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1}; | |||
std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"}; | |||
for (size_t i = 0; i < divisions.size(); i++) { | |||
ClockDivisionItem *item = MenuItem::create<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i])); | |||
ClockDivisionItem *item = createMenuItem<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i])); | |||
item->module = module; | |||
item->index = index; | |||
item->division = divisions[i]; | |||
@@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
menu->addChild(construct<MenuLabel>()); | |||
for (int i = 0; i < 2; i++) { | |||
ClockItem *item = MenuItem::create<ClockItem>(string::stringf("CLK %d rate", i + 1)); | |||
ClockItem *item = createMenuItem<ClockItem>(string::stringf("CLK %d rate", i + 1)); | |||
item->module = module; | |||
item->index = i; | |||
menu->addChild(item); | |||
@@ -326,4 +326,4 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
}; | |||
Model *modelMIDIToCVInterface = Model::create<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG); | |||
Model *modelMIDIToCVInterface = createModel<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG); |
@@ -171,17 +171,17 @@ struct MidiTrigChoice : GridChoice { | |||
text = string::stringf("%s%d", noteNames[semi], oct); | |||
color.a = 1.0; | |||
if (gFocusedWidget == this) | |||
gFocusedWidget = NULL; | |||
if (gSelectedWidget == this) | |||
gSelectedWidget = NULL; | |||
} | |||
} | |||
void onFocus(EventFocus &e) override { | |||
e.consumed = true; | |||
void on(event::Select &e) override { | |||
e.target = this; | |||
module->learningId = id; | |||
} | |||
void onDefocus(EventDefocus &e) override { | |||
void on(event::Deselect &e) override { | |||
module->learningId = -1; | |||
} | |||
}; | |||
@@ -201,29 +201,29 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||
MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/MIDITriggerToCVInterface.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 4)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 5)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 6)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 7)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 8)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 9)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 10)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 11)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 12)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 13)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15)); | |||
MidiTrigWidget *midiWidget = Widget::create<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 4)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 5)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 6)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 7)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 8)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 9)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 10)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 11)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 12)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 13)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15)); | |||
MidiTrigWidget *midiWidget = createWidget<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339))); | |||
midiWidget->module = module; | |||
midiWidget->box.size = mm2px(Vec(44, 54.667)); | |||
midiWidget->midiIO = &module->midiInput; | |||
@@ -236,17 +236,17 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||
struct VelocityItem : MenuItem { | |||
MIDITriggerToCVInterface *module; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
module->velocity ^= true; | |||
} | |||
}; | |||
menu->addChild(MenuEntry::create()); | |||
VelocityItem *velocityItem = MenuItem::create<VelocityItem>("Velocity", CHECKMARK(module->velocity)); | |||
menu->addChild(new MenuEntry()); | |||
VelocityItem *velocityItem = createMenuItem<VelocityItem>("Velocity", CHECKMARK(module->velocity)); | |||
velocityItem->module = module; | |||
menu->addChild(velocityItem); | |||
} | |||
}; | |||
Model *modelMIDITriggerToCVInterface = Model::create<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG); | |||
Model *modelMIDITriggerToCVInterface = createModel<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG); |
@@ -10,12 +10,12 @@ struct NotesWidget : ModuleWidget { | |||
NotesWidget(Module *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/Notes.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
textField = Widget::create<LedDisplayTextField>(mm2px(Vec(3.39962, 14.8373))); | |||
textField = createWidget<LedDisplayTextField>(mm2px(Vec(3.39962, 14.8373))); | |||
textField->box.size = mm2px(Vec(74.480, 102.753)); | |||
textField->multiline = true; | |||
addChild(textField); | |||
@@ -41,4 +41,4 @@ struct NotesWidget : ModuleWidget { | |||
}; | |||
Model *modelNotes = Model::create<Module, NotesWidget>("Core", "Notes", "Notes", BLANK_TAG); | |||
Model *modelNotes = createModel<Module, NotesWidget>("Core", "Notes", "Notes", BLANK_TAG); |
@@ -303,29 +303,29 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { | |||
QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) { | |||
setPanel(SVG::load(asset::global("res/Core/QuadMIDIToCVInterface.svg"))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3)); | |||
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3)); | |||
MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.4009969, 14.837336))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), module, QuadMIDIToCVInterface::CV_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), module, QuadMIDIToCVInterface::CV_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), module, QuadMIDIToCVInterface::CV_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), module, QuadMIDIToCVInterface::CV_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3)); | |||
MidiWidget *midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.4009969, 14.837336))); | |||
midiWidget->box.size = mm2px(Vec(44, 28)); | |||
midiWidget->midiIO = &module->midiInput; | |||
addChild(midiWidget); | |||
@@ -337,17 +337,19 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { | |||
struct PolyphonyItem : MenuItem { | |||
QuadMIDIToCVInterface *module; | |||
QuadMIDIToCVInterface::PolyMode polyMode; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
module->polyMode = polyMode; | |||
module->onReset(); | |||
} | |||
}; | |||
menu->addChild(MenuEntry::create()); | |||
menu->addChild(MenuLabel::create("Polyphony mode")); | |||
menu->addChild(new MenuEntry()); | |||
menu->addChild(createMenuLabel("Polyphony mode")); | |||
auto addPolyphonyItem = [&](QuadMIDIToCVInterface::PolyMode polyMode, std::string name) { | |||
PolyphonyItem *item = MenuItem::create<PolyphonyItem>(name, CHECKMARK(module->polyMode == polyMode)); | |||
PolyphonyItem *item = new PolyphonyItem(); | |||
item->text = name; | |||
item->rightText = CHECKMARK(module->polyMode == polyMode); | |||
item->module = module; | |||
item->polyMode = polyMode; | |||
menu->addChild(item); | |||
@@ -362,5 +364,5 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { | |||
}; | |||
Model *modelQuadMIDIToCVInterface = Model::create<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG); | |||
Model *modelQuadMIDIToCVInterface = createModel<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG); | |||
@@ -1,5 +1,6 @@ | |||
#include "app.hpp" | |||
#include "audio.hpp" | |||
#include "helpers.hpp" | |||
namespace rack { | |||
@@ -8,16 +9,16 @@ namespace rack { | |||
struct AudioDriverItem : MenuItem { | |||
AudioIO *audioIO; | |||
int driver; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
audioIO->setDriver(driver); | |||
} | |||
}; | |||
struct AudioDriverChoice : LedDisplayChoice { | |||
AudioWidget *audioWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio driver")); | |||
menu->addChild(createMenuLabel("Audio driver")); | |||
for (int driver : audioWidget->audioIO->getDrivers()) { | |||
AudioDriverItem *item = new AudioDriverItem(); | |||
item->audioIO = audioWidget->audioIO; | |||
@@ -37,7 +38,7 @@ struct AudioDeviceItem : MenuItem { | |||
AudioIO *audioIO; | |||
int device; | |||
int offset; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
audioIO->setDevice(device, offset); | |||
} | |||
}; | |||
@@ -47,9 +48,9 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
/** Prevents devices with a ridiculous number of channels from being displayed */ | |||
int maxTotalChannels = 128; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device")); | |||
menu->addChild(createMenuLabel("Audio device")); | |||
int deviceCount = audioWidget->audioIO->getDeviceCount(); | |||
{ | |||
AudioDeviceItem *item = new AudioDeviceItem(); | |||
@@ -88,19 +89,19 @@ struct AudioDeviceChoice : LedDisplayChoice { | |||
struct AudioSampleRateItem : MenuItem { | |||
AudioIO *audioIO; | |||
int sampleRate; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
audioIO->setSampleRate(sampleRate); | |||
} | |||
}; | |||
struct AudioSampleRateChoice : LedDisplayChoice { | |||
AudioWidget *audioWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Sample rate")); | |||
menu->addChild(createMenuLabel("Sample rate")); | |||
std::vector<int> sampleRates = audioWidget->audioIO->getSampleRates(); | |||
if (sampleRates.empty()) { | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)")); | |||
menu->addChild(createMenuLabel("(Locked by device)")); | |||
} | |||
for (int sampleRate : sampleRates) { | |||
AudioSampleRateItem *item = new AudioSampleRateItem(); | |||
@@ -120,19 +121,19 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||
struct AudioBlockSizeItem : MenuItem { | |||
AudioIO *audioIO; | |||
int blockSize; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
audioIO->setBlockSize(blockSize); | |||
} | |||
}; | |||
struct AudioBlockSizeChoice : LedDisplayChoice { | |||
AudioWidget *audioWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Block size")); | |||
menu->addChild(createMenuLabel("Block size")); | |||
std::vector<int> blockSizes = audioWidget->audioIO->getBlockSizes(); | |||
if (blockSizes.empty()) { | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(Locked by device)")); | |||
menu->addChild(createMenuLabel("(Locked by device)")); | |||
} | |||
for (int blockSize : blockSizes) { | |||
AudioBlockSizeItem *item = new AudioBlockSizeItem(); | |||
@@ -155,34 +156,34 @@ AudioWidget::AudioWidget() { | |||
math::Vec pos = math::Vec(); | |||
AudioDriverChoice *driverChoice = Widget::create<AudioDriverChoice>(pos); | |||
AudioDriverChoice *driverChoice = createWidget<AudioDriverChoice>(pos); | |||
driverChoice->audioWidget = this; | |||
addChild(driverChoice); | |||
pos = driverChoice->box.getBottomLeft(); | |||
this->driverChoice = driverChoice; | |||
this->driverSeparator = Widget::create<LedDisplaySeparator>(pos); | |||
this->driverSeparator = createWidget<LedDisplaySeparator>(pos); | |||
addChild(this->driverSeparator); | |||
AudioDeviceChoice *deviceChoice = Widget::create<AudioDeviceChoice>(pos); | |||
AudioDeviceChoice *deviceChoice = createWidget<AudioDeviceChoice>(pos); | |||
deviceChoice->audioWidget = this; | |||
addChild(deviceChoice); | |||
pos = deviceChoice->box.getBottomLeft(); | |||
this->deviceChoice = deviceChoice; | |||
this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos); | |||
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos); | |||
addChild(this->deviceSeparator); | |||
AudioSampleRateChoice *sampleRateChoice = Widget::create<AudioSampleRateChoice>(pos); | |||
AudioSampleRateChoice *sampleRateChoice = createWidget<AudioSampleRateChoice>(pos); | |||
sampleRateChoice->audioWidget = this; | |||
addChild(sampleRateChoice); | |||
this->sampleRateChoice = sampleRateChoice; | |||
this->sampleRateSeparator = Widget::create<LedDisplaySeparator>(pos); | |||
this->sampleRateSeparator = createWidget<LedDisplaySeparator>(pos); | |||
this->sampleRateSeparator->box.size.y = this->sampleRateChoice->box.size.y; | |||
addChild(this->sampleRateSeparator); | |||
AudioBlockSizeChoice *bufferSizeChoice = Widget::create<AudioBlockSizeChoice>(pos); | |||
AudioBlockSizeChoice *bufferSizeChoice = createWidget<AudioBlockSizeChoice>(pos); | |||
bufferSizeChoice->audioWidget = this; | |||
addChild(bufferSizeChoice); | |||
this->bufferSizeChoice = bufferSizeChoice; | |||
@@ -15,13 +15,13 @@ Knob::Knob() { | |||
smooth = true; | |||
} | |||
void Knob::onDragStart(EventDragStart &e) { | |||
void Knob::on(event::DragStart &e) { | |||
windowCursorLock(); | |||
dragValue = value; | |||
randomizable = false; | |||
} | |||
void Knob::onDragMove(EventDragMove &e) { | |||
void Knob::on(event::DragMove &e) { | |||
float range; | |||
if (std::isfinite(minValue) && std::isfinite(maxValue)) { | |||
range = maxValue - minValue; | |||
@@ -30,7 +30,7 @@ void Knob::onDragMove(EventDragMove &e) { | |||
// Continuous encoders scale as if their limits are +/-1 | |||
range = 1.f - (-1.f); | |||
} | |||
float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed * range; | |||
float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range; | |||
// Drag slower if Mod is held | |||
if (windowIsModPressed()) | |||
@@ -43,7 +43,7 @@ void Knob::onDragMove(EventDragMove &e) { | |||
setValue(dragValue); | |||
} | |||
void Knob::onDragEnd(EventDragEnd &e) { | |||
void Knob::on(event::DragEnd &e) { | |||
windowCursorUnlock(); | |||
randomizable = true; | |||
} | |||
@@ -52,11 +52,10 @@ void LedDisplayChoice::draw(NVGcontext *vg) { | |||
nvgResetScissor(vg); | |||
} | |||
void LedDisplayChoice::onMouseDown(EventMouseDown &e) { | |||
if (e.button == 0 || e.button == 1) { | |||
EventAction eAction; | |||
onAction(eAction); | |||
e.consumed = true; | |||
void LedDisplayChoice::on(event::Button &e) { | |||
if (e.action == GLFW_PRESS && (e.button == GLFW_MOUSE_BUTTON_LEFT || e.button == GLFW_MOUSE_BUTTON_RIGHT)) { | |||
event::Action eAction; | |||
handleEvent(eAction); | |||
e.target = this; | |||
} | |||
} | |||
@@ -85,7 +84,7 @@ void LedDisplayTextField::draw(NVGcontext *vg) { | |||
NVGcolor highlightColor = color; | |||
highlightColor.a = 0.5; | |||
int begin = std::min(cursor, selection); | |||
int end = (this == gFocusedWidget) ? std::max(cursor, selection) : -1; | |||
int end = (this == gSelectedWidget) ? std::max(cursor, selection) : -1; | |||
bndIconLabelCaret(vg, textOffset.x, textOffset.y, | |||
box.size.x - 2*textOffset.x, box.size.y - 2*textOffset.y, | |||
-1, color, 12, text.c_str(), highlightColor, begin, end); | |||
@@ -1,5 +1,6 @@ | |||
#include "app.hpp" | |||
#include "midi.hpp" | |||
#include "helpers.hpp" | |||
namespace rack { | |||
@@ -8,16 +9,16 @@ namespace rack { | |||
struct MidiDriverItem : MenuItem { | |||
MidiIO *midiIO; | |||
int driverId; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
midiIO->setDriverId(driverId); | |||
} | |||
}; | |||
struct MidiDriverChoice : LedDisplayChoice { | |||
MidiWidget *midiWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI driver")); | |||
menu->addChild(createMenuLabel("MIDI driver")); | |||
for (int driverId : midiWidget->midiIO->getDriverIds()) { | |||
MidiDriverItem *item = new MidiDriverItem(); | |||
item->midiIO = midiWidget->midiIO; | |||
@@ -42,16 +43,16 @@ struct MidiDriverChoice : LedDisplayChoice { | |||
struct MidiDeviceItem : MenuItem { | |||
MidiIO *midiIO; | |||
int deviceId; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
midiIO->setDeviceId(deviceId); | |||
} | |||
}; | |||
struct MidiDeviceChoice : LedDisplayChoice { | |||
MidiWidget *midiWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI device")); | |||
menu->addChild(createMenuLabel("MIDI device")); | |||
{ | |||
MidiDeviceItem *item = new MidiDeviceItem(); | |||
item->midiIO = midiWidget->midiIO; | |||
@@ -84,16 +85,16 @@ struct MidiDeviceChoice : LedDisplayChoice { | |||
struct MidiChannelItem : MenuItem { | |||
MidiIO *midiIO; | |||
int channel; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
midiIO->channel = channel; | |||
} | |||
}; | |||
struct MidiChannelChoice : LedDisplayChoice { | |||
MidiWidget *midiWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI channel")); | |||
menu->addChild(createMenuLabel("MIDI channel")); | |||
for (int channel = -1; channel < 16; channel++) { | |||
MidiChannelItem *item = new MidiChannelItem(); | |||
item->midiIO = midiWidget->midiIO; | |||
@@ -114,25 +115,25 @@ MidiWidget::MidiWidget() { | |||
math::Vec pos = math::Vec(); | |||
MidiDriverChoice *driverChoice = Widget::create<MidiDriverChoice>(pos); | |||
MidiDriverChoice *driverChoice = createWidget<MidiDriverChoice>(pos); | |||
driverChoice->midiWidget = this; | |||
addChild(driverChoice); | |||
pos = driverChoice->box.getBottomLeft(); | |||
this->driverChoice = driverChoice; | |||
this->driverSeparator = Widget::create<LedDisplaySeparator>(pos); | |||
this->driverSeparator = createWidget<LedDisplaySeparator>(pos); | |||
addChild(this->driverSeparator); | |||
MidiDeviceChoice *deviceChoice = Widget::create<MidiDeviceChoice>(pos); | |||
MidiDeviceChoice *deviceChoice = createWidget<MidiDeviceChoice>(pos); | |||
deviceChoice->midiWidget = this; | |||
addChild(deviceChoice); | |||
pos = deviceChoice->box.getBottomLeft(); | |||
this->deviceChoice = deviceChoice; | |||
this->deviceSeparator = Widget::create<LedDisplaySeparator>(pos); | |||
this->deviceSeparator = createWidget<LedDisplaySeparator>(pos); | |||
addChild(this->deviceSeparator); | |||
MidiChannelChoice *channelChoice = Widget::create<MidiChannelChoice>(pos); | |||
MidiChannelChoice *channelChoice = createWidget<MidiChannelChoice>(pos); | |||
channelChoice->midiWidget = this; | |||
addChild(channelChoice); | |||
this->channelChoice = channelChoice; | |||
@@ -1,6 +1,7 @@ | |||
#include "app.hpp" | |||
#include "plugin.hpp" | |||
#include "window.hpp" | |||
#include "helpers.hpp" | |||
#include <set> | |||
#include <algorithm> | |||
@@ -45,7 +46,7 @@ static bool isModelMatch(Model *model, std::string search) { | |||
struct FavoriteRadioButton : RadioButton { | |||
Model *model = NULL; | |||
void onAction(EventAction &e) override; | |||
void on(event::Action &e) override; | |||
}; | |||
@@ -56,7 +57,7 @@ struct SeparatorItem : OpaqueWidget { | |||
void setText(std::string text) { | |||
clearChildren(); | |||
Label *label = Widget::create<Label>(math::Vec(0, 12 + itemMargin)); | |||
Label *label = createWidget<Label>(math::Vec(0, 12 + itemMargin)); | |||
label->text = text; | |||
label->fontSize = 20; | |||
label->color.a *= 0.5; | |||
@@ -78,19 +79,19 @@ struct BrowserListItem : OpaqueWidget { | |||
Widget::draw(vg); | |||
} | |||
void onDragStart(EventDragStart &e) override; | |||
void on(event::DragStart &e) override; | |||
void onDragDrop(EventDragDrop &e) override { | |||
void on(event::DragDrop &e) override { | |||
if (e.origin != this) | |||
return; | |||
doAction(); | |||
} | |||
void doAction() { | |||
EventAction eAction; | |||
eAction.consumed = true; | |||
onAction(eAction); | |||
if (eAction.consumed) { | |||
event::Action eAction; | |||
eAction.target = this; | |||
handleEvent(eAction); | |||
if (eAction.target) { | |||
// deletes `this` | |||
gScene->setOverlay(NULL); | |||
} | |||
@@ -107,7 +108,7 @@ struct ModelItem : BrowserListItem { | |||
assert(model); | |||
this->model = model; | |||
FavoriteRadioButton *favoriteButton = Widget::create<FavoriteRadioButton>(math::Vec(8, itemMargin)); | |||
FavoriteRadioButton *favoriteButton = createWidget<FavoriteRadioButton>(math::Vec(8, itemMargin)); | |||
favoriteButton->box.size.x = 20; | |||
favoriteButton->label = "★"; | |||
addChild(favoriteButton); | |||
@@ -118,11 +119,11 @@ struct ModelItem : BrowserListItem { | |||
favoriteButton->setValue(1); | |||
favoriteButton->model = model; | |||
Label *nameLabel = Widget::create<Label>(favoriteButton->box.getTopRight()); | |||
Label *nameLabel = createWidget<Label>(favoriteButton->box.getTopRight()); | |||
nameLabel->text = model->name; | |||
addChild(nameLabel); | |||
pluginLabel = Widget::create<Label>(math::Vec(0, itemMargin)); | |||
pluginLabel = createWidget<Label>(math::Vec(0, itemMargin)); | |||
pluginLabel->alignment = Label::RIGHT_ALIGNMENT; | |||
pluginLabel->text = model->plugin->slug + " " + model->plugin->version; | |||
pluginLabel->color.a = 0.5; | |||
@@ -135,7 +136,7 @@ struct ModelItem : BrowserListItem { | |||
pluginLabel->box.size.x = box.size.x - BND_SCROLLBAR_WIDTH; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
ModuleWidget *moduleWidget = model->createModuleWidget(); | |||
if (!moduleWidget) | |||
return; | |||
@@ -153,7 +154,7 @@ struct AuthorItem : BrowserListItem { | |||
void setAuthor(std::string author) { | |||
clearChildren(); | |||
this->author = author; | |||
Label *authorLabel = Widget::create<Label>(math::Vec(0, 0 + itemMargin)); | |||
Label *authorLabel = createWidget<Label>(math::Vec(0, 0 + itemMargin)); | |||
if (author.empty()) | |||
authorLabel->text = "Show all modules"; | |||
else | |||
@@ -161,7 +162,7 @@ struct AuthorItem : BrowserListItem { | |||
addChild(authorLabel); | |||
} | |||
void onAction(EventAction &e) override; | |||
void on(event::Action &e) override; | |||
}; | |||
@@ -171,7 +172,7 @@ struct TagItem : BrowserListItem { | |||
void setTag(ModelTag tag) { | |||
clearChildren(); | |||
this->tag = tag; | |||
Label *tagLabel = Widget::create<Label>(math::Vec(0, 0 + itemMargin)); | |||
Label *tagLabel = createWidget<Label>(math::Vec(0, 0 + itemMargin)); | |||
if (tag == NO_TAG) | |||
tagLabel->text = "Show all tags"; | |||
else | |||
@@ -179,18 +180,18 @@ struct TagItem : BrowserListItem { | |||
addChild(tagLabel); | |||
} | |||
void onAction(EventAction &e) override; | |||
void on(event::Action &e) override; | |||
}; | |||
struct ClearFilterItem : BrowserListItem { | |||
ClearFilterItem() { | |||
Label *label = Widget::create<Label>(math::Vec(0, 0 + itemMargin)); | |||
Label *label = createWidget<Label>(math::Vec(0, 0 + itemMargin)); | |||
label->text = "Back"; | |||
addChild(label); | |||
} | |||
void onAction(EventAction &e) override; | |||
void on(event::Action &e) override; | |||
}; | |||
@@ -270,8 +271,8 @@ struct ModuleBrowser; | |||
struct SearchModuleField : TextField { | |||
ModuleBrowser *moduleBrowser; | |||
void onTextChange() override; | |||
void onKey(EventKey &e) override; | |||
void on(event::Change &e) override; | |||
void on(event::SelectKey &e) override; | |||
}; | |||
@@ -428,7 +429,7 @@ struct ModuleBrowser : OpaqueWidget { | |||
moduleScroll->box.size.y = std::min(box.size.y - moduleScroll->box.pos.y, moduleList->box.size.y); | |||
box.size.y = std::min(box.size.y, moduleScroll->box.getBottomRight().y); | |||
gFocusedWidget = searchField; | |||
gSelectedWidget = searchField; | |||
Widget::step(); | |||
} | |||
}; | |||
@@ -436,31 +437,31 @@ struct ModuleBrowser : OpaqueWidget { | |||
// Implementations of inline methods above | |||
void AuthorItem::onAction(EventAction &e) { | |||
void AuthorItem::on(event::Action &e) { | |||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | |||
sAuthorFilter = author; | |||
moduleBrowser->clearSearch(); | |||
moduleBrowser->refreshSearch(); | |||
e.consumed = false; | |||
e.target = this; | |||
} | |||
void TagItem::onAction(EventAction &e) { | |||
void TagItem::on(event::Action &e) { | |||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | |||
sTagFilter = tag; | |||
moduleBrowser->clearSearch(); | |||
moduleBrowser->refreshSearch(); | |||
e.consumed = false; | |||
e.target = this; | |||
} | |||
void ClearFilterItem::onAction(EventAction &e) { | |||
void ClearFilterItem::on(event::Action &e) { | |||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | |||
sAuthorFilter = ""; | |||
sTagFilter = NO_TAG; | |||
moduleBrowser->refreshSearch(); | |||
e.consumed = false; | |||
e.target = this; | |||
} | |||
void FavoriteRadioButton::onAction(EventAction &e) { | |||
void FavoriteRadioButton::on(event::Action &e) { | |||
if (!model) | |||
return; | |||
if (value) { | |||
@@ -477,56 +478,56 @@ void FavoriteRadioButton::onAction(EventAction &e) { | |||
moduleBrowser->refreshSearch(); | |||
} | |||
void BrowserListItem::onDragStart(EventDragStart &e) { | |||
void BrowserListItem::on(event::DragStart &e) { | |||
BrowserList *list = dynamic_cast<BrowserList*>(parent); | |||
if (list) { | |||
list->selectItem(this); | |||
} | |||
} | |||
void SearchModuleField::onTextChange() { | |||
void SearchModuleField::on(event::Change &e) { | |||
moduleBrowser->refreshSearch(); | |||
} | |||
void SearchModuleField::onKey(EventKey &e) { | |||
void SearchModuleField::on(event::SelectKey &e) { | |||
switch (e.key) { | |||
case GLFW_KEY_ESCAPE: { | |||
gScene->setOverlay(NULL); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} break; | |||
case GLFW_KEY_UP: { | |||
moduleBrowser->moduleList->incrementSelection(-1); | |||
moduleBrowser->moduleList->scrollSelected(); | |||
e.consumed = true; | |||
e.target = this; | |||
} break; | |||
case GLFW_KEY_DOWN: { | |||
moduleBrowser->moduleList->incrementSelection(1); | |||
moduleBrowser->moduleList->scrollSelected(); | |||
e.consumed = true; | |||
e.target = this; | |||
} break; | |||
case GLFW_KEY_PAGE_UP: { | |||
moduleBrowser->moduleList->incrementSelection(-5); | |||
moduleBrowser->moduleList->scrollSelected(); | |||
e.consumed = true; | |||
e.target = this; | |||
} break; | |||
case GLFW_KEY_PAGE_DOWN: { | |||
moduleBrowser->moduleList->incrementSelection(5); | |||
moduleBrowser->moduleList->scrollSelected(); | |||
e.consumed = true; | |||
e.target = this; | |||
} break; | |||
case GLFW_KEY_ENTER: { | |||
BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | |||
if (item) { | |||
item->doAction(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
} | |||
if (!e.consumed) { | |||
TextField::onKey(e); | |||
if (!e.target) { | |||
TextField::on(e); | |||
} | |||
} | |||
@@ -330,93 +330,86 @@ void ModuleWidget::drawShadow(NVGcontext *vg) { | |||
nvgFill(vg); | |||
} | |||
void ModuleWidget::onMouseDown(EventMouseDown &e) { | |||
Widget::onMouseDown(e); | |||
if (e.consumed) | |||
return; | |||
if (e.button == 1) { | |||
createContextMenu(); | |||
void ModuleWidget::on(event::Hover &e) { | |||
OpaqueWidget::on(e); | |||
// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget. | |||
if (glfwGetKey(gWindow, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_BACKSPACE) == GLFW_PRESS) { | |||
if (!windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->deleteModule(this); | |||
delete this; | |||
// e.target = this; | |||
return; | |||
} | |||
} | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void ModuleWidget::onMouseMove(EventMouseMove &e) { | |||
OpaqueWidget::onMouseMove(e); | |||
// Don't delete the ModuleWidget if a TextField is focused | |||
if (!gFocusedWidget) { | |||
// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget. | |||
if (glfwGetKey(gWindow, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_BACKSPACE) == GLFW_PRESS) { | |||
if (!windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->deleteModule(this); | |||
this->finalizeEvents(); | |||
delete this; | |||
e.consumed = true; | |||
return; | |||
} | |||
void ModuleWidget::on(event::Button &e) { | |||
OpaqueWidget::on(e); | |||
if (e.target == this) { | |||
if (e.button == 1) { | |||
createContextMenu(); | |||
} | |||
} | |||
} | |||
void ModuleWidget::onHoverKey(EventHoverKey &e) { | |||
void ModuleWidget::on(event::HoverKey &e) { | |||
switch (e.key) { | |||
case GLFW_KEY_I: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
reset(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
case GLFW_KEY_R: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
randomize(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
case GLFW_KEY_C: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
copyClipboard(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
case GLFW_KEY_V: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
pasteClipboard(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
case GLFW_KEY_D: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->cloneModule(this); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
case GLFW_KEY_U: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
disconnect(); | |||
e.consumed = true; | |||
e.target = this; | |||
return; | |||
} | |||
} break; | |||
} | |||
Widget::onHoverKey(e); | |||
OpaqueWidget::on(e); | |||
} | |||
void ModuleWidget::onDragStart(EventDragStart &e) { | |||
void ModuleWidget::on(event::DragStart &e) { | |||
dragPos = gRackWidget->lastMousePos.minus(box.pos); | |||
} | |||
void ModuleWidget::onDragEnd(EventDragEnd &e) { | |||
void ModuleWidget::on(event::DragEnd &e) { | |||
} | |||
void ModuleWidget::onDragMove(EventDragMove &e) { | |||
void ModuleWidget::on(event::DragMove &e) { | |||
if (!gRackWidget->lockModules) { | |||
math::Rect newBox = box; | |||
newBox.pos = gRackWidget->lastMousePos.minus(dragPos); | |||
@@ -427,65 +420,64 @@ void ModuleWidget::onDragMove(EventDragMove &e) { | |||
struct ModuleDisconnectItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->disconnect(); | |||
} | |||
}; | |||
struct ModuleResetItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->reset(); | |||
} | |||
}; | |||
struct ModuleRandomizeItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->randomize(); | |||
} | |||
}; | |||
struct ModuleCopyItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->copyClipboard(); | |||
} | |||
}; | |||
struct ModulePasteItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->pasteClipboard(); | |||
} | |||
}; | |||
struct ModuleSaveItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->saveDialog(); | |||
} | |||
}; | |||
struct ModuleLoadItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
moduleWidget->loadDialog(); | |||
} | |||
}; | |||
struct ModuleCloneItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->cloneModule(moduleWidget); | |||
} | |||
}; | |||
struct ModuleDeleteItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->deleteModule(moduleWidget); | |||
moduleWidget->finalizeEvents(); | |||
delete moduleWidget; | |||
} | |||
}; | |||
@@ -4,13 +4,11 @@ | |||
namespace rack { | |||
void MomentarySwitch::onDragStart(EventDragStart &e) { | |||
void MomentarySwitch::on(event::DragStart &e) { | |||
setValue(maxValue); | |||
EventAction eAction; | |||
onAction(eAction); | |||
} | |||
void MomentarySwitch::onDragEnd(EventDragEnd &e) { | |||
void MomentarySwitch::on(event::DragEnd &e) { | |||
setValue(minValue); | |||
} | |||
@@ -36,15 +36,16 @@ void ParamWidget::randomize() { | |||
} | |||
} | |||
void ParamWidget::onMouseDown(EventMouseDown &e) { | |||
if (e.button == 1) { | |||
reset(); | |||
void ParamWidget::on(event::Button &e) { | |||
OpaqueWidget::on(e); | |||
if (e.target == this) { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | |||
reset(); | |||
} | |||
} | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void ParamWidget::onChange(EventChange &e) { | |||
void ParamWidget::on(event::Change &e) { | |||
if (!module) | |||
return; | |||
@@ -3,6 +3,7 @@ | |||
#include "app.hpp" | |||
#include "plugin.hpp" | |||
#include "window.hpp" | |||
#include "helpers.hpp" | |||
#include "osdialog.h" | |||
@@ -10,7 +11,7 @@ namespace rack { | |||
struct RegisterButton : Button { | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
std::thread t([&]() { | |||
system::openBrowser("https://vcvrack.com/"); | |||
}); | |||
@@ -22,7 +23,7 @@ struct RegisterButton : Button { | |||
struct LogInButton : Button { | |||
TextField *emailField; | |||
TextField *passwordField; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
std::thread t(pluginLogIn, emailField->text, passwordField->text); | |||
t.detach(); | |||
passwordField->text = ""; | |||
@@ -38,7 +39,7 @@ struct StatusLabel : Label { | |||
struct ManageButton : Button { | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
std::thread t([&]() { | |||
system::openBrowser("https://vcvrack.com/plugins.html"); | |||
}); | |||
@@ -84,7 +85,7 @@ struct SyncButton : Button { | |||
nvgStroke(vg); | |||
} | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
available = false; | |||
std::thread t([this]() { | |||
if (pluginSync(false)) | |||
@@ -96,7 +97,7 @@ struct SyncButton : Button { | |||
struct LogOutButton : Button { | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
pluginLogOut(); | |||
} | |||
}; | |||
@@ -114,7 +115,7 @@ struct DownloadProgressBar : ProgressBar { | |||
struct CancelButton : Button { | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
pluginCancelDownload(); | |||
} | |||
}; | |||
@@ -124,7 +125,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
{ | |||
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0)); | |||
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0)); | |||
layout->spacing = 5; | |||
loginWidget = layout; | |||
@@ -157,7 +158,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
} | |||
{ | |||
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0)); | |||
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0)); | |||
layout->spacing = 5; | |||
manageWidget = layout; | |||
@@ -180,7 +181,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
} | |||
{ | |||
SequentialLayout *layout = Widget::create<SequentialLayout>(math::Vec(0, 0)); | |||
SequentialLayout *layout = createWidget<SequentialLayout>(math::Vec(0, 0)); | |||
layout->spacing = 5; | |||
downloadWidget = layout; | |||
@@ -49,20 +49,19 @@ void Port::draw(NVGcontext *vg) { | |||
} | |||
} | |||
void Port::onMouseDown(EventMouseDown &e) { | |||
if (e.button == 1) { | |||
void Port::on(event::Button &e) { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | |||
gRackWidget->wireContainer->removeTopWire(this); | |||
// HACK | |||
// Update hovered*Port of active wire if applicable | |||
EventDragEnter e; | |||
onDragEnter(e); | |||
event::DragEnter eDragEnter; | |||
on(eDragEnter); | |||
} | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void Port::onDragStart(EventDragStart &e) { | |||
void Port::on(event::DragStart &e) { | |||
// Try to grab wire on top of stack | |||
WireWidget *wire = gRackWidget->wireContainer->getTopWire(this); | |||
if (type == OUTPUT && windowIsModPressed()) { | |||
@@ -88,16 +87,16 @@ void Port::onDragStart(EventDragStart &e) { | |||
gRackWidget->wireContainer->setActiveWire(wire); | |||
} | |||
void Port::onDragEnd(EventDragEnd &e) { | |||
void Port::on(event::DragEnd &e) { | |||
// FIXME | |||
// If the source Port is deleted, this will be called, removing the cable | |||
gRackWidget->wireContainer->commitActiveWire(); | |||
} | |||
void Port::onDragDrop(EventDragDrop &e) { | |||
void Port::on(event::DragDrop &e) { | |||
} | |||
void Port::onDragEnter(EventDragEnter &e) { | |||
void Port::on(event::DragEnter &e) { | |||
// Reject ports if this is an input port and something is already plugged into it | |||
if (type == INPUT) { | |||
WireWidget *topWire = gRackWidget->wireContainer->getTopWire(this); | |||
@@ -114,7 +113,7 @@ void Port::onDragEnter(EventDragEnter &e) { | |||
} | |||
} | |||
void Port::onDragLeave(EventDragEnter &e) { | |||
void Port::on(event::DragLeave &e) { | |||
WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | |||
if (activeWire) { | |||
if (type == INPUT) | |||
@@ -55,53 +55,53 @@ void RackScene::draw(NVGcontext *vg) { | |||
Scene::draw(vg); | |||
} | |||
void RackScene::onHoverKey(EventHoverKey &e) { | |||
Widget::onHoverKey(e); | |||
void RackScene::on(event::HoverKey &e) { | |||
Scene::on(e); | |||
if (!e.consumed) { | |||
if (!e.target) { | |||
switch (e.key) { | |||
case GLFW_KEY_N: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->reset(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} break; | |||
case GLFW_KEY_Q: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
windowClose(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} break; | |||
case GLFW_KEY_O: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->loadDialog(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
if (windowIsModPressed() && windowIsShiftPressed()) { | |||
gRackWidget->revert(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} break; | |||
case GLFW_KEY_S: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->saveDialog(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
if (windowIsModPressed() && windowIsShiftPressed()) { | |||
gRackWidget->saveAsDialog(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} break; | |||
case GLFW_KEY_V: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->pastePresetClipboard(); | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
} break; | |||
case GLFW_KEY_ENTER: | |||
case GLFW_KEY_KP_ENTER: { | |||
appModuleBrowserCreate(); | |||
e.consumed = true; | |||
e.target = this; | |||
} break; | |||
case GLFW_KEY_F11: { | |||
windowSetFullScreen(!windowGetFullScreen()); | |||
@@ -110,17 +110,17 @@ void RackScene::onHoverKey(EventHoverKey &e) { | |||
} | |||
} | |||
void RackScene::onPathDrop(EventPathDrop &e) { | |||
void RackScene::on(event::PathDrop &e) { | |||
if (e.paths.size() >= 1) { | |||
const std::string &firstPath = e.paths.front(); | |||
if (string::extension(firstPath) == "vcv") { | |||
gRackWidget->load(firstPath); | |||
e.consumed = true; | |||
const std::string &path = e.paths[0]; | |||
if (string::extension(path) == "vcv") { | |||
gRackWidget->load(path); | |||
e.target = this; | |||
} | |||
} | |||
if (!e.consumed) | |||
Scene::onPathDrop(e); | |||
if (!e.target) | |||
Scene::on(e); | |||
} | |||
@@ -497,26 +497,23 @@ void RackWidget::draw(NVGcontext *vg) { | |||
Widget::draw(vg); | |||
} | |||
void RackWidget::onMouseMove(EventMouseMove &e) { | |||
OpaqueWidget::onMouseMove(e); | |||
void RackWidget::on(event::Hover &e) { | |||
OpaqueWidget::on(e); | |||
lastMousePos = e.pos; | |||
} | |||
void RackWidget::onMouseDown(EventMouseDown &e) { | |||
Widget::onMouseDown(e); | |||
if (e.consumed) | |||
return; | |||
if (e.button == 1) { | |||
appModuleBrowserCreate(); | |||
void RackWidget::on(event::Button &e) { | |||
OpaqueWidget::on(e); | |||
if (e.target == this) { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | |||
appModuleBrowserCreate(); | |||
} | |||
} | |||
e.consumed = true; | |||
e.target = this; | |||
} | |||
void RackWidget::onZoom(EventZoom &e) { | |||
void RackWidget::on(event::Zoom &e) { | |||
rails->box.size = math::Vec(); | |||
Widget::onZoom(e); | |||
EventWidget::on(e); | |||
} | |||
@@ -16,14 +16,14 @@ void SVGButton::setSVGs(std::shared_ptr<SVG> defaultSVG, std::shared_ptr<SVG> ac | |||
this->activeSVG = activeSVG ? activeSVG : defaultSVG; | |||
} | |||
void SVGButton::onDragStart(EventDragStart &e) { | |||
EventAction eAction; | |||
onAction(eAction); | |||
void SVGButton::on(event::DragStart &e) { | |||
event::Action eAction; | |||
handleEvent(eAction); | |||
sw->setSVG(activeSVG); | |||
dirty = true; | |||
} | |||
void SVGButton::onDragEnd(EventDragEnd &e) { | |||
void SVGButton::on(event::DragEnd &e) { | |||
sw->setSVG(defaultSVG); | |||
dirty = true; | |||
} | |||
@@ -46,9 +46,9 @@ void SVGKnob::step() { | |||
FramebufferWidget::step(); | |||
} | |||
void SVGKnob::onChange(EventChange &e) { | |||
void SVGKnob::on(event::Change &e) { | |||
dirty = true; | |||
Knob::onChange(e); | |||
ParamWidget::on(e); | |||
} | |||
@@ -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(); | |||
} | |||
void SVGSlider::onChange(EventChange &e) { | |||
void SVGSlider::on(event::Change &e) { | |||
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); | |||
float valueScaled = math::rescale(value, minValue, maxValue, 0, frames.size() - 1); | |||
int index = math::clamp((int) roundf(valueScaled), 0, (int) frames.size() - 1); | |||
sw->setSVG(frames[index]); | |||
dirty = true; | |||
ParamWidget::onChange(e); | |||
ParamWidget::on(e); | |||
} | |||
@@ -4,7 +4,7 @@ | |||
namespace rack { | |||
void ToggleSwitch::onDragStart(EventDragStart &e) { | |||
void ToggleSwitch::on(event::DragStart &e) { | |||
// Cycle through values | |||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | |||
if (value >= maxValue) | |||
@@ -2,6 +2,7 @@ | |||
#include "window.hpp" | |||
#include "engine.hpp" | |||
#include "asset.hpp" | |||
#include "helpers.hpp" | |||
namespace rack { | |||
@@ -10,22 +11,22 @@ namespace rack { | |||
struct TooltipIconButton : IconButton { | |||
Tooltip *tooltip = NULL; | |||
std::string tooltipText; | |||
void onMouseEnter(EventMouseEnter &e) override { | |||
void on(event::Enter &e) override { | |||
if (!tooltip) { | |||
tooltip = new Tooltip(); | |||
tooltip->box.pos = getAbsoluteOffset(math::Vec(0, BND_WIDGET_HEIGHT)); | |||
tooltip->text = tooltipText; | |||
gScene->addChild(tooltip); | |||
} | |||
IconButton::onMouseEnter(e); | |||
IconButton::on(e); | |||
} | |||
void onMouseLeave(EventMouseLeave &e) override { | |||
void on(event::Leave &e) override { | |||
if (tooltip) { | |||
gScene->removeChild(tooltip); | |||
delete tooltip; | |||
tooltip = NULL; | |||
} | |||
IconButton::onMouseLeave(e); | |||
IconButton::on(e); | |||
} | |||
}; | |||
@@ -34,7 +35,7 @@ struct NewButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_146097_cc.svg"))); | |||
tooltipText = "New patch (" WINDOW_MOD_KEY_NAME "+N)"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->reset(); | |||
} | |||
}; | |||
@@ -44,7 +45,7 @@ struct OpenButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_31859_cc.svg"))); | |||
tooltipText = "Open patch (" WINDOW_MOD_KEY_NAME "+O)"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->loadDialog(); | |||
} | |||
}; | |||
@@ -54,7 +55,7 @@ struct SaveButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_1343816_cc.svg"))); | |||
tooltipText = "Save patch (" WINDOW_MOD_KEY_NAME "+S)"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->saveDialog(); | |||
} | |||
}; | |||
@@ -64,7 +65,7 @@ struct SaveAsButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_1343811_cc.svg"))); | |||
tooltipText = "Save patch as (" WINDOW_MOD_KEY_NAME "+Shift+S)"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->saveAsDialog(); | |||
} | |||
}; | |||
@@ -74,7 +75,7 @@ struct RevertButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_1084369_cc.svg"))); | |||
tooltipText = "Revert patch"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->revert(); | |||
} | |||
}; | |||
@@ -84,7 +85,7 @@ struct DisconnectCablesButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_1745061_cc.svg"))); | |||
tooltipText = "Disconnect cables"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->disconnect(); | |||
} | |||
}; | |||
@@ -94,20 +95,20 @@ struct PowerMeterButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_305536_cc.svg"))); | |||
tooltipText = "Toggle power meter (see manual for explanation)"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gPowerMeter ^= true; | |||
} | |||
}; | |||
struct EnginePauseItem : MenuItem { | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gPaused ^= true; | |||
} | |||
}; | |||
struct SampleRateItem : MenuItem { | |||
float sampleRate; | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
engineSetSampleRate(sampleRate); | |||
gPaused = false; | |||
} | |||
@@ -118,12 +119,12 @@ struct SampleRateButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_1240789_cc.svg"))); | |||
tooltipText = "Engine sample rate"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | |||
menu->box.size.x = box.size.x; | |||
menu->addChild(MenuLabel::create("Engine sample rate")); | |||
menu->addChild(createMenuLabel("Engine sample rate")); | |||
EnginePauseItem *pauseItem = new EnginePauseItem(); | |||
pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; | |||
@@ -145,15 +146,15 @@ struct RackLockButton : TooltipIconButton { | |||
setSVG(SVG::load(asset::global("res/icons/noun_468341_cc.svg"))); | |||
tooltipText = "Lock modules"; | |||
} | |||
void onAction(EventAction &e) override { | |||
void on(event::Action &e) override { | |||
gRackWidget->lockModules ^= true; | |||
} | |||
}; | |||
struct ZoomSlider : Slider { | |||
void onAction(EventAction &e) override { | |||
Slider::onAction(e); | |||
gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); | |||
void on(event::Action &e) override { | |||
EventWidget::on(e); | |||
gRackScene->zoomWidget->setZoom(std::round(value) / 100.0); | |||
} | |||
}; | |||
@@ -1,4 +1,4 @@ | |||
#include "widgets.hpp" | |||
#include "event.hpp" | |||
namespace rack { | |||
@@ -6,8 +6,7 @@ namespace rack { | |||
Widget *gHoveredWidget = NULL; | |||
Widget *gDraggedWidget = NULL; | |||
Widget *gDragHoveredWidget = NULL; | |||
Widget *gFocusedWidget = NULL; | |||
Widget *gTempWidget = NULL; | |||
Widget *gSelectedWidget = NULL; | |||
} // namespace rack |
@@ -1,4 +1,4 @@ | |||
#include "widgets.hpp" | |||
#include "window.hpp" | |||
// #define DEBUG_ONLY(x) x | |||
@@ -52,7 +52,7 @@ static float getLineCrossing(math::Vec p0, math::Vec p1, math::Vec p2, math::Vec | |||
return -(d.x * b.y - d.y * b.x) / m; | |||
} | |||
static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
void svgDraw(NVGcontext *vg, NSVGimage *svg) { | |||
DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | |||
int shapeIndex = 0; | |||
// Iterate shape linked list | |||
@@ -195,26 +195,4 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
} | |||
void SVGWidget::wrap() { | |||
if (svg && svg->handle) { | |||
box.size = math::Vec(svg->handle->width, svg->handle->height); | |||
} | |||
else { | |||
box.size = math::Vec(); | |||
} | |||
} | |||
void SVGWidget::setSVG(std::shared_ptr<SVG> svg) { | |||
this->svg = svg; | |||
wrap(); | |||
} | |||
void SVGWidget::draw(NVGcontext *vg) { | |||
if (svg && svg->handle) { | |||
// printf("drawing svg %f %f\n", box.size.x, box.size.y); | |||
drawSVG(vg, svg->handle); | |||
} | |||
} | |||
} // namespace rack |
@@ -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_utils.h" | |||
@@ -118,10 +117,5 @@ int FramebufferWidget::getImageHandle() { | |||
return internal->fb->image; | |||
} | |||
void FramebufferWidget::onZoom(EventZoom &e) { | |||
dirty = true; | |||
Widget::onZoom(e); | |||
} | |||
} // namespace rack |
@@ -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 (gDraggedWidget == this) gDraggedWidget = NULL; | |||
if (gDragHoveredWidget == this) gDragHoveredWidget = NULL; | |||
if (gFocusedWidget == this) gFocusedWidget = NULL; | |||
if (gTempWidget == this) gTempWidget = NULL; | |||
if (gSelectedWidget == this) gSelectedWidget = NULL; | |||
clearChildren(); | |||
} | |||
@@ -75,34 +74,18 @@ void Widget::clearChildren() { | |||
children.clear(); | |||
} | |||
void Widget::finalizeEvents() { | |||
// Stop dragging and hovering this widget | |||
if (gHoveredWidget == this) { | |||
EventMouseLeave e; | |||
gHoveredWidget->onMouseLeave(e); | |||
gHoveredWidget = NULL; | |||
} | |||
if (gDraggedWidget == this) { | |||
EventDragEnd e; | |||
gDraggedWidget->onDragEnd(e); | |||
gDraggedWidget = NULL; | |||
} | |||
if (gDragHoveredWidget == this) { | |||
gDragHoveredWidget = NULL; | |||
} | |||
if (gFocusedWidget == this) { | |||
EventDefocus e; | |||
gFocusedWidget->onDefocus(e); | |||
gFocusedWidget = NULL; | |||
} | |||
for (Widget *child : children) { | |||
child->finalizeEvents(); | |||
} | |||
} | |||
void Widget::step() { | |||
for (Widget *child : children) { | |||
for (auto it = children.begin(); it != children.end();) { | |||
Widget *child = *it; | |||
// Delete children if a delete is requested | |||
if (child->requestedDelete) { | |||
it = children.erase(it); | |||
delete child; | |||
continue; | |||
} | |||
child->step(); | |||
it++; | |||
} | |||
} | |||
@@ -117,52 +100,5 @@ void Widget::draw(NVGcontext *vg) { | |||
} | |||
} | |||
#define RECURSE_EVENT_POSITION(_method) { \ | |||
math::Vec pos = e.pos; \ | |||
for (auto it = children.rbegin(); it != children.rend(); it++) { \ | |||
Widget *child = *it; \ | |||
if (!child->visible) \ | |||
continue; \ | |||
if (child->box.contains(pos)) { \ | |||
e.pos = pos.minus(child->box.pos); \ | |||
child->_method(e); \ | |||
if (e.consumed) \ | |||
break; \ | |||
} \ | |||
} \ | |||
e.pos = pos; \ | |||
} | |||
void Widget::onMouseDown(EventMouseDown &e) { | |||
RECURSE_EVENT_POSITION(onMouseDown); | |||
} | |||
void Widget::onMouseUp(EventMouseUp &e) { | |||
RECURSE_EVENT_POSITION(onMouseUp); | |||
} | |||
void Widget::onMouseMove(EventMouseMove &e) { | |||
RECURSE_EVENT_POSITION(onMouseMove); | |||
} | |||
void Widget::onHoverKey(EventHoverKey &e) { | |||
RECURSE_EVENT_POSITION(onHoverKey); | |||
} | |||
void Widget::onScroll(EventScroll &e) { | |||
RECURSE_EVENT_POSITION(onScroll); | |||
} | |||
void Widget::onPathDrop(EventPathDrop &e) { | |||
RECURSE_EVENT_POSITION(onPathDrop); | |||
} | |||
void Widget::onZoom(EventZoom &e) { | |||
for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
Widget *child = *it; | |||
child->onZoom(e); | |||
} | |||
} | |||
} // namespace rack |
@@ -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; | |||
void windowSizeCallback(GLFWwindow* window, int width, int height) { | |||
} | |||
static void windowSizeCallback(GLFWwindow* window, int width, int height) {} | |||
void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
static void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
#ifdef ARCH_MAC | |||
// Ctrl-left click --> right click | |||
// Remap Ctrl-left click to right click on Mac | |||
if (button == GLFW_MOUSE_BUTTON_LEFT) { | |||
if (glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { | |||
if (mods & GLFW_MOD_CONTROL) { | |||
button = GLFW_MOUSE_BUTTON_RIGHT; | |||
} | |||
} | |||
#endif | |||
if (action == GLFW_PRESS) { | |||
gTempWidget = NULL; | |||
// onMouseDown | |||
{ | |||
EventMouseDown e; | |||
e.pos = gMousePos; | |||
e.button = button; | |||
gScene->onMouseDown(e); | |||
gTempWidget = e.target; | |||
// event::Button | |||
event::Button eButton; | |||
eButton.button = button; | |||
eButton.action = action; | |||
eButton.mods = mods; | |||
gScene->handleEvent(eButton); | |||
Widget *clickedWidget = eButton.target; | |||
// Dragging | |||
if (clickedWidget) { | |||
// TODO keep track of dragged mouse button | |||
if (action == GLFW_PRESS) { | |||
event::DragStart eDragStart; | |||
eDragStart.button = button; | |||
clickedWidget->handleEvent(eDragStart); | |||
gDraggedWidget = eDragStart.target; | |||
} | |||
if (action == GLFW_RELEASE) { | |||
event::DragEnd eDragEnd; | |||
// TODO Use dragged button | |||
eDragEnd.button = button; | |||
clickedWidget->handleEvent(eDragEnd); | |||
gDraggedWidget = eDragEnd.target; | |||
} | |||
} | |||
// Selection | |||
if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { | |||
if (clickedWidget != gSelectedWidget) { | |||
if (gSelectedWidget) { | |||
event::Deselect eDeselect; | |||
gSelectedWidget->handleEvent(eDeselect); | |||
} | |||
gSelectedWidget = clickedWidget; | |||
if (gSelectedWidget) { | |||
event::Select eSelect; | |||
gSelectedWidget->handleEvent(eSelect); | |||
} | |||
} | |||
} | |||
/* | |||
if (action == GLFW_PRESS) { | |||
if (button == GLFW_MOUSE_BUTTON_LEFT) { | |||
if (gTempWidget) { | |||
// onDragStart | |||
@@ -77,19 +111,19 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
} | |||
gDraggedWidget = gTempWidget; | |||
if (gTempWidget != gFocusedWidget) { | |||
if (gFocusedWidget) { | |||
if (gTempWidget != gSelectedWidget) { | |||
if (gSelectedWidget) { | |||
// onDefocus | |||
EventDefocus e; | |||
gFocusedWidget->onDefocus(e); | |||
gSelectedWidget->onDefocus(e); | |||
} | |||
gFocusedWidget = NULL; | |||
gSelectedWidget = NULL; | |||
if (gTempWidget) { | |||
// onFocus | |||
EventFocus e; | |||
gTempWidget->onFocus(e); | |||
if (e.consumed) { | |||
gFocusedWidget = gTempWidget; | |||
gSelectedWidget = gTempWidget; | |||
} | |||
} | |||
} | |||
@@ -125,6 +159,7 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
} | |||
gTempWidget = NULL; | |||
} | |||
*/ | |||
} | |||
struct MouseButtonArguments { | |||
@@ -151,7 +186,7 @@ void mouseButtonStickyCallback(GLFWwindow *window, int button, int action, int m | |||
void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
math::Vec mousePos = math::Vec(xpos, ypos).div(gPixelRatio / gWindowRatio).round(); | |||
math::Vec mouseRel = mousePos.minus(gMousePos); | |||
math::Vec mouseDelta = mousePos.minus(gMousePos); | |||
int cursorMode = glfwGetInputMode(gWindow, GLFW_CURSOR); | |||
(void) cursorMode; | |||
@@ -171,20 +206,24 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
gMousePos = mousePos; | |||
gTempWidget = NULL; | |||
// onMouseMove | |||
{ | |||
EventMouseMove e; | |||
e.pos = mousePos; | |||
e.mouseRel = mouseRel; | |||
gScene->onMouseMove(e); | |||
gTempWidget = e.target; | |||
event::Hover eHover; | |||
eHover.pos = mousePos; | |||
eHover.mouseDelta = mouseDelta; | |||
gScene->handleEvent(eHover); | |||
if (gDraggedWidget) { | |||
event::DragMove eDragMove; | |||
// TODO | |||
eDragMove.button = 0; | |||
eDragMove.mouseDelta = mouseDelta; | |||
gDraggedWidget->handleEvent(eDragMove); | |||
} | |||
/* | |||
if (gDraggedWidget) { | |||
// onDragMove | |||
EventDragMove e; | |||
e.mouseRel = mouseRel; | |||
e.mouseDelta = mouseDelta; | |||
gDraggedWidget->onDragMove(e); | |||
if (gTempWidget != gDragHoveredWidget) { | |||
@@ -222,59 +261,63 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
// Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed | |||
EventScroll e; | |||
e.pos = mousePos; | |||
e.scrollRel = mouseRel; | |||
e.scrollRel = mouseDelta; | |||
gScene->onScroll(e); | |||
} | |||
*/ | |||
} | |||
void cursorEnterCallback(GLFWwindow* window, int entered) { | |||
if (!entered) { | |||
if (gHoveredWidget) { | |||
// onMouseLeave | |||
EventMouseLeave e; | |||
gHoveredWidget->onMouseLeave(e); | |||
event::Leave eLeave; | |||
gHoveredWidget->handleEvent(eLeave); | |||
} | |||
gHoveredWidget = NULL; | |||
} | |||
} | |||
void scrollCallback(GLFWwindow *window, double x, double y) { | |||
math::Vec scrollRel = math::Vec(x, y); | |||
math::Vec scrollDelta = math::Vec(x, y); | |||
#if ARCH_LIN || ARCH_WIN | |||
if (windowIsShiftPressed()) | |||
scrollRel = math::Vec(y, x); | |||
scrollDelta = math::Vec(y, x); | |||
#endif | |||
// onScroll | |||
EventScroll e; | |||
e.pos = gMousePos; | |||
e.scrollRel = scrollRel.mult(50.0); | |||
gScene->onScroll(e); | |||
scrollDelta = scrollDelta.mult(50.0); | |||
event::HoverScroll eHoverScroll; | |||
eHoverScroll.scrollDelta = scrollDelta; | |||
gScene->handleEvent(eHoverScroll); | |||
} | |||
void charCallback(GLFWwindow *window, unsigned int codepoint) { | |||
if (gFocusedWidget) { | |||
// onText | |||
EventText e; | |||
e.codepoint = codepoint; | |||
gFocusedWidget->onText(e); | |||
if (gSelectedWidget) { | |||
event::SelectText eSelectText; | |||
eSelectText.codepoint = codepoint; | |||
gSelectedWidget->handleEvent(eSelectText); | |||
} | |||
} | |||
void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { | |||
if (action == GLFW_PRESS || action == GLFW_REPEAT) { | |||
if (gFocusedWidget) { | |||
// onKey | |||
EventKey e; | |||
e.key = key; | |||
gFocusedWidget->onKey(e); | |||
if (e.consumed) | |||
if (gSelectedWidget) { | |||
event::SelectKey eSelectKey; | |||
eSelectKey.key = key; | |||
eSelectKey.scancode = scancode; | |||
eSelectKey.action = action; | |||
eSelectKey.mods = mods; | |||
gSelectedWidget->handleEvent(eSelectKey); | |||
if (eSelectKey.target) | |||
return; | |||
} | |||
// onHoverKey | |||
EventHoverKey e; | |||
e.pos = gMousePos; | |||
e.key = key; | |||
gScene->onHoverKey(e); | |||
event::HoverKey eHoverKey; | |||
eHoverKey.key = key; | |||
eHoverKey.scancode = scancode; | |||
eHoverKey.action = action; | |||
eHoverKey.mods = mods; | |||
eHoverKey.pos = gMousePos; | |||
gScene->handleEvent(eHoverKey); | |||
} | |||
// Keyboard MIDI driver | |||
@@ -289,13 +332,12 @@ void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods | |||
} | |||
void dropCallback(GLFWwindow *window, int count, const char **paths) { | |||
// onPathDrop | |||
EventPathDrop e; | |||
e.pos = gMousePos; | |||
event::PathDrop ePathDrop; | |||
ePathDrop.pos = gMousePos; | |||
for (int i = 0; i < count; i++) { | |||
e.paths.push_back(paths[i]); | |||
ePathDrop.paths.push_back(paths[i]); | |||
} | |||
gScene->onPathDrop(e); | |||
gScene->handleEvent(ePathDrop); | |||
} | |||
void errorCallback(int error, const char *description) { | |||
@@ -464,8 +506,8 @@ void windowRun() { | |||
glfwGetWindowContentScale(gWindow, &pixelRatio, NULL); | |||
pixelRatio = roundf(pixelRatio); | |||
if (pixelRatio != gPixelRatio) { | |||
EventZoom eZoom; | |||
gScene->onZoom(eZoom); | |||
event::Zoom eZoom; | |||
gScene->handleEvent(eZoom); | |||
gPixelRatio = pixelRatio; | |||
} | |||