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