@@ -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 | else | ||||
FILES_UI = \ | FILES_UI = \ | ||||
CardinalUI.cpp \ | CardinalUI.cpp \ | ||||
AsyncDialog.cpp \ | |||||
override/MenuBar.cpp \ | override/MenuBar.cpp \ | ||||
override/Window.cpp | override/Window.cpp | ||||
endif | endif | ||||
@@ -28,8 +28,6 @@ | |||||
#include <thread> | #include <thread> | ||||
#include <utility> | #include <utility> | ||||
#include <osdialog.h> | |||||
#include <app/MenuBar.hpp> | #include <app/MenuBar.hpp> | ||||
#include <app/TipWindow.hpp> | #include <app/TipWindow.hpp> | ||||
#include <widget/OpaqueWidget.hpp> | #include <widget/OpaqueWidget.hpp> | ||||
@@ -57,11 +55,18 @@ | |||||
# undef DEBUG | # undef DEBUG | ||||
#endif | #endif | ||||
// for finding home dir | |||||
#ifndef ARCH_WIN | |||||
# include <pwd.h> | |||||
# include <unistd.h> | |||||
#endif | |||||
#ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
# include <lo/lo.h> | # include <lo/lo.h> | ||||
#endif | #endif | ||||
#include <Window.hpp> | #include <Window.hpp> | ||||
#include "../AsyncDialog.hpp" | |||||
#include "../PluginContext.hpp" | #include "../PluginContext.hpp" | ||||
// #define REMOTE_HOST "localhost" | // #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 { | struct FileButton : MenuButton { | ||||
Window& window; | Window& window; | ||||
const bool isStandalone; | const bool isStandalone; | ||||
@@ -129,16 +142,45 @@ struct FileButton : MenuButton { | |||||
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | ||||
menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() { | 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]() { | 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", []() { | menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | ||||
// APP->patch->revertDialog(); | // 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) { | if (isStandalone) { | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||