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.

571 lines
13KB

  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/Scene.hpp"
  13. #include "context.hpp"
  14. #include "settings.hpp"
  15. #include "helpers.hpp"
  16. #include "system.hpp"
  17. #include "plugin.hpp"
  18. #include <thread>
  19. namespace rack {
  20. struct MenuButton : Button {
  21. void step() override {
  22. box.size.x = bndLabelWidth(context()->window->vg, -1, text.c_str());
  23. Widget::step();
  24. }
  25. void draw(NVGcontext *vg) override {
  26. bndMenuItem(vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
  27. }
  28. };
  29. struct NewItem : MenuItem {
  30. NewItem() {
  31. text = "New";
  32. rightText = "(" WINDOW_MOD_KEY_NAME "+N)";
  33. }
  34. void onAction(const event::Action &e) override {
  35. context()->scene->rackWidget->reset();
  36. }
  37. };
  38. struct OpenItem : MenuItem {
  39. OpenItem() {
  40. text = "Open";
  41. rightText = "(" WINDOW_MOD_KEY_NAME "+O)";
  42. }
  43. void onAction(const event::Action &e) override {
  44. context()->scene->rackWidget->loadDialog();
  45. }
  46. };
  47. struct SaveItem : MenuItem {
  48. SaveItem() {
  49. text = "Save";
  50. rightText = "(" WINDOW_MOD_KEY_NAME "+S)";
  51. }
  52. void onAction(const event::Action &e) override {
  53. context()->scene->rackWidget->saveDialog();
  54. }
  55. };
  56. struct SaveAsItem : MenuItem {
  57. SaveAsItem() {
  58. text = "Save as";
  59. rightText = "(" WINDOW_MOD_KEY_NAME "+Shift+S)";
  60. }
  61. void onAction(const event::Action &e) override {
  62. context()->scene->rackWidget->saveAsDialog();
  63. }
  64. };
  65. struct SaveTemplateItem : MenuItem {
  66. SaveTemplateItem() {
  67. text = "Save template";
  68. }
  69. void onAction(const event::Action &e) override {
  70. context()->scene->rackWidget->saveTemplate();
  71. }
  72. };
  73. struct RevertItem : MenuItem {
  74. RevertItem() {
  75. text = "Revert";
  76. }
  77. void onAction(const event::Action &e) override {
  78. context()->scene->rackWidget->revert();
  79. }
  80. };
  81. struct DisconnectCablesItem : MenuItem {
  82. DisconnectCablesItem() {
  83. text = "Disconnect cables";
  84. }
  85. void onAction(const event::Action &e) override {
  86. context()->scene->rackWidget->disconnect();
  87. }
  88. };
  89. struct QuitItem : MenuItem {
  90. QuitItem() {
  91. text = "Quit";
  92. rightText = "(" WINDOW_MOD_KEY_NAME "+Q)";
  93. }
  94. void onAction(const event::Action &e) override {
  95. context()->window->close();
  96. }
  97. };
  98. struct FileButton : MenuButton {
  99. FileButton() {
  100. text = "File";
  101. }
  102. void onAction(const event::Action &e) override {
  103. Menu *menu = createMenu();
  104. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  105. menu->box.size.x = box.size.x;
  106. menu->addChild(new NewItem);
  107. menu->addChild(new OpenItem);
  108. menu->addChild(new SaveItem);
  109. menu->addChild(new SaveAsItem);
  110. menu->addChild(new SaveTemplateItem);
  111. menu->addChild(new RevertItem);
  112. menu->addChild(new DisconnectCablesItem);
  113. menu->addChild(new QuitItem);
  114. }
  115. };
  116. struct ZoomQuantity : Quantity {
  117. void setValue(float value) override {
  118. settings::zoom = math::clamp(value, getMinValue(), getMaxValue());
  119. }
  120. float getValue() override {
  121. return settings::zoom;
  122. }
  123. float getMinValue() override {return 0.25;}
  124. float getMaxValue() override {return 2.0;}
  125. float getDefaultValue() override {return 1.0;}
  126. float getDisplayValue() override {return getValue() * 100.0;}
  127. void setDisplayValue(float displayValue) override {setValue(displayValue / 100.0);}
  128. std::string getLabel() override {return "Zoom";}
  129. std::string getUnit() override {return "%";}
  130. int getDisplayPrecision() override {return 0;}
  131. };
  132. struct WireOpacityQuantity : Quantity {
  133. void setValue(float value) override {
  134. settings::wireOpacity = math::clamp(value, getMinValue(), getMaxValue());
  135. }
  136. float getValue() override {
  137. return settings::wireOpacity;
  138. }
  139. float getDefaultValue() override {return 0.5;}
  140. float getDisplayValue() override {return getValue() * 100.0;}
  141. void setDisplayValue(float displayValue) override {setValue(displayValue / 100.0);}
  142. std::string getLabel() override {return "Cable opacity";}
  143. std::string getUnit() override {return "%";}
  144. int getDisplayPrecision() override {return 0;}
  145. };
  146. struct WireTensionQuantity : Quantity {
  147. void setValue(float value) override {
  148. settings::wireTension = math::clamp(value, getMinValue(), getMaxValue());
  149. }
  150. float getValue() override {
  151. return settings::wireTension;
  152. }
  153. float getDefaultValue() override {return 0.5;}
  154. std::string getLabel() override {return "Cable tension";}
  155. int getDisplayPrecision() override {return 2;}
  156. };
  157. struct PowerMeterItem : MenuItem {
  158. PowerMeterItem() {
  159. text = "Power meter";
  160. rightText = CHECKMARK(settings::powerMeter);
  161. }
  162. void onAction(const event::Action &e) override {
  163. settings::powerMeter ^= true;
  164. }
  165. };
  166. struct LockModulesItem : MenuItem {
  167. LockModulesItem() {
  168. text = "Lock modules";
  169. rightText = CHECKMARK(settings::lockModules);
  170. }
  171. void onAction(const event::Action &e) override {
  172. settings::lockModules ^= true;
  173. }
  174. };
  175. struct EnginePauseItem : MenuItem {
  176. EnginePauseItem() {
  177. text = "Pause engine";
  178. rightText = CHECKMARK(context()->engine->paused);
  179. }
  180. void onAction(const event::Action &e) override {
  181. context()->engine->paused ^= true;
  182. }
  183. };
  184. struct SampleRateValueItem : MenuItem {
  185. float sampleRate;
  186. SampleRateValueItem(float sampleRate) {
  187. this->sampleRate = sampleRate;
  188. text = string::f("%.0f Hz", sampleRate);
  189. rightText = CHECKMARK(context()->engine->getSampleRate() == sampleRate);
  190. }
  191. void onAction(const event::Action &e) override {
  192. context()->engine->setSampleRate(sampleRate);
  193. context()->engine->paused = false;
  194. }
  195. };
  196. struct SampleRateItem : MenuItem {
  197. SampleRateItem() {
  198. text = "Engine sample rate";
  199. }
  200. Menu *createChildMenu() override {
  201. Menu *menu = new Menu;
  202. menu->addChild(new EnginePauseItem);
  203. std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000};
  204. for (float sampleRate : sampleRates) {
  205. menu->addChild(new SampleRateValueItem(sampleRate));
  206. }
  207. return menu;
  208. }
  209. };
  210. struct SettingsButton : MenuButton {
  211. SettingsButton() {
  212. text = "Settings";
  213. }
  214. void onAction(const event::Action &e) override {
  215. Menu *menu = createMenu();
  216. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  217. menu->box.size.x = box.size.x;
  218. menu->addChild(new PowerMeterItem);
  219. menu->addChild(new LockModulesItem);
  220. menu->addChild(new SampleRateItem);
  221. Slider *zoomSlider = new Slider;
  222. zoomSlider->box.size.x = 200.0;
  223. zoomSlider->quantity = new ZoomQuantity;
  224. menu->addChild(zoomSlider);
  225. Slider *wireOpacitySlider = new Slider;
  226. wireOpacitySlider->box.size.x = 200.0;
  227. wireOpacitySlider->quantity = new WireOpacityQuantity;
  228. menu->addChild(wireOpacitySlider);
  229. Slider *wireTensionSlider = new Slider;
  230. wireTensionSlider->box.size.x = 200.0;
  231. wireTensionSlider->quantity = new WireTensionQuantity;
  232. menu->addChild(wireTensionSlider);
  233. }
  234. };
  235. struct RegisterItem : MenuItem {
  236. RegisterItem() {
  237. text = "Register VCV account";
  238. }
  239. void onAction(const event::Action &e) override {
  240. std::thread t([&]() {
  241. system::openBrowser("https://vcvrack.com/");
  242. });
  243. t.detach();
  244. }
  245. };
  246. struct AccountEmailField : TextField {
  247. TextField *passwordField;
  248. AccountEmailField() {
  249. placeholder = "Email";
  250. }
  251. void onSelectKey(const event::SelectKey &e) override {
  252. if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) {
  253. context()->event->selectedWidget = passwordField;
  254. e.consume(this);
  255. return;
  256. }
  257. TextField::onSelectKey(e);
  258. }
  259. };
  260. struct AccountPasswordField : PasswordField {
  261. MenuItem *logInItem;
  262. AccountPasswordField() {
  263. placeholder = "Password";
  264. }
  265. void onSelectKey(const event::SelectKey &e) override {
  266. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  267. logInItem->doAction();
  268. e.consume(this);
  269. return;
  270. }
  271. PasswordField::onSelectKey(e);
  272. }
  273. };
  274. struct LogInItem : MenuItem {
  275. TextField *emailField;
  276. TextField *passwordField;
  277. LogInItem() {
  278. text = "Log in";
  279. }
  280. void onAction(const event::Action &e) override {
  281. std::string email = emailField->text;
  282. std::string password = passwordField->text;
  283. std::thread t([&, email, password]() {
  284. plugin::logIn(email, password);
  285. });
  286. t.detach();
  287. }
  288. };
  289. struct ManageItem : MenuItem {
  290. ManageItem() {
  291. text = "Manage plugins";
  292. }
  293. void onAction(const event::Action &e) override {
  294. std::thread t([&]() {
  295. system::openBrowser("https://vcvrack.com/plugins.html");
  296. });
  297. t.detach();
  298. }
  299. };
  300. struct SyncItem : MenuItem {
  301. SyncItem() {
  302. text = "Sync plugins";
  303. disabled = true;
  304. }
  305. void onAction(const event::Action &e) override {
  306. }
  307. };
  308. // struct SyncButton : Button {
  309. // bool checked = false;
  310. // /** Updates are available */
  311. // bool available = false;
  312. // /** Plugins have been updated */
  313. // bool completed = false;
  314. // void step() override {
  315. // // Check for plugin update on first step()
  316. // if (!checked) {
  317. // std::thread t([this]() {
  318. // if (plugin::sync(true))
  319. // available = true;
  320. // });
  321. // t.detach();
  322. // checked = true;
  323. // }
  324. // // Display message if we've completed updates
  325. // if (completed) {
  326. // if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been updated. Close Rack and re-launch it to load new updates.")) {
  327. // context()->window->close();
  328. // }
  329. // completed = false;
  330. // }
  331. // }
  332. // void onAction(const event::Action &e) override {
  333. // available = false;
  334. // std::thread t([this]() {
  335. // if (plugin::sync(false))
  336. // completed = true;
  337. // });
  338. // t.detach();
  339. // }
  340. // };
  341. struct LogOutItem : MenuItem {
  342. LogOutItem() {
  343. text = "Log out";
  344. }
  345. void onAction(const event::Action &e) override {
  346. plugin::logOut();
  347. }
  348. };
  349. struct DownloadQuantity : Quantity {
  350. float getValue() override {
  351. return plugin::downloadProgress;
  352. }
  353. float getDisplayValue() override {
  354. return getValue() * 100.f;
  355. }
  356. int getDisplayPrecision() override {return 0;}
  357. std::string getLabel() override {
  358. return "Downloading " + plugin::downloadName;
  359. }
  360. std::string getUnit() override {return "%";}
  361. };
  362. struct PluginsButton : MenuButton {
  363. PluginsButton() {
  364. text = "Plugins";
  365. }
  366. void onAction(const event::Action &e) override {
  367. Menu *menu = createMenu();
  368. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  369. menu->box.size.x = box.size.x;
  370. // TODO Design dialog box for plugin syncing
  371. if (plugin::isDownloading) {
  372. ProgressBar *downloadProgressBar = new ProgressBar;
  373. downloadProgressBar->quantity = new DownloadQuantity;
  374. menu->addChild(downloadProgressBar);
  375. }
  376. else if (plugin::isLoggedIn()) {
  377. menu->addChild(new ManageItem);
  378. menu->addChild(new SyncItem);
  379. menu->addChild(new LogOutItem);
  380. }
  381. else {
  382. menu->addChild(new RegisterItem);
  383. AccountEmailField *emailField = new AccountEmailField;
  384. emailField->box.size.x = 200.0;
  385. menu->addChild(emailField);
  386. AccountPasswordField *passwordField = new AccountPasswordField;
  387. passwordField->box.size.x = 200.0;
  388. emailField->passwordField = passwordField;
  389. menu->addChild(passwordField);
  390. LogInItem *logInItem = new LogInItem;
  391. logInItem->emailField = emailField;
  392. logInItem->passwordField = passwordField;
  393. passwordField->logInItem = logInItem;
  394. menu->addChild(logInItem);
  395. }
  396. }
  397. void draw(NVGcontext *vg) override {
  398. MenuButton::draw(vg);
  399. // if (1) {
  400. // // Notification circle
  401. // nvgBeginPath(vg);
  402. // nvgCircle(vg, box.size.x - 3, 3, 4.0);
  403. // nvgFillColor(vg, nvgRGBf(1.0, 0.0, 0.0));
  404. // nvgFill(vg);
  405. // nvgStrokeColor(vg, nvgRGBf(0.5, 0.0, 0.0));
  406. // nvgStroke(vg);
  407. // }
  408. }
  409. };
  410. struct ManualItem : MenuItem {
  411. ManualItem() {
  412. text = "Manual";
  413. }
  414. void onAction(const event::Action &e) override {
  415. std::thread t([&]() {
  416. system::openBrowser("https://vcvrack.com/manual/");
  417. });
  418. t.detach();
  419. }
  420. };
  421. struct WebsiteItem : MenuItem {
  422. WebsiteItem() {
  423. text = "VCVRack.com";
  424. }
  425. void onAction(const event::Action &e) override {
  426. std::thread t([&]() {
  427. system::openBrowser("https://vcvrack.com/");
  428. });
  429. t.detach();
  430. }
  431. };
  432. struct CheckVersionItem : MenuItem {
  433. CheckVersionItem() {
  434. text = "Check version on launch";
  435. rightText = CHECKMARK(settings::checkVersion);
  436. }
  437. void onAction(const event::Action &e) override {
  438. settings::checkVersion ^= true;
  439. }
  440. };
  441. struct HelpButton : MenuButton {
  442. HelpButton() {
  443. text = "Help";
  444. }
  445. void onAction(const event::Action &e) override {
  446. Menu *menu = createMenu();
  447. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  448. menu->box.size.x = box.size.x;
  449. menu->addChild(new ManualItem);
  450. menu->addChild(new WebsiteItem);
  451. menu->addChild(new CheckVersionItem);
  452. }
  453. };
  454. Toolbar::Toolbar() {
  455. const float margin = 5;
  456. box.size.y = BND_WIDGET_HEIGHT + 2*margin;
  457. SequentialLayout *layout = new SequentialLayout;
  458. layout->box.pos = math::Vec(margin, margin);
  459. layout->spacing = 0.0;
  460. addChild(layout);
  461. FileButton *fileButton = new FileButton;
  462. layout->addChild(fileButton);
  463. SettingsButton *settingsButton = new SettingsButton;
  464. layout->addChild(settingsButton);
  465. PluginsButton *pluginsButton = new PluginsButton;
  466. layout->addChild(pluginsButton);
  467. HelpButton *helpButton = new HelpButton;
  468. layout->addChild(helpButton);
  469. }
  470. void Toolbar::draw(NVGcontext *vg) {
  471. bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, 0);
  472. bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y);
  473. Widget::draw(vg);
  474. }
  475. } // namespace rack