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