diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index 33f37dc..060fa90 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -29,6 +29,7 @@ # undef DEBUG #endif +#include #include "DistrhoUI.hpp" #include "PluginContext.hpp" #include "WindowParameters.hpp" @@ -40,6 +41,9 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode) { return nullptr; } GLFWAPI int glfwGetKeyScancode(int key) { return 0; } namespace rack { +namespace app { + widget::Widget* createMenuBar(CardinalPluginContext* context, bool isStandalone); +} namespace window { void WindowInit(Window* window, DISTRHO_NAMESPACE::UI* ui); void WindowMods(Window* window, int mods); @@ -50,66 +54,6 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- -struct CardinalMenuButton : rack::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); - } -}; - -struct CardinalFileButton : CardinalMenuButton { - CardinalFileButton() - : CardinalMenuButton() - { - text = "File"; - } - - void onAction(const ActionEvent& e) override { - rack::ui::Menu* menu = rack::createMenu(); - menu->cornerFlags = BND_CORNER_TOP; - menu->box.pos = getAbsoluteOffset(rack::math::Vec(0, box.size.y)); - - menu->addChild(rack::createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() { - APP->patch->loadTemplateDialog(); - })); - - menu->addChild(rack::createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", []() { - APP->patch->loadDialog(); - })); - - /* - menu->addChild(rack::createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { - APP->patch->saveDialog(); - })); - - menu->addChild(rack::createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { - APP->patch->saveAsDialog(); - })); - */ - - menu->addChild(rack::createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { - APP->patch->revertDialog(); - }, APP->patch->path == "")); - - menu->addChild(new rack::ui::MenuSeparator); - - menu->addChild(rack::createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() { - APP->window->close(); - })); - } -}; - -// ----------------------------------------------------------------------------------------------------------- - CardinalPluginContext* getRackContextFromPlugin(void* ptr); // ----------------------------------------------------------------------------------------------------------- @@ -169,56 +113,11 @@ public: { const ScopedContext sc(this); - rack::window::WindowInit(fContext->window, this); - // Hide non-wanted menu entries - typedef rack::ui::Button rButton; - // typedef rack::ui::MenuItem rMenuItem; - typedef rack::widget::Widget rWidget; - typedef std::list::iterator rWidgetIterator; - - rWidget* const layout = fContext->scene->menuBar->children.front(); - - const auto removeMenu = [layout](const char* const name) -> void - { - for (rWidgetIterator it = layout->children.begin(); it != layout->children.end(); ++it) - { - if (rButton* const button = reinterpret_cast(*it)) - { - if (button->text == name) - { - layout->removeChild(button); - // button->parent = nullptr; - delete button; - break; - } - } - } - }; - - removeMenu("File"); - removeMenu("Library"); - - layout->addChildBottom(new CardinalFileButton()); - - /* FIXME this doesnt work - if (button->text == "Engine") - { - for (rWidgetIterator it2 = button->children.begin(); it2 != button->children.end(); ++it2) - { - if (rMenuItem* const item = reinterpret_cast(*it2)) - { - if (item->text == "Sample rate") - { - button->children.erase(it2); - delete button; - break; - } - } - } - } - */ + fContext->scene->removeChild(fContext->scene->menuBar); + fContext->scene->menuBar = rack::app::createMenuBar(fContext, getApp().isStandalone()); + fContext->scene->addChildBelow(fContext->scene->menuBar, fContext->scene->rackScroll); } WindowParametersSetCallback(fContext->window, this); diff --git a/src/Makefile b/src/Makefile index 93330ec..47901e1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,13 +20,18 @@ NAME = Cardinal # Files to build (DPF stuff) FILES_DSP = \ - CardinalPlugin.cpp + CardinalPlugin.cpp \ + override/asset.cpp \ + override/common.cpp \ + override/context.cpp \ + override/dep.cpp \ + override/library.cpp \ + override/network.cpp FILES_UI = \ CardinalUI.cpp \ - context.cpp \ - dep.cpp \ - Window.cpp + override/MenuBar.cpp \ + override/Window.cpp # -------------------------------------------------------------- # Import base definitions @@ -67,7 +72,7 @@ IGNORED_FILES += Rack/src/rtmidi.cpp FILES_DSP += $(wildcard Rack/src/*.c) FILES_DSP += $(wildcard Rack/src/*/*.c) FILES_DSP += $(filter-out $(IGNORED_FILES),$(wildcard Rack/src/*.cpp)) -FILES_DSP += $(filter-out Rack/src/window/Window.cpp, $(wildcard Rack/src/*/*.cpp)) +FILES_DSP += $(filter-out Rack/src/app/MenuBar.cpp Rack/src/window/Window.cpp, $(wildcard Rack/src/*/*.cpp)) # -------------------------------------------------------------- # Extra libraries to link against diff --git a/src/dep.cpp b/src/dep.cpp deleted file mode 100644 index 0bb8e83..0000000 --- a/src/dep.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * DISTRHO Cardinal Plugin - * Copyright (C) 2021 Filipe Coelho - * - * 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 -#include - -#ifdef NDEBUG -# undef DEBUG -#endif - -#include "OpenGL.hpp" -#include "DistrhoPluginUtils.hpp" - -// fix blendish build, missing symbol in debug mode -#ifdef DEBUG -extern "C" { -float bnd_clamp(float v, float mn, float mx) { - return (v > mx)?mx:(v < mn)?mn:v; -} -} -#endif - -// fix bogaudio build, another missing symbol -#ifdef DEBUG -namespace bogaudio { -struct FollowerBase { - static float efGainMaxDecibelsDebug; -}; -float FollowerBase::efGainMaxDecibelsDebug = 12.0f; -} -#endif - -// fopen_u8 -#ifdef ARCH_WIN -#include -FILE* fopen_u8(const char* filename, const char* mode) -{ - return _wfopen(rack::string::UTF8toUTF16(filename).c_str(), rack::string::UTF8toUTF16(mode).c_str()); -} -#endif - -// Compile those nice implementation-in-header little libraries -#define NANOSVG_IMPLEMENTATION -#define NANOSVG_ALL_COLOR_KEYWORDS -#include - -// Define the global names to indicate this is Cardinal and not VCVRack -namespace rack { -const std::string APP_NAME = "Cardinal"; -const std::string APP_EDITION = getPluginFormatName(); -const std::string APP_EDITION_NAME = "Audio Plugin"; -const std::string APP_VERSION_MAJOR = "2"; -const std::string APP_VERSION = "2.0"; -#if defined(ARCH_WIN) -const std::string APP_ARCH = "win"; -#elif defined(ARCH_MAC) -const std::string APP_ARCH = "mac"; -#else -const std::string APP_ARCH = "lin"; -#endif -const std::string API_URL = ""; -Exception::Exception(const char* format, ...) -{ - va_list args; - va_start(args, format); - msg = string::fV(format, args); - va_end(args); -} -} - -// Custom assets location -#include -#include -#include -#include -namespace rack { -namespace asset { - -std::string userDir; // ignored -std::string systemDir; // points to plugin resources dir (or installed/local Rack dir) -std::string bundlePath; // points to plugin manifests dir (or empty) - -// get rid of "res/" prefix -static inline std::string& trim(std::string& s) -{ - if (std::strncmp(s.c_str(), "res" DISTRHO_OS_SEP_STR, 4) == 0) - s = s.substr(4, s.size()-4); -#if DISTRHO_OS_SEP != '/' - if (std::strncmp(s.c_str(), "res/", 4) == 0) - s = s.substr(4, s.size()-4); -#endif - return s; -} - -// ignored, returns the same as `system` -std::string user(std::string filename) { - return system(filename); -} - -// get system resource, trimming "res/" prefix if we are loaded as a plugin bundle -std::string system(std::string filename) { - return system::join(systemDir, bundlePath.empty() ? filename : trim(filename)); -} - -// get plugin resource, also trims "res/" as needed -std::string plugin(plugin::Plugin* plugin, std::string filename) { - DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr, {}); - return system::join(plugin->path, bundlePath.empty() ? filename : trim(filename)); -} - -// path to plugin manifest -std::string pluginManifest(const std::string& dirname) { - if (bundlePath.empty()) - { - if (dirname == "Core") - return system::join(systemDir, "Core.json"); - return system::join(systemDir, "..", "..", "plugins", dirname, "plugin.json"); - } - return system::join(bundlePath, dirname + ".json"); -} - -// path to plugin files -std::string pluginPath(const std::string& dirname) { - if (bundlePath.empty()) - { - if (dirname == "Core") - return systemDir; - return system::join(systemDir, "..", "..", "plugins", dirname); - } - return system::join(systemDir, dirname); -} - -} -} - -// Define the stuff needed for VCVRack but unused for Cardinal -#include -#include -namespace rack { -namespace library { - std::string appChangelogUrl; - std::string appDownloadUrl; - std::string appVersion; - std::string loginStatus; - std::map updateInfos; - std::string updateStatus; - std::string updateSlug; - float updateProgress = 0.f; - bool isSyncing = false; - bool restartRequested = false; - void checkAppUpdate() {} - void checkUpdates() {} - bool hasUpdates() { return false; } - bool isAppUpdateAvailable() { return false; } - bool isLoggedIn() { return false; } - void logIn(const std::string&, const std::string&) {} - void logOut() {} - void syncUpdate(const std::string&) {} - void syncUpdates() {} -} -namespace network { - std::string encodeUrl(const std::string&) { return {}; } - json_t* requestJson(Method, const std::string&, json_t*, const CookieMap&) { return nullptr; } - bool requestDownload(const std::string&, const std::string&, float*, const CookieMap&) { return false; } -} -} diff --git a/src/override/MenuBar.cpp b/src/override/MenuBar.cpp new file mode 100644 index 0000000..9a88827 --- /dev/null +++ b/src/override/MenuBar.cpp @@ -0,0 +1,646 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 MenuBar.cpp + * Copyright (C) 2016-2021 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NDEBUG +# undef DEBUG +#endif + +#include "../PluginContext.hpp" + + +namespace rack { +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; + + FileButton(const bool standalone) + : MenuButton(), isStandalone(standalone) {} + + 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("New", RACK_MOD_CTRL_NAME "+N", []() { + APP->patch->loadTemplateDialog(); + })); + + menu->addChild(createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", []() { + APP->patch->loadDialog(); + })); + + /* + 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("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { + APP->patch->revertDialog(); + }, APP->patch->path == "")); + + if (isStandalone) { + menu->addChild(new ui::MenuSeparator); + + menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() { + APP->window->close(); + })); + }; + } +}; + + +//////////////////// +// 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("", 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("", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+Z")); + + menu->addChild(createMenuItem("Clear cables", "", [=]() { + APP->patch->disconnectDialog(); + })); + + menu->addChild(new ui::MenuSeparator); + + APP->scene->rack->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.5; + } + std::string getLabel() override { + return "Cable tension"; + } + int getDisplayPrecision() override { + return 2; + } +}; +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; + } +}; + + +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(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(createBoolPtrMenuItem("Hide cursor while dragging", "", &settings::allowCursorLock)); + + static const std::vector knobModeLabels = { + "Linear", + "Scaled linear", + "Absolute rotary", + "Relative rotary", + }; + static const std::vector 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(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules)); + } +}; + + +//////////////////// +// Engine +//////////////////// + + +struct EngineButton : 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)); + + std::string cpuMeterText = "F3"; + if (settings::cpuMeter) + cpuMeterText += " " CHECKMARK_STRING; + menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() { + settings::cpuMeter ^= true; + })); + + menu->addChild(createSubmenuItem("Threads | DO NOT USE", string::f("%d", settings::threadCount), [=](ui::Menu* menu) { + // BUG This assumes SMT is enabled. + int cores = system::getLogicalCoreCount() / 2; + + for (int i = 1; i <= 2 * cores; i++) { + std::string rightText; + if (i == cores) + rightText += "(most modules)"; + else if (i == 1) + rightText += "(lowest CPU usage)"; + menu->addChild(createCheckMenuItem(string::f("%d", i), rightText, + [=]() {return settings::threadCount == i;}, + [=]() {settings::threadCount = i;} + )); + } + })); + } +}; + + +//////////////////// +// 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("Tips", "", [=]() { + APP->scene->addChild(tipWindowCreate()); + })); + + menu->addChild(createMenuItem("VCV User manual", "F1", [=]() { + system::openBrowser("https://vcvrack.com/manual/"); + })); + + menu->addChild(createMenuItem("Cardinal Project page", "", [=]() { + system::openBrowser("https://github.com/DISTRHO/Cardinal/"); + })); + + menu->addChild(new ui::MenuSeparator); + + menu->addChild(createMenuLabel(APP_EDITION + " " + APP_EDITION_NAME)); + + menu->addChild(createMenuLabel("VCVRack " + APP_VERSION + " Compatible")); + } +}; + + +//////////////////// +// MenuBar +//////////////////// + + +struct MeterLabel : ui::Label { + int frameIndex = 0; + double frameDurationTotal = 0.0; + double frameDurationAvg = 0.0; + double uiLastTime = 0.0; + double uiLastThreadTime = 0.0; + double uiFrac = 0.0; + + void step() override { + // Compute frame rate + double frameDuration = APP->window->getLastFrameDuration(); + frameDurationTotal += frameDuration; + frameIndex++; + if (frameDurationTotal >= 1.0) { + frameDurationAvg = frameDurationTotal / frameIndex; + frameDurationTotal = 0.0; + frameIndex = 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; + // } + + double meterAverage = APP->engine->getMeterAverage(); + double meterMax = APP->engine->getMeterMax(); + text = string::f("%.1f fps %.1f%% avg %.1f%% max", 1.0 / frameDurationAvg, meterAverage * 100, meterMax * 100); + Label::step(); + } +}; + + +struct MenuBar : widget::OpaqueWidget { + CardinalPluginContext* const context; + MeterLabel* meterLabel; + + MenuBar(CardinalPluginContext* const ctx, const bool isStandalone) + : context(ctx) + { + 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); + + EngineButton* engineButton = new EngineButton; + engineButton->text = "Engine"; + layout->addChild(engineButton); + + HelpButton* helpButton = new HelpButton; + helpButton->text = "Help"; + layout->addChild(helpButton); + + // ui::Label* titleLabel = new ui::Label; + // titleLabel->color.a = 0.5; + // layout->addChild(titleLabel); + + meterLabel = new MeterLabel; + meterLabel->box.pos.y = margin; + meterLabel->box.size.x = 300; + meterLabel->alignment = ui::Label::RIGHT_ALIGNMENT; + meterLabel->color.a = 0.5; + addChild(meterLabel); + } + + 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 { + meterLabel->box.pos.x = box.size.x - meterLabel->box.size.x - 5; + Widget::step(); + } +}; + + +} // namespace menuBar + + +widget::Widget* createMenuBar() { + return new widget::Widget; +} + +widget::Widget* createMenuBar(CardinalPluginContext* const context, const bool isStandalone) { + menuBar::MenuBar* menuBar = new menuBar::MenuBar(context, isStandalone); + return menuBar; +} + + +} // namespace app +} // namespace rack diff --git a/src/Window.cpp b/src/override/Window.cpp similarity index 91% rename from src/Window.cpp rename to src/override/Window.cpp index 086544a..7547a20 100644 --- a/src/Window.cpp +++ b/src/override/Window.cpp @@ -1,3 +1,30 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 Window.cpp + * Copyright (C) 2016-2021 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 #include #include @@ -19,7 +46,7 @@ #endif #include "DistrhoUI.hpp" -#include "WindowParameters.hpp" +#include "../WindowParameters.hpp" namespace rack { namespace window { diff --git a/src/override/asset.cpp b/src/override/asset.cpp new file mode 100644 index 0000000..d9a9b13 --- /dev/null +++ b/src/override/asset.cpp @@ -0,0 +1,84 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 +#include +#include + +#include + +#include "DistrhoUtils.hpp" + +namespace rack { +namespace asset { + +std::string userDir; // ignored +std::string systemDir; // points to plugin resources dir (or installed/local Rack dir) +std::string bundlePath; // points to plugin manifests dir (or empty) + +// get rid of "res/" prefix +static inline std::string& trim(std::string& s) +{ + if (std::strncmp(s.c_str(), "res" DISTRHO_OS_SEP_STR, 4) == 0) + s = s.substr(4, s.size()-4); +#if DISTRHO_OS_SEP != '/' + if (std::strncmp(s.c_str(), "res/", 4) == 0) + s = s.substr(4, s.size()-4); +#endif + return s; +} + +// ignored, returns the same as `system` +std::string user(std::string filename) { + return system(filename); +} + +// get system resource, trimming "res/" prefix if we are loaded as a plugin bundle +std::string system(std::string filename) { + return system::join(systemDir, bundlePath.empty() ? filename : trim(filename)); +} + +// get plugin resource, also trims "res/" as needed +std::string plugin(plugin::Plugin* plugin, std::string filename) { + DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr, {}); + return system::join(plugin->path, bundlePath.empty() ? filename : trim(filename)); +} + +// path to plugin manifest +std::string pluginManifest(const std::string& dirname) { + if (bundlePath.empty()) + { + if (dirname == "Core") + return system::join(systemDir, "Core.json"); + return system::join(systemDir, "..", "..", "plugins", dirname, "plugin.json"); + } + return system::join(bundlePath, dirname + ".json"); +} + +// path to plugin files +std::string pluginPath(const std::string& dirname) { + if (bundlePath.empty()) + { + if (dirname == "Core") + return systemDir; + return system::join(systemDir, "..", "..", "plugins", dirname); + } + return system::join(systemDir, dirname); +} + +} +} diff --git a/src/override/common.cpp b/src/override/common.cpp new file mode 100644 index 0000000..fe80771 --- /dev/null +++ b/src/override/common.cpp @@ -0,0 +1,59 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 +#include + +#include "DistrhoPluginUtils.hpp" + +// fopen_u8 +#ifdef ARCH_WIN +#include + +FILE* fopen_u8(const char* filename, const char* mode) +{ + return _wfopen(rack::string::UTF8toUTF16(filename).c_str(), rack::string::UTF8toUTF16(mode).c_str()); +} +#endif + +// Define the global names to indicate this is Cardinal and not VCVRack +namespace rack { + +const std::string APP_NAME = "Cardinal"; +const std::string APP_EDITION = getPluginFormatName(); +const std::string APP_EDITION_NAME = "Audio Plugin"; +const std::string APP_VERSION_MAJOR = "2"; +const std::string APP_VERSION = "2.0"; +#if defined(ARCH_WIN) +const std::string APP_ARCH = "win"; +#elif defined(ARCH_MAC) +const std::string APP_ARCH = "mac"; +#else +const std::string APP_ARCH = "lin"; +#endif +const std::string API_URL = ""; + + +Exception::Exception(const char* format, ...) +{ + va_list args; + va_start(args, format); + msg = string::fV(format, args); + va_end(args); +} + +} diff --git a/src/context.cpp b/src/override/context.cpp similarity index 100% rename from src/context.cpp rename to src/override/context.cpp diff --git a/src/override/dep.cpp b/src/override/dep.cpp new file mode 100644 index 0000000..1e389ae --- /dev/null +++ b/src/override/dep.cpp @@ -0,0 +1,42 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 "OpenGL.hpp" + +// fix blendish build, missing symbol in debug mode +#ifdef DEBUG +extern "C" { +float bnd_clamp(float v, float mn, float mx) { + return (v > mx)?mx:(v < mn)?mn:v; +} +} +#endif + +// fix bogaudio build, another missing symbol +#ifdef DEBUG +namespace bogaudio { +struct FollowerBase { + static float efGainMaxDecibelsDebug; +}; +float FollowerBase::efGainMaxDecibelsDebug = 12.0f; +} +#endif + +// Compile those nice implementation-in-header little libraries +#define NANOSVG_IMPLEMENTATION +#define NANOSVG_ALL_COLOR_KEYWORDS +#include diff --git a/src/override/library.cpp b/src/override/library.cpp new file mode 100644 index 0000000..e8f4475 --- /dev/null +++ b/src/override/library.cpp @@ -0,0 +1,46 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 + +// Define the stuff needed for VCVRack but unused for Cardinal +namespace rack { +namespace library { + +std::string appChangelogUrl; +std::string appDownloadUrl; +std::string appVersion; +std::string loginStatus; +std::map updateInfos; +std::string updateStatus; +std::string updateSlug; +float updateProgress = 0.f; +bool isSyncing = false; +bool restartRequested = false; + +void checkAppUpdate() {} +void checkUpdates() {} +bool hasUpdates() { return false; } +bool isAppUpdateAvailable() { return false; } +bool isLoggedIn() { return false; } +void logIn(const std::string&, const std::string&) {} +void logOut() {} +void syncUpdate(const std::string&) {} +void syncUpdates() {} + +} +} diff --git a/src/override/network.cpp b/src/override/network.cpp new file mode 100644 index 0000000..fff4844 --- /dev/null +++ b/src/override/network.cpp @@ -0,0 +1,29 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * 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 + +// Define the stuff needed for VCVRack but unused for Cardinal +namespace rack { +namespace network { + +std::string encodeUrl(const std::string&) { return {}; } +json_t* requestJson(Method, const std::string&, json_t*, const CookieMap&) { return nullptr; } +bool requestDownload(const std::string&, const std::string&, float*, const CookieMap&) { return false; } + +} +}