| @@ -116,6 +116,9 @@ ifeq ($(ARCH), mac) | |||
| mkdir -p $(BUNDLE)/Contents/Resources/plugins | |||
| cp -R plugins/Fundamental/dist/Fundamental $(BUNDLE)/Contents/Resources/plugins | |||
| # Make DMG image | |||
| cd dist && ln -s /Applications Applications | |||
| cd dist && hdiutil create -srcfolder . -volname Rack -ov -format UDZO Rack-$(VERSION)-$(ARCH).dmg | |||
| endif | |||
| ifeq ($(ARCH), win) | |||
| mkdir -p dist/Rack | |||
| @@ -135,6 +138,11 @@ ifeq ($(ARCH), win) | |||
| cp dep/bin/portaudio_x64.dll dist/Rack/ | |||
| mkdir -p dist/Rack/plugins | |||
| cp -R plugins/Fundamental/dist/Fundamental dist/Rack/plugins/ | |||
| # Make ZIP | |||
| cd dist && zip -5 -r Rack-$(VERSION)-$(ARCH).zip Rack | |||
| # Make NSIS installer | |||
| makensis installer.nsi | |||
| mv Rack-setup.exe dist/Rack-$(VERSION)-$(ARCH).exe | |||
| endif | |||
| ifeq ($(ARCH), lin) | |||
| mkdir -p dist/Rack | |||
| @@ -149,16 +157,10 @@ ifeq ($(ARCH), lin) | |||
| cp dep/lib/libportaudio.so.2 dist/Rack/ | |||
| cp dep/lib/librtmidi.so.4 dist/Rack/ | |||
| mkdir -p dist/Rack/plugins | |||
| # Make ZIP | |||
| cp -R plugins/Fundamental/dist/Fundamental dist/Rack/plugins/ | |||
| endif | |||
| ifeq ($(ARCH), mac) | |||
| cd dist && ln -s /Applications Applications | |||
| cd dist && hdiutil create -srcfolder . -volname Rack -ov -format UDZO Rack-$(VERSION)-$(ARCH).dmg | |||
| else | |||
| cd dist && zip -5 -r Rack-$(VERSION)-$(ARCH).zip Rack | |||
| endif | |||
| # Plugin helpers | |||
| @@ -1 +1 @@ | |||
| Subproject commit 4dd22f56d6b733c8de13d6e8b12f13390aa5782e | |||
| Subproject commit 015d020615e8169d2f227ad385c5f2aa1e091fd1 | |||
| @@ -67,12 +67,12 @@ struct ModuleWidget : OpaqueWidget { | |||
| void draw(NVGcontext *vg) override; | |||
| Vec dragPos; | |||
| Widget *onMouseMove(Vec pos, Vec mouseRel) override; | |||
| Widget *onHoverKey(Vec pos, int key) override; | |||
| void onDragStart() override; | |||
| void onDragMove(Vec mouseRel) override; | |||
| void onDragEnd() override; | |||
| void onMouseDownOpaque(int button) override; | |||
| 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; | |||
| }; | |||
| struct ValueLight; | |||
| @@ -144,9 +144,9 @@ struct RackWidget : OpaqueWidget { | |||
| void step() override; | |||
| void draw(NVGcontext *vg) override; | |||
| Widget *onMouseMove(Vec pos, Vec mouseRel) override; | |||
| void onMouseDownOpaque(int button) override; | |||
| void onZoom() override; | |||
| void onMouseMove(EventMouseMove &e) override; | |||
| void onMouseDown(EventMouseDown &e) override; | |||
| void onZoom(EventZoom &e) override; | |||
| }; | |||
| struct RackRail : TransparentWidget { | |||
| @@ -184,8 +184,8 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| virtual void randomize(); | |||
| void onMouseDownOpaque(int button) override; | |||
| void onChange() override; | |||
| void onMouseDown(EventMouseDown &e) override; | |||
| void onChange(EventChange &e) override; | |||
| }; | |||
| /** Implements vertical dragging behavior for ParamWidgets */ | |||
| @@ -193,11 +193,11 @@ struct Knob : ParamWidget { | |||
| /** Snap to nearest integer while dragging */ | |||
| bool snap = false; | |||
| float dragValue; | |||
| void onDragStart() override; | |||
| void onDragMove(Vec mouseRel) override; | |||
| void onDragEnd() override; | |||
| void onDragStart(EventDragStart &e) override; | |||
| void onDragMove(EventDragMove &e) override; | |||
| void onDragEnd(EventDragEnd &e) override; | |||
| /** Tell engine to smoothly vary this parameter */ | |||
| void onChange() override; | |||
| void onChange(EventChange &e) override; | |||
| }; | |||
| struct SpriteKnob : virtual Knob, SpriteWidget { | |||
| @@ -216,7 +216,7 @@ struct SVGKnob : virtual Knob, FramebufferWidget { | |||
| SVGKnob(); | |||
| void setSVG(std::shared_ptr<SVG> svg); | |||
| void step() override; | |||
| void onChange() override; | |||
| void onChange(EventChange &e) override; | |||
| }; | |||
| struct SVGSlider : Knob, FramebufferWidget { | |||
| @@ -228,7 +228,7 @@ struct SVGSlider : Knob, FramebufferWidget { | |||
| SVGSlider(); | |||
| void step() override; | |||
| void onChange() override; | |||
| void onChange(EventChange &e) override; | |||
| }; | |||
| struct Switch : ParamWidget { | |||
| @@ -243,12 +243,12 @@ struct SVGSwitch : virtual Switch, FramebufferWidget { | |||
| /** Adds an SVG file to represent the next switch position */ | |||
| void addFrame(std::shared_ptr<SVG> svg); | |||
| void step() override; | |||
| void onChange() override; | |||
| void onChange(EventChange &e) override; | |||
| }; | |||
| /** A switch that cycles through each mechanical position */ | |||
| struct ToggleSwitch : virtual Switch { | |||
| void onDragStart() override { | |||
| void onDragStart(EventDragStart &e) override { | |||
| // Cycle through values | |||
| // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | |||
| if (value >= maxValue) | |||
| @@ -262,10 +262,10 @@ struct ToggleSwitch : virtual Switch { | |||
| struct MomentarySwitch : virtual Switch { | |||
| /** Don't randomize state */ | |||
| void randomize() override {} | |||
| void onDragStart() override { | |||
| void onDragStart(EventDragStart &e) override { | |||
| setValue(maxValue); | |||
| } | |||
| void onDragEnd() override { | |||
| void onDragEnd(EventDragEnd &e) override { | |||
| setValue(minValue); | |||
| } | |||
| }; | |||
| @@ -286,12 +286,12 @@ struct Port : OpaqueWidget { | |||
| ~Port(); | |||
| void draw(NVGcontext *vg) override; | |||
| void onMouseDownOpaque(int button) override; | |||
| void onDragEnd() override; | |||
| void onDragStart() override; | |||
| void onDragDrop(Widget *origin) override; | |||
| void onDragEnter(Widget *origin) override; | |||
| void onDragLeave(Widget *origin) 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; | |||
| }; | |||
| struct SVGPort : Port, FramebufferWidget { | |||
| @@ -365,7 +365,8 @@ struct RackScene : Scene { | |||
| RackScene(); | |||
| void step() override; | |||
| void draw(NVGcontext *vg) override; | |||
| Widget *onHoverKey(Vec pos, int key) override; | |||
| void onHoverKey(EventHoverKey &e) override; | |||
| void onPathDrop(EventPathDrop &e) override; | |||
| }; | |||
| //////////////////// | |||
| @@ -409,34 +409,39 @@ struct RedGreenBlueLight : ColorLightWidget { | |||
| }; | |||
| /** Based on the size of 5mm LEDs */ | |||
| template <typename BASE> | |||
| struct LargeLight : BASE { | |||
| LargeLight() { | |||
| this->box.size = Vec(20, 20); | |||
| this->box.size = mm2px(Vec(5.179, 5.179)); | |||
| } | |||
| }; | |||
| /** Based on the size of 3mm LEDs */ | |||
| template <typename BASE> | |||
| struct MediumLight : BASE { | |||
| MediumLight() { | |||
| this->box.size = Vec(12, 12); | |||
| this->box.size = mm2px(Vec(3.176, 3.176)); | |||
| } | |||
| }; | |||
| /** Based on the size of 2mm LEDs */ | |||
| template <typename BASE> | |||
| struct SmallLight : BASE { | |||
| SmallLight() { | |||
| this->box.size = Vec(8, 8); | |||
| this->box.size = mm2px(Vec(2.176, 2.176)); | |||
| } | |||
| }; | |||
| /** Based on the size of 1mm LEDs */ | |||
| template <typename BASE> | |||
| struct TinyLight : BASE { | |||
| TinyLight() { | |||
| this->box.size = Vec(5, 5); | |||
| this->box.size = mm2px(Vec(1.088, 1.088)); | |||
| } | |||
| }; | |||
| //////////////////// | |||
| // Switches and Buttons | |||
| //////////////////// | |||
| @@ -497,6 +502,12 @@ struct PB61303 : SVGSwitch, MomentarySwitch { | |||
| } | |||
| }; | |||
| struct LEDBezel : SVGSwitch, MomentarySwitch { | |||
| LEDBezel() { | |||
| addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); | |||
| } | |||
| }; | |||
| //////////////////// | |||
| // Misc | |||
| @@ -63,15 +63,15 @@ struct Module { | |||
| virtual void step() {} | |||
| virtual void onSampleRateChange() {} | |||
| /** Override these to store extra internal data in the "data" property */ | |||
| virtual json_t *toJson() { return NULL; } | |||
| virtual void fromJson(json_t *root) {} | |||
| /** Override these to implement spacial behavior when user clicks Initialize and Randomize */ | |||
| virtual void reset() {} | |||
| virtual void randomize() {} | |||
| /** Deprecated */ | |||
| virtual void initialize() final {} | |||
| /** Override these to store extra internal data in the "data" property */ | |||
| virtual json_t *toJson() { return NULL; } | |||
| virtual void fromJson(json_t *root) {} | |||
| }; | |||
| struct Wire { | |||
| @@ -0,0 +1,107 @@ | |||
| #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 { | |||
| 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 { | |||
| int button; | |||
| Widget *target = NULL; | |||
| }; | |||
| struct EventMouseMove : EventPosition { | |||
| 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 { | |||
| Vec scrollRel; | |||
| }; | |||
| ///////////// | |||
| struct EventDragStart : Event { | |||
| }; | |||
| struct EventDragEnd : Event { | |||
| }; | |||
| struct EventDragMove : Event { | |||
| 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 | |||
| @@ -8,6 +8,7 @@ | |||
| #include "math.hpp" | |||
| #include "util.hpp" | |||
| #include "events.hpp" | |||
| #define SVG_DPI 75.0 | |||
| @@ -122,35 +123,36 @@ struct Widget { | |||
| Return `this` to accept the event. | |||
| Return NULL to reject the event and pass it to the widget behind this one. | |||
| */ | |||
| virtual Widget *onMouseDown(Vec pos, int button); | |||
| virtual Widget *onMouseUp(Vec pos, int button); | |||
| virtual void onMouseDown(EventMouseDown &e); | |||
| virtual void onMouseUp(EventMouseUp &e); | |||
| /** Called on every frame, even if mouseRel = Vec(0, 0) */ | |||
| virtual Widget *onMouseMove(Vec pos, Vec mouseRel); | |||
| virtual Widget *onHoverKey(Vec pos, int key); | |||
| virtual void onMouseMove(EventMouseMove &e); | |||
| virtual void onHoverKey(EventHoverKey &e); | |||
| /** Called when this widget begins responding to `onMouseMove` events */ | |||
| virtual void onMouseEnter() {} | |||
| virtual void onMouseEnter(EventMouseEnter &e) {} | |||
| /** Called when another widget begins responding to `onMouseMove` events */ | |||
| virtual void onMouseLeave() {} | |||
| virtual bool onFocus() {return false;} | |||
| virtual void onDefocus() {} | |||
| virtual bool onFocusText(int codepoint) {return false;} | |||
| virtual bool onFocusKey(int key) {return false;} | |||
| virtual Widget *onScroll(Vec pos, Vec scrollRel); | |||
| virtual void onMouseLeave(EventMouseLeave &e) {} | |||
| virtual void onFocus(EventFocus &e) {} | |||
| virtual void onDefocus(EventDefocus &e) {} | |||
| virtual void onText(EventText &e) {} | |||
| virtual void onKey(EventKey &e) {} | |||
| virtual void onScroll(EventScroll &e); | |||
| /** Called when a widget responds to `onMouseDown` for a left button press */ | |||
| virtual void onDragStart() {} | |||
| virtual void onDragStart(EventDragStart &e) {} | |||
| /** Called when the left button is released and this widget is being dragged */ | |||
| virtual void onDragEnd() {} | |||
| virtual void onDragEnd(EventDragEnd &e) {} | |||
| /** Called when a widget responds to `onMouseMove` and is being dragged */ | |||
| virtual void onDragMove(Vec mouseRel) {} | |||
| 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(Widget *origin) {} | |||
| virtual void onDragLeave(Widget *origin) {} | |||
| virtual void onDragDrop(Widget *origin) {} | |||
| virtual void onAction() {} | |||
| virtual void onChange() {} | |||
| virtual void onZoom(); | |||
| virtual void onDragEnter(EventDragEnter &e) {} | |||
| virtual void onDragLeave(EventDragEnter &e) {} | |||
| virtual void onDragDrop(EventDragDrop &e) {} | |||
| virtual void onPathDrop(EventPathDrop &e); | |||
| virtual void onAction(EventAction &e) {} | |||
| virtual void onChange(EventChange &e) {} | |||
| virtual void onZoom(EventZoom &e); | |||
| }; | |||
| struct TransformWidget : Widget { | |||
| @@ -170,11 +172,11 @@ struct ZoomWidget : Widget { | |||
| Rect getViewport(Rect r) override; | |||
| void setZoom(float zoom); | |||
| void draw(NVGcontext *vg) override; | |||
| Widget *onMouseDown(Vec pos, int button) override; | |||
| Widget *onMouseUp(Vec pos, int button) override; | |||
| Widget *onMouseMove(Vec pos, Vec mouseRel) override; | |||
| Widget *onHoverKey(Vec pos, int key) override; | |||
| Widget *onScroll(Vec pos, Vec scrollRel) 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; | |||
| }; | |||
| //////////////////// | |||
| @@ -183,47 +185,36 @@ struct ZoomWidget : Widget { | |||
| /** Widget that does not respond to events */ | |||
| struct TransparentWidget : virtual Widget { | |||
| Widget *onMouseDown(Vec pos, int button) override {return NULL;} | |||
| Widget *onMouseUp(Vec pos, int button) override {return NULL;} | |||
| Widget *onMouseMove(Vec pos, Vec mouseRel) override {return NULL;} | |||
| Widget *onScroll(Vec pos, Vec scrollRel) override {return NULL;} | |||
| 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 : virtual Widget { | |||
| Widget *onMouseDown(Vec pos, int button) override { | |||
| Widget *w = Widget::onMouseDown(pos, button); | |||
| if (w) return w; | |||
| onMouseDownOpaque(button); | |||
| return this; | |||
| void onMouseDown(EventMouseDown &e) override { | |||
| Widget::onMouseDown(e); | |||
| if (!e.target) | |||
| e.target = this; | |||
| e.consumed = true; | |||
| } | |||
| Widget *onMouseUp(Vec pos, int button) override { | |||
| Widget *w = Widget::onMouseUp(pos, button); | |||
| if (w) return w; | |||
| onMouseUpOpaque(button); | |||
| return this; | |||
| void onMouseUp(EventMouseUp &e) override { | |||
| Widget::onMouseUp(e); | |||
| if (!e.target) | |||
| e.target = this; | |||
| e.consumed = true; | |||
| } | |||
| Widget *onMouseMove(Vec pos, Vec mouseRel) override { | |||
| Widget *w = Widget::onMouseMove(pos, mouseRel); | |||
| if (w) return w; | |||
| onMouseMoveOpaque(mouseRel); | |||
| return this; | |||
| void onMouseMove(EventMouseMove &e) override { | |||
| Widget::onMouseMove(e); | |||
| if (!e.target) | |||
| e.target = this; | |||
| e.consumed = true; | |||
| } | |||
| Widget *onScroll(Vec pos, Vec scrollRel) override { | |||
| Widget *w = Widget::onScroll(pos, scrollRel); | |||
| if (w) return w; | |||
| if (onScrollOpaque(scrollRel)) | |||
| return this; | |||
| return NULL; | |||
| void onScroll(EventScroll &e) override { | |||
| Widget::onScroll(e); | |||
| e.consumed = true; | |||
| } | |||
| /** "High level" events called by the above lower level events. | |||
| Use these if you don't care about the clicked position. | |||
| */ | |||
| virtual void onMouseDownOpaque(int button) {} | |||
| virtual void onMouseUpOpaque(int button) {} | |||
| virtual void onMouseMoveOpaque(Vec mouseRel) {} | |||
| virtual bool onScrollOpaque(Vec scrollRel) {return false;} | |||
| }; | |||
| struct SpriteWidget : virtual Widget { | |||
| @@ -264,7 +255,7 @@ struct FramebufferWidget : virtual Widget { | |||
| ~FramebufferWidget(); | |||
| void draw(NVGcontext *vg) override; | |||
| int getImageHandle(); | |||
| void onZoom() override; | |||
| void onZoom(EventZoom &e) override; | |||
| }; | |||
| struct QuantityWidget : virtual Widget { | |||
| @@ -302,9 +293,9 @@ struct Label : Widget { | |||
| // Deletes itself from parent when clicked | |||
| struct MenuOverlay : OpaqueWidget { | |||
| void onDragDrop(Widget *origin) override; | |||
| bool onScrollOpaque(Vec scrollRel) override {return true;} | |||
| Widget *onHoverKey(Vec pos, int key) override; | |||
| void onDragDrop(EventDragDrop &e) override; | |||
| void onScroll(EventScroll &e) override; | |||
| void onHoverKey(EventHoverKey &e) override; | |||
| }; | |||
| struct MenuEntry; | |||
| @@ -324,7 +315,7 @@ struct Menu : OpaqueWidget { | |||
| void setChildMenu(Menu *menu); | |||
| void step() override; | |||
| void draw(NVGcontext *vg) override; | |||
| bool onScrollOpaque(Vec scrollRel) override; | |||
| void onScroll(EventScroll &e) override; | |||
| }; | |||
| struct MenuEntry : OpaqueWidget { | |||
| @@ -343,8 +334,8 @@ struct MenuLabel : MenuEntry { | |||
| struct MenuItem : MenuEntry { | |||
| void draw(NVGcontext *vg) override; | |||
| virtual Menu *createChildMenu() {return NULL;} | |||
| void onMouseEnter() override; | |||
| void onDragDrop(Widget *origin) override; | |||
| void onMouseEnter(EventMouseEnter &e) override; | |||
| void onDragDrop(EventDragDrop &e) override; | |||
| }; | |||
| struct Button : OpaqueWidget { | |||
| @@ -355,11 +346,11 @@ struct Button : OpaqueWidget { | |||
| box.size.y = BND_WIDGET_HEIGHT; | |||
| } | |||
| void draw(NVGcontext *vg) override; | |||
| void onMouseEnter() override; | |||
| void onMouseLeave() override; | |||
| void onDragStart() override; | |||
| void onDragEnd() override; | |||
| void onDragDrop(Widget *origin) 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 ChoiceButton : Button { | |||
| @@ -373,9 +364,9 @@ struct RadioButton : OpaqueWidget, QuantityWidget { | |||
| box.size.y = BND_WIDGET_HEIGHT; | |||
| } | |||
| void draw(NVGcontext *vg) override; | |||
| void onMouseEnter() override; | |||
| void onMouseLeave() override; | |||
| void onDragDrop(Widget *origin) override; | |||
| void onMouseEnter(EventMouseEnter &e) override; | |||
| void onMouseLeave(EventMouseLeave &e) override; | |||
| void onDragDrop(EventDragDrop &e) override; | |||
| }; | |||
| struct Slider : OpaqueWidget, QuantityWidget { | |||
| @@ -385,10 +376,10 @@ struct Slider : OpaqueWidget, QuantityWidget { | |||
| box.size.y = BND_WIDGET_HEIGHT; | |||
| } | |||
| void draw(NVGcontext *vg) override; | |||
| void onDragStart() override; | |||
| void onDragMove(Vec mouseRel) override; | |||
| void onDragEnd() override; | |||
| void onMouseDownOpaque(int button) override; | |||
| void onDragStart(EventDragStart &e) override; | |||
| void onDragMove(EventDragMove &e) override; | |||
| void onDragEnd(EventDragEnd &e) override; | |||
| void onMouseDown(EventMouseDown &e) override; | |||
| }; | |||
| /** Parent must be a ScrollWidget */ | |||
| @@ -400,9 +391,9 @@ struct ScrollBar : OpaqueWidget { | |||
| box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); | |||
| } | |||
| void draw(NVGcontext *vg) override; | |||
| void onDragStart() override; | |||
| void onDragMove(Vec mouseRel) override; | |||
| void onDragEnd() override; | |||
| void onDragStart(EventDragStart &e) override; | |||
| void onDragMove(EventDragMove &e) override; | |||
| void onDragEnd(EventDragEnd &e) override; | |||
| }; | |||
| /** Handles a container with ScrollBar */ | |||
| @@ -414,7 +405,7 @@ struct ScrollWidget : OpaqueWidget { | |||
| ScrollWidget(); | |||
| void step() override; | |||
| bool onScrollOpaque(Vec scrollRel) override; | |||
| void onScroll(EventScroll &e) override; | |||
| }; | |||
| struct TextField : OpaqueWidget { | |||
| @@ -428,10 +419,10 @@ struct TextField : OpaqueWidget { | |||
| box.size.y = BND_WIDGET_HEIGHT; | |||
| } | |||
| void draw(NVGcontext *vg) override; | |||
| Widget *onMouseDown(Vec pos, int button) override; | |||
| bool onFocusText(int codepoint) override; | |||
| bool onFocusKey(int key) override; | |||
| bool onFocus() override; | |||
| void onMouseDown(EventMouseDown &e) override; | |||
| void onFocus(EventFocus &e) override; | |||
| void onText(EventText &e) override; | |||
| void onKey(EventKey &e) override; | |||
| void insertText(std::string newText); | |||
| virtual void onTextChange() {} | |||
| }; | |||
| @@ -0,0 +1,88 @@ | |||
| !include "MUI2.nsh" | |||
| Name "VCV Rack" | |||
| OutFile "Rack-setup.exe" | |||
| SetCompressor "bzip2" | |||
| CRCCheck On | |||
| ;Default installation folder | |||
| InstallDir "$PROGRAMFILES\VCV" | |||
| ;Get installation folder from registry if available | |||
| InstallDirRegKey HKCU "Software\VCV Rack" "" | |||
| ;Request application privileges for Windows Vista | |||
| RequestExecutionLevel admin | |||
| !define MUI_ICON "icon.ico" | |||
| !define MUI_HEADERIMAGE | |||
| !define MUI_HEADERIMAGE_BITMAP "installer-banner.bmp" ; 150x57 | |||
| ; !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\win.bmp" ; 164x314 | |||
| ; !define MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\win.bmp" ; 164x314 | |||
| !define MUI_COMPONENTSPAGE_NODESC | |||
| ; Pages | |||
| ; !insertmacro MUI_PAGE_WELCOME | |||
| !insertmacro MUI_PAGE_COMPONENTS | |||
| !insertmacro MUI_PAGE_DIRECTORY | |||
| ;second directory selection | |||
| ; Var DbInstDir | |||
| ; !define MUI_PAGE_HEADER_SUBTEXT "Choose the folder in which to install the database." | |||
| ; !define MUI_DIRECTORYPAGE_TEXT_TOP "The installer will install the database(s) in the following folder. To install in a differenct folder, click Browse and select another folder. Click Next to continue." | |||
| ; !define MUI_DIRECTORYPAGE_VARIABLE $DbInstDir ; <= the other directory will be stored into that variable | |||
| ; !insertmacro MUI_PAGE_DIRECTORY | |||
| !insertmacro MUI_PAGE_INSTFILES | |||
| !insertmacro MUI_UNPAGE_CONFIRM | |||
| !insertmacro MUI_UNPAGE_INSTFILES | |||
| !insertmacro MUI_LANGUAGE "English" | |||
| Section "!VCV Rack" VCVRACK | |||
| SetOutPath "$INSTDIR" | |||
| File /r "dist\Rack" | |||
| ;Store installation folder | |||
| WriteRegStr HKCU "Software\VCV Rack" "" $INSTDIR | |||
| WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" "DisplayName" "VCV Rack" | |||
| WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" "UninstallString" "$\"$INSTDIR\UninstallRack.exe$\"" | |||
| WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" "QuietUninstallString" "$\"$INSTDIR\UninstallRack.exe$\" /S" | |||
| WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" "InstallLocation" "$\"$INSTDIR$\"" | |||
| WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" "Publisher" "VCV" | |||
| ;Create uninstaller | |||
| WriteUninstaller "$INSTDIR\UninstallRack.exe" | |||
| ;Create shortcuts | |||
| CreateDirectory "$SMPROGRAMS" | |||
| ; Set working directory of shortcut | |||
| SetOutPath "$INSTDIR\Rack" | |||
| CreateShortcut "$SMPROGRAMS\VCV Rack.lnk" "$INSTDIR\Rack\Rack.exe" | |||
| SectionEnd | |||
| ; Section "VST Plugin" VST | |||
| ; SectionEnd | |||
| Section "Uninstall" | |||
| RMDir /r "$INSTDIR\Rack" | |||
| Delete "$INSTDIR\UninstallRack.exe" | |||
| RMDir "$INSTDIR" | |||
| Delete "$SMPROGRAMS\VCV Rack.lnk" | |||
| DeleteRegKey /ifempty HKCU "Software\VCV Rack" | |||
| DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VCV Rack" | |||
| SectionEnd | |||
| @@ -0,0 +1,71 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
| <svg | |||
| xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
| xmlns:cc="http://creativecommons.org/ns#" | |||
| xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
| xmlns:svg="http://www.w3.org/2000/svg" | |||
| xmlns="http://www.w3.org/2000/svg" | |||
| xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
| xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
| width="7.5001564mm" | |||
| height="7.5000095mm" | |||
| viewBox="0 0 7.5001564 7.5000095" | |||
| version="1.1" | |||
| id="svg64581" | |||
| inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
| sodipodi:docname="LEDBezel.svg"> | |||
| <defs | |||
| id="defs64575" /> | |||
| <sodipodi:namedview | |||
| id="base" | |||
| pagecolor="#ffffff" | |||
| bordercolor="#666666" | |||
| borderopacity="1.0" | |||
| inkscape:pageopacity="0.0" | |||
| inkscape:pageshadow="2" | |||
| inkscape:zoom="1.979899" | |||
| inkscape:cx="-100.74496" | |||
| inkscape:cy="9.8683694" | |||
| inkscape:document-units="mm" | |||
| inkscape:current-layer="layer1" | |||
| showgrid="false" | |||
| fit-margin-top="0" | |||
| fit-margin-left="0" | |||
| fit-margin-right="0" | |||
| fit-margin-bottom="0" | |||
| inkscape:window-width="2560" | |||
| inkscape:window-height="1422" | |||
| inkscape:window-x="0" | |||
| inkscape:window-y="18" | |||
| inkscape:window-maximized="0" /> | |||
| <metadata | |||
| id="metadata64578"> | |||
| <rdf:RDF> | |||
| <cc:Work | |||
| rdf:about=""> | |||
| <dc:format>image/svg+xml</dc:format> | |||
| <dc:type | |||
| rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
| <dc:title></dc:title> | |||
| </cc:Work> | |||
| </rdf:RDF> | |||
| </metadata> | |||
| <g | |||
| inkscape:label="Layer 1" | |||
| inkscape:groupmode="layer" | |||
| id="layer1" | |||
| transform="translate(-94.523732,-120.89285)"> | |||
| <path | |||
| inkscape:connector-curvature="0" | |||
| id="path9729" | |||
| d="m 94.750061,123.36034 c -0.706931,1.94578 0.297674,4.09829 2.240704,4.8066 1.947157,0.70556 4.098285,-0.29626 4.805225,-2.24208 0.70968,-1.94578 -0.29355,-4.09688 -2.239332,-4.80522 -1.945781,-0.70693 -4.096914,0.29355 -4.806597,2.2407" | |||
| style="fill:#2b2b2b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
| <path | |||
| inkscape:connector-curvature="0" | |||
| id="path64013" | |||
| d="m 99.30033,121.82382 c -1.557161,-0.56497 -3.279704,0.23703 -3.846089,1.79285 -0.565009,1.55716 0.237031,3.27833 1.794192,3.84334 1.557197,0.56776 3.276987,-0.23428 3.844747,-1.79144 0.56498,-1.55582 -0.23565,-3.27837 -1.79285,-3.84475" | |||
| style="clip-rule:nonzero;fill:#5a5a5a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
| </g> | |||
| </svg> | |||
| @@ -10,29 +10,30 @@ namespace rack { | |||
| #define KNOB_SENSITIVITY 0.0015 | |||
| void Knob::onDragStart() { | |||
| void Knob::onDragStart(EventDragStart &e) { | |||
| guiCursorLock(); | |||
| dragValue = value; | |||
| randomizable = false; | |||
| } | |||
| void Knob::onDragMove(Vec mouseRel) { | |||
| void Knob::onDragMove(EventDragMove &e) { | |||
| // Drag slower if Mod | |||
| float delta = KNOB_SENSITIVITY * (maxValue - minValue) * -e.mouseRel.y; | |||
| if (guiIsModPressed()) | |||
| mouseRel = mouseRel.mult(1/16.0); | |||
| dragValue += KNOB_SENSITIVITY * (maxValue - minValue) * -mouseRel.y; | |||
| delta /= 16.0; | |||
| dragValue += delta; | |||
| if (snap) | |||
| setValue(roundf(dragValue)); | |||
| else | |||
| setValue(dragValue); | |||
| } | |||
| void Knob::onDragEnd() { | |||
| void Knob::onDragEnd(EventDragEnd &e) { | |||
| guiCursorUnlock(); | |||
| randomizable = true; | |||
| } | |||
| void Knob::onChange() { | |||
| void Knob::onChange(EventChange &e) { | |||
| if (!module) | |||
| return; | |||
| @@ -6,7 +6,7 @@ namespace rack { | |||
| void LightWidget::draw(NVGcontext *vg) { | |||
| float radius = box.size.x / 2.0; | |||
| float oradius = radius + 20.0; | |||
| float oradius = radius + 15.0; | |||
| color.r = clampf(color.r, 0.0, 1.0); | |||
| color.g = clampf(color.g, 0.0, 1.0); | |||
| @@ -36,7 +36,7 @@ void LightWidget::draw(NVGcontext *vg) { | |||
| nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); | |||
| NVGpaint paint; | |||
| NVGcolor icol = color; | |||
| icol.a *= 0.15; | |||
| icol.a *= 0.10; | |||
| NVGcolor ocol = color; | |||
| ocol.a = 0.0; | |||
| paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); | |||
| @@ -165,7 +165,22 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
| nvgResetScissor(vg); | |||
| } | |||
| Widget *ModuleWidget::onMouseMove(Vec pos, Vec mouseRel) { | |||
| void ModuleWidget::onMouseDown(EventMouseDown &e) { | |||
| Widget::onMouseDown(e); | |||
| if (e.consumed) | |||
| return; | |||
| if (e.button == 1) { | |||
| createContextMenu(); | |||
| } | |||
| 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) { | |||
| @@ -174,82 +189,85 @@ Widget *ModuleWidget::onMouseMove(Vec pos, Vec mouseRel) { | |||
| this->finalizeEvents(); | |||
| delete this; | |||
| // Kinda sketchy because events will be passed further down the tree | |||
| return NULL; | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| return OpaqueWidget::onMouseMove(pos, mouseRel); | |||
| } | |||
| Widget *ModuleWidget::onHoverKey(Vec pos, int key) { | |||
| switch (key) { | |||
| void ModuleWidget::onHoverKey(EventHoverKey &e) { | |||
| switch (e.key) { | |||
| case GLFW_KEY_I: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| reset(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| case GLFW_KEY_R: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| randomize(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| case GLFW_KEY_D: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| gRackWidget->cloneModule(this); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| } | |||
| return Widget::onHoverKey(pos, key); | |||
| Widget::onHoverKey(e); | |||
| } | |||
| void ModuleWidget::onDragStart() { | |||
| void ModuleWidget::onDragStart(EventDragStart &e) { | |||
| dragPos = gRackWidget->lastMousePos.minus(box.pos); | |||
| } | |||
| void ModuleWidget::onDragMove(Vec mouseRel) { | |||
| void ModuleWidget::onDragEnd(EventDragEnd &e) { | |||
| } | |||
| void ModuleWidget::onDragMove(EventDragMove &e) { | |||
| Rect newBox = box; | |||
| newBox.pos = gRackWidget->lastMousePos.minus(dragPos); | |||
| gRackWidget->requestModuleBoxNearest(this, newBox); | |||
| } | |||
| void ModuleWidget::onDragEnd() { | |||
| } | |||
| struct DisconnectMenuItem : MenuItem { | |||
| ModuleWidget *moduleWidget; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| moduleWidget->disconnect(); | |||
| } | |||
| }; | |||
| struct ResetMenuItem : MenuItem { | |||
| ModuleWidget *moduleWidget; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| moduleWidget->reset(); | |||
| } | |||
| }; | |||
| struct RandomizeMenuItem : MenuItem { | |||
| ModuleWidget *moduleWidget; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| moduleWidget->randomize(); | |||
| } | |||
| }; | |||
| struct CloneMenuItem : MenuItem { | |||
| ModuleWidget *moduleWidget; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->cloneModule(moduleWidget); | |||
| } | |||
| }; | |||
| struct DeleteMenuItem : MenuItem { | |||
| ModuleWidget *moduleWidget; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->deleteModule(moduleWidget); | |||
| moduleWidget->finalizeEvents(); | |||
| delete moduleWidget; | |||
| @@ -295,11 +313,5 @@ Menu *ModuleWidget::createContextMenu() { | |||
| return menu; | |||
| } | |||
| void ModuleWidget::onMouseDownOpaque(int button) { | |||
| if (button == 1) { | |||
| createContextMenu(); | |||
| } | |||
| } | |||
| } // namespace rack | |||
| @@ -19,13 +19,15 @@ void ParamWidget::randomize() { | |||
| setValue(rescalef(randomf(), 0.0, 1.0, minValue, maxValue)); | |||
| } | |||
| void ParamWidget::onMouseDownOpaque(int button) { | |||
| if (button == 1) { | |||
| void ParamWidget::onMouseDown(EventMouseDown &e) { | |||
| if (e.button == 1) { | |||
| setValue(defaultValue); | |||
| } | |||
| e.consumed = true; | |||
| e.target = this; | |||
| } | |||
| void ParamWidget::onChange() { | |||
| void ParamWidget::onChange(EventChange &e) { | |||
| if (!module) | |||
| return; | |||
| @@ -15,7 +15,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| Vec pos = Vec(0, 0); | |||
| struct RegisterButton : Button { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t(openBrowser, "https://vcvrack.com/"); | |||
| t.detach(); | |||
| } | |||
| @@ -46,7 +46,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| struct LogInButton : Button { | |||
| TextField *emailField; | |||
| TextField *passwordField; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t(pluginLogIn, emailField->text, passwordField->text); | |||
| t.detach(); | |||
| passwordField->text = ""; | |||
| @@ -79,7 +79,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| Vec pos = Vec(0, 0); | |||
| struct ManageButton : Button { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t(openBrowser, "https://vcvrack.com/"); | |||
| t.detach(); | |||
| } | |||
| @@ -92,7 +92,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| pos.x += manageButton->box.size.x; | |||
| struct RefreshButton : Button { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t(pluginRefresh); | |||
| t.detach(); | |||
| } | |||
| @@ -106,7 +106,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| pos.x += refreshButton->box.size.x; | |||
| struct LogOutButton : Button { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| pluginLogOut(); | |||
| } | |||
| }; | |||
| @@ -142,7 +142,7 @@ PluginManagerWidget::PluginManagerWidget() { | |||
| pos.x += downloadProgress->box.size.x; | |||
| // struct CancelButton : Button { | |||
| // void onAction() override { | |||
| // void onAction(EventAction &e) override { | |||
| // pluginCancelDownload(); | |||
| // } | |||
| // }; | |||
| @@ -17,23 +17,20 @@ void Port::draw(NVGcontext *vg) { | |||
| } | |||
| } | |||
| void Port::onMouseDownOpaque(int button) { | |||
| if (button == 1) { | |||
| void Port::onMouseDown(EventMouseDown &e) { | |||
| if (e.button == 1) { | |||
| gRackWidget->wireContainer->removeTopWire(this); | |||
| // HACK | |||
| // Update hovered*Port of active wire if applicable | |||
| onDragEnter(NULL); | |||
| EventDragEnter e; | |||
| onDragEnter(e); | |||
| } | |||
| e.consumed = true; | |||
| e.target = this; | |||
| } | |||
| void Port::onDragEnd() { | |||
| // FIXME | |||
| // If the source Port is deleted, this will be called, removing the cable | |||
| gRackWidget->wireContainer->commitActiveWire(); | |||
| } | |||
| void Port::onDragStart() { | |||
| void Port::onDragStart(EventDragStart &e) { | |||
| // Try to grab wire on top of stack | |||
| WireWidget *wire = gRackWidget->wireContainer->getTopWire(this); | |||
| if (guiIsModPressed()) { | |||
| @@ -62,10 +59,16 @@ void Port::onDragStart() { | |||
| gRackWidget->wireContainer->setActiveWire(wire); | |||
| } | |||
| void Port::onDragDrop(Widget *origin) { | |||
| void Port::onDragEnd(EventDragEnd &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::onDragEnter(Widget *origin) { | |||
| void Port::onDragEnter(EventDragEnter &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); | |||
| @@ -82,7 +85,7 @@ void Port::onDragEnter(Widget *origin) { | |||
| } | |||
| } | |||
| void Port::onDragLeave(Widget *origin) { | |||
| void Port::onDragLeave(EventDragEnter &e) { | |||
| WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | |||
| if (activeWire) { | |||
| if (type == INPUT) | |||
| @@ -82,39 +82,55 @@ void RackScene::draw(NVGcontext *vg) { | |||
| Scene::draw(vg); | |||
| } | |||
| Widget *RackScene::onHoverKey(Vec pos, int key) { | |||
| switch (key) { | |||
| void RackScene::onHoverKey(EventHoverKey &e) { | |||
| switch (e.key) { | |||
| case GLFW_KEY_N: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| gRackWidget->reset(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| case GLFW_KEY_Q: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| guiClose(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| case GLFW_KEY_O: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| gRackWidget->openDialog(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| case GLFW_KEY_S: | |||
| if (guiIsModPressed() && !guiIsShiftPressed()) { | |||
| gRackWidget->saveDialog(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| if (guiIsModPressed() && guiIsShiftPressed()) { | |||
| gRackWidget->saveAsDialog(); | |||
| return this; | |||
| e.consumed = true; | |||
| return; | |||
| } | |||
| break; | |||
| } | |||
| return Widget::onHoverKey(pos, key); | |||
| Widget::onHoverKey(e); | |||
| } | |||
| void RackScene::onPathDrop(EventPathDrop &e) { | |||
| if (e.paths.size() >= 1) { | |||
| const std::string& firstPath = e.paths.front(); | |||
| if (extractExtension(firstPath) == "vcv") { | |||
| gRackWidget->loadPatch(firstPath); | |||
| e.consumed = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -375,7 +375,7 @@ void RackWidget::draw(NVGcontext *vg) { | |||
| struct AddModuleMenuItem : MenuItem { | |||
| Model *model; | |||
| Vec modulePos; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| ModuleWidget *moduleWidget = model->createModuleWidget(); | |||
| gRackWidget->moduleContainer->addChild(moduleWidget); | |||
| // Move module nearest to the mouse position | |||
| @@ -388,7 +388,7 @@ struct AddModuleMenuItem : MenuItem { | |||
| struct UrlItem : MenuItem { | |||
| std::string url; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t(openBrowser, url); | |||
| t.detach(); | |||
| } | |||
| @@ -487,13 +487,17 @@ struct SearchModuleField : TextField { | |||
| } | |||
| }; | |||
| Widget *RackWidget::onMouseMove(Vec pos, Vec mouseRel) { | |||
| lastMousePos = pos; | |||
| return OpaqueWidget::onMouseMove(pos, mouseRel); | |||
| void RackWidget::onMouseMove(EventMouseMove &e) { | |||
| OpaqueWidget::onMouseMove(e); | |||
| lastMousePos = e.pos; | |||
| } | |||
| void RackWidget::onMouseDownOpaque(int button) { | |||
| if (button == 1) { | |||
| void RackWidget::onMouseDown(EventMouseDown &e) { | |||
| Widget::onMouseDown(e); | |||
| if (e.consumed) | |||
| return; | |||
| if (e.button == 1) { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->pushChild(construct<MenuLabel>(&MenuLabel::text, "Add module")); | |||
| @@ -519,15 +523,17 @@ void RackWidget::onMouseDownOpaque(int button) { | |||
| AddManufacturerMenuItem *item = new AddManufacturerMenuItem(); | |||
| item->text = manufacturerName; | |||
| item->manufacturerName = manufacturerName; | |||
| item->modulePos = lastMousePos; | |||
| item->modulePos = e.pos; | |||
| menu->pushChild(item); | |||
| } | |||
| } | |||
| e.consumed = true; | |||
| e.target = this; | |||
| } | |||
| void RackWidget::onZoom() { | |||
| void RackWidget::onZoom(EventZoom &e) { | |||
| rails->box.size = Vec(); | |||
| Widget::onZoom(); | |||
| Widget::onZoom(e); | |||
| } | |||
| @@ -35,9 +35,9 @@ void SVGKnob::step() { | |||
| FramebufferWidget::step(); | |||
| } | |||
| void SVGKnob::onChange() { | |||
| void SVGKnob::onChange(EventChange &e) { | |||
| dirty = true; | |||
| Knob::onChange(); | |||
| Knob::onChange(e); | |||
| } | |||
| @@ -21,9 +21,9 @@ void SVGSlider::step() { | |||
| FramebufferWidget::step(); | |||
| } | |||
| void SVGSlider::onChange() { | |||
| void SVGSlider::onChange(EventChange &e) { | |||
| dirty = true; | |||
| ParamWidget::onChange(); | |||
| ParamWidget::onChange(e); | |||
| } | |||
| @@ -22,12 +22,12 @@ void SVGSwitch::step() { | |||
| FramebufferWidget::step(); | |||
| } | |||
| void SVGSwitch::onChange() { | |||
| void SVGSwitch::onChange(EventChange &e) { | |||
| assert(frames.size() > 0); | |||
| int index = clampi((int) roundf(value), 0, frames.size() - 1); | |||
| sw->setSVG(frames[index]); | |||
| dirty = true; | |||
| Switch::onChange(); | |||
| Switch::onChange(e); | |||
| } | |||
| @@ -7,37 +7,37 @@ namespace rack { | |||
| struct NewItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->reset(); | |||
| } | |||
| }; | |||
| struct OpenItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->openDialog(); | |||
| } | |||
| }; | |||
| struct SaveItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->saveDialog(); | |||
| } | |||
| }; | |||
| struct SaveAsItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gRackWidget->saveAsDialog(); | |||
| } | |||
| }; | |||
| struct QuitItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| guiClose(); | |||
| } | |||
| }; | |||
| struct FileChoice : ChoiceButton { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | |||
| menu->box.size.x = box.size.x; | |||
| @@ -54,21 +54,21 @@ struct FileChoice : ChoiceButton { | |||
| struct PauseItem : MenuItem { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| gPaused = !gPaused; | |||
| } | |||
| }; | |||
| struct SampleRateItem : MenuItem { | |||
| float sampleRate; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| engineSetSampleRate(sampleRate); | |||
| gPaused = false; | |||
| } | |||
| }; | |||
| struct SampleRateChoice : ChoiceButton { | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | |||
| menu->box.size.x = box.size.x; | |||
| @@ -149,8 +149,8 @@ Toolbar::Toolbar() { | |||
| xPos += margin; | |||
| { | |||
| struct ZoomSlider : Slider { | |||
| void onAction() override { | |||
| Slider::onAction(); | |||
| void onAction(EventAction &e) override { | |||
| Slider::onAction(e); | |||
| gRackScene->zoomWidget->setZoom(value / 100.0); | |||
| } | |||
| }; | |||
| @@ -9,6 +9,11 @@ | |||
| #include <pwd.h> | |||
| #endif | |||
| #if ARCH_WIN | |||
| #include <Windows.h> | |||
| #include <Shlobj.h> | |||
| #endif | |||
| namespace rack { | |||
| @@ -76,9 +81,14 @@ std::string assetLocal(std::string filename) { | |||
| path += "/" + filename; | |||
| #endif | |||
| #if ARCH_WIN | |||
| // TODO | |||
| // Use ~/My Documents/Rack or something | |||
| path = "./" + filename; | |||
| // Get My Documents folder | |||
| char buf[MAX_PATH]; | |||
| HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, buf); | |||
| assert(result == S_OK); | |||
| path = buf; | |||
| path += "/Rack"; | |||
| CreateDirectory(path.c_str(), NULL); | |||
| path += "/" + filename; | |||
| #endif | |||
| #if ARCH_LIN | |||
| // TODO | |||
| @@ -326,14 +326,14 @@ void AudioInterface::closeDevice() { | |||
| struct AudioItem : MenuItem { | |||
| AudioInterface *audioInterface; | |||
| int deviceId; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| audioInterface->setDeviceId(deviceId); | |||
| } | |||
| }; | |||
| struct AudioChoice : ChoiceButton { | |||
| AudioInterface *audioInterface; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
| menu->box.size.x = box.size.x; | |||
| @@ -364,14 +364,14 @@ struct AudioChoice : ChoiceButton { | |||
| struct SampleRateItem : MenuItem { | |||
| AudioInterface *audioInterface; | |||
| float sampleRate; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| audioInterface->setSampleRate(sampleRate); | |||
| } | |||
| }; | |||
| struct SampleRateChoice : ChoiceButton { | |||
| AudioInterface *audioInterface; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
| menu->box.size.x = box.size.x; | |||
| @@ -395,14 +395,14 @@ struct SampleRateChoice : ChoiceButton { | |||
| struct BlockSizeItem : MenuItem { | |||
| AudioInterface *audioInterface; | |||
| int blockSize; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| audioInterface->setBlockSize(blockSize); | |||
| } | |||
| }; | |||
| struct BlockSizeChoice : ChoiceButton { | |||
| AudioInterface *audioInterface; | |||
| void onAction() override { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
| menu->box.size.x = box.size.x; | |||
| @@ -10,20 +10,21 @@ struct ModuleResizeHandle : Widget { | |||
| ModuleResizeHandle() { | |||
| box.size = Vec(RACK_GRID_WIDTH * 1, RACK_GRID_HEIGHT); | |||
| } | |||
| Widget *onMouseDown(Vec pos, int button) override { | |||
| if (button == 0) | |||
| return this; | |||
| return NULL; | |||
| void onMouseDown(EventMouseDown &e) override { | |||
| if (e.button == 0) { | |||
| e.consumed = true; | |||
| e.target = this; | |||
| } | |||
| } | |||
| void onDragStart() override { | |||
| void onDragStart(EventDragStart &e) override { | |||
| assert(parent); | |||
| originalWidth = parent->box.size.x; | |||
| totalX = 0.0; | |||
| } | |||
| void onDragMove(Vec mouseRel) override { | |||
| void onDragMove(EventDragMove &e) override { | |||
| ModuleWidget *m = dynamic_cast<ModuleWidget*>(parent); | |||
| assert(m); | |||
| totalX += mouseRel.x; | |||
| totalX += e.mouseRel.x; | |||
| float targetWidth = originalWidth; | |||
| if (right) | |||
| targetWidth += totalX; | |||
| @@ -41,8 +41,8 @@ float Light::getBrightness() { | |||
| void Light::setBrightnessSmooth(float brightness) { | |||
| float v = brightness * brightness; | |||
| if (v < value) { | |||
| // Fade out light with lambda = 3 * framerate | |||
| value += (v - value) * sampleTime * (60.0 * 3.0); | |||
| // Fade out light with lambda = 2 * framerate | |||
| value += (v - value) * sampleTime * (60.0 * 2.0); | |||
| } | |||
| else { | |||
| // Immediately illuminate light | |||
| @@ -54,25 +54,36 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
| #endif | |||
| if (action == GLFW_PRESS) { | |||
| Widget *w = NULL; | |||
| // onMouseDown | |||
| Widget *w = gScene->onMouseDown(gMousePos, button); | |||
| { | |||
| EventMouseDown e; | |||
| e.pos = gMousePos; | |||
| e.button = button; | |||
| gScene->onMouseDown(e); | |||
| w = e.target; | |||
| } | |||
| if (button == GLFW_MOUSE_BUTTON_LEFT) { | |||
| if (w) { | |||
| // onDragStart | |||
| w->onDragStart(); | |||
| EventDragStart e; | |||
| w->onDragStart(e); | |||
| } | |||
| gDraggedWidget = w; | |||
| if (w != gFocusedWidget) { | |||
| if (gFocusedWidget) { | |||
| // onDefocus | |||
| w->onDefocus(); | |||
| EventDefocus e; | |||
| w->onDefocus(e); | |||
| } | |||
| gFocusedWidget = NULL; | |||
| if (w) { | |||
| // onFocus | |||
| if (w->onFocus()) { | |||
| EventFocus e; | |||
| w->onFocus(e); | |||
| if (e.consumed) { | |||
| gFocusedWidget = w; | |||
| } | |||
| } | |||
| @@ -81,17 +92,27 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
| } | |||
| else if (action == GLFW_RELEASE) { | |||
| // onMouseUp | |||
| Widget *w = gScene->onMouseUp(gMousePos, button); | |||
| Widget *w = NULL; | |||
| { | |||
| EventMouseUp e; | |||
| e.pos = gMousePos; | |||
| e.button = button; | |||
| gScene->onMouseUp(e); | |||
| w = e.target; | |||
| } | |||
| if (button == GLFW_MOUSE_BUTTON_LEFT) { | |||
| if (gDraggedWidget) { | |||
| // onDragDrop | |||
| w->onDragDrop(gDraggedWidget); | |||
| EventDragDrop e; | |||
| e.origin = gDraggedWidget; | |||
| w->onDragDrop(e); | |||
| } | |||
| // gDraggedWidget might have been set to null in the last event, recheck here | |||
| if (gDraggedWidget) { | |||
| // onDragEnd | |||
| gDraggedWidget->onDragEnd(); | |||
| EventDragEnd e; | |||
| gDraggedWidget->onDragEnd(e); | |||
| } | |||
| gDraggedWidget = NULL; | |||
| gDragHoveredWidget = NULL; | |||
| @@ -141,19 +162,32 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
| gMousePos = mousePos; | |||
| Widget *hovered = NULL; | |||
| // onMouseMove | |||
| Widget *hovered = gScene->onMouseMove(gMousePos, mouseRel); | |||
| { | |||
| EventMouseMove e; | |||
| e.pos = mousePos; | |||
| e.mouseRel = mouseRel; | |||
| gScene->onMouseMove(e); | |||
| hovered = e.target; | |||
| } | |||
| if (gDraggedWidget) { | |||
| // onDragMove | |||
| gDraggedWidget->onDragMove(mouseRel); | |||
| EventDragMove e; | |||
| e.mouseRel = mouseRel; | |||
| gDraggedWidget->onDragMove(e); | |||
| if (hovered != gDragHoveredWidget) { | |||
| if (gDragHoveredWidget) { | |||
| gDragHoveredWidget->onDragLeave(gDraggedWidget); | |||
| EventDragEnter e; | |||
| e.origin = gDraggedWidget; | |||
| gDragHoveredWidget->onDragLeave(e); | |||
| } | |||
| if (hovered) { | |||
| hovered->onDragEnter(gDraggedWidget); | |||
| EventDragEnter e; | |||
| e.origin = gDraggedWidget; | |||
| hovered->onDragEnter(e); | |||
| } | |||
| gDragHoveredWidget = hovered; | |||
| } | |||
| @@ -162,11 +196,13 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
| if (hovered != gHoveredWidget) { | |||
| if (gHoveredWidget) { | |||
| // onMouseLeave | |||
| gHoveredWidget->onMouseLeave(); | |||
| EventMouseLeave e; | |||
| gHoveredWidget->onMouseLeave(e); | |||
| } | |||
| if (hovered) { | |||
| // onMouseEnter | |||
| hovered->onMouseEnter(); | |||
| // onMouseEnter | |||
| EventMouseEnter e; | |||
| hovered->onMouseEnter(e); | |||
| } | |||
| gHoveredWidget = hovered; | |||
| } | |||
| @@ -174,14 +210,19 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
| if (glfwGetMouseButton(gWindow, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { | |||
| // TODO | |||
| // Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed | |||
| gScene->onScroll(mousePos, mouseRel); | |||
| EventScroll e; | |||
| e.pos = mousePos; | |||
| e.scrollRel = mouseRel; | |||
| gScene->onScroll(e); | |||
| } | |||
| } | |||
| void cursorEnterCallback(GLFWwindow* window, int entered) { | |||
| if (!entered) { | |||
| if (gHoveredWidget) { | |||
| gHoveredWidget->onMouseLeave(); | |||
| // onMouseLeave | |||
| EventMouseLeave e; | |||
| gHoveredWidget->onMouseLeave(e); | |||
| } | |||
| gHoveredWidget = NULL; | |||
| } | |||
| @@ -194,23 +235,47 @@ void scrollCallback(GLFWwindow *window, double x, double y) { | |||
| scrollRel = Vec(y, x); | |||
| #endif | |||
| // onScroll | |||
| gScene->onScroll(gMousePos, scrollRel.mult(50.0)); | |||
| EventScroll e; | |||
| e.pos = gMousePos; | |||
| e.scrollRel = scrollRel.mult(50.0); | |||
| gScene->onScroll(e); | |||
| } | |||
| void charCallback(GLFWwindow *window, unsigned int codepoint) { | |||
| if (gFocusedWidget) { | |||
| gFocusedWidget->onFocusText(codepoint); | |||
| // onText | |||
| EventText e; | |||
| e.codepoint = codepoint; | |||
| gFocusedWidget->onText(e); | |||
| } | |||
| } | |||
| void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { | |||
| void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { | |||
| if (action == GLFW_PRESS || action == GLFW_REPEAT) { | |||
| // onFocusKey | |||
| if (gFocusedWidget && gFocusedWidget->onFocusKey(key)) | |||
| return; | |||
| if (gFocusedWidget) { | |||
| // onKey | |||
| EventKey e; | |||
| e.key = key; | |||
| gFocusedWidget->onKey(e); | |||
| if (e.consumed) | |||
| return; | |||
| } | |||
| // onHoverKey | |||
| gScene->onHoverKey(gMousePos, key); | |||
| EventHoverKey e; | |||
| e.pos = gMousePos; | |||
| e.key = key; | |||
| gScene->onHoverKey(e); | |||
| } | |||
| } | |||
| void dropCallback(GLFWwindow *window, int count, const char **paths) { | |||
| // onPathDrop | |||
| EventPathDrop e; | |||
| e.pos = gMousePos; | |||
| for (int i = 0; i < count; i++) { | |||
| e.paths.push_back(paths[i]); | |||
| } | |||
| gScene->onPathDrop(e); | |||
| } | |||
| void errorCallback(int error, const char *description) { | |||
| @@ -272,6 +337,7 @@ void guiInit() { | |||
| glfwSetScrollCallback(gWindow, scrollCallback); | |||
| glfwSetCharCallback(gWindow, charCallback); | |||
| glfwSetKeyCallback(gWindow, keyCallback); | |||
| glfwSetDropCallback(gWindow, dropCallback); | |||
| // Set up GLEW | |||
| glewExperimental = GL_TRUE; | |||
| @@ -10,9 +10,15 @@ | |||
| using namespace rack; | |||
| int main(int argc, char* argv[]) { | |||
| char *cwd = getcwd(NULL, 0); | |||
| printf("Current working directory is %s\n", cwd); | |||
| free(cwd); | |||
| { | |||
| char *cwd = getcwd(NULL, 0); | |||
| printf("Current working directory: %s\n", cwd); | |||
| free(cwd); | |||
| std::string globalDir = assetGlobal(""); | |||
| std::string localDir = assetLocal(""); | |||
| printf("Global directory: %s\n", globalDir.c_str()); | |||
| printf("Local directory: %s\n", localDir.c_str()); | |||
| } | |||
| pluginInit(); | |||
| engineInit(); | |||
| @@ -7,25 +7,26 @@ 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()); | |||
| } | |||
| void Button::onMouseEnter() { | |||
| void Button::onMouseEnter(EventMouseEnter &e) { | |||
| state = BND_HOVER; | |||
| } | |||
| void Button::onMouseLeave() { | |||
| void Button::onMouseLeave(EventMouseLeave &e) { | |||
| state = BND_DEFAULT; | |||
| } | |||
| void Button::onDragStart() { | |||
| void Button::onDragStart(EventDragStart &e) { | |||
| state = BND_ACTIVE; | |||
| } | |||
| void Button::onDragEnd() { | |||
| void Button::onDragEnd(EventDragEnd &e) { | |||
| state = BND_HOVER; | |||
| } | |||
| void Button::onDragDrop(Widget *origin) { | |||
| if (origin == this) { | |||
| onAction(); | |||
| void Button::onDragDrop(EventDragDrop &e) { | |||
| if (e.origin == this) { | |||
| EventAction eAction; | |||
| onAction(eAction); | |||
| } | |||
| } | |||
| @@ -117,7 +117,7 @@ int FramebufferWidget::getImageHandle() { | |||
| return internal->fb->image; | |||
| } | |||
| void FramebufferWidget::onZoom() { | |||
| void FramebufferWidget::onZoom(EventZoom &e) { | |||
| dirty = true; | |||
| } | |||
| @@ -52,12 +52,12 @@ void Menu::draw(NVGcontext *vg) { | |||
| } | |||
| bool Menu::onScrollOpaque(Vec scrollRel) { | |||
| void Menu::onScroll(EventScroll &e) { | |||
| if (!parent) | |||
| return true; | |||
| return; | |||
| if (!parent->box.contains(box)) | |||
| box.pos = box.pos.plus(scrollRel); | |||
| return true; | |||
| box.pos = box.pos.plus(e.scrollRel); | |||
| e.consumed = true; | |||
| } | |||
| @@ -22,7 +22,7 @@ void MenuItem::draw(NVGcontext *vg) { | |||
| 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::onMouseEnter() { | |||
| void MenuItem::onMouseEnter(EventMouseEnter &e) { | |||
| Menu *parentMenu = dynamic_cast<Menu*>(parent); | |||
| if (!parentMenu) | |||
| return; | |||
| @@ -38,11 +38,12 @@ void MenuItem::onMouseEnter() { | |||
| parentMenu->setChildMenu(childMenu); | |||
| } | |||
| void MenuItem::onDragDrop(Widget *origin) { | |||
| if (origin != this) | |||
| void MenuItem::onDragDrop(EventDragDrop &e) { | |||
| if (e.origin != this) | |||
| return; | |||
| onAction(); | |||
| EventAction eAction; | |||
| onAction(eAction); | |||
| // deletes `this` | |||
| gScene->setOverlay(NULL); | |||
| } | |||
| @@ -3,18 +3,22 @@ | |||
| namespace rack { | |||
| void MenuOverlay::onDragDrop(Widget *origin) { | |||
| if (origin == this) { | |||
| void MenuOverlay::onDragDrop(EventDragDrop &e) { | |||
| if (e.origin == this) { | |||
| // deletes `this` | |||
| gScene->setOverlay(NULL); | |||
| } | |||
| } | |||
| Widget *MenuOverlay::onHoverKey(Vec pos, int key) { | |||
| Widget *w = Widget::onHoverKey(pos, key); | |||
| if (w) return w; | |||
| // Steal all keys | |||
| return this; | |||
| void MenuOverlay::onScroll(EventScroll &e) { | |||
| // Don't recurse children, consume the event | |||
| e.consumed = true; | |||
| } | |||
| void MenuOverlay::onHoverKey(EventHoverKey &e) { | |||
| // Recurse children but consume the event | |||
| Widget::onHoverKey(e); | |||
| e.consumed = true; | |||
| } | |||
| @@ -4,12 +4,14 @@ | |||
| namespace rack { | |||
| QuantityWidget::QuantityWidget() { | |||
| onChange(); | |||
| EventChange e; | |||
| onChange(e); | |||
| } | |||
| void QuantityWidget::setValue(float value) { | |||
| this->value = clampf(value, fminf(minValue, maxValue), fmaxf(minValue, maxValue)); | |||
| onChange(); | |||
| EventChange e; | |||
| onChange(e); | |||
| } | |||
| void QuantityWidget::setLimits(float minValue, float maxValue) { | |||
| @@ -7,22 +7,23 @@ 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() { | |||
| void RadioButton::onMouseEnter(EventMouseEnter &e) { | |||
| state = BND_HOVER; | |||
| } | |||
| void RadioButton::onMouseLeave() { | |||
| void RadioButton::onMouseLeave(EventMouseLeave &e) { | |||
| state = BND_DEFAULT; | |||
| } | |||
| void RadioButton::onDragDrop(Widget *origin) { | |||
| if (origin == this) { | |||
| void RadioButton::onDragDrop(EventDragDrop &e) { | |||
| if (e.origin == this) { | |||
| if (value == 0.0) | |||
| value = 1.0; | |||
| else | |||
| value = 0.0; | |||
| onAction(); | |||
| EventAction eAction; | |||
| onAction(eAction); | |||
| } | |||
| } | |||
| @@ -19,21 +19,21 @@ void ScrollBar::draw(NVGcontext *vg) { | |||
| bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size); | |||
| } | |||
| void ScrollBar::onDragStart() { | |||
| void ScrollBar::onDragStart(EventDragStart &e) { | |||
| state = BND_ACTIVE; | |||
| guiCursorLock(); | |||
| } | |||
| void ScrollBar::onDragMove(Vec mouseRel) { | |||
| void ScrollBar::onDragMove(EventDragMove &e) { | |||
| ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent); | |||
| assert(scrollWidget); | |||
| if (orientation == HORIZONTAL) | |||
| scrollWidget->offset.x += mouseRel.x; | |||
| scrollWidget->offset.x += e.mouseRel.x; | |||
| else | |||
| scrollWidget->offset.y += mouseRel.y; | |||
| scrollWidget->offset.y += e.mouseRel.y; | |||
| } | |||
| void ScrollBar::onDragEnd() { | |||
| void ScrollBar::onDragEnd(EventDragEnd &e) { | |||
| state = BND_DEFAULT; | |||
| guiCursorUnlock(); | |||
| } | |||
| @@ -58,9 +58,9 @@ void ScrollWidget::step() { | |||
| Widget::step(); | |||
| } | |||
| bool ScrollWidget::onScrollOpaque(Vec scrollRel) { | |||
| offset = offset.minus(scrollRel); | |||
| return true; | |||
| void ScrollWidget::onScroll(EventScroll &e) { | |||
| offset = offset.minus(e.scrollRel); | |||
| e.consumed = true; | |||
| } | |||
| @@ -11,26 +11,30 @@ void Slider::draw(NVGcontext *vg) { | |||
| bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL); | |||
| } | |||
| void Slider::onDragStart() { | |||
| void Slider::onDragStart(EventDragStart &e) { | |||
| state = BND_ACTIVE; | |||
| guiCursorLock(); | |||
| } | |||
| void Slider::onDragMove(Vec mouseRel) { | |||
| setValue(value + SLIDER_SENSITIVITY * (maxValue - minValue) * mouseRel.x); | |||
| void Slider::onDragMove(EventDragMove &e) { | |||
| setValue(value + SLIDER_SENSITIVITY * (maxValue - minValue) * e.mouseRel.x); | |||
| } | |||
| void Slider::onDragEnd() { | |||
| void Slider::onDragEnd(EventDragEnd &e) { | |||
| state = BND_DEFAULT; | |||
| guiCursorUnlock(); | |||
| onAction(); | |||
| EventAction eAction; | |||
| onAction(eAction); | |||
| } | |||
| void Slider::onMouseDownOpaque(int button) { | |||
| if (button == 1) { | |||
| void Slider::onMouseDown(EventMouseDown &e) { | |||
| if (e.button == 1) { | |||
| setValue(defaultValue); | |||
| onAction(); | |||
| EventAction eAction; | |||
| onAction(eAction); | |||
| } | |||
| e.consumed = true; | |||
| e.target = this; | |||
| } | |||
| @@ -24,21 +24,25 @@ void TextField::draw(NVGcontext *vg) { | |||
| } | |||
| } | |||
| Widget *TextField::onMouseDown(Vec pos, int button) { | |||
| end = begin = bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), pos.x, pos.y); | |||
| return OpaqueWidget::onMouseDown(pos, button); | |||
| void TextField::onMouseDown(EventMouseDown &e) { | |||
| end = begin = bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), e.pos.x, e.pos.y); | |||
| OpaqueWidget::onMouseDown(e); | |||
| } | |||
| void TextField::onFocus(EventFocus &e) { | |||
| begin = 0; | |||
| end = text.size(); | |||
| e.consumed = true; | |||
| } | |||
| bool TextField::onFocusText(int codepoint) { | |||
| char c = codepoint; | |||
| std::string newText(1, c); | |||
| void TextField::onText(EventText &e) { | |||
| std::string newText(1, (char) e.codepoint); | |||
| insertText(newText); | |||
| return true; | |||
| e.consumed = true; | |||
| } | |||
| bool TextField::onFocusKey(int key) { | |||
| switch (key) { | |||
| void TextField::onKey(EventKey &e) { | |||
| switch (e.key) { | |||
| case GLFW_KEY_BACKSPACE: | |||
| if (begin < end) { | |||
| text.erase(begin, end - begin); | |||
| @@ -107,20 +111,15 @@ bool TextField::onFocusKey(int key) { | |||
| insertText("\n"); | |||
| } | |||
| else { | |||
| onAction(); | |||
| EventAction e; | |||
| onAction(e); | |||
| } | |||
| break; | |||
| } | |||
| begin = mini(maxi(begin, 0), text.size()); | |||
| end = mini(maxi(end, 0), text.size()); | |||
| return true; | |||
| } | |||
| bool TextField::onFocus() { | |||
| begin = 0; | |||
| end = text.size(); | |||
| return true; | |||
| e.consumed = true; | |||
| } | |||
| void TextField::insertText(std::string newText) { | |||
| @@ -78,18 +78,21 @@ void Widget::clearChildren() { | |||
| void Widget::finalizeEvents() { | |||
| // Stop dragging and hovering this widget | |||
| if (gHoveredWidget == this) { | |||
| gHoveredWidget->onMouseLeave(); | |||
| EventMouseLeave e; | |||
| gHoveredWidget->onMouseLeave(e); | |||
| gHoveredWidget = NULL; | |||
| } | |||
| if (gDraggedWidget == this) { | |||
| gDraggedWidget->onDragEnd(); | |||
| EventDragEnd e; | |||
| gDraggedWidget->onDragEnd(e); | |||
| gDraggedWidget = NULL; | |||
| } | |||
| if (gDragHoveredWidget == this) { | |||
| gDragHoveredWidget = NULL; | |||
| } | |||
| if (gFocusedWidget == this) { | |||
| gFocusedWidget->onDefocus(); | |||
| EventDefocus e; | |||
| gFocusedWidget->onDefocus(e); | |||
| gFocusedWidget = NULL; | |||
| } | |||
| for (Widget *child : children) { | |||
| @@ -114,80 +117,51 @@ void Widget::draw(NVGcontext *vg) { | |||
| } | |||
| } | |||
| Widget *Widget::onMouseDown(Vec pos, int button) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| if (!child->visible) | |||
| continue; | |||
| if (child->box.contains(pos)) { | |||
| Widget *w = child->onMouseDown(pos.minus(child->box.pos), button); | |||
| if (w) | |||
| return w; | |||
| } | |||
| } | |||
| return NULL; | |||
| #define RECURSE_EVENT_POSITION(_method) { \ | |||
| 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; \ | |||
| } | |||
| Widget *Widget::onMouseUp(Vec pos, int button) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| if (!child->visible) | |||
| continue; | |||
| if (child->box.contains(pos)) { | |||
| Widget *w = child->onMouseUp(pos.minus(child->box.pos), button); | |||
| if (w) | |||
| return w; | |||
| } | |||
| } | |||
| return NULL; | |||
| void Widget::onMouseDown(EventMouseDown &e) { | |||
| RECURSE_EVENT_POSITION(onMouseDown); | |||
| } | |||
| Widget *Widget::onMouseMove(Vec pos, Vec mouseRel) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| if (!child->visible) | |||
| continue; | |||
| if (child->box.contains(pos)) { | |||
| Widget *w = child->onMouseMove(pos.minus(child->box.pos), mouseRel); | |||
| if (w) | |||
| return w; | |||
| } | |||
| } | |||
| return NULL; | |||
| void Widget::onMouseUp(EventMouseUp &e) { | |||
| RECURSE_EVENT_POSITION(onMouseUp); | |||
| } | |||
| Widget *Widget::onHoverKey(Vec pos, int key) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| if (!child->visible) | |||
| continue; | |||
| if (child->box.contains(pos)) { | |||
| Widget *w = child->onHoverKey(pos.minus(child->box.pos), key); | |||
| if (w) | |||
| return w; | |||
| } | |||
| } | |||
| return NULL; | |||
| void Widget::onMouseMove(EventMouseMove &e) { | |||
| RECURSE_EVENT_POSITION(onMouseMove); | |||
| } | |||
| Widget *Widget::onScroll(Vec pos, Vec scrollRel) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| if (!child->visible) | |||
| continue; | |||
| if (child->box.contains(pos)) { | |||
| Widget *w = child->onScroll(pos.minus(child->box.pos), scrollRel); | |||
| if (w) | |||
| return w; | |||
| } | |||
| } | |||
| return NULL; | |||
| 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() { | |||
| void Widget::onZoom(EventZoom &e) { | |||
| for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
| Widget *child = *it; | |||
| child->onZoom(); | |||
| child->onZoom(e); | |||
| } | |||
| } | |||
| @@ -18,8 +18,10 @@ Rect ZoomWidget::getViewport(Rect r) { | |||
| } | |||
| void ZoomWidget::setZoom(float zoom) { | |||
| if (zoom != this->zoom) | |||
| onZoom(); | |||
| if (zoom != this->zoom) { | |||
| EventZoom e; | |||
| onZoom(e); | |||
| } | |||
| this->zoom = zoom; | |||
| } | |||
| @@ -28,24 +30,39 @@ void ZoomWidget::draw(NVGcontext *vg) { | |||
| Widget::draw(vg); | |||
| } | |||
| Widget *ZoomWidget::onMouseDown(Vec pos, int button) { | |||
| return Widget::onMouseDown(pos.div(zoom), button); | |||
| void ZoomWidget::onMouseDown(EventMouseDown &e) { | |||
| Vec pos = e.pos; | |||
| e.pos = e.pos.div(zoom); | |||
| Widget::onMouseDown(e); | |||
| e.pos = pos; | |||
| } | |||
| Widget *ZoomWidget::onMouseUp(Vec pos, int button) { | |||
| return Widget::onMouseUp(pos.div(zoom), button); | |||
| void ZoomWidget::onMouseUp(EventMouseUp &e) { | |||
| Vec pos = e.pos; | |||
| e.pos = e.pos.div(zoom); | |||
| Widget::onMouseUp(e); | |||
| e.pos = pos; | |||
| } | |||
| Widget *ZoomWidget::onMouseMove(Vec pos, Vec mouseRel) { | |||
| return Widget::onMouseMove(pos.div(zoom), mouseRel); | |||
| void ZoomWidget::onMouseMove(EventMouseMove &e) { | |||
| Vec pos = e.pos; | |||
| e.pos = e.pos.div(zoom); | |||
| Widget::onMouseMove(e); | |||
| e.pos = pos; | |||
| } | |||
| Widget *ZoomWidget::onHoverKey(Vec pos, int key) { | |||
| return Widget::onHoverKey(pos.div(zoom), key); | |||
| void ZoomWidget::onHoverKey(EventHoverKey &e) { | |||
| Vec pos = e.pos; | |||
| e.pos = e.pos.div(zoom); | |||
| Widget::onHoverKey(e); | |||
| e.pos = pos; | |||
| } | |||
| Widget *ZoomWidget::onScroll(Vec pos, Vec scrollRel) { | |||
| return Widget::onScroll(pos.div(zoom), scrollRel); | |||
| void ZoomWidget::onScroll(EventScroll &e) { | |||
| Vec pos = e.pos; | |||
| e.pos = e.pos.div(zoom); | |||
| Widget::onScroll(e); | |||
| e.pos = pos; | |||
| } | |||