@@ -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); | |||