diff --git a/Makefile b/Makefile index 70ffcf23..7f8b9cc1 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/include/app.hpp b/include/app.hpp index 0f6e5bba..b0bcb4b4 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -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); 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); 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; }; //////////////////// diff --git a/include/components.hpp b/include/components.hpp index 640618f9..40849dff 100644 --- a/include/components.hpp +++ b/include/components.hpp @@ -409,34 +409,39 @@ struct RedGreenBlueLight : ColorLightWidget { }; +/** Based on the size of 5mm LEDs */ template 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 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 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 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 diff --git a/include/engine.hpp b/include/engine.hpp index 1496f0d6..2f0bc546 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -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 { diff --git a/include/events.hpp b/include/events.hpp new file mode 100644 index 00000000..12d95b04 --- /dev/null +++ b/include/events.hpp @@ -0,0 +1,107 @@ +#pragma once +#include + +#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 paths; +}; + +struct EventAction : Event { +}; + +struct EventChange : Event { +}; + +struct EventZoom : Event { +}; + + +} // namespace rack diff --git a/include/widgets.hpp b/include/widgets.hpp index 18d7321d..13c1df0e 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -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() {} }; diff --git a/installer-banner.bmp b/installer-banner.bmp new file mode 100644 index 00000000..616104b3 Binary files /dev/null and b/installer-banner.bmp differ diff --git a/installer.nsi b/installer.nsi new file mode 100644 index 00000000..d347c4b8 --- /dev/null +++ b/installer.nsi @@ -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 diff --git a/res/ComponentLibrary/LEDBezel.svg b/res/ComponentLibrary/LEDBezel.svg new file mode 100644 index 00000000..c4302dc4 --- /dev/null +++ b/res/ComponentLibrary/LEDBezel.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index 01061ef6..4495b98e 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -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; diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 02acac06..a75bf23c 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -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); diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 695fe0e9..548bdc06 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -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 \ No newline at end of file diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index 5ebe765b..dcfde25f 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -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; diff --git a/src/app/PluginManagerWidget.cpp b/src/app/PluginManagerWidget.cpp index 8e4b6023..c246c325 100644 --- a/src/app/PluginManagerWidget.cpp +++ b/src/app/PluginManagerWidget.cpp @@ -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(); // } // }; diff --git a/src/app/Port.cpp b/src/app/Port.cpp index 881583d9..0f1e5ae0 100644 --- a/src/app/Port.cpp +++ b/src/app/Port.cpp @@ -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) diff --git a/src/app/RackScene.cpp b/src/app/RackScene.cpp index e55ab507..7c6cf7c8 100644 --- a/src/app/RackScene.cpp +++ b/src/app/RackScene.cpp @@ -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; + } + } } diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 3177567c..2d368c95 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -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::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); } diff --git a/src/app/SVGKnob.cpp b/src/app/SVGKnob.cpp index 256e6917..0db5e415 100644 --- a/src/app/SVGKnob.cpp +++ b/src/app/SVGKnob.cpp @@ -35,9 +35,9 @@ void SVGKnob::step() { FramebufferWidget::step(); } -void SVGKnob::onChange() { +void SVGKnob::onChange(EventChange &e) { dirty = true; - Knob::onChange(); + Knob::onChange(e); } diff --git a/src/app/SVGSlider.cpp b/src/app/SVGSlider.cpp index 2bd40e8e..5d0beb60 100644 --- a/src/app/SVGSlider.cpp +++ b/src/app/SVGSlider.cpp @@ -21,9 +21,9 @@ void SVGSlider::step() { FramebufferWidget::step(); } -void SVGSlider::onChange() { +void SVGSlider::onChange(EventChange &e) { dirty = true; - ParamWidget::onChange(); + ParamWidget::onChange(e); } diff --git a/src/app/SVGSwitch.cpp b/src/app/SVGSwitch.cpp index 3b48819d..5b66518d 100644 --- a/src/app/SVGSwitch.cpp +++ b/src/app/SVGSwitch.cpp @@ -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); } diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 5a04460a..df95947b 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -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); } }; diff --git a/src/asset.cpp b/src/asset.cpp index 8fc4d13c..f01e31c1 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -9,6 +9,11 @@ #include #endif +#if ARCH_WIN + #include + #include +#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 diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 0da52dc0..21e5c16e 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -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; diff --git a/src/core/Blank.cpp b/src/core/Blank.cpp index ca1db935..07f4c691 100644 --- a/src/core/Blank.cpp +++ b/src/core/Blank.cpp @@ -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(parent); assert(m); - totalX += mouseRel.x; + totalX += e.mouseRel.x; float targetWidth = originalWidth; if (right) targetWidth += totalX; diff --git a/src/engine.cpp b/src/engine.cpp index 6663e2bf..2e8f984e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -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 diff --git a/src/gui.cpp b/src/gui.cpp index 01de9ab1..00ffd0f6 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -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; diff --git a/src/main.cpp b/src/main.cpp index 4fae38f7..3278c84a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(); diff --git a/src/widgets/Button.cpp b/src/widgets/Button.cpp index 42c01873..d2fa4f2a 100644 --- a/src/widgets/Button.cpp +++ b/src/widgets/Button.cpp @@ -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); } } diff --git a/src/widgets/FramebufferWidget.cpp b/src/widgets/FramebufferWidget.cpp index 9d4a633d..030cc2ce 100644 --- a/src/widgets/FramebufferWidget.cpp +++ b/src/widgets/FramebufferWidget.cpp @@ -117,7 +117,7 @@ int FramebufferWidget::getImageHandle() { return internal->fb->image; } -void FramebufferWidget::onZoom() { +void FramebufferWidget::onZoom(EventZoom &e) { dirty = true; } diff --git a/src/widgets/Menu.cpp b/src/widgets/Menu.cpp index a2987f9c..a5009a68 100644 --- a/src/widgets/Menu.cpp +++ b/src/widgets/Menu.cpp @@ -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; } diff --git a/src/widgets/MenuItem.cpp b/src/widgets/MenuItem.cpp index 8bc8c03e..f1907767 100644 --- a/src/widgets/MenuItem.cpp +++ b/src/widgets/MenuItem.cpp @@ -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(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); } diff --git a/src/widgets/MenuOverlay.cpp b/src/widgets/MenuOverlay.cpp index e887c274..e7bde39b 100644 --- a/src/widgets/MenuOverlay.cpp +++ b/src/widgets/MenuOverlay.cpp @@ -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; } diff --git a/src/widgets/QuantityWidget.cpp b/src/widgets/QuantityWidget.cpp index b083ff02..268e4e99 100644 --- a/src/widgets/QuantityWidget.cpp +++ b/src/widgets/QuantityWidget.cpp @@ -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) { diff --git a/src/widgets/RadioButton.cpp b/src/widgets/RadioButton.cpp index e1a75641..fb7192a6 100644 --- a/src/widgets/RadioButton.cpp +++ b/src/widgets/RadioButton.cpp @@ -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); } } diff --git a/src/widgets/ScrollBar.cpp b/src/widgets/ScrollBar.cpp index db912ece..803071e5 100644 --- a/src/widgets/ScrollBar.cpp +++ b/src/widgets/ScrollBar.cpp @@ -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(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(); } diff --git a/src/widgets/ScrollWidget.cpp b/src/widgets/ScrollWidget.cpp index 42fa4bff..c4faabf0 100644 --- a/src/widgets/ScrollWidget.cpp +++ b/src/widgets/ScrollWidget.cpp @@ -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; } diff --git a/src/widgets/Slider.cpp b/src/widgets/Slider.cpp index a0e2c3aa..de3e22cb 100644 --- a/src/widgets/Slider.cpp +++ b/src/widgets/Slider.cpp @@ -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; } diff --git a/src/widgets/TextField.cpp b/src/widgets/TextField.cpp index f3703f36..8bea1bf5 100644 --- a/src/widgets/TextField.cpp +++ b/src/widgets/TextField.cpp @@ -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) { diff --git a/src/widgets/Widget.cpp b/src/widgets/Widget.cpp index 65af2567..d6c6c0fc 100644 --- a/src/widgets/Widget.cpp +++ b/src/widgets/Widget.cpp @@ -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); } } diff --git a/src/widgets/ZoomWidget.cpp b/src/widgets/ZoomWidget.cpp index a0bb4b0c..c6d6cb5d 100644 --- a/src/widgets/ZoomWidget.cpp +++ b/src/widgets/ZoomWidget.cpp @@ -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; }