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.

1120 lines
27KB

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