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.

946 lines
23KB

  1. #include <thread>
  2. #include <utility>
  3. #include <osdialog.h>
  4. #include <app/MenuBar.hpp>
  5. #include <window.hpp>
  6. #include <engine/Engine.hpp>
  7. #include <asset.hpp>
  8. #include <ui/Button.hpp>
  9. #include <ui/MenuItem.hpp>
  10. #include <ui/MenuSeparator.hpp>
  11. #include <ui/SequentialLayout.hpp>
  12. #include <ui/Slider.hpp>
  13. #include <ui/TextField.hpp>
  14. #include <ui/PasswordField.hpp>
  15. #include <ui/ProgressBar.hpp>
  16. #include <context.hpp>
  17. #include <settings.hpp>
  18. #include <helpers.hpp>
  19. #include <system.hpp>
  20. #include <plugin.hpp>
  21. #include <patch.hpp>
  22. #include <updater.hpp>
  23. namespace rack {
  24. namespace app {
  25. struct MenuButton : ui::Button {
  26. void step() override {
  27. box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0;
  28. Widget::step();
  29. }
  30. void draw(const DrawArgs& args) override {
  31. BNDwidgetState state = BND_DEFAULT;
  32. if (APP->event->hoveredWidget == this)
  33. state = BND_HOVER;
  34. if (APP->event->draggedWidget == this)
  35. state = BND_ACTIVE;
  36. bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
  37. Widget::draw(args);
  38. }
  39. };
  40. struct NotificationIcon : widget::Widget {
  41. void draw(const DrawArgs& args) override {
  42. nvgBeginPath(args.vg);
  43. float radius = 4;
  44. nvgCircle(args.vg, radius, radius, radius);
  45. nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0));
  46. nvgFill(args.vg);
  47. nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0));
  48. nvgStroke(args.vg);
  49. }
  50. };
  51. struct UrlItem : ui::MenuItem {
  52. std::string url;
  53. void onAction(const event::Action& e) override {
  54. std::thread t([ = ] {
  55. system::openBrowser(url);
  56. });
  57. t.detach();
  58. }
  59. };
  60. struct FolderItem : ui::MenuItem {
  61. std::string path;
  62. void onAction(const event::Action& e) override {
  63. std::thread t([ = ] {
  64. system::openFolder(path);
  65. });
  66. t.detach();
  67. }
  68. };
  69. ////////////////////
  70. // File
  71. ////////////////////
  72. struct NewItem : ui::MenuItem {
  73. void onAction(const event::Action& e) override {
  74. APP->patch->loadTemplateDialog();
  75. }
  76. };
  77. struct OpenItem : ui::MenuItem {
  78. void onAction(const event::Action& e) override {
  79. APP->patch->loadDialog();
  80. }
  81. };
  82. struct OpenPathItem : ui::MenuItem {
  83. std::string path;
  84. void onAction(const event::Action& e) override {
  85. APP->patch->loadPathDialog(path);
  86. }
  87. };
  88. struct OpenRecentItem : ui::MenuItem {
  89. ui::Menu* createChildMenu() override {
  90. ui::Menu* menu = new ui::Menu;
  91. for (const std::string& path : settings::recentPatchPaths) {
  92. OpenPathItem* item = new OpenPathItem;
  93. item->text = string::filename(path);
  94. item->path = path;
  95. menu->addChild(item);
  96. }
  97. return menu;
  98. }
  99. };
  100. struct SaveItem : ui::MenuItem {
  101. void onAction(const event::Action& e) override {
  102. APP->patch->saveDialog();
  103. }
  104. };
  105. struct SaveAsItem : ui::MenuItem {
  106. void onAction(const event::Action& e) override {
  107. APP->patch->saveAsDialog();
  108. }
  109. };
  110. struct SaveTemplateItem : ui::MenuItem {
  111. void onAction(const event::Action& e) override {
  112. APP->patch->saveTemplateDialog();
  113. }
  114. };
  115. struct RevertItem : ui::MenuItem {
  116. void onAction(const event::Action& e) override {
  117. APP->patch->revertDialog();
  118. }
  119. };
  120. struct QuitItem : ui::MenuItem {
  121. void onAction(const event::Action& e) override {
  122. APP->window->close();
  123. }
  124. };
  125. struct FileButton : MenuButton {
  126. void onAction(const event::Action& e) override {
  127. ui::Menu* menu = createMenu();
  128. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  129. menu->box.size.x = box.size.x;
  130. NewItem* newItem = new NewItem;
  131. newItem->text = "New";
  132. newItem->rightText = RACK_MOD_CTRL_NAME "+N";
  133. menu->addChild(newItem);
  134. OpenItem* openItem = new OpenItem;
  135. openItem->text = "Open";
  136. openItem->rightText = RACK_MOD_CTRL_NAME "+O";
  137. menu->addChild(openItem);
  138. OpenRecentItem* openRecentItem = new OpenRecentItem;
  139. openRecentItem->text = "Open recent";
  140. openRecentItem->rightText = RIGHT_ARROW;
  141. menu->addChild(openRecentItem);
  142. SaveItem* saveItem = new SaveItem;
  143. saveItem->text = "Save";
  144. saveItem->rightText = RACK_MOD_CTRL_NAME "+S";
  145. menu->addChild(saveItem);
  146. SaveAsItem* saveAsItem = new SaveAsItem;
  147. saveAsItem->text = "Save as";
  148. saveAsItem->rightText = RACK_MOD_CTRL_NAME "+Shift+S";
  149. menu->addChild(saveAsItem);
  150. SaveTemplateItem* saveTemplateItem = new SaveTemplateItem;
  151. saveTemplateItem->text = "Save template";
  152. menu->addChild(saveTemplateItem);
  153. RevertItem* revertItem = new RevertItem;
  154. revertItem->text = "Revert";
  155. revertItem->rightText = RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O";
  156. menu->addChild(revertItem);
  157. QuitItem* quitItem = new QuitItem;
  158. quitItem->text = "Quit";
  159. quitItem->rightText = RACK_MOD_CTRL_NAME "+Q";
  160. menu->addChild(quitItem);
  161. }
  162. };
  163. ////////////////////
  164. // Edit
  165. ////////////////////
  166. struct UndoItem : ui::MenuItem {
  167. void onAction(const event::Action& e) override {
  168. APP->history->undo();
  169. }
  170. };
  171. struct RedoItem : ui::MenuItem {
  172. void onAction(const event::Action& e) override {
  173. APP->history->redo();
  174. }
  175. };
  176. struct DisconnectCablesItem : ui::MenuItem {
  177. void onAction(const event::Action& e) override {
  178. APP->patch->disconnectDialog();
  179. }
  180. };
  181. struct EditButton : MenuButton {
  182. void onAction(const event::Action& e) override {
  183. ui::Menu* menu = createMenu();
  184. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  185. menu->box.size.x = box.size.x;
  186. UndoItem* undoItem = new UndoItem;
  187. undoItem->text = "Undo " + APP->history->getUndoName();
  188. undoItem->rightText = RACK_MOD_CTRL_NAME "+Z";
  189. undoItem->disabled = !APP->history->canUndo();
  190. menu->addChild(undoItem);
  191. RedoItem* redoItem = new RedoItem;
  192. redoItem->text = "Redo " + APP->history->getRedoName();
  193. redoItem->rightText = RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+Z";
  194. redoItem->disabled = !APP->history->canRedo();
  195. menu->addChild(redoItem);
  196. DisconnectCablesItem* disconnectCablesItem = new DisconnectCablesItem;
  197. disconnectCablesItem->text = "Clear cables";
  198. menu->addChild(disconnectCablesItem);
  199. }
  200. };
  201. ////////////////////
  202. // View
  203. ////////////////////
  204. struct ZoomQuantity : Quantity {
  205. void setValue(float value) override {
  206. settings::zoom = value;
  207. }
  208. float getValue() override {
  209. return settings::zoom;
  210. }
  211. float getMinValue() override {
  212. return -2.0;
  213. }
  214. float getMaxValue() override {
  215. return 2.0;
  216. }
  217. float getDefaultValue() override {
  218. return 0.0;
  219. }
  220. float getDisplayValue() override {
  221. return std::round(std::pow(2.f, getValue()) * 100);
  222. }
  223. void setDisplayValue(float displayValue) override {
  224. setValue(std::log2(displayValue / 100));
  225. }
  226. std::string getLabel() override {
  227. return "Zoom";
  228. }
  229. std::string getUnit() override {
  230. return "%";
  231. }
  232. };
  233. struct ZoomSlider : ui::Slider {
  234. ZoomSlider() {
  235. quantity = new ZoomQuantity;
  236. }
  237. ~ZoomSlider() {
  238. delete quantity;
  239. }
  240. };
  241. struct CableOpacityQuantity : Quantity {
  242. void setValue(float value) override {
  243. settings::cableOpacity = math::clamp(value, getMinValue(), getMaxValue());
  244. }
  245. float getValue() override {
  246. return settings::cableOpacity;
  247. }
  248. float getDefaultValue() override {
  249. return 0.5;
  250. }
  251. float getDisplayValue() override {
  252. return getValue() * 100;
  253. }
  254. void setDisplayValue(float displayValue) override {
  255. setValue(displayValue / 100);
  256. }
  257. std::string getLabel() override {
  258. return "Cable opacity";
  259. }
  260. std::string getUnit() override {
  261. return "%";
  262. }
  263. };
  264. struct CableOpacitySlider : ui::Slider {
  265. CableOpacitySlider() {
  266. quantity = new CableOpacityQuantity;
  267. }
  268. ~CableOpacitySlider() {
  269. delete quantity;
  270. }
  271. };
  272. struct CableTensionQuantity : Quantity {
  273. void setValue(float value) override {
  274. settings::cableTension = math::clamp(value, getMinValue(), getMaxValue());
  275. }
  276. float getValue() override {
  277. return settings::cableTension;
  278. }
  279. float getDefaultValue() override {
  280. return 0.5;
  281. }
  282. std::string getLabel() override {
  283. return "Cable tension";
  284. }
  285. int getDisplayPrecision() override {
  286. return 2;
  287. }
  288. };
  289. struct CableTensionSlider : ui::Slider {
  290. CableTensionSlider() {
  291. quantity = new CableTensionQuantity;
  292. }
  293. ~CableTensionSlider() {
  294. delete quantity;
  295. }
  296. };
  297. struct ParamTooltipItem : ui::MenuItem {
  298. void onAction(const event::Action& e) override {
  299. settings::paramTooltip ^= true;
  300. }
  301. };
  302. struct AllowCursorLockItem : ui::MenuItem {
  303. void onAction(const event::Action& e) override {
  304. settings::allowCursorLock ^= true;
  305. }
  306. };
  307. struct KnobModeValueItem : ui::MenuItem {
  308. settings::KnobMode knobMode;
  309. void onAction(const event::Action& e) override {
  310. settings::knobMode = knobMode;
  311. }
  312. };
  313. struct KnobModeItem : ui::MenuItem {
  314. ui::Menu* createChildMenu() override {
  315. ui::Menu* menu = new ui::Menu;
  316. static const std::vector<std::pair<settings::KnobMode, std::string>> knobModes = {
  317. {settings::KNOB_MODE_LINEAR, "Linear"},
  318. {settings::KNOB_MODE_SCALED_LINEAR, "Scaled linear"},
  319. {settings::KNOB_MODE_ROTARY_ABSOLUTE, "Absolute rotary"},
  320. {settings::KNOB_MODE_ROTARY_RELATIVE, "Relative rotary"},
  321. };
  322. for (const auto& pair : knobModes) {
  323. KnobModeValueItem* item = new KnobModeValueItem;
  324. item->knobMode = pair.first;
  325. item->text = pair.second;
  326. item->rightText = CHECKMARK(settings::knobMode == pair.first);
  327. menu->addChild(item);
  328. }
  329. return menu;
  330. }
  331. };
  332. struct LockModulesItem : ui::MenuItem {
  333. void onAction(const event::Action& e) override {
  334. settings::lockModules ^= true;
  335. }
  336. };
  337. struct FrameRateValueItem : ui::MenuItem {
  338. int frameSwapInterval;
  339. void onAction(const event::Action& e) override {
  340. settings::frameSwapInterval = frameSwapInterval;
  341. }
  342. };
  343. struct FrameRateItem : ui::MenuItem {
  344. ui::Menu* createChildMenu() override {
  345. ui::Menu* menu = new ui::Menu;
  346. for (int i = 1; i <= 6; i++) {
  347. float frameRate = APP->window->getMonitorRefreshRate() / i;
  348. FrameRateValueItem* item = new FrameRateValueItem;
  349. item->frameSwapInterval = i;
  350. item->text = string::f("%.0f Hz", frameRate);
  351. item->rightText += CHECKMARK(settings::frameSwapInterval == i);
  352. menu->addChild(item);
  353. }
  354. return menu;
  355. }
  356. };
  357. struct FullscreenItem : ui::MenuItem {
  358. void onAction(const event::Action& e) override {
  359. APP->window->setFullScreen(!APP->window->isFullScreen());
  360. }
  361. };
  362. struct ViewButton : MenuButton {
  363. void onAction(const event::Action& e) override {
  364. ui::Menu* menu = createMenu();
  365. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  366. menu->box.size.x = box.size.x;
  367. ParamTooltipItem* paramTooltipItem = new ParamTooltipItem;
  368. paramTooltipItem->text = "Show tooltips";
  369. paramTooltipItem->rightText = CHECKMARK(settings::paramTooltip);
  370. menu->addChild(paramTooltipItem);
  371. AllowCursorLockItem* allowCursorLockItem = new AllowCursorLockItem;
  372. allowCursorLockItem->text = "Lock cursor when dragging parameters";
  373. allowCursorLockItem->rightText = CHECKMARK(settings::allowCursorLock);
  374. menu->addChild(allowCursorLockItem);
  375. KnobModeItem* knobModeItem = new KnobModeItem;
  376. knobModeItem->text = "Knob mode";
  377. knobModeItem->rightText = RIGHT_ARROW;
  378. menu->addChild(knobModeItem);
  379. LockModulesItem* lockModulesItem = new LockModulesItem;
  380. lockModulesItem->text = "Lock module positions";
  381. lockModulesItem->rightText = CHECKMARK(settings::lockModules);
  382. menu->addChild(lockModulesItem);
  383. ZoomSlider* zoomSlider = new ZoomSlider;
  384. zoomSlider->box.size.x = 200.0;
  385. menu->addChild(zoomSlider);
  386. CableOpacitySlider* cableOpacitySlider = new CableOpacitySlider;
  387. cableOpacitySlider->box.size.x = 200.0;
  388. menu->addChild(cableOpacitySlider);
  389. CableTensionSlider* cableTensionSlider = new CableTensionSlider;
  390. cableTensionSlider->box.size.x = 200.0;
  391. menu->addChild(cableTensionSlider);
  392. FrameRateItem* frameRateItem = new FrameRateItem;
  393. frameRateItem->text = "Frame rate";
  394. frameRateItem->rightText = RIGHT_ARROW;
  395. menu->addChild(frameRateItem);
  396. FullscreenItem* fullscreenItem = new FullscreenItem;
  397. fullscreenItem->text = "Fullscreen";
  398. fullscreenItem->rightText = "F11";
  399. if (APP->window->isFullScreen())
  400. fullscreenItem->rightText = CHECKMARK_STRING " " + fullscreenItem->rightText;
  401. menu->addChild(fullscreenItem);
  402. }
  403. };
  404. ////////////////////
  405. // Engine
  406. ////////////////////
  407. struct CpuMeterItem : ui::MenuItem {
  408. void onAction(const event::Action& e) override {
  409. settings::cpuMeter ^= true;
  410. }
  411. };
  412. struct SampleRateValueItem : ui::MenuItem {
  413. float sampleRate;
  414. void onAction(const event::Action& e) override {
  415. settings::sampleRate = sampleRate;
  416. }
  417. };
  418. struct SampleRateItem : ui::MenuItem {
  419. ui::Menu* createChildMenu() override {
  420. ui::Menu* menu = new ui::Menu;
  421. for (int i = -2; i <= 4; i++) {
  422. for (int j = 0; j < 2; j++) {
  423. float oversample = std::pow(2.f, i);
  424. float sampleRate = (j == 0) ? 44100.f : 48000.f;
  425. sampleRate *= oversample;
  426. SampleRateValueItem* item = new SampleRateValueItem;
  427. item->sampleRate = sampleRate;
  428. item->text = string::f("%g kHz", sampleRate / 1000.0);
  429. if (oversample > 1.f) {
  430. item->rightText += string::f("(%.0fx)", oversample);
  431. }
  432. else if (oversample < 1.f) {
  433. item->rightText += string::f("(1/%.0fx)", 1.f / oversample);
  434. }
  435. item->rightText += " ";
  436. item->rightText += CHECKMARK(settings::sampleRate == sampleRate);
  437. menu->addChild(item);
  438. }
  439. }
  440. return menu;
  441. }
  442. };
  443. struct ThreadCountValueItem : ui::MenuItem {
  444. int threadCount;
  445. void setThreadCount(int threadCount) {
  446. this->threadCount = threadCount;
  447. text = string::f("%d", threadCount);
  448. if (threadCount == system::getLogicalCoreCount() / 2)
  449. text += " (most modules)";
  450. else if (threadCount == 1)
  451. text += " (lowest CPU usage)";
  452. rightText = CHECKMARK(settings::threadCount == threadCount);
  453. }
  454. void onAction(const event::Action& e) override {
  455. settings::threadCount = threadCount;
  456. }
  457. };
  458. struct ThreadCountItem : ui::MenuItem {
  459. ui::Menu* createChildMenu() override {
  460. ui::Menu* menu = new ui::Menu;
  461. int coreCount = system::getLogicalCoreCount();
  462. for (int i = 1; i <= coreCount; i++) {
  463. ThreadCountValueItem* item = new ThreadCountValueItem;
  464. item->setThreadCount(i);
  465. menu->addChild(item);
  466. }
  467. return menu;
  468. }
  469. };
  470. struct EngineButton : MenuButton {
  471. void onAction(const event::Action& e) override {
  472. ui::Menu* menu = createMenu();
  473. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  474. menu->box.size.x = box.size.x;
  475. CpuMeterItem* cpuMeterItem = new CpuMeterItem;
  476. cpuMeterItem->text = "CPU meter";
  477. cpuMeterItem->rightText = "F3 ";
  478. cpuMeterItem->rightText += CHECKMARK(settings::cpuMeter);
  479. menu->addChild(cpuMeterItem);
  480. SampleRateItem* sampleRateItem = new SampleRateItem;
  481. sampleRateItem->text = "Sample rate";
  482. sampleRateItem->rightText = RIGHT_ARROW;
  483. menu->addChild(sampleRateItem);
  484. ThreadCountItem* threadCount = new ThreadCountItem;
  485. threadCount->text = "Threads";
  486. threadCount->rightText = RIGHT_ARROW;
  487. menu->addChild(threadCount);
  488. }
  489. };
  490. ////////////////////
  491. // Plugins
  492. ////////////////////
  493. static bool isLoggingIn = false;
  494. struct AccountEmailField : ui::TextField {
  495. ui::TextField* passwordField;
  496. void onSelectKey(const event::SelectKey& e) override {
  497. if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) {
  498. APP->event->setSelected(passwordField);
  499. e.consume(this);
  500. }
  501. if (!e.getTarget())
  502. ui::TextField::onSelectKey(e);
  503. }
  504. };
  505. struct AccountPasswordField : ui::PasswordField {
  506. ui::MenuItem* logInItem;
  507. void onSelectKey(const event::SelectKey& e) override {
  508. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  509. logInItem->doAction();
  510. e.consume(this);
  511. }
  512. if (!e.getTarget())
  513. ui::PasswordField::onSelectKey(e);
  514. }
  515. };
  516. struct LogInItem : ui::MenuItem {
  517. ui::TextField* emailField;
  518. ui::TextField* passwordField;
  519. void onAction(const event::Action& e) override {
  520. isLoggingIn = true;
  521. std::string email = emailField->text;
  522. std::string password = passwordField->text;
  523. std::thread t([ = ] {
  524. plugin::logIn(email, password);
  525. isLoggingIn = false;
  526. });
  527. t.detach();
  528. e.consume(NULL);
  529. }
  530. void step() override {
  531. disabled = isLoggingIn;
  532. text = "Log in";
  533. rightText = plugin::loginStatus;
  534. MenuItem::step();
  535. }
  536. };
  537. struct SyncItem : ui::MenuItem {
  538. void step() override {
  539. disabled = true;
  540. if (plugin::updateStatus != "") {
  541. text = plugin::updateStatus;
  542. }
  543. else if (plugin::isSyncing()) {
  544. text = "Updating...";
  545. }
  546. else if (!plugin::hasUpdates()) {
  547. text = "Up-to-date";
  548. }
  549. else {
  550. text = "Update all";
  551. disabled = false;
  552. }
  553. MenuItem::step();
  554. }
  555. void onAction(const event::Action& e) override {
  556. std::thread t([ = ] {
  557. plugin::syncUpdates();
  558. });
  559. t.detach();
  560. e.consume(NULL);
  561. }
  562. };
  563. struct PluginSyncItem : ui::MenuItem {
  564. plugin::Update* update;
  565. void setUpdate(plugin::Update* update) {
  566. this->update = update;
  567. text = update->pluginName;
  568. plugin::Plugin* p = plugin::getPlugin(update->pluginSlug);
  569. if (p) {
  570. rightText += "v" + p->version + " → ";
  571. }
  572. rightText += "v" + update->version;
  573. }
  574. ui::Menu* createChildMenu() override {
  575. if (update->changelogUrl != "") {
  576. ui::Menu* menu = new ui::Menu;
  577. UrlItem* changelogUrl = new UrlItem;
  578. changelogUrl->text = "Changelog";
  579. changelogUrl->url = update->changelogUrl;
  580. menu->addChild(changelogUrl);
  581. return menu;
  582. }
  583. return NULL;
  584. }
  585. void step() override {
  586. disabled = plugin::isSyncing();
  587. if (update->progress >= 1) {
  588. rightText = CHECKMARK_STRING;
  589. disabled = true;
  590. }
  591. else if (update->progress > 0) {
  592. rightText = string::f("%.0f%%", update->progress * 100.f);
  593. }
  594. MenuItem::step();
  595. }
  596. void onAction(const event::Action& e) override {
  597. std::thread t([ = ] {
  598. plugin::syncUpdate(update);
  599. });
  600. t.detach();
  601. e.consume(NULL);
  602. }
  603. };
  604. struct LogOutItem : ui::MenuItem {
  605. void onAction(const event::Action& e) override {
  606. plugin::logOut();
  607. }
  608. };
  609. struct LibraryMenu : ui::Menu {
  610. bool loggedIn = false;
  611. LibraryMenu() {
  612. refresh();
  613. }
  614. void step() override {
  615. // Refresh menu when appropriate
  616. if (!loggedIn && plugin::isLoggedIn())
  617. refresh();
  618. Menu::step();
  619. }
  620. void refresh() {
  621. setChildMenu(NULL);
  622. clearChildren();
  623. if (settings::devMode) {
  624. addChild(createMenuLabel("Disabled in development mode"));
  625. }
  626. else if (!plugin::isLoggedIn()) {
  627. UrlItem* registerItem = new UrlItem;
  628. registerItem->text = "Register VCV account";
  629. registerItem->url = "https://vcvrack.com/";
  630. addChild(registerItem);
  631. AccountEmailField* emailField = new AccountEmailField;
  632. emailField->placeholder = "Email";
  633. emailField->box.size.x = 240.0;
  634. addChild(emailField);
  635. AccountPasswordField* passwordField = new AccountPasswordField;
  636. passwordField->placeholder = "Password";
  637. passwordField->box.size.x = 240.0;
  638. emailField->passwordField = passwordField;
  639. addChild(passwordField);
  640. LogInItem* logInItem = new LogInItem;
  641. logInItem->emailField = emailField;
  642. logInItem->passwordField = passwordField;
  643. passwordField->logInItem = logInItem;
  644. addChild(logInItem);
  645. }
  646. else {
  647. loggedIn = true;
  648. UrlItem* manageItem = new UrlItem;
  649. manageItem->text = "Manage plugins";
  650. manageItem->url = "https://vcvrack.com/plugins.html";
  651. addChild(manageItem);
  652. LogOutItem* logOutItem = new LogOutItem;
  653. logOutItem->text = "Log out";
  654. addChild(logOutItem);
  655. SyncItem* syncItem = new SyncItem;
  656. syncItem->text = "Update all";
  657. addChild(syncItem);
  658. if (plugin::hasUpdates()) {
  659. addChild(new ui::MenuSeparator);
  660. ui::MenuLabel* updatesLabel = new ui::MenuLabel;
  661. updatesLabel->text = "Updates";
  662. addChild(updatesLabel);
  663. for (plugin::Update& update : plugin::updates) {
  664. PluginSyncItem* updateItem = new PluginSyncItem;
  665. updateItem->setUpdate(&update);
  666. addChild(updateItem);
  667. }
  668. }
  669. }
  670. }
  671. };
  672. struct LibraryButton : MenuButton {
  673. NotificationIcon* notification;
  674. LibraryButton() {
  675. notification = new NotificationIcon;
  676. addChild(notification);
  677. }
  678. void onAction(const event::Action& e) override {
  679. ui::Menu* menu = createMenu<LibraryMenu>();
  680. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  681. menu->box.size.x = box.size.x;
  682. }
  683. void step() override {
  684. notification->box.pos = math::Vec(0, 0);
  685. notification->visible = plugin::hasUpdates();
  686. // Popup when updates finish downloading
  687. if (plugin::restartRequested) {
  688. plugin::restartRequested = false;
  689. if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been downloaded. Close and re-launch Rack to load new updates.")) {
  690. APP->window->close();
  691. }
  692. }
  693. MenuButton::step();
  694. }
  695. };
  696. ////////////////////
  697. // Help
  698. ////////////////////
  699. struct UpdateItem : ui::MenuItem {
  700. ui::Menu* createChildMenu() override {
  701. ui::Menu* menu = new ui::Menu;
  702. UrlItem* changelogUrl = new UrlItem;
  703. changelogUrl->text = "Changelog";
  704. changelogUrl->url = updater::changelogUrl;
  705. menu->addChild(changelogUrl);
  706. return menu;
  707. }
  708. void step() override {
  709. if (updater::progress > 0) {
  710. rightText = string::f("%.0f%%", updater::progress * 100.f);
  711. }
  712. MenuItem::step();
  713. }
  714. void onAction(const event::Action& e) override {
  715. std::thread t([ = ] {
  716. updater::update();
  717. });
  718. t.detach();
  719. e.consume(NULL);
  720. }
  721. };
  722. struct HelpButton : MenuButton {
  723. NotificationIcon* notification;
  724. HelpButton() {
  725. notification = new NotificationIcon;
  726. addChild(notification);
  727. }
  728. void onAction(const event::Action& e) override {
  729. ui::Menu* menu = createMenu();
  730. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  731. menu->box.size.x = box.size.x;
  732. if (updater::isUpdateAvailable()) {
  733. UpdateItem* updateItem = new UpdateItem;
  734. updateItem->text = "Update " + APP_NAME;
  735. updateItem->rightText = APP_VERSION + " → " + updater::version;
  736. menu->addChild(updateItem);
  737. }
  738. UrlItem* manualItem = new UrlItem;
  739. manualItem->text = "Manual";
  740. manualItem->rightText = "F1";
  741. manualItem->url = "https://vcvrack.com/manual/";
  742. menu->addChild(manualItem);
  743. UrlItem* websiteItem = new UrlItem;
  744. websiteItem->text = "VCVRack.com";
  745. websiteItem->url = "https://vcvrack.com/";
  746. menu->addChild(websiteItem);
  747. FolderItem* folderItem = new FolderItem;
  748. folderItem->text = "Open user folder";
  749. folderItem->path = asset::user("");
  750. menu->addChild(folderItem);
  751. }
  752. void step() override {
  753. notification->box.pos = math::Vec(0, 0);
  754. notification->visible = updater::isUpdateAvailable();
  755. MenuButton::step();
  756. }
  757. };
  758. ////////////////////
  759. // MenuBar
  760. ////////////////////
  761. void MenuBar::draw(const DrawArgs& args) {
  762. bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL);
  763. bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y);
  764. Widget::draw(args);
  765. }
  766. MenuBar* createMenuBar() {
  767. MenuBar* menuBar = new MenuBar;
  768. const float margin = 5;
  769. menuBar->box.size.y = BND_WIDGET_HEIGHT + 2 * margin;
  770. ui::SequentialLayout* layout = new ui::SequentialLayout;
  771. layout->box.pos = math::Vec(margin, margin);
  772. layout->spacing = math::Vec(0, 0);
  773. menuBar->addChild(layout);
  774. FileButton* fileButton = new FileButton;
  775. fileButton->text = "File";
  776. layout->addChild(fileButton);
  777. EditButton* editButton = new EditButton;
  778. editButton->text = "Edit";
  779. layout->addChild(editButton);
  780. ViewButton* viewButton = new ViewButton;
  781. viewButton->text = "View";
  782. layout->addChild(viewButton);
  783. EngineButton* engineButton = new EngineButton;
  784. engineButton->text = "Engine";
  785. layout->addChild(engineButton);
  786. LibraryButton* libraryButton = new LibraryButton;
  787. libraryButton->text = "Library";
  788. layout->addChild(libraryButton);
  789. HelpButton* helpButton = new HelpButton;
  790. helpButton->text = "Help";
  791. layout->addChild(helpButton);
  792. return menuBar;
  793. }
  794. } // namespace app
  795. } // namespace rack