|
- /*
- * DISTRHO Cardinal Plugin
- * Copyright (C) 2021-2024 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.
- */
-
- /**
- * This file is an edited version of VCVRack's app/MenuBar.cpp
- * Copyright (C) 2016-2023 VCV.
- *
- * 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 (at your option) any later version.
- */
-
- #include <thread>
- #include <utility>
-
- #include <app/MenuBar.hpp>
- #include <app/TipWindow.hpp>
- #include <widget/OpaqueWidget.hpp>
- #include <ui/Button.hpp>
- #include <ui/MenuItem.hpp>
- #include <ui/MenuSeparator.hpp>
- #include <ui/SequentialLayout.hpp>
- #include <ui/Slider.hpp>
- #include <ui/TextField.hpp>
- #include <ui/ProgressBar.hpp>
- #include <ui/Label.hpp>
- #include <engine/Engine.hpp>
- #include <widget/FramebufferWidget.hpp>
- #include <window/Window.hpp>
- #include <asset.hpp>
- #include <context.hpp>
- #include <settings.hpp>
- #include <helpers.hpp>
- #include <system.hpp>
- #include <plugin.hpp>
- #include <patch.hpp>
- #include <library.hpp>
-
- #include "../CardinalCommon.hpp"
- #include "../CardinalRemote.hpp"
- #include "../CardinalPluginContext.hpp"
- #include "DistrhoPlugin.hpp"
- #include "DistrhoStandaloneUtils.hpp"
-
- #ifdef DISTRHO_OS_WASM
- # include <emscripten/emscripten.h>
- # undef HAVE_LIBLO
- #endif
-
- #ifdef HAVE_LIBLO
- # include <lo/lo.h>
- #endif
-
- namespace rack {
- namespace asset {
- std::string patchesPath();
- }
- namespace engine {
- void Engine_setRemoteDetails(Engine*, remoteUtils::RemoteDetails*);
- }
-
- namespace app {
- namespace menuBar {
-
-
- struct MenuButton : ui::Button {
- void step() override {
- box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0;
- Widget::step();
- }
- void draw(const DrawArgs& args) override {
- BNDwidgetState state = BND_DEFAULT;
- if (APP->event->hoveredWidget == this)
- state = BND_HOVER;
- if (APP->event->draggedWidget == this)
- state = BND_ACTIVE;
- bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
- Widget::draw(args);
- }
- };
-
-
- ////////////////////
- // File
- ////////////////////
-
-
- struct FileButton : MenuButton {
- const bool isStandalone;
- std::vector<std::string> demoPatches;
-
- #ifdef DISTRHO_OS_WASM
- static void WebBrowserDataSaved(const int err)
- {
- err ? async_dialog_message("Error, could not save web browser data!")
- : async_dialog_message("Web browser data saved!");
- }
-
- static void wasmSaveAs()
- {
- async_dialog_text_input("Filename", nullptr, [](char* const filename) {
- if (filename == nullptr)
- return;
-
- APP->patch->path = asset::user("patches");
- system::createDirectories(APP->patch->path);
-
- APP->patch->path += filename;
- if (rack::system::getExtension(filename) != ".vcv")
- APP->patch->path += ".vcv";
-
- patchUtils::saveDialog(APP->patch->path);
- std::free(filename);
- });
- }
- #endif
-
- FileButton(const bool standalone)
- : MenuButton(), isStandalone(standalone)
- {
- #if CARDINAL_VARIANT_MINI
- const std::string patchesDir = asset::patchesPath() + DISTRHO_OS_SEP_STR "mini";
- #else
- const std::string patchesDir = asset::patchesPath() + DISTRHO_OS_SEP_STR "examples";
- #endif
-
- if (system::isDirectory(patchesDir))
- {
- demoPatches = system::getEntries(patchesDir);
- std::sort(demoPatches.begin(), demoPatches.end(), [](const std::string& a, const std::string& b){
- return string::lowercase(a) < string::lowercase(b);
- });
- }
- }
-
- void onAction(const ActionEvent& e) override {
- ui::Menu* menu = createMenu();
- menu->cornerFlags = BND_CORNER_TOP;
- menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-
- #ifndef DISTRHO_OS_WASM
- constexpr const char* const NewShortcut = RACK_MOD_CTRL_NAME "+N";
- #else
- constexpr const char* const NewShortcut = "";
- #endif
- menu->addChild(createMenuItem("New", NewShortcut, []() {
- patchUtils::loadTemplateDialog(false);
- }));
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- menu->addChild(createMenuItem("New (factory template)", "", []() {
- patchUtils::loadTemplateDialog(true);
- }));
-
- #ifndef DISTRHO_OS_WASM
- constexpr const char* const OpenName = "Open...";
- #else
- constexpr const char* const OpenName = "Import patch...";
- #endif
- menu->addChild(createMenuItem(OpenName, RACK_MOD_CTRL_NAME "+O", []() {
- patchUtils::loadDialog();
- }));
-
- const std::string patchesDir = asset::user("patches");
- const std::vector<std::string> patches = system::isDirectory(patchesDir) ? system::getEntries(patchesDir) : std::vector<std::string>();
- menu->addChild(createSubmenuItem("Open local patch", "", [patches](ui::Menu* menu) {
- for (const std::string& path : patches) {
- std::string name = system::getStem(path);
- menu->addChild(createMenuItem(name, "", [=]() {
- patchUtils::loadPathDialog(path, false);
- }));
- }
- }, patches.empty()));
-
- menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) {
- for (const std::string& path : settings::recentPatchPaths) {
- std::string name = system::getStem(path);
- menu->addChild(createMenuItem(name, "", [=]() {
- patchUtils::loadPathDialog(path, false);
- }));
- }
- }, settings::recentPatchPaths.empty()));
- #endif
-
- if (!demoPatches.empty())
- {
- menu->addChild(createSubmenuItem("Open demo / example project", "", [=](ui::Menu* const menu) {
- for (std::string path : demoPatches) {
- std::string label = system::getStem(path);
-
- for (size_t i=0, len=label.size(); i<len; ++i) {
- if (label[i] == '_')
- label[i] = ' ';
- }
-
- menu->addChild(createMenuItem(label, "", [path]() {
- patchUtils::loadPathDialog(path, true);
- }));
- }
-
- menu->addChild(new ui::MenuSeparator);
-
- menu->addChild(createMenuItem("Open patchstorage.com for more patches", "", []() {
- patchUtils::openBrowser("https://patchstorage.com/platform/cardinal/");
- }));
- }));
- }
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- menu->addChild(createMenuItem("Import selection...", "", [=]() {
- patchUtils::loadSelectionDialog();
- }, false, true));
-
- menu->addChild(new ui::MenuSeparator);
-
- #ifndef DISTRHO_OS_WASM
- menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() {
- // NOTE: for plugin versions it will do nothing if path is empty, intentionally
- patchUtils::saveDialog(APP->patch->path);
- }, APP->patch->path.empty() && !isStandalone));
-
- menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() {
- patchUtils::saveAsDialog();
- }));
- #else
- menu->addChild(createMenuItem("Save", "", []() {
- if (APP->patch->path.empty())
- wasmSaveAs();
- else
- patchUtils::saveDialog(APP->patch->path);
- }));
-
- menu->addChild(createMenuItem("Save as...", "", []() {
- wasmSaveAs();
- }));
-
- menu->addChild(createMenuItem("Save and download compressed", "", []() {
- patchUtils::saveAsDialog();
- }));
-
- menu->addChild(createMenuItem("Save and download uncompressed", "", []() {
- patchUtils::saveAsDialogUncompressed();
- }));
- #endif
- #endif
-
- menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() {
- patchUtils::revertDialog();
- }, APP->patch->path.empty()));
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- menu->addChild(createMenuItem("Overwrite template", "", []() {
- patchUtils::saveTemplateDialog();
- }));
-
- #ifdef DISTRHO_OS_WASM
- menu->addChild(new ui::MenuSeparator);
-
- menu->addChild(createMenuItem("Save persistent browser data", "", []() {
- settings::save();
- EM_ASM({
- Module.FS.syncfs(false, function(err){ dynCall('vi', $0, [!!err]) });
- }, WebBrowserDataSaved);
- }));
- #endif
- #endif
-
- #if defined(HAVE_LIBLO) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- #ifdef __MOD_DEVICES__
- #define REMOTE_NAME "MOD"
- #else
- #define REMOTE_NAME "Remote"
- #endif
- menu->addChild(new ui::MenuSeparator);
-
- remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote();
-
- if (remoteDetails != nullptr && remoteDetails->connected) {
- menu->addChild(createMenuItem("Deploy to " REMOTE_NAME, "F7", [remoteDetails]() {
- remoteUtils::sendFullPatchToRemote(remoteDetails);
- }));
-
- menu->addChild(createCheckMenuItem("Auto deploy to " REMOTE_NAME, "",
- [remoteDetails]() {return remoteDetails->autoDeploy;},
- [remoteDetails]() {
- remoteDetails->autoDeploy = !remoteDetails->autoDeploy;
- Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr);
- }
- ));
- #ifndef __MOD_DEVICES__
- } else {
- menu->addChild(createMenuItem("Connect to " REMOTE_NAME "...", "", [remoteDetails]() {
- const std::string url = remoteDetails != nullptr ? remoteDetails->url : CARDINAL_DEFAULT_REMOTE_URL;
- async_dialog_text_input("Remote:", url.c_str(), [](char* const url) {
- if (url == nullptr)
- return;
-
- DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote(url));
- std::free(url);
- });
- }));
- #endif
- }
- #endif
-
- #ifndef DISTRHO_OS_WASM
- if (isStandalone) {
- menu->addChild(new ui::MenuSeparator);
-
- menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
- APP->window->close();
- }));
- }
- #endif
- }
- };
-
-
- ////////////////////
- // Edit
- ////////////////////
-
-
- struct EditButton : MenuButton {
- void onAction(const ActionEvent& e) override {
- ui::Menu* menu = createMenu();
- menu->cornerFlags = BND_CORNER_TOP;
- menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-
- struct UndoItem : ui::MenuItem {
- void step() override {
- text = "Undo " + APP->history->getUndoName();
- disabled = !APP->history->canUndo();
- MenuItem::step();
- }
- void onAction(const ActionEvent& e) override {
- APP->history->undo();
- }
- };
- menu->addChild(createMenuItem<UndoItem>("", RACK_MOD_CTRL_NAME "+Z"));
-
- struct RedoItem : ui::MenuItem {
- void step() override {
- text = "Redo " + APP->history->getRedoName();
- disabled = !APP->history->canRedo();
- MenuItem::step();
- }
- void onAction(const ActionEvent& e) override {
- APP->history->redo();
- }
- };
- menu->addChild(createMenuItem<RedoItem>("", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+Z"));
-
- menu->addChild(createMenuItem("Clear cables", "", [=]() {
- APP->patch->disconnectDialog();
- }));
-
- menu->addChild(new ui::MenuSeparator);
-
- patchUtils::appendSelectionContextMenu(menu);
- }
- };
-
-
- ////////////////////
- // View
- ////////////////////
-
-
- struct ZoomQuantity : Quantity {
- void setValue(float value) override {
- APP->scene->rackScroll->setZoom(std::pow(2.f, value));
- }
- float getValue() override {
- return std::log2(APP->scene->rackScroll->getZoom());
- }
- float getMinValue() override {
- return -2.f;
- }
- float getMaxValue() override {
- return 2.f;
- }
- float getDefaultValue() override {
- return 0.0;
- }
- float getDisplayValue() override {
- return std::round(std::pow(2.f, getValue()) * 100);
- }
- void setDisplayValue(float displayValue) override {
- setValue(std::log2(displayValue / 100));
- }
- std::string getLabel() override {
- return "Zoom";
- }
- std::string getUnit() override {
- return "%";
- }
- };
- struct ZoomSlider : ui::Slider {
- ZoomSlider() {
- quantity = new ZoomQuantity;
- }
- ~ZoomSlider() {
- delete quantity;
- }
- };
-
-
- struct CableOpacityQuantity : Quantity {
- void setValue(float value) override {
- settings::cableOpacity = math::clamp(value, getMinValue(), getMaxValue());
- }
- float getValue() override {
- return settings::cableOpacity;
- }
- float getDefaultValue() override {
- return 0.5;
- }
- float getDisplayValue() override {
- return getValue() * 100;
- }
- void setDisplayValue(float displayValue) override {
- setValue(displayValue / 100);
- }
- std::string getLabel() override {
- return "Cable opacity";
- }
- std::string getUnit() override {
- return "%";
- }
- };
- struct CableOpacitySlider : ui::Slider {
- CableOpacitySlider() {
- quantity = new CableOpacityQuantity;
- }
- ~CableOpacitySlider() {
- delete quantity;
- }
- };
-
-
- struct CableTensionQuantity : Quantity {
- void setValue(float value) override {
- settings::cableTension = math::clamp(value, getMinValue(), getMaxValue());
- }
- float getValue() override {
- return settings::cableTension;
- }
- float getDefaultValue() override {
- return 0.75;
- }
- float getDisplayValue() override {
- return getValue() * 100;
- }
- void setDisplayValue(float displayValue) override {
- setValue(displayValue / 100);
- }
- std::string getLabel() override {
- return "Cable tension";
- }
- std::string getUnit() override {
- return "%";
- }
- };
- struct CableTensionSlider : ui::Slider {
- CableTensionSlider() {
- quantity = new CableTensionQuantity;
- }
- ~CableTensionSlider() {
- delete quantity;
- }
- };
-
-
- struct RackBrightnessQuantity : Quantity {
- void setValue(float value) override {
- settings::rackBrightness = math::clamp(value, getMinValue(), getMaxValue());
- }
- float getValue() override {
- return settings::rackBrightness;
- }
- float getDefaultValue() override {
- return 1.0;
- }
- float getDisplayValue() override {
- return getValue() * 100;
- }
- void setDisplayValue(float displayValue) override {
- setValue(displayValue / 100);
- }
- std::string getUnit() override {
- return "%";
- }
- std::string getLabel() override {
- return "Room brightness";
- }
- int getDisplayPrecision() override {
- return 3;
- }
- };
- struct RackBrightnessSlider : ui::Slider {
- RackBrightnessSlider() {
- quantity = new RackBrightnessQuantity;
- }
- ~RackBrightnessSlider() {
- delete quantity;
- }
- };
-
-
- struct HaloBrightnessQuantity : Quantity {
- void setValue(float value) override {
- settings::haloBrightness = math::clamp(value, getMinValue(), getMaxValue());
- }
- float getValue() override {
- return settings::haloBrightness;
- }
- float getDefaultValue() override {
- return 0.25;
- }
- float getDisplayValue() override {
- return getValue() * 100;
- }
- void setDisplayValue(float displayValue) override {
- setValue(displayValue / 100);
- }
- std::string getUnit() override {
- return "%";
- }
- std::string getLabel() override {
- return "Light bloom";
- }
- int getDisplayPrecision() override {
- return 3;
- }
- };
- struct HaloBrightnessSlider : ui::Slider {
- HaloBrightnessSlider() {
- quantity = new HaloBrightnessQuantity;
- }
- ~HaloBrightnessSlider() {
- delete quantity;
- }
- };
-
-
- struct KnobScrollSensitivityQuantity : Quantity {
- void setValue(float value) override {
- value = math::clamp(value, getMinValue(), getMaxValue());
- settings::knobScrollSensitivity = std::pow(2.f, value);
- }
- float getValue() override {
- return std::log2(settings::knobScrollSensitivity);
- }
- float getMinValue() override {
- return std::log2(1e-4f);
- }
- float getMaxValue() override {
- return std::log2(1e-2f);
- }
- float getDefaultValue() override {
- return std::log2(1e-3f);
- }
- float getDisplayValue() override {
- return std::pow(2.f, getValue() - getDefaultValue());
- }
- void setDisplayValue(float displayValue) override {
- setValue(std::log2(displayValue) + getDefaultValue());
- }
- std::string getLabel() override {
- return "Scroll wheel knob sensitivity";
- }
- int getDisplayPrecision() override {
- return 2;
- }
- };
- struct KnobScrollSensitivitySlider : ui::Slider {
- KnobScrollSensitivitySlider() {
- quantity = new KnobScrollSensitivityQuantity;
- }
- ~KnobScrollSensitivitySlider() {
- delete quantity;
- }
- };
-
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- static void setAllFramebufferWidgetsDirty(widget::Widget* const widget)
- {
- for (widget::Widget* child : widget->children)
- {
- if (widget::FramebufferWidget* const fbw = dynamic_cast<widget::FramebufferWidget*>(child))
- fbw->setDirty();
-
- setAllFramebufferWidgetsDirty(child);
- }
- }
- #endif
-
-
- struct ViewButton : MenuButton {
- void onAction(const ActionEvent& e) override {
- ui::Menu* menu = createMenu();
- menu->cornerFlags = BND_CORNER_TOP;
- menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-
- menu->addChild(createMenuLabel("Appearance"));
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- std::string darkModeText;
- if (settings::preferDarkPanels)
- darkModeText = CHECKMARK_STRING;
- menu->addChild(createMenuItem("Dark Mode", darkModeText, []() {
- switchDarkMode(!settings::preferDarkPanels);
- setAllFramebufferWidgetsDirty(APP->scene);
- }));
- #endif
-
- menu->addChild(createBoolPtrMenuItem("Show tooltips", "", &settings::tooltips));
-
- ZoomSlider* zoomSlider = new ZoomSlider;
- zoomSlider->box.size.x = 250.0;
- menu->addChild(zoomSlider);
-
- CableOpacitySlider* cableOpacitySlider = new CableOpacitySlider;
- cableOpacitySlider->box.size.x = 250.0;
- menu->addChild(cableOpacitySlider);
-
- CableTensionSlider* cableTensionSlider = new CableTensionSlider;
- cableTensionSlider->box.size.x = 250.0;
- menu->addChild(cableTensionSlider);
-
- RackBrightnessSlider* rackBrightnessSlider = new RackBrightnessSlider;
- rackBrightnessSlider->box.size.x = 250.0;
- menu->addChild(rackBrightnessSlider);
-
- HaloBrightnessSlider* haloBrightnessSlider = new HaloBrightnessSlider;
- haloBrightnessSlider->box.size.x = 250.0;
- menu->addChild(haloBrightnessSlider);
-
- menu->addChild(new ui::MenuSeparator);
- menu->addChild(createMenuLabel("Module dragging"));
-
- menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
-
- menu->addChild(createBoolPtrMenuItem("Auto-squeeze modules when dragging", "", &settings::squeezeModules));
-
- menu->addChild(new ui::MenuSeparator);
- menu->addChild(createMenuLabel("Parameters"));
-
- #ifdef DISTRHO_OS_WASM
- menu->addChild(createBoolPtrMenuItem("Lock cursor while dragging", "", &settings::allowCursorLock));
- #endif
-
- static const std::vector<std::string> knobModeLabels = {
- "Linear",
- "Scaled linear",
- "Absolute rotary",
- "Relative rotary",
- };
- static const std::vector<int> knobModes = {0, 2, 3};
- menu->addChild(createSubmenuItem("Knob mode", knobModeLabels[settings::knobMode], [=](ui::Menu* menu) {
- for (int knobMode : knobModes) {
- menu->addChild(createCheckMenuItem(knobModeLabels[knobMode], "",
- [=]() {return settings::knobMode == knobMode;},
- [=]() {settings::knobMode = (settings::KnobMode) knobMode;}
- ));
- }
- }));
-
- menu->addChild(createBoolPtrMenuItem("Scroll wheel knob control", "", &settings::knobScroll));
-
- KnobScrollSensitivitySlider* knobScrollSensitivitySlider = new KnobScrollSensitivitySlider;
- knobScrollSensitivitySlider->box.size.x = 250.0;
- menu->addChild(knobScrollSensitivitySlider);
-
- menu->addChild(new ui::MenuSeparator);
- menu->addChild(createMenuLabel("Window"));
-
- #ifdef DISTRHO_OS_WASM
- const bool fullscreen = APP->window->isFullScreen();
- std::string rightText = "F11";
- if (fullscreen)
- rightText += " " CHECKMARK_STRING;
- menu->addChild(createMenuItem("Fullscreen", rightText, [=]() {
- APP->window->setFullScreen(!fullscreen);
- }));
- #endif
-
- menu->addChild(createBoolPtrMenuItem("Invert zoom", "", &settings::invertZoom));
-
- static const std::vector<std::string> rateLimitLabels = {
- "None",
- "2x",
- "4x",
- };
- static const std::vector<int> rateLimits = {0, 1, 2};
- menu->addChild(createSubmenuItem("Update rate limit", rateLimitLabels[settings::rateLimit], [=](ui::Menu* menu) {
- for (int rateLimit : rateLimits) {
- menu->addChild(createCheckMenuItem(rateLimitLabels[rateLimit], "",
- [=]() {return settings::rateLimit == rateLimit;},
- [=]() {settings::rateLimit = rateLimit;}
- ));
- }
- }));
- }
- };
-
-
- ////////////////////
- // Engine
- ////////////////////
-
-
- struct EngineButton : MenuButton {
- #ifdef HAVE_LIBLO
- bool remoteServerStarted = false;
- #endif
-
- void onAction(const ActionEvent& e) override {
- ui::Menu* menu = createMenu();
- menu->cornerFlags = BND_CORNER_TOP;
- menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-
- std::string cpuMeterText = "F3";
- if (settings::cpuMeter)
- cpuMeterText += " " CHECKMARK_STRING;
- menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() {
- settings::cpuMeter ^= true;
- }));
-
- #ifdef HAVE_LIBLO
- if (isStandalone()) {
- CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
- CardinalBasePlugin* const plugin = static_cast<CardinalBasePlugin*>(context->plugin);
-
- // const bool remoteServerStarted = this->remoteServerStarted;
- const std::string remoteControlText = remoteServerStarted ? " " CHECKMARK_STRING : "";
-
- menu->addChild(createMenuItem("Enable OSC remote control", remoteControlText, [=]() {
- if (remoteServerStarted) {
- remoteServerStarted = false;
- plugin->stopRemoteServer();
- return;
- }
-
- async_dialog_text_input("OSC network port", CARDINAL_DEFAULT_REMOTE_PORT, [=](char* const port) {
- if (port == nullptr)
- return;
-
- if (plugin->startRemoteServer(port))
- remoteServerStarted = true;
-
- std::free(port);
- });
- }));
- }
- #endif
-
- if (isUsingNativeAudio()) {
- if (supportsAudioInput()) {
- const bool enabled = isAudioInputEnabled();
- std::string rightText;
- if (enabled)
- rightText = CHECKMARK_STRING;
- menu->addChild(createMenuItem("Enable Audio Input", rightText, [enabled]() {
- if (!enabled)
- requestAudioInput();
- }));
- }
-
- if (supportsMIDI()) {
- std::string rightText;
- if (isMIDIEnabled())
- rightText = CHECKMARK_STRING;
- menu->addChild(createMenuItem("Enable/Reconnect MIDI", rightText, []() {
- requestMIDI();
- }));
- }
-
- if (supportsBufferSizeChanges()) {
- static const std::vector<uint32_t> bufferSizes = {
- #ifdef DISTRHO_OS_WASM
- 256, 512, 1024, 2048, 4096, 8192, 16384
- #else
- 128, 256, 512, 1024, 2048, 4096, 8192
- #endif
- };
- const uint32_t currentBufferSize = getBufferSize();
- menu->addChild(createSubmenuItem("Buffer Size", std::to_string(currentBufferSize), [=](ui::Menu* menu) {
- for (uint32_t bufferSize : bufferSizes) {
- menu->addChild(createCheckMenuItem(std::to_string(bufferSize), "",
- [=]() {return currentBufferSize == bufferSize;},
- [=]() {requestBufferSizeChange(bufferSize);}
- ));
- }
- }));
- }
- }
- }
-
- #ifdef HAVE_LIBLO
- void step() override {
- MenuButton::step();
-
- if (remoteServerStarted) {
- CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
- CardinalBasePlugin* const plugin = static_cast<CardinalBasePlugin*>(context->plugin);
- plugin->stepRemoteServer();
- }
- }
- #endif
- };
-
-
- ////////////////////
- // Help
- ////////////////////
-
-
- struct HelpButton : MenuButton {
- void onAction(const ActionEvent& e) override {
- ui::Menu* menu = createMenu();
- menu->cornerFlags = BND_CORNER_TOP;
- menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-
- menu->addChild(createMenuItem("Rack User manual", "F1", [=]() {
- patchUtils::openBrowser("https://vcvrack.com/manual");
- }));
-
- menu->addChild(createMenuItem("Cardinal project page", "", [=]() {
- patchUtils::openBrowser("https://github.com/DISTRHO/Cardinal/");
- }));
-
- menu->addChild(new ui::MenuSeparator);
-
- #ifndef DISTRHO_OS_WASM
- menu->addChild(createMenuItem("Open user folder", "", [=]() {
- system::openDirectory(asset::user(""));
- }));
-
- menu->addChild(new ui::MenuSeparator);
- #endif
-
- menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible"));
- }
- };
-
-
- ////////////////////
- // MenuBar
- ////////////////////
-
-
- struct InfoLabel : ui::Label {
- int frameCount = 0;
- double frameDurationTotal = 0.0;
- double frameDurationAvg = NAN;
- // double uiLastTime = 0.0;
- // double uiLastThreadTime = 0.0;
- // double uiFrac = 0.0;
-
- void step() override {
- // Compute frame rate
- double frameDuration = APP->window->getLastFrameDuration();
- if (std::isfinite(frameDuration)) {
- frameDurationTotal += frameDuration;
- frameCount++;
- }
- if (frameDurationTotal >= 1.0) {
- frameDurationAvg = frameDurationTotal / frameCount;
- frameDurationTotal = 0.0;
- frameCount = 0;
- }
-
- // Compute UI thread CPU
- // double time = system::getTime();
- // double uiDuration = time - uiLastTime;
- // if (uiDuration >= 1.0) {
- // double threadTime = system::getThreadTime();
- // uiFrac = (threadTime - uiLastThreadTime) / uiDuration;
- // uiLastThreadTime = threadTime;
- // uiLastTime = time;
- // }
-
- text = "";
-
- if (box.size.x >= 400) {
- double fps = std::isfinite(frameDurationAvg) ? 1.0 / frameDurationAvg : 0.0;
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- double meterAverage = APP->engine->getMeterAverage();
- double meterMax = APP->engine->getMeterMax();
- text = string::f("%.1f fps %.1f%% avg %.1f%% max", fps, meterAverage * 100, meterMax * 100);
- #else
- text = string::f("%.1f fps", fps);
- #endif
- text += " ";
- }
-
- text += "Cardinal " + APP_EDITION + " " + CARDINAL_VERSION;
-
- Label::step();
- }
- };
-
-
- struct MenuBar : widget::OpaqueWidget {
- InfoLabel* infoLabel;
-
- MenuBar(const bool isStandalone)
- : widget::OpaqueWidget()
- {
- const float margin = 5;
- box.size.y = BND_WIDGET_HEIGHT + 2 * margin;
-
- ui::SequentialLayout* layout = new ui::SequentialLayout;
- layout->margin = math::Vec(margin, margin);
- layout->spacing = math::Vec(0, 0);
- addChild(layout);
-
- FileButton* fileButton = new FileButton(isStandalone);
- fileButton->text = "File";
- layout->addChild(fileButton);
-
- EditButton* editButton = new EditButton;
- editButton->text = "Edit";
- layout->addChild(editButton);
-
- ViewButton* viewButton = new ViewButton;
- viewButton->text = "View";
- layout->addChild(viewButton);
-
- #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- EngineButton* engineButton = new EngineButton;
- engineButton->text = "Engine";
- layout->addChild(engineButton);
- #endif
-
- HelpButton* helpButton = new HelpButton;
- helpButton->text = "Help";
- layout->addChild(helpButton);
-
- infoLabel = new InfoLabel;
- infoLabel->box.size.x = 600;
- infoLabel->alignment = ui::Label::RIGHT_ALIGNMENT;
- layout->addChild(infoLabel);
- }
-
- void draw(const DrawArgs& args) override {
- bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL);
- bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y);
-
- Widget::draw(args);
- }
-
- void step() override {
- Widget::step();
- infoLabel->box.size.x = box.size.x - infoLabel->box.pos.x - 5;
- // Setting 50% alpha prevents Label from using the default UI theme color, so set the color manually here.
- infoLabel->color = color::alpha(bndGetTheme()->regularTheme.textColor, 0.5);
- }
- };
-
-
- } // namespace menuBar
-
-
- widget::Widget* createMenuBar() {
- menuBar::MenuBar* menuBar = new menuBar::MenuBar(isStandalone());
- return menuBar;
- }
-
-
- } // namespace app
- } // namespace rack
|