diff --git a/dpf b/dpf index fbbfe11..29709cb 160000 --- a/dpf +++ b/dpf @@ -1 +1 @@ -Subproject commit fbbfe11a5bb1a9d02f9fa7a341caae7e7d532e7e +Subproject commit 29709cbe4e1f5d97b7630fd1664e2eb1210bb82f diff --git a/src/AsyncDialog.cpp b/src/AsyncDialog.cpp index 75b573b..ed985d5 100644 --- a/src/AsyncDialog.cpp +++ b/src/AsyncDialog.cpp @@ -34,42 +34,34 @@ using namespace rack::widget; struct AsyncDialog : OpaqueWidget { + static const constexpr float margin = 10; + static const constexpr float buttonWidth = 100; + SequentialLayout* layout; SequentialLayout* contentLayout; SequentialLayout* buttonLayout; Label* label; + AsyncDialog(const char* const message) + { + setup(message); + + struct AsyncDismissButton : Button { + AsyncDialog* dialog; + void onAction(const ActionEvent& e) override { + dialog->getParent()->requestDelete(); + } + }; + AsyncDismissButton* const dismissButton = new AsyncDismissButton; + dismissButton->box.size.x = buttonWidth; + dismissButton->text = "Dismiss"; + dismissButton->dialog = this; + buttonLayout->addChild(dismissButton); + } + AsyncDialog(const char* const message, const std::function action) { - box.size = math::Vec(400, 120); - const float margin = 10; - const float buttonWidth = 100; - - layout = new SequentialLayout; - layout->box.pos = math::Vec(0, 0); - layout->box.size = box.size; - layout->orientation = SequentialLayout::VERTICAL_ORIENTATION; - layout->margin = math::Vec(margin, margin); - layout->spacing = math::Vec(margin, margin); - layout->wrap = false; - addChild(layout); - - contentLayout = new SequentialLayout; - contentLayout->spacing = math::Vec(margin, margin); - layout->addChild(contentLayout); - - buttonLayout = new SequentialLayout; - buttonLayout->alignment = SequentialLayout::CENTER_ALIGNMENT; - buttonLayout->box.size = box.size; - buttonLayout->spacing = math::Vec(margin, margin); - layout->addChild(buttonLayout); - - label = new Label; - label->box.size.x = box.size.x - 2*margin; - label->box.size.y = box.size.y - 2*margin - 40; - label->fontSize = 16; - label->text = message; - contentLayout->addChild(label); + setup(message); struct AsyncCancelButton : Button { AsyncDialog* dialog; @@ -99,6 +91,37 @@ struct AsyncDialog : OpaqueWidget buttonLayout->addChild(okButton); } + void setup(const char* const message) + { + box.size = math::Vec(400, 120); + + layout = new SequentialLayout; + layout->box.pos = math::Vec(0, 0); + layout->box.size = box.size; + layout->orientation = SequentialLayout::VERTICAL_ORIENTATION; + layout->margin = math::Vec(margin, margin); + layout->spacing = math::Vec(margin, margin); + layout->wrap = false; + addChild(layout); + + contentLayout = new SequentialLayout; + contentLayout->spacing = math::Vec(margin, margin); + layout->addChild(contentLayout); + + buttonLayout = new SequentialLayout; + buttonLayout->alignment = SequentialLayout::CENTER_ALIGNMENT; + buttonLayout->box.size = box.size; + buttonLayout->spacing = math::Vec(margin, margin); + layout->addChild(buttonLayout); + + label = new Label; + label->box.size.x = box.size.x - 2*margin; + label->box.size.y = box.size.y - 2*margin - 40; + label->fontSize = 16; + label->text = message; + contentLayout->addChild(label); + } + void step() override { OpaqueWidget::step(); @@ -113,6 +136,17 @@ struct AsyncDialog : OpaqueWidget } }; +void create(const char* const message) +{ + MenuOverlay* const overlay = new MenuOverlay; + overlay->bgColor = nvgRGBAf(0, 0, 0, 0.33); + + AsyncDialog* const dialog = new AsyncDialog(message); + overlay->addChild(dialog); + + APP->scene->addChild(overlay); +} + void create(const char* const message, const std::function action) { MenuOverlay* const overlay = new MenuOverlay; @@ -121,7 +155,7 @@ void create(const char* const message, const std::function action) AsyncDialog* const dialog = new AsyncDialog(message, action); overlay->addChild(dialog); - APP->scene->addChild(overlay); + APP->scene->addChild(overlay); } } diff --git a/src/AsyncDialog.hpp b/src/AsyncDialog.hpp index 4fc4c85..671abe5 100644 --- a/src/AsyncDialog.hpp +++ b/src/AsyncDialog.hpp @@ -22,6 +22,7 @@ namespace asyncDialog { +void create(const char* message); void create(const char* message, std::function action); } diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index 4a66301..8777c2f 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,7 +32,7 @@ #endif #include -#include "DistrhoUI.hpp" +#include "AsyncDialog.hpp" #include "PluginContext.hpp" #include "WindowParameters.hpp" #include "ResizeHandle.hpp" @@ -42,7 +44,7 @@ GLFWAPI int glfwGetKeyScancode(int key) { return 0; } namespace rack { namespace app { - widget::Widget* createMenuBar(Window& window, bool isStandalone); + widget::Widget* createMenuBar(CardinalBaseUI* const ui, bool isStandalone); } namespace window { void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui); @@ -54,14 +56,9 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- -CardinalPluginContext* getRackContextFromPlugin(void* ptr); - -// ----------------------------------------------------------------------------------------------------------- - -class CardinalUI : public UI, +class CardinalUI : public CardinalBaseUI, public WindowParametersCallback { - CardinalPluginContext* const fContext; rack::math::Vec fLastMousePos; ResizeHandle fResizeHandle; WindowParameters fWindowParameters; @@ -70,14 +67,14 @@ class CardinalUI : public UI, CardinalPluginContext* const context; ScopedContext(CardinalUI* const ui) - : context(ui->fContext) + : context(ui->context) { rack::contextSet(context); WindowParametersRestore(context->window); } ScopedContext(CardinalUI* const ui, const int mods) - : context(ui->fContext) + : context(ui->context) { rack::contextSet(context); rack::window::WindowSetMods(context->window, mods); @@ -94,14 +91,13 @@ class CardinalUI : public UI, public: CardinalUI() - : UI(1228, 666), - fContext(getRackContextFromPlugin(getPluginInstancePointer())), + : CardinalBaseUI(1228, 666), fResizeHandle(this) { Window& window(getWindow()); window.setIgnoringKeyRepeat(true); - fContext->nativeWindowId = window.getNativeWindowHandle(); + context->nativeWindowId = window.getNativeWindowHandle(); if (isResizable()) fResizeHandle.hide(); @@ -111,32 +107,32 @@ public: if (scaleFactor != 1) setSize(1228 * scaleFactor, 666 * scaleFactor); - rack::contextSet(fContext); + rack::contextSet(context); - rack::window::WindowSetPluginUI(fContext->window, this); + rack::window::WindowSetPluginUI(context->window, this); - if (fContext->scene->menuBar != nullptr) - fContext->scene->removeChild(fContext->scene->menuBar); + if (context->scene->menuBar != nullptr) + context->scene->removeChild(context->scene->menuBar); - fContext->scene->menuBar = rack::app::createMenuBar(window, getApp().isStandalone()); - fContext->scene->addChildBelow(fContext->scene->menuBar, fContext->scene->rackScroll); + context->scene->menuBar = rack::app::createMenuBar(this, getApp().isStandalone()); + context->scene->addChildBelow(context->scene->menuBar, context->scene->rackScroll); - fContext->window->step(); + context->window->step(); rack::contextSet(nullptr); - WindowParametersSetCallback(fContext->window, this); + WindowParametersSetCallback(context->window, this); } ~CardinalUI() override { - rack::contextSet(fContext); + rack::contextSet(context); - rack::widget::Widget* const menuBar = fContext->scene->menuBar; - fContext->scene->menuBar = nullptr; - fContext->scene->removeChild(menuBar); + rack::widget::Widget* const menuBar = context->scene->menuBar; + context->scene->menuBar = nullptr; + context->scene->removeChild(menuBar); - rack::window::WindowSetPluginUI(fContext->window, nullptr); + rack::window::WindowSetPluginUI(context->window, nullptr); rack::contextSet(nullptr); } @@ -144,7 +140,7 @@ public: void onNanoDisplay() override { const ScopedContext sc(this); - fContext->window->step(); + context->window->step(); } void uiIdle() override @@ -268,7 +264,7 @@ protected: return; } - WindowParametersSetValues(fContext->window, fWindowParameters); + WindowParametersSetValues(context->window, fWindowParameters); } void stateChanged(const char* key, const char* value) override @@ -378,7 +374,7 @@ protected: */ const ScopedContext sc(this, mods); - return fContext->event->handleButton(fLastMousePos, button, action, mods); + return context->event->handleButton(fLastMousePos, button, action, mods); } bool onMotion(const MotionEvent& ev) override @@ -389,7 +385,7 @@ protected: fLastMousePos = mousePos; const ScopedContext sc(this, glfwMods(ev.mod)); - return fContext->event->handleHover(mousePos, mouseDelta); + return context->event->handleHover(mousePos, mouseDelta); } bool onScroll(const ScrollEvent& ev) override @@ -402,7 +398,7 @@ protected: #endif const ScopedContext sc(this, glfwMods(ev.mod)); - return fContext->event->handleScroll(fLastMousePos, scrollDelta); + return context->event->handleScroll(fLastMousePos, scrollDelta); } bool onCharacterInput(const CharacterInputEvent& ev) override @@ -411,7 +407,7 @@ protected: return false; const ScopedContext sc(this, glfwMods(ev.mod)); - return fContext->event->handleText(fLastMousePos, ev.character); + return context->event->handleText(fLastMousePos, ev.character); } bool onKeyboard(const KeyboardEvent& ev) override @@ -486,15 +482,15 @@ protected: } const ScopedContext sc(this, mods); - return fContext->event->handleKey(fLastMousePos, key, ev.keycode, action, mods); + return context->event->handleKey(fLastMousePos, key, ev.keycode, action, mods); } void onResize(const ResizeEvent& ev) override { UI::onResize(ev); - if (fContext->window != nullptr) - fContext->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); + if (context->window != nullptr) + context->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); const double scaleFactor = getScaleFactor(); char sizeString[64]; @@ -509,7 +505,7 @@ protected: return; const ScopedContext sc(this, 0); - fContext->event->handleLeave(); + context->event->handleLeave(); } void uiFileBrowserSelected(const char* const filename) override @@ -517,9 +513,38 @@ protected: if (filename == nullptr) return; - rack::contextSet(fContext); - WindowParametersRestore(fContext->window); - fContext->patch->loadAction(filename); + rack::contextSet(context); + WindowParametersRestore(context->window); + + std::string sfilename = filename; + + if (saving) + { + if (rack::system::getExtension(sfilename) != ".vcv") + sfilename += ".vcv"; + + try { + context->patch->save(sfilename); + } + catch (rack::Exception& e) { + std::string message = rack::string::f("Could not save patch: %s", e.what()); + asyncDialog::create(message.c_str()); + return; + } + } + else + { + try { + context->patch->load(sfilename); + } catch (rack::Exception& e) { + std::string message = rack::string::f("Could not load patch: %s", e.what()); + asyncDialog::create(message.c_str()); + return; + } + } + + context->patch->path = sfilename; + context->history->setSaved(); } #if 0 diff --git a/src/PluginContext.hpp b/src/PluginContext.hpp index 453db61..f7c5b9d 100644 --- a/src/PluginContext.hpp +++ b/src/PluginContext.hpp @@ -28,6 +28,10 @@ #include "DistrhoPlugin.hpp" #include "extra/Mutex.hpp" +#ifndef HEADLESS +# include "DistrhoUI.hpp" +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- @@ -86,6 +90,8 @@ struct CardinalAudioDevice; struct CardinalMidiInputDevice; struct CardinalMidiOutputDevice; +CardinalPluginContext* getRackContextFromPlugin(void* ptr); + class CardinalBasePlugin : public Plugin { public: CardinalPluginContext* const context; @@ -106,6 +112,18 @@ public: virtual bool clearMidiOutputDevice(CardinalMidiOutputDevice* dev) noexcept = 0; }; +class CardinalBaseUI : public UI { +public: + CardinalPluginContext* const context; + bool saving; + + CardinalBaseUI(const uint width, const uint height) + : UI(width, height), + context(getRackContextFromPlugin(getPluginInstancePointer())), + saving(false) {} + ~CardinalBaseUI() override {} +}; + // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/src/override/MenuBar.cpp b/src/override/MenuBar.cpp index b23ab0f..ceefda7 100644 --- a/src/override/MenuBar.cpp +++ b/src/override/MenuBar.cpp @@ -108,8 +108,29 @@ static void promptClear(const char* const message, const std::function a asyncDialog::create(message, action); } +static std::string homeDir() +{ +#ifdef ARCH_WIN + if (const char* const userprofile = getenv("USERPROFILE")) + { + return userprofile; + } + else if (const char* const homedrive = getenv("HOMEDRIVE")) + { + if (const char* const homepath = getenv("HOMEPATH")) + return system::join(homedrive, homepath); + } +#else + if (struct passwd* const pwd = getpwuid(getuid())) + return pwd->pw_dir; + else if (const char* const home = getenv("HOME")) + return home; +#endif + return {}; +} + struct FileButton : MenuButton { - Window& window; + CardinalBaseUI* const ui; const bool isStandalone; #ifdef HAVE_LIBLO @@ -133,8 +154,8 @@ struct FileButton : MenuButton { } #endif - FileButton(Window& win, const bool standalone) - : MenuButton(), window(win), isStandalone(standalone) {} + FileButton(CardinalBaseUI* const ui2, const bool standalone) + : MenuButton(), ui(ui2), isStandalone(standalone) {} void onAction(const ActionEvent& e) override { ui::Menu* menu = createMenu(); @@ -153,50 +174,31 @@ struct FileButton : MenuButton { promptClear("The current patch is unsaved. Clear it and open a new patch?", [this]() { std::string dir; if (! APP->patch->path.empty()) - { dir = system::getDirectory(APP->patch->path); - } else - { - // find home directory -#ifdef ARCH_WIN - if (const char* const userprofile = getenv("USERPROFILE")) - { - dir = userprofile; - } - else if (const char* const homedrive = getenv("HOMEDRIVE")) - { - if (const char* const homepath = getenv("HOMEPATH")) - dir = system::join(homedrive, homepath); - } -#else - if (struct passwd* const pwd = getpwuid(getuid())) - dir = pwd->pw_dir; - else if (const char* const home = getenv("HOME")) - dir = home; -#endif - } + dir = homeDir(); Window::FileBrowserOptions opts; opts.startDir = dir.c_str(); - window.openFileBrowser(opts); + opts.saving = ui->saving = false; + ui->openFileBrowser(opts); }); })); - /* - menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { - APP->patch->saveDialog(); - })); + menu->addChild(createMenuItem("Export...", RACK_MOD_CTRL_NAME "+Shift+S", [this]() { + // see APP->patch->saveAsDialog(); + std::string dir; + if (! APP->patch->path.empty()) + dir = system::getDirectory(APP->patch->path); + else + dir = homeDir(); - menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { - APP->patch->saveAsDialog(); + Window::FileBrowserOptions opts; + opts.startDir = dir.c_str(); + opts.saving = ui->saving = true; + ui->openFileBrowser(opts); })); - menu->addChild(createMenuItem("Save template", "", []() { - APP->patch->saveTemplateDialog(); - })); - */ - #ifdef HAVE_LIBLO if (oscServer == nullptr || !oscConnected) { menu->addChild(createMenuItem("Connect to MOD", "", [this]() { @@ -680,7 +682,7 @@ struct MenuBar : widget::OpaqueWidget { // CardinalPluginContext* const context; MeterLabel* meterLabel; - MenuBar(Window& window, const bool isStandalone) + MenuBar(CardinalBaseUI* const ui, const bool isStandalone) : widget::OpaqueWidget() // : context(ctx) { @@ -692,7 +694,7 @@ struct MenuBar : widget::OpaqueWidget { layout->spacing = math::Vec(0, 0); addChild(layout); - FileButton* fileButton = new FileButton(window, isStandalone); + FileButton* fileButton = new FileButton(ui, isStandalone); fileButton->text = "File"; layout->addChild(fileButton); @@ -745,8 +747,8 @@ widget::Widget* createMenuBar() { return new widget::Widget; } -widget::Widget* createMenuBar(Window& window, const bool isStandalone) { - menuBar::MenuBar* menuBar = new menuBar::MenuBar(window, isStandalone); +widget::Widget* createMenuBar(CardinalBaseUI* const ui, const bool isStandalone) { + menuBar::MenuBar* menuBar = new menuBar::MenuBar(ui, isStandalone); return menuBar; }