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