You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

895 lines
25KB

  1. --- ../Rack/src/app/MenuBar.cpp 2022-09-21 19:49:12.198540676 +0100
  2. +++ MenuBar.cpp 2022-12-30 14:50:06.801891005 +0000
  3. @@ -1,8 +1,33 @@
  4. +/*
  5. + * DISTRHO Cardinal Plugin
  6. + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  7. + *
  8. + * This program is free software; you can redistribute it and/or
  9. + * modify it under the terms of the GNU General Public License as
  10. + * published by the Free Software Foundation; either version 3 of
  11. + * the License, or any later version.
  12. + *
  13. + * This program is distributed in the hope that it will be useful,
  14. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. + * GNU General Public License for more details.
  17. + *
  18. + * For a full copy of the GNU General Public License see the LICENSE file.
  19. + */
  20. +
  21. +/**
  22. + * This file is an edited version of VCVRack's app/MenuBar.cpp
  23. + * Copyright (C) 2016-2021 VCV.
  24. + *
  25. + * This program is free software: you can redistribute it and/or
  26. + * modify it under the terms of the GNU General Public License as
  27. + * published by the Free Software Foundation; either version 3 of
  28. + * the License, or (at your option) any later version.
  29. + */
  30. +
  31. #include <thread>
  32. #include <utility>
  33. -#include <osdialog.h>
  34. -
  35. #include <app/MenuBar.hpp>
  36. #include <app/TipWindow.hpp>
  37. #include <widget/OpaqueWidget.hpp>
  38. @@ -15,6 +40,7 @@
  39. #include <ui/ProgressBar.hpp>
  40. #include <ui/Label.hpp>
  41. #include <engine/Engine.hpp>
  42. +#include <widget/FramebufferWidget.hpp>
  43. #include <window/Window.hpp>
  44. #include <asset.hpp>
  45. #include <context.hpp>
  46. @@ -25,8 +51,28 @@
  47. #include <patch.hpp>
  48. #include <library.hpp>
  49. +#include "../CardinalCommon.hpp"
  50. +#include "../CardinalRemote.hpp"
  51. +#include "DistrhoPlugin.hpp"
  52. +#include "DistrhoStandaloneUtils.hpp"
  53. +
  54. +#ifdef HAVE_LIBLO
  55. +# include <lo/lo.h>
  56. +#endif
  57. +
  58. +void switchDarkMode(bool darkMode);
  59. namespace rack {
  60. +namespace asset {
  61. +std::string patchesPath();
  62. +}
  63. +namespace engine {
  64. +void Engine_setRemoteDetails(Engine*, remoteUtils::RemoteDetails*);
  65. +}
  66. +namespace plugin {
  67. +void updateStaticPluginsDarkMode();
  68. +}
  69. +
  70. namespace app {
  71. namespace menuBar {
  72. @@ -48,79 +94,160 @@
  73. };
  74. -struct NotificationIcon : widget::Widget {
  75. - void draw(const DrawArgs& args) override {
  76. - nvgBeginPath(args.vg);
  77. - float radius = 4;
  78. - nvgCircle(args.vg, radius, radius, radius);
  79. - nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0));
  80. - nvgFill(args.vg);
  81. - nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0));
  82. - nvgStroke(args.vg);
  83. - }
  84. -};
  85. -
  86. -
  87. ////////////////////
  88. // File
  89. ////////////////////
  90. struct FileButton : MenuButton {
  91. + const bool isStandalone;
  92. +#if ! CARDINAL_VARIANT_MINI
  93. + std::vector<std::string> demoPatches;
  94. +#endif
  95. +
  96. + FileButton(const bool standalone)
  97. + : MenuButton(), isStandalone(standalone)
  98. + {
  99. +#if ! CARDINAL_VARIANT_MINI
  100. + const std::string patchesDir = asset::patchesPath() + DISTRHO_OS_SEP_STR "examples";
  101. +
  102. + if (system::isDirectory(patchesDir))
  103. + {
  104. + demoPatches = system::getEntries(patchesDir);
  105. + std::sort(demoPatches.begin(), demoPatches.end(), [](const std::string& a, const std::string& b){
  106. + return string::lowercase(a) < string::lowercase(b);
  107. + });
  108. + }
  109. +#endif
  110. + }
  111. +
  112. void onAction(const ActionEvent& e) override {
  113. ui::Menu* menu = createMenu();
  114. menu->cornerFlags = BND_CORNER_TOP;
  115. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  116. - menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() {
  117. - APP->patch->loadTemplateDialog();
  118. +#ifndef DISTRHO_OS_WASM
  119. + const char* const NewShortcut = RACK_MOD_CTRL_NAME "+N";
  120. +#else
  121. + const char* const NewShortcut = "";
  122. +#endif
  123. + menu->addChild(createMenuItem("New", NewShortcut, []() {
  124. + patchUtils::loadTemplateDialog();
  125. }));
  126. - menu->addChild(createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", []() {
  127. - APP->patch->loadDialog();
  128. +#if ! CARDINAL_VARIANT_MINI
  129. +#ifndef DISTRHO_OS_WASM
  130. + menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() {
  131. + patchUtils::loadDialog();
  132. }));
  133. - menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) {
  134. - for (const std::string& path : settings::recentPatchPaths) {
  135. - std::string name = system::getStem(path);
  136. - menu->addChild(createMenuItem(name, "", [=]() {
  137. - APP->patch->loadPathDialog(path);
  138. - }));
  139. - }
  140. - }, settings::recentPatchPaths.empty()));
  141. -
  142. menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() {
  143. - APP->patch->saveDialog();
  144. + // NOTE: will do nothing if path is empty, intentionally
  145. + patchUtils::saveDialog(APP->patch->path);
  146. + }, APP->patch->path.empty()));
  147. +
  148. + menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() {
  149. + patchUtils::saveAsDialog();
  150. + }));
  151. +#else
  152. + menu->addChild(createMenuItem("Import patch...", RACK_MOD_CTRL_NAME "+O", []() {
  153. + patchUtils::loadDialog();
  154. }));
  155. - menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() {
  156. - APP->patch->saveAsDialog();
  157. + menu->addChild(createMenuItem("Import selection...", "", [=]() {
  158. + patchUtils::loadSelectionDialog();
  159. + }, false, true));
  160. +
  161. + menu->addChild(createMenuItem("Save and download compressed", RACK_MOD_CTRL_NAME "+Shift+S", []() {
  162. + patchUtils::saveAsDialog();
  163. }));
  164. - menu->addChild(createMenuItem("Save a copy", "", []() {
  165. - APP->patch->saveAsDialog(false);
  166. + menu->addChild(createMenuItem("Save and download uncompressed", "", []() {
  167. + patchUtils::saveAsDialogUncompressed();
  168. }));
  169. +#endif
  170. +#endif
  171. menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() {
  172. - APP->patch->revertDialog();
  173. - }, APP->patch->path == ""));
  174. + patchUtils::revertDialog();
  175. + }, APP->patch->path.empty()));
  176. - menu->addChild(createMenuItem("Overwrite template", "", []() {
  177. - APP->patch->saveTemplateDialog();
  178. - }));
  179. +#if defined(HAVE_LIBLO) && ! CARDINAL_VARIANT_MINI
  180. + menu->addChild(new ui::MenuSeparator);
  181. +
  182. + remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote();
  183. +
  184. + if (remoteDetails != nullptr && remoteDetails->connected) {
  185. + menu->addChild(createMenuItem("Deploy to MOD", "F7", [remoteDetails]() {
  186. + remoteUtils::sendFullPatchToRemote(remoteDetails);
  187. + }));
  188. + menu->addChild(createCheckMenuItem("Auto deploy to MOD", "",
  189. + [remoteDetails]() {return remoteDetails->autoDeploy;},
  190. + [remoteDetails]() {
  191. + remoteDetails->autoDeploy = !remoteDetails->autoDeploy;
  192. + Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr);
  193. + }
  194. + ));
  195. + } else {
  196. + menu->addChild(createMenuItem("Connect to MOD", "", []() {
  197. + DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote());
  198. + }));
  199. + }
  200. +#endif
  201. +
  202. +#if ! CARDINAL_VARIANT_MINI
  203. +#ifndef DISTRHO_OS_WASM
  204. menu->addChild(new ui::MenuSeparator);
  205. // Load selection
  206. - menu->addChild(createMenuItem("Import selection", "", [=]() {
  207. - APP->scene->rack->loadSelectionDialog();
  208. + menu->addChild(createMenuItem("Import selection...", "", [=]() {
  209. + patchUtils::loadSelectionDialog();
  210. }, false, true));
  211. - menu->addChild(new ui::MenuSeparator);
  212. -
  213. - menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
  214. - APP->window->close();
  215. + menu->addChild(createMenuItem("Export uncompressed json...", "", []() {
  216. + patchUtils::saveAsDialogUncompressed();
  217. }));
  218. +#endif
  219. +#endif
  220. +
  221. +#if ! CARDINAL_VARIANT_MINI
  222. + if (!demoPatches.empty())
  223. + {
  224. + menu->addChild(new ui::MenuSeparator);
  225. +
  226. + menu->addChild(createSubmenuItem("Open Demo / Example project", "", [=](ui::Menu* const menu) {
  227. + for (std::string path : demoPatches) {
  228. + std::string label = system::getStem(path);
  229. +
  230. + for (size_t i=0, len=label.size(); i<len; ++i) {
  231. + if (label[i] == '_')
  232. + label[i] = ' ';
  233. + }
  234. +
  235. + menu->addChild(createMenuItem(label, "", [path]() {
  236. + patchUtils::loadPathDialog(path, true);
  237. + }));
  238. + }
  239. +
  240. + menu->addChild(new ui::MenuSeparator);
  241. +
  242. + menu->addChild(createMenuItem("Open PatchStorage.com for more patches", "", []() {
  243. + patchUtils::openBrowser("https://patchstorage.com/platform/cardinal/");
  244. + }));
  245. + }));
  246. + }
  247. +#endif
  248. +
  249. +#ifndef DISTRHO_OS_WASM
  250. + if (isStandalone) {
  251. + menu->addChild(new ui::MenuSeparator);
  252. +
  253. + menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
  254. + APP->window->close();
  255. + }));
  256. + }
  257. +#endif
  258. }
  259. };
  260. @@ -166,7 +293,7 @@
  261. menu->addChild(new ui::MenuSeparator);
  262. - APP->scene->rack->appendSelectionContextMenu(menu);
  263. + patchUtils::appendSelectionContextMenu(menu);
  264. }
  265. };
  266. @@ -256,7 +383,7 @@
  267. return settings::cableTension;
  268. }
  269. float getDefaultValue() override {
  270. - return 0.5;
  271. + return 0.75;
  272. }
  273. float getDisplayValue() override {
  274. return getValue() * 100;
  275. @@ -393,36 +520,37 @@
  276. };
  277. +static void setAllFramebufferWidgetsDirty(widget::Widget* const widget)
  278. +{
  279. + for (widget::Widget* child : widget->children)
  280. + {
  281. + if (widget::FramebufferWidget* const fbw = dynamic_cast<widget::FramebufferWidget*>(child))
  282. + {
  283. + fbw->setDirty();
  284. + break;
  285. + }
  286. + setAllFramebufferWidgetsDirty(child);
  287. + }
  288. +}
  289. +
  290. +
  291. struct ViewButton : MenuButton {
  292. void onAction(const ActionEvent& e) override {
  293. ui::Menu* menu = createMenu();
  294. menu->cornerFlags = BND_CORNER_TOP;
  295. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  296. - menu->addChild(createMenuLabel("Window"));
  297. -
  298. - bool fullscreen = APP->window->isFullScreen();
  299. - std::string fullscreenText = "F11";
  300. - if (fullscreen)
  301. - fullscreenText += " " CHECKMARK_STRING;
  302. - menu->addChild(createMenuItem("Fullscreen", fullscreenText, [=]() {
  303. - APP->window->setFullScreen(!fullscreen);
  304. - }));
  305. + menu->addChild(createMenuLabel("Appearance"));
  306. - double frameRate = APP->window->getMonitorRefreshRate() / settings::frameSwapInterval;
  307. - menu->addChild(createSubmenuItem("Frame rate", string::f("%.0f Hz", frameRate), [=](ui::Menu* menu) {
  308. - for (int i = 1; i <= 6; i++) {
  309. - double frameRate = APP->window->getMonitorRefreshRate() / i;
  310. - menu->addChild(createCheckMenuItem(string::f("%.0f Hz", frameRate), "",
  311. - [=]() {return settings::frameSwapInterval == i;},
  312. - [=]() {settings::frameSwapInterval = i;}
  313. - ));
  314. - }
  315. + std::string darkModeText;
  316. + if (settings::darkMode)
  317. + darkModeText = CHECKMARK_STRING;
  318. + menu->addChild(createMenuItem("Dark Mode", darkModeText, []() {
  319. + switchDarkMode(!settings::darkMode);
  320. + plugin::updateStaticPluginsDarkMode();
  321. + setAllFramebufferWidgetsDirty(APP->scene);
  322. }));
  323. - menu->addChild(new ui::MenuSeparator);
  324. - menu->addChild(createMenuLabel("Appearance"));
  325. -
  326. menu->addChild(createBoolPtrMenuItem("Show tooltips", "", &settings::tooltips));
  327. ZoomSlider* zoomSlider = new ZoomSlider;
  328. @@ -446,9 +574,18 @@
  329. menu->addChild(haloBrightnessSlider);
  330. menu->addChild(new ui::MenuSeparator);
  331. + menu->addChild(createMenuLabel("Module dragging"));
  332. +
  333. + menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
  334. +
  335. + menu->addChild(createBoolPtrMenuItem("Auto-squeeze modules when dragging", "", &settings::squeezeModules));
  336. +
  337. + menu->addChild(new ui::MenuSeparator);
  338. menu->addChild(createMenuLabel("Parameters"));
  339. +#ifdef DISTRHO_OS_WASM
  340. menu->addChild(createBoolPtrMenuItem("Lock cursor while dragging", "", &settings::allowCursorLock));
  341. +#endif
  342. static const std::vector<std::string> knobModeLabels = {
  343. "Linear",
  344. @@ -473,11 +610,34 @@
  345. menu->addChild(knobScrollSensitivitySlider);
  346. menu->addChild(new ui::MenuSeparator);
  347. - menu->addChild(createMenuLabel("Module dragging"));
  348. + menu->addChild(createMenuLabel("Window"));
  349. +
  350. +#ifdef DISTRHO_OS_WASM
  351. + const bool fullscreen = APP->window->isFullScreen();
  352. + std::string rightText = "F11";
  353. + if (fullscreen)
  354. + rightText += " " CHECKMARK_STRING;
  355. + menu->addChild(createMenuItem("Fullscreen", rightText, [=]() {
  356. + APP->window->setFullScreen(!fullscreen);
  357. + }));
  358. +#endif
  359. - menu->addChild(createBoolPtrMenuItem("Lock positions", "", &settings::lockModules));
  360. + menu->addChild(createBoolPtrMenuItem("Invert zoom", "", &settings::invertZoom));
  361. - menu->addChild(createBoolPtrMenuItem("Auto-squeeze algorithm (experimental)", "", &settings::squeezeModules));
  362. + static const std::vector<std::string> rateLimitLabels = {
  363. + "None",
  364. + "2x",
  365. + "4x",
  366. + };
  367. + static const std::vector<int> rateLimits = {0, 1, 2};
  368. + menu->addChild(createSubmenuItem("Update rate limit", rateLimitLabels[settings::rateLimit], [=](ui::Menu* menu) {
  369. + for (int rateLimit : rateLimits) {
  370. + menu->addChild(createCheckMenuItem(rateLimitLabels[rateLimit], "",
  371. + [=]() {return settings::rateLimit == rateLimit;},
  372. + [=]() {settings::rateLimit = rateLimit;}
  373. + ));
  374. + }
  375. + }));
  376. }
  377. };
  378. @@ -487,47 +647,6 @@
  379. ////////////////////
  380. -struct SampleRateItem : ui::MenuItem {
  381. - ui::Menu* createChildMenu() override {
  382. - ui::Menu* menu = new ui::Menu;
  383. -
  384. - // Auto sample rate
  385. - std::string rightText;
  386. - if (settings::sampleRate == 0) {
  387. - float sampleRate = APP->engine->getSampleRate();
  388. - rightText += string::f("(%g kHz) ", sampleRate / 1000.f);
  389. - }
  390. - menu->addChild(createCheckMenuItem("Auto", rightText,
  391. - [=]() {return settings::sampleRate == 0;},
  392. - [=]() {settings::sampleRate = 0;}
  393. - ));
  394. -
  395. - // Power-of-2 oversample times 44.1kHz or 48kHz
  396. - for (int i = -2; i <= 4; i++) {
  397. - for (int j = 0; j < 2; j++) {
  398. - float oversample = std::pow(2.f, i);
  399. - float sampleRate = (j == 0) ? 44100.f : 48000.f;
  400. - sampleRate *= oversample;
  401. -
  402. - std::string text = string::f("%g kHz", sampleRate / 1000.f);
  403. - std::string rightText;
  404. - if (oversample > 1.f) {
  405. - rightText += string::f("(%.0fx)", oversample);
  406. - }
  407. - else if (oversample < 1.f) {
  408. - rightText += string::f("(1/%.0fx)", 1.f / oversample);
  409. - }
  410. - menu->addChild(createCheckMenuItem(text, rightText,
  411. - [=]() {return settings::sampleRate == sampleRate;},
  412. - [=]() {settings::sampleRate = sampleRate;}
  413. - ));
  414. - }
  415. - }
  416. - return menu;
  417. - }
  418. -};
  419. -
  420. -
  421. struct EngineButton : MenuButton {
  422. void onAction(const ActionEvent& e) override {
  423. ui::Menu* menu = createMenu();
  424. @@ -541,268 +660,46 @@
  425. settings::cpuMeter ^= true;
  426. }));
  427. - menu->addChild(createMenuItem<SampleRateItem>("Sample rate", RIGHT_ARROW));
  428. -
  429. - menu->addChild(createSubmenuItem("Threads", string::f("%d", settings::threadCount), [=](ui::Menu* menu) {
  430. - // BUG This assumes SMT is enabled.
  431. - int cores = system::getLogicalCoreCount() / 2;
  432. -
  433. - for (int i = 1; i <= 2 * cores; i++) {
  434. + if (isUsingNativeAudio()) {
  435. + if (supportsAudioInput()) {
  436. + const bool enabled = isAudioInputEnabled();
  437. std::string rightText;
  438. - if (i == cores)
  439. - rightText += "(most modules)";
  440. - else if (i == 1)
  441. - rightText += "(lowest CPU usage)";
  442. - menu->addChild(createCheckMenuItem(string::f("%d", i), rightText,
  443. - [=]() {return settings::threadCount == i;},
  444. - [=]() {settings::threadCount = i;}
  445. - ));
  446. - }
  447. - }));
  448. - }
  449. -};
  450. -
  451. -
  452. -////////////////////
  453. -// Plugins
  454. -////////////////////
  455. -
  456. -
  457. -struct AccountPasswordField : ui::PasswordField {
  458. - ui::MenuItem* logInItem;
  459. - void onAction(const ActionEvent& e) override {
  460. - logInItem->doAction();
  461. - }
  462. -};
  463. -
  464. -
  465. -struct LogInItem : ui::MenuItem {
  466. - ui::TextField* emailField;
  467. - ui::TextField* passwordField;
  468. -
  469. - void onAction(const ActionEvent& e) override {
  470. - std::string email = emailField->text;
  471. - std::string password = passwordField->text;
  472. - std::thread t([=] {
  473. - library::logIn(email, password);
  474. - library::checkUpdates();
  475. - });
  476. - t.detach();
  477. - e.unconsume();
  478. - }
  479. -
  480. - void step() override {
  481. - text = "Log in";
  482. - rightText = library::loginStatus;
  483. - MenuItem::step();
  484. - }
  485. -};
  486. -
  487. -
  488. -struct SyncUpdatesItem : ui::MenuItem {
  489. - void step() override {
  490. - if (library::updateStatus != "") {
  491. - text = library::updateStatus;
  492. - }
  493. - else if (library::isSyncing) {
  494. - text = "Updating...";
  495. - }
  496. - else if (!library::hasUpdates()) {
  497. - text = "Up-to-date";
  498. - }
  499. - else {
  500. - text = "Update all";
  501. - }
  502. -
  503. - disabled = library::isSyncing || !library::hasUpdates();
  504. - MenuItem::step();
  505. - }
  506. -
  507. - void onAction(const ActionEvent& e) override {
  508. - std::thread t([=] {
  509. - library::syncUpdates();
  510. - });
  511. - t.detach();
  512. - e.unconsume();
  513. - }
  514. -};
  515. -
  516. -
  517. -struct SyncUpdateItem : ui::MenuItem {
  518. - std::string slug;
  519. -
  520. - void setUpdate(const std::string& slug) {
  521. - this->slug = slug;
  522. -
  523. - auto it = library::updateInfos.find(slug);
  524. - if (it == library::updateInfos.end())
  525. - return;
  526. - library::UpdateInfo update = it->second;
  527. -
  528. - text = update.name;
  529. - }
  530. -
  531. - ui::Menu* createChildMenu() override {
  532. - auto it = library::updateInfos.find(slug);
  533. - if (it == library::updateInfos.end())
  534. - return NULL;
  535. - library::UpdateInfo update = it->second;
  536. -
  537. - if (update.changelogUrl == "")
  538. - return NULL;
  539. -
  540. - ui::Menu* menu = new ui::Menu;
  541. -
  542. - std::string changelogUrl = update.changelogUrl;
  543. - menu->addChild(createMenuItem("Changelog", "", [=]() {
  544. - system::openBrowser(changelogUrl);
  545. - }));
  546. -
  547. - return menu;
  548. - }
  549. -
  550. - void step() override {
  551. - disabled = library::isSyncing;
  552. -
  553. - auto it = library::updateInfos.find(slug);
  554. - if (it != library::updateInfos.end()) {
  555. - library::UpdateInfo update = it->second;
  556. -
  557. - if (update.downloaded) {
  558. - rightText = CHECKMARK_STRING;
  559. - disabled = true;
  560. - }
  561. - else if (slug == library::updateSlug) {
  562. - rightText = string::f("%.0f%%", library::updateProgress * 100.f);
  563. - }
  564. - else {
  565. - rightText = "";
  566. - plugin::Plugin* p = plugin::getPlugin(slug);
  567. - if (p) {
  568. - rightText += p->version + " → ";
  569. - }
  570. - rightText += update.version;
  571. + if (enabled)
  572. + rightText = CHECKMARK_STRING;
  573. + menu->addChild(createMenuItem("Enable Audio Input", rightText, [enabled]() {
  574. + if (!enabled)
  575. + requestAudioInput();
  576. + }));
  577. }
  578. - }
  579. - MenuItem::step();
  580. - }
  581. -
  582. - void onAction(const ActionEvent& e) override {
  583. - std::thread t([=] {
  584. - library::syncUpdate(slug);
  585. - });
  586. - t.detach();
  587. - e.unconsume();
  588. - }
  589. -};
  590. -
  591. -
  592. -struct LibraryMenu : ui::Menu {
  593. - LibraryMenu() {
  594. - refresh();
  595. - }
  596. -
  597. - void step() override {
  598. - // Refresh menu when appropriate
  599. - if (library::refreshRequested) {
  600. - library::refreshRequested = false;
  601. - refresh();
  602. - }
  603. - Menu::step();
  604. - }
  605. -
  606. - void refresh() {
  607. - setChildMenu(NULL);
  608. - clearChildren();
  609. -
  610. - if (settings::devMode) {
  611. - addChild(createMenuLabel("Disabled in development mode"));
  612. - }
  613. - else if (!library::isLoggedIn()) {
  614. - addChild(createMenuItem("Register VCV account", "", [=]() {
  615. - system::openBrowser("https://vcvrack.com/login");
  616. - }));
  617. -
  618. - ui::TextField* emailField = new ui::TextField;
  619. - emailField->placeholder = "Email";
  620. - emailField->box.size.x = 240.0;
  621. - addChild(emailField);
  622. -
  623. - AccountPasswordField* passwordField = new AccountPasswordField;
  624. - passwordField->placeholder = "Password";
  625. - passwordField->box.size.x = 240.0;
  626. - passwordField->nextField = emailField;
  627. - emailField->nextField = passwordField;
  628. - addChild(passwordField);
  629. -
  630. - LogInItem* logInItem = new LogInItem;
  631. - logInItem->emailField = emailField;
  632. - logInItem->passwordField = passwordField;
  633. - passwordField->logInItem = logInItem;
  634. - addChild(logInItem);
  635. - }
  636. - else {
  637. - addChild(createMenuItem("Log out", "", [=]() {
  638. - library::logOut();
  639. - }));
  640. -
  641. - addChild(createMenuItem("Browse VCV Library", "", [=]() {
  642. - system::openBrowser("https://library.vcvrack.com/");
  643. - }));
  644. -
  645. - SyncUpdatesItem* syncItem = new SyncUpdatesItem;
  646. - syncItem->text = "Update all";
  647. - addChild(syncItem);
  648. -
  649. - if (!library::updateInfos.empty()) {
  650. - addChild(new ui::MenuSeparator);
  651. - addChild(createMenuLabel("Updates"));
  652. -
  653. - for (auto& pair : library::updateInfos) {
  654. - SyncUpdateItem* updateItem = new SyncUpdateItem;
  655. - updateItem->setUpdate(pair.first);
  656. - addChild(updateItem);
  657. - }
  658. + if (supportsMIDI()) {
  659. + std::string rightText;
  660. + if (isMIDIEnabled())
  661. + rightText = CHECKMARK_STRING;
  662. + menu->addChild(createMenuItem("Enable/Reconnect MIDI", rightText, []() {
  663. + requestMIDI();
  664. + }));
  665. }
  666. - }
  667. - }
  668. -};
  669. -
  670. -
  671. -struct LibraryButton : MenuButton {
  672. - NotificationIcon* notification;
  673. -
  674. - LibraryButton() {
  675. - notification = new NotificationIcon;
  676. - addChild(notification);
  677. - }
  678. -
  679. - void onAction(const ActionEvent& e) override {
  680. - ui::Menu* menu = createMenu<LibraryMenu>();
  681. - menu->cornerFlags = BND_CORNER_TOP;
  682. - menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  683. - // Check for updates when menu is opened
  684. - std::thread t([&]() {
  685. - system::setThreadName("Library");
  686. - library::checkUpdates();
  687. - });
  688. - t.detach();
  689. - }
  690. -
  691. - void step() override {
  692. - notification->box.pos = math::Vec(0, 0);
  693. - notification->visible = library::hasUpdates();
  694. - // Popup when updates finish downloading
  695. - if (library::restartRequested) {
  696. - library::restartRequested = false;
  697. - if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been downloaded. Close and re-launch Rack to load new updates.")) {
  698. - APP->window->close();
  699. + if (supportsBufferSizeChanges()) {
  700. + static const std::vector<uint32_t> bufferSizes = {
  701. + #ifdef DISTRHO_OS_WASM
  702. + 256, 512, 1024, 2048, 4096, 8192, 16384
  703. + #else
  704. + 128, 256, 512, 1024, 2048, 4096, 8192
  705. + #endif
  706. + };
  707. + const uint32_t currentBufferSize = getBufferSize();
  708. + menu->addChild(createSubmenuItem("Buffer Size", std::to_string(currentBufferSize), [=](ui::Menu* menu) {
  709. + for (uint32_t bufferSize : bufferSizes) {
  710. + menu->addChild(createCheckMenuItem(std::to_string(bufferSize), "",
  711. + [=]() {return currentBufferSize == bufferSize;},
  712. + [=]() {requestBufferSizeChange(bufferSize);}
  713. + ));
  714. + }
  715. + }));
  716. }
  717. }
  718. -
  719. - MenuButton::step();
  720. }
  721. };
  722. @@ -813,65 +710,23 @@
  723. struct HelpButton : MenuButton {
  724. - NotificationIcon* notification;
  725. -
  726. - HelpButton() {
  727. - notification = new NotificationIcon;
  728. - addChild(notification);
  729. - }
  730. -
  731. void onAction(const ActionEvent& e) override {
  732. ui::Menu* menu = createMenu();
  733. menu->cornerFlags = BND_CORNER_TOP;
  734. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  735. - menu->addChild(createMenuItem("Tips", "", [=]() {
  736. - APP->scene->addChild(tipWindowCreate());
  737. - }));
  738. -
  739. - menu->addChild(createMenuItem("User manual", "F1", [=]() {
  740. - system::openBrowser("https://vcvrack.com/manual");
  741. - }));
  742. -
  743. - menu->addChild(createMenuItem("Support", "", [=]() {
  744. - system::openBrowser("https://vcvrack.com/support");
  745. + menu->addChild(createMenuItem("Rack User manual", "F1", [=]() {
  746. + patchUtils::openBrowser("https://vcvrack.com/manual");
  747. }));
  748. - menu->addChild(createMenuItem("VCVRack.com", "", [=]() {
  749. - system::openBrowser("https://vcvrack.com/");
  750. + menu->addChild(createMenuItem("Cardinal Project page", "", [=]() {
  751. + patchUtils::openBrowser("https://github.com/DISTRHO/Cardinal/");
  752. }));
  753. menu->addChild(new ui::MenuSeparator);
  754. - menu->addChild(createMenuLabel(APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION));
  755. -
  756. - menu->addChild(createMenuItem("Open user folder", "", [=]() {
  757. - system::openDirectory(asset::user(""));
  758. - }));
  759. -
  760. - menu->addChild(createMenuItem("Changelog", "", [=]() {
  761. - system::openBrowser("https://github.com/VCVRack/Rack/blob/v2/CHANGELOG.md");
  762. - }));
  763. -
  764. - if (library::isAppUpdateAvailable()) {
  765. - menu->addChild(createMenuItem("Update " + APP_NAME, APP_VERSION + " → " + library::appVersion, [=]() {
  766. - system::openBrowser(library::appDownloadUrl);
  767. - }));
  768. - }
  769. - else if (!settings::autoCheckUpdates && !settings::devMode) {
  770. - menu->addChild(createMenuItem("Check for " + APP_NAME + " update", "", [=]() {
  771. - std::thread t([&]() {
  772. - library::checkAppUpdate();
  773. - });
  774. - t.detach();
  775. - }, false, true));
  776. - }
  777. - }
  778. -
  779. - void step() override {
  780. - notification->box.pos = math::Vec(0, 0);
  781. - notification->visible = library::isAppUpdateAvailable();
  782. - MenuButton::step();
  783. + menu->addChild(createMenuLabel("Cardinal " + APP_EDITION + " " + CARDINAL_VERSION));
  784. + menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible"));
  785. }
  786. };
  787. @@ -910,9 +765,14 @@
  788. // uiLastTime = time;
  789. // }
  790. +#if CARDINAL_VARIANT_MINI
  791. + text = string::f("%.1f fps", 1.0 / frameDurationAvg);
  792. +#else
  793. double meterAverage = APP->engine->getMeterAverage();
  794. double meterMax = APP->engine->getMeterMax();
  795. text = string::f("%.1f fps %.1f%% avg %.1f%% max", 1.0 / frameDurationAvg, meterAverage * 100, meterMax * 100);
  796. +#endif
  797. +
  798. Label::step();
  799. }
  800. };
  801. @@ -921,7 +781,9 @@
  802. struct MenuBar : widget::OpaqueWidget {
  803. MeterLabel* meterLabel;
  804. - MenuBar() {
  805. + MenuBar(const bool isStandalone)
  806. + : widget::OpaqueWidget()
  807. + {
  808. const float margin = 5;
  809. box.size.y = BND_WIDGET_HEIGHT + 2 * margin;
  810. @@ -930,7 +792,7 @@
  811. layout->spacing = math::Vec(0, 0);
  812. addChild(layout);
  813. - FileButton* fileButton = new FileButton;
  814. + FileButton* fileButton = new FileButton(isStandalone);
  815. fileButton->text = "File";
  816. layout->addChild(fileButton);
  817. @@ -942,13 +804,11 @@
  818. viewButton->text = "View";
  819. layout->addChild(viewButton);
  820. +#if ! CARDINAL_VARIANT_MINI
  821. EngineButton* engineButton = new EngineButton;
  822. engineButton->text = "Engine";
  823. layout->addChild(engineButton);
  824. -
  825. - LibraryButton* libraryButton = new LibraryButton;
  826. - libraryButton->text = "Library";
  827. - layout->addChild(libraryButton);
  828. +#endif
  829. HelpButton* helpButton = new HelpButton;
  830. helpButton->text = "Help";
  831. @@ -984,7 +844,7 @@
  832. widget::Widget* createMenuBar() {
  833. - menuBar::MenuBar* menuBar = new menuBar::MenuBar;
  834. + menuBar::MenuBar* menuBar = new menuBar::MenuBar(isStandalone());
  835. return menuBar;
  836. }