| @@ -1 +1 @@ | |||
| Subproject commit 2fa587358b9224646a69c0ac8bb5a31790acac3b | |||
| Subproject commit f3d38188c95b482d3311e84e9140837734f28af6 | |||
| @@ -0,0 +1,127 @@ | |||
| /* | |||
| * DISTRHO Cardinal Plugin | |||
| * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 3 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||
| */ | |||
| #include "AsyncDialog.hpp" | |||
| #include <context.hpp> | |||
| #include <app/Scene.hpp> | |||
| #include <ui/Button.hpp> | |||
| #include <ui/Label.hpp> | |||
| #include <ui/MenuOverlay.hpp> | |||
| #include <ui/SequentialLayout.hpp> | |||
| #include <widget/OpaqueWidget.hpp> | |||
| namespace asyncDialog | |||
| { | |||
| using namespace rack; | |||
| using namespace rack::ui; | |||
| using namespace rack::widget; | |||
| struct AsyncDialog : OpaqueWidget | |||
| { | |||
| SequentialLayout* layout; | |||
| SequentialLayout* contentLayout; | |||
| SequentialLayout* buttonLayout; | |||
| Label* label; | |||
| AsyncDialog(const char* const message, const std::function<void()> 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); | |||
| struct AsyncCancelButton : Button { | |||
| AsyncDialog* dialog; | |||
| void onAction(const ActionEvent& e) override { | |||
| dialog->getParent()->requestDelete(); | |||
| } | |||
| }; | |||
| AsyncCancelButton* const cancelButton = new AsyncCancelButton; | |||
| cancelButton->box.size.x = buttonWidth; | |||
| cancelButton->text = "Cancel"; | |||
| cancelButton->dialog = this; | |||
| buttonLayout->addChild(cancelButton); | |||
| struct AsyncOkButton : Button { | |||
| AsyncDialog* dialog; | |||
| std::function<void()> action; | |||
| void onAction(const ActionEvent& e) override { | |||
| action(); | |||
| dialog->getParent()->requestDelete(); | |||
| } | |||
| }; | |||
| AsyncOkButton* const okButton = new AsyncOkButton; | |||
| okButton->box.size.x = buttonWidth; | |||
| okButton->text = "Ok"; | |||
| okButton->dialog = this; | |||
| okButton->action = action; | |||
| buttonLayout->addChild(okButton); | |||
| } | |||
| void step() override | |||
| { | |||
| OpaqueWidget::step(); | |||
| box.pos = parent->box.size.minus(box.size).div(2).round(); | |||
| } | |||
| void draw(const DrawArgs& args) override | |||
| { | |||
| bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, 0); | |||
| Widget::draw(args); | |||
| } | |||
| }; | |||
| void create(const char* const message, const std::function<void()> action) | |||
| { | |||
| MenuOverlay* const overlay = new MenuOverlay; | |||
| overlay->bgColor = nvgRGBAf(0, 0, 0, 0.33); | |||
| AsyncDialog* const dialog = new AsyncDialog(message, action); | |||
| overlay->addChild(dialog); | |||
| APP->scene->addChild(overlay); | |||
| } | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| /* | |||
| * DISTRHO Cardinal Plugin | |||
| * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 3 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||
| */ | |||
| #include <functional> | |||
| #pragma once | |||
| namespace asyncDialog | |||
| { | |||
| void create(const char* message, std::function<void()> action); | |||
| } | |||
| @@ -36,6 +36,7 @@ FILES_DSP += \ | |||
| else | |||
| FILES_UI = \ | |||
| CardinalUI.cpp \ | |||
| AsyncDialog.cpp \ | |||
| override/MenuBar.cpp \ | |||
| override/Window.cpp | |||
| endif | |||
| @@ -28,8 +28,6 @@ | |||
| #include <thread> | |||
| #include <utility> | |||
| #include <osdialog.h> | |||
| #include <app/MenuBar.hpp> | |||
| #include <app/TipWindow.hpp> | |||
| #include <widget/OpaqueWidget.hpp> | |||
| @@ -57,11 +55,18 @@ | |||
| # undef DEBUG | |||
| #endif | |||
| // for finding home dir | |||
| #ifndef ARCH_WIN | |||
| # include <pwd.h> | |||
| # include <unistd.h> | |||
| #endif | |||
| #ifdef HAVE_LIBLO | |||
| # include <lo/lo.h> | |||
| #endif | |||
| #include <Window.hpp> | |||
| #include "../AsyncDialog.hpp" | |||
| #include "../PluginContext.hpp" | |||
| // #define REMOTE_HOST "localhost" | |||
| @@ -95,6 +100,14 @@ struct MenuButton : ui::Button { | |||
| //////////////////// | |||
| static void promptClear(const char* const message, const std::function<void()> action) | |||
| { | |||
| if (APP->history->isSaved() || APP->scene->rack->hasModules()) | |||
| return action(); | |||
| asyncDialog::create(message, action); | |||
| } | |||
| struct FileButton : MenuButton { | |||
| Window& window; | |||
| const bool isStandalone; | |||
| @@ -129,16 +142,45 @@ struct FileButton : MenuButton { | |||
| menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | |||
| menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() { | |||
| // APP->patch->loadTemplateDialog(); | |||
| APP->patch->loadTemplate(); | |||
| // see APP->patch->loadTemplateDialog(); | |||
| promptClear("The current patch is unsaved. Clear it and start a new patch?", []() { | |||
| APP->patch->loadTemplate(); | |||
| }); | |||
| })); | |||
| menu->addChild(createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", [this]() { | |||
| Window::FileBrowserOptions opts; | |||
| const std::string dir = system::getDirectory(APP->patch->path); | |||
| opts.startDir = dir.c_str(); | |||
| window.openFileBrowser(opts); | |||
| // APP->patch->loadDialog(); | |||
| // see APP->patch->loadDialog(); | |||
| 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 | |||
| } | |||
| Window::FileBrowserOptions opts; | |||
| opts.startDir = dir.c_str(); | |||
| window.openFileBrowser(opts); | |||
| }); | |||
| })); | |||
| /* | |||
| @@ -191,8 +233,12 @@ struct FileButton : MenuButton { | |||
| menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | |||
| // APP->patch->revertDialog(); | |||
| APP->patch->loadAction(APP->patch->path); | |||
| }, APP->patch->path == "")); | |||
| if (APP->patch->path.empty()) | |||
| return; | |||
| promptClear("Revert patch to the last saved state?", []{ | |||
| APP->patch->loadAction(APP->patch->path); | |||
| }); | |||
| }, APP->patch->path.empty())); | |||
| if (isStandalone) { | |||
| menu->addChild(new ui::MenuSeparator); | |||