@@ -1 +1 @@ | |||
Subproject commit fbbfe11a5bb1a9d02f9fa7a341caae7e7d532e7e | |||
Subproject commit 29709cbe4e1f5d97b7630fd1664e2eb1210bb82f |
@@ -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, const std::function<void()> action) | |||
AsyncDialog(const char* const message) | |||
{ | |||
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); | |||
setup(message); | |||
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); | |||
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); | |||
} | |||
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); | |||
AsyncDialog(const char* const message, const std::function<void()> action) | |||
{ | |||
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<void()> action) | |||
{ | |||
MenuOverlay* const overlay = new MenuOverlay; | |||
@@ -121,7 +155,7 @@ void create(const char* const message, const std::function<void()> action) | |||
AsyncDialog* const dialog = new AsyncDialog(message, action); | |||
overlay->addChild(dialog); | |||
APP->scene->addChild(overlay); | |||
APP->scene->addChild(overlay); | |||
} | |||
} |
@@ -22,6 +22,7 @@ | |||
namespace asyncDialog | |||
{ | |||
void create(const char* message); | |||
void create(const char* message, std::function<void()> action); | |||
} |
@@ -20,6 +20,8 @@ | |||
#include <helpers.hpp> | |||
#include <patch.hpp> | |||
#include <settings.hpp> | |||
#include <string.hpp> | |||
#include <system.hpp> | |||
#include <ui/Button.hpp> | |||
#include <ui/MenuItem.hpp> | |||
#include <ui/MenuSeparator.hpp> | |||
@@ -30,7 +32,7 @@ | |||
#endif | |||
#include <Application.hpp> | |||
#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 | |||
@@ -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 |
@@ -108,8 +108,29 @@ static void promptClear(const char* const message, const std::function<void()> 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,49 +174,30 @@ 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("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||
APP->patch->saveAsDialog(); | |||
})); | |||
menu->addChild(createMenuItem("Save template", "", []() { | |||
APP->patch->saveTemplateDialog(); | |||
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(); | |||
Window::FileBrowserOptions opts; | |||
opts.startDir = dir.c_str(); | |||
opts.saving = ui->saving = true; | |||
ui->openFileBrowser(opts); | |||
})); | |||
*/ | |||
#ifdef HAVE_LIBLO | |||
if (oscServer == nullptr || !oscConnected) { | |||
@@ -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; | |||
} | |||