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.

1012 lines
24KB

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