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.

689 lines
16KB

  1. #include "app/Toolbar.hpp"
  2. #include "window.hpp"
  3. #include "engine/Engine.hpp"
  4. #include "asset.hpp"
  5. #include "ui/Button.hpp"
  6. #include "ui/MenuItem.hpp"
  7. #include "ui/SequentialLayout.hpp"
  8. #include "ui/Slider.hpp"
  9. #include "ui/TextField.hpp"
  10. #include "ui/PasswordField.hpp"
  11. #include "ui/ProgressBar.hpp"
  12. #include "app.hpp"
  13. #include "settings.hpp"
  14. #include "helpers.hpp"
  15. #include "system.hpp"
  16. #include "plugin.hpp"
  17. #include "patch.hpp"
  18. #include <thread>
  19. namespace rack {
  20. namespace app {
  21. struct MenuButton : ui::Button {
  22. void step() override {
  23. box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0;
  24. widget::Widget::step();
  25. }
  26. void draw(const DrawArgs &args) override {
  27. bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
  28. }
  29. };
  30. struct NewItem : ui::MenuItem {
  31. NewItem() {
  32. text = "New";
  33. rightText = WINDOW_MOD_CTRL_NAME "+N";
  34. }
  35. void onAction(const event::Action &e) override {
  36. APP->patch->resetDialog();
  37. }
  38. };
  39. struct OpenItem : ui::MenuItem {
  40. OpenItem() {
  41. text = "Open";
  42. rightText = WINDOW_MOD_CTRL_NAME "+O";
  43. }
  44. void onAction(const event::Action &e) override {
  45. APP->patch->loadDialog();
  46. }
  47. };
  48. struct SaveItem : ui::MenuItem {
  49. SaveItem() {
  50. text = "Save";
  51. rightText = WINDOW_MOD_CTRL_NAME "+S";
  52. }
  53. void onAction(const event::Action &e) override {
  54. APP->patch->saveDialog();
  55. }
  56. };
  57. struct SaveAsItem : ui::MenuItem {
  58. SaveAsItem() {
  59. text = "Save as";
  60. rightText = WINDOW_MOD_CTRL_NAME "+Shift+S";
  61. }
  62. void onAction(const event::Action &e) override {
  63. APP->patch->saveAsDialog();
  64. }
  65. };
  66. struct SaveTemplateItem : ui::MenuItem {
  67. SaveTemplateItem() {
  68. text = "Save template";
  69. }
  70. void onAction(const event::Action &e) override {
  71. APP->patch->saveTemplateDialog();
  72. }
  73. };
  74. struct RevertItem : ui::MenuItem {
  75. RevertItem() {
  76. text = "Revert";
  77. }
  78. void onAction(const event::Action &e) override {
  79. APP->patch->revertDialog();
  80. }
  81. };
  82. struct DisconnectCablesItem : ui::MenuItem {
  83. DisconnectCablesItem() {
  84. text = "Disconnect cables";
  85. }
  86. void onAction(const event::Action &e) override {
  87. APP->patch->disconnectDialog();
  88. }
  89. };
  90. struct QuitItem : ui::MenuItem {
  91. QuitItem() {
  92. text = "Quit";
  93. rightText = WINDOW_MOD_CTRL_NAME "+Q";
  94. }
  95. void onAction(const event::Action &e) override {
  96. APP->window->close();
  97. }
  98. };
  99. struct FileButton : MenuButton {
  100. FileButton() {
  101. text = "File";
  102. }
  103. void onAction(const event::Action &e) override {
  104. ui::Menu *menu = createMenu();
  105. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  106. menu->box.size.x = box.size.x;
  107. menu->addChild(new NewItem);
  108. menu->addChild(new OpenItem);
  109. menu->addChild(new SaveItem);
  110. menu->addChild(new SaveAsItem);
  111. menu->addChild(new SaveTemplateItem);
  112. menu->addChild(new RevertItem);
  113. menu->addChild(new DisconnectCablesItem);
  114. menu->addChild(new QuitItem);
  115. }
  116. };
  117. struct UndoItem : ui::MenuItem {
  118. UndoItem() {
  119. text = "Undo " + APP->history->getUndoName();
  120. rightText = WINDOW_MOD_CTRL_NAME "+Z";
  121. disabled = !APP->history->canUndo();
  122. }
  123. void onAction(const event::Action &e) override {
  124. APP->history->undo();
  125. }
  126. };
  127. struct RedoItem : ui::MenuItem {
  128. RedoItem() {
  129. text = "Redo " + APP->history->getRedoName();
  130. rightText = WINDOW_MOD_CTRL_NAME "+" WINDOW_MOD_SHIFT_NAME "+Z";
  131. disabled = !APP->history->canRedo();
  132. }
  133. void onAction(const event::Action &e) override {
  134. APP->history->redo();
  135. }
  136. };
  137. struct EditButton : MenuButton {
  138. EditButton() {
  139. text = "Edit";
  140. }
  141. void onAction(const event::Action &e) override {
  142. ui::Menu *menu = createMenu();
  143. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  144. menu->box.size.x = box.size.x;
  145. menu->addChild(new UndoItem);
  146. menu->addChild(new RedoItem);
  147. }
  148. };
  149. struct ZoomQuantity : ui::Quantity {
  150. void setValue(float value) override {
  151. settings.zoom = math::clamp(value, getMinValue(), getMaxValue());
  152. }
  153. float getValue() override {
  154. return settings.zoom;
  155. }
  156. float getMinValue() override {return 0.25;}
  157. float getMaxValue() override {return 2.0;}
  158. float getDefaultValue() override {return 1.0;}
  159. float getDisplayValue() override {return std::round(getValue() * 100);}
  160. void setDisplayValue(float displayValue) override {setValue(displayValue / 100);}
  161. std::string getLabel() override {return "Zoom";}
  162. std::string getUnit() override {return "%";}
  163. };
  164. struct CableOpacityQuantity : ui::Quantity {
  165. void setValue(float value) override {
  166. settings.cableOpacity = math::clamp(value, getMinValue(), getMaxValue());
  167. }
  168. float getValue() override {
  169. return settings.cableOpacity;
  170. }
  171. float getDefaultValue() override {return 0.5;}
  172. float getDisplayValue() override {return getValue() * 100;}
  173. void setDisplayValue(float displayValue) override {setValue(displayValue / 100);}
  174. std::string getLabel() override {return "Cable opacity";}
  175. std::string getUnit() override {return "%";}
  176. };
  177. struct CableTensionQuantity : ui::Quantity {
  178. void setValue(float value) override {
  179. settings.cableTension = math::clamp(value, getMinValue(), getMaxValue());
  180. }
  181. float getValue() override {
  182. return settings.cableTension;
  183. }
  184. float getDefaultValue() override {return 0.5;}
  185. std::string getLabel() override {return "Cable tension";}
  186. int getDisplayPrecision() override {return 2;}
  187. };
  188. struct CpuMeterItem : ui::MenuItem {
  189. CpuMeterItem() {
  190. text = "CPU meter";
  191. rightText = CHECKMARK(settings.cpuMeter);
  192. }
  193. void onAction(const event::Action &e) override {
  194. settings.cpuMeter ^= true;
  195. }
  196. };
  197. struct ParamTooltipItem : ui::MenuItem {
  198. ParamTooltipItem() {
  199. text = "Parameter tooltips";
  200. rightText = CHECKMARK(settings.paramTooltip);
  201. }
  202. void onAction(const event::Action &e) override {
  203. settings.paramTooltip ^= true;
  204. }
  205. };
  206. struct LockModulesItem : ui::MenuItem {
  207. LockModulesItem() {
  208. text = "Lock modules";
  209. rightText = CHECKMARK(settings.lockModules);
  210. }
  211. void onAction(const event::Action &e) override {
  212. settings.lockModules ^= true;
  213. }
  214. };
  215. struct EnginePauseItem : ui::MenuItem {
  216. EnginePauseItem() {
  217. text = "Pause engine";
  218. rightText = CHECKMARK(APP->engine->isPaused());
  219. }
  220. void onAction(const event::Action &e) override {
  221. APP->engine->setPaused(!APP->engine->isPaused());
  222. }
  223. };
  224. struct SampleRateValueItem : ui::MenuItem {
  225. float sampleRate;
  226. void setSampleRate(float sampleRate) {
  227. this->sampleRate = sampleRate;
  228. text = string::f("%.0f Hz", sampleRate);
  229. rightText = CHECKMARK(APP->engine->getSampleRate() == sampleRate);
  230. }
  231. void onAction(const event::Action &e) override {
  232. APP->engine->setSampleRate(sampleRate);
  233. APP->engine->setPaused(false);
  234. }
  235. };
  236. struct SampleRateItem : ui::MenuItem {
  237. SampleRateItem() {
  238. text = "Engine sample rate";
  239. }
  240. ui::Menu *createChildMenu() override {
  241. ui::Menu *menu = new ui::Menu;
  242. menu->addChild(new EnginePauseItem);
  243. for (int i = 0; i <= 4; i++) {
  244. int oversample = 1 << i;
  245. SampleRateValueItem *item = new SampleRateValueItem;
  246. item->setSampleRate(44100.f * oversample);
  247. if (oversample > 1)
  248. item->text += string::f(" (%dx)", oversample);
  249. menu->addChild(item);
  250. item = new SampleRateValueItem;
  251. item->setSampleRate(48000.f * oversample);
  252. if (oversample > 1)
  253. item->text += string::f(" (%dx)", oversample);
  254. menu->addChild(item);
  255. }
  256. return menu;
  257. }
  258. };
  259. struct ThreadCountValueItem : ui::MenuItem {
  260. int threadCount;
  261. void setThreadCount(int threadCount) {
  262. this->threadCount = threadCount;
  263. text = string::f("%d", threadCount);
  264. if (threadCount == system::getLogicalCoreCount() / 2)
  265. text += " (most modules)";
  266. else if (threadCount == 1)
  267. text += " (lowest CPU usage)";
  268. rightText = CHECKMARK(APP->engine->getThreadCount() == threadCount);
  269. }
  270. void onAction(const event::Action &e) override {
  271. APP->engine->setThreadCount(threadCount);
  272. }
  273. };
  274. struct ThreadCount : ui::MenuItem {
  275. ThreadCount() {
  276. text = "Thread count";
  277. }
  278. ui::Menu *createChildMenu() override {
  279. ui::Menu *menu = new ui::Menu;
  280. int coreCount = system::getLogicalCoreCount();
  281. for (int i = 1; i <= coreCount; i++) {
  282. ThreadCountValueItem *item = new ThreadCountValueItem;
  283. item->setThreadCount(i);
  284. menu->addChild(item);
  285. }
  286. return menu;
  287. }
  288. };
  289. struct FullscreenItem : ui::MenuItem {
  290. FullscreenItem() {
  291. text = "Fullscreen";
  292. rightText = "F11";
  293. if (APP->window->isFullScreen())
  294. rightText = CHECKMARK_STRING " " + rightText;
  295. }
  296. void onAction(const event::Action &e) override {
  297. APP->window->setFullScreen(!APP->window->isFullScreen());
  298. }
  299. };
  300. struct SettingsButton : MenuButton {
  301. SettingsButton() {
  302. text = "Settings";
  303. }
  304. void onAction(const event::Action &e) override {
  305. ui::Menu *menu = createMenu();
  306. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  307. menu->box.size.x = box.size.x;
  308. menu->addChild(new ParamTooltipItem);
  309. menu->addChild(new CpuMeterItem);
  310. menu->addChild(new LockModulesItem);
  311. menu->addChild(new SampleRateItem);
  312. menu->addChild(new ThreadCount);
  313. menu->addChild(new FullscreenItem);
  314. ui::Slider *zoomSlider = new ui::Slider;
  315. zoomSlider->box.size.x = 200.0;
  316. zoomSlider->quantity = new ZoomQuantity;
  317. menu->addChild(zoomSlider);
  318. ui::Slider *cableOpacitySlider = new ui::Slider;
  319. cableOpacitySlider->box.size.x = 200.0;
  320. cableOpacitySlider->quantity = new CableOpacityQuantity;
  321. menu->addChild(cableOpacitySlider);
  322. ui::Slider *cableTensionSlider = new ui::Slider;
  323. cableTensionSlider->box.size.x = 200.0;
  324. cableTensionSlider->quantity = new CableTensionQuantity;
  325. menu->addChild(cableTensionSlider);
  326. }
  327. };
  328. struct RegisterItem : ui::MenuItem {
  329. RegisterItem() {
  330. text = "Register VCV account";
  331. }
  332. void onAction(const event::Action &e) override {
  333. std::thread t([&]() {
  334. system::openBrowser("https://vcvrack.com/");
  335. });
  336. t.detach();
  337. }
  338. };
  339. struct AccountEmailField : ui::TextField {
  340. ui::TextField *passwordField;
  341. AccountEmailField() {
  342. placeholder = "Email";
  343. }
  344. void onSelectKey(const event::SelectKey &e) override {
  345. if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) {
  346. APP->event->selectedWidget = passwordField;
  347. e.consume(this);
  348. }
  349. if (!e.getConsumed())
  350. ui::TextField::onSelectKey(e);
  351. }
  352. };
  353. struct AccountPasswordField : ui::PasswordField {
  354. ui::MenuItem *logInItem;
  355. AccountPasswordField() {
  356. placeholder = "Password";
  357. }
  358. void onSelectKey(const event::SelectKey &e) override {
  359. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  360. logInItem->doAction();
  361. e.consume(this);
  362. }
  363. if (!e.getConsumed())
  364. ui::PasswordField::onSelectKey(e);
  365. }
  366. };
  367. struct LogInItem : ui::MenuItem {
  368. ui::TextField *emailField;
  369. ui::TextField *passwordField;
  370. LogInItem() {
  371. text = "Log in";
  372. }
  373. void onAction(const event::Action &e) override {
  374. std::string email = emailField->text;
  375. std::string password = passwordField->text;
  376. std::thread t([&, email, password]() {
  377. plugin::logIn(email, password);
  378. });
  379. t.detach();
  380. }
  381. };
  382. struct ManageItem : ui::MenuItem {
  383. ManageItem() {
  384. text = "Manage plugins";
  385. }
  386. void onAction(const event::Action &e) override {
  387. std::thread t([&]() {
  388. system::openBrowser("https://vcvrack.com/plugins.html");
  389. });
  390. t.detach();
  391. }
  392. };
  393. struct SyncItem : ui::MenuItem {
  394. SyncItem() {
  395. text = "Sync plugins";
  396. disabled = true;
  397. }
  398. void onAction(const event::Action &e) override {
  399. }
  400. };
  401. // struct SyncButton : ui::Button {
  402. // bool checked = false;
  403. // /** Updates are available */
  404. // bool available = false;
  405. // /** Plugins have been updated */
  406. // bool completed = false;
  407. // void step() override {
  408. // // Check for plugin update on first step()
  409. // if (!checked) {
  410. // std::thread t([this]() {
  411. // if (plugin::sync(true))
  412. // available = true;
  413. // });
  414. // t.detach();
  415. // checked = true;
  416. // }
  417. // // Display message if we've completed updates
  418. // if (completed) {
  419. // if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been updated. Close Rack and re-launch it to load new updates.")) {
  420. // APP->window->close();
  421. // }
  422. // completed = false;
  423. // }
  424. // }
  425. // void onAction(const event::Action &e) override {
  426. // available = false;
  427. // std::thread t([this]() {
  428. // if (plugin::sync(false))
  429. // completed = true;
  430. // });
  431. // t.detach();
  432. // }
  433. // };
  434. struct LogOutItem : ui::MenuItem {
  435. LogOutItem() {
  436. text = "Log out";
  437. }
  438. void onAction(const event::Action &e) override {
  439. plugin::logOut();
  440. }
  441. };
  442. struct DownloadQuantity : ui::Quantity {
  443. float getValue() override {
  444. return plugin::downloadProgress;
  445. }
  446. float getDisplayValue() override {
  447. return getValue() * 100.f;
  448. }
  449. int getDisplayPrecision() override {return 0;}
  450. std::string getLabel() override {
  451. return "Downloading " + plugin::downloadName;
  452. }
  453. std::string getUnit() override {return "%";}
  454. };
  455. struct PluginsButton : MenuButton {
  456. PluginsButton() {
  457. text = "Plugins";
  458. }
  459. void onAction(const event::Action &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. // TODO Design dialog box for plugin syncing
  464. if (plugin::isDownloading) {
  465. ui::ProgressBar *downloadProgressBar = new ui::ProgressBar;
  466. downloadProgressBar->quantity = new DownloadQuantity;
  467. menu->addChild(downloadProgressBar);
  468. }
  469. else if (plugin::isLoggedIn()) {
  470. menu->addChild(new ManageItem);
  471. menu->addChild(new SyncItem);
  472. menu->addChild(new LogOutItem);
  473. }
  474. else {
  475. menu->addChild(new RegisterItem);
  476. AccountEmailField *emailField = new AccountEmailField;
  477. emailField->box.size.x = 200.0;
  478. menu->addChild(emailField);
  479. AccountPasswordField *passwordField = new AccountPasswordField;
  480. passwordField->box.size.x = 200.0;
  481. emailField->passwordField = passwordField;
  482. menu->addChild(passwordField);
  483. LogInItem *logInItem = new LogInItem;
  484. logInItem->emailField = emailField;
  485. logInItem->passwordField = passwordField;
  486. passwordField->logInItem = logInItem;
  487. menu->addChild(logInItem);
  488. }
  489. }
  490. void draw(const DrawArgs &args) override {
  491. MenuButton::draw(args);
  492. // if (1) {
  493. // // Notification circle
  494. // nvgBeginPath(args.vg);
  495. // nvgCircle(args.vg, box.size.x - 3, 3, 4.0);
  496. // nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0));
  497. // nvgFill(args.vg);
  498. // nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0));
  499. // nvgStroke(args.vg);
  500. // }
  501. }
  502. };
  503. struct ManualItem : ui::MenuItem {
  504. ManualItem() {
  505. text = "Manual";
  506. rightText = "F1";
  507. }
  508. void onAction(const event::Action &e) override {
  509. std::thread t([&]() {
  510. system::openBrowser("https://vcvrack.com/manual/");
  511. });
  512. t.detach();
  513. }
  514. };
  515. struct WebsiteItem : ui::MenuItem {
  516. WebsiteItem() {
  517. text = "VCVRack.com";
  518. }
  519. void onAction(const event::Action &e) override {
  520. std::thread t([&]() {
  521. system::openBrowser("https://vcvrack.com/");
  522. });
  523. t.detach();
  524. }
  525. };
  526. struct CheckVersionItem : ui::MenuItem {
  527. CheckVersionItem() {
  528. text = "Check version on launch";
  529. rightText = CHECKMARK(settings.checkVersion);
  530. }
  531. void onAction(const event::Action &e) override {
  532. settings.checkVersion ^= true;
  533. }
  534. };
  535. struct HelpButton : MenuButton {
  536. HelpButton() {
  537. text = "Help";
  538. }
  539. void onAction(const event::Action &e) override {
  540. ui::Menu *menu = createMenu();
  541. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  542. menu->box.size.x = box.size.x;
  543. menu->addChild(new ManualItem);
  544. menu->addChild(new WebsiteItem);
  545. menu->addChild(new CheckVersionItem);
  546. }
  547. };
  548. Toolbar::Toolbar() {
  549. const float margin = 5;
  550. box.size.y = BND_WIDGET_HEIGHT + 2*margin;
  551. ui::SequentialLayout *layout = new ui::SequentialLayout;
  552. layout->box.pos = math::Vec(margin, margin);
  553. layout->spacing = math::Vec(0, 0);
  554. addChild(layout);
  555. FileButton *fileButton = new FileButton;
  556. layout->addChild(fileButton);
  557. EditButton *editButton = new EditButton;
  558. layout->addChild(editButton);
  559. SettingsButton *settingsButton = new SettingsButton;
  560. layout->addChild(settingsButton);
  561. PluginsButton *pluginsButton = new PluginsButton;
  562. layout->addChild(pluginsButton);
  563. HelpButton *helpButton = new HelpButton;
  564. layout->addChild(helpButton);
  565. }
  566. void Toolbar::draw(const DrawArgs &args) {
  567. bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL);
  568. bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y);
  569. widget::Widget::draw(args);
  570. }
  571. } // namespace app
  572. } // namespace rack