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.

778 lines
19KB

  1. #include "app/MenuBar.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::step();
  25. }
  26. void draw(const DrawArgs &args) override {
  27. BNDwidgetState state = BND_DEFAULT;
  28. if (APP->event->hoveredWidget == this)
  29. state = BND_HOVER;
  30. if (APP->event->draggedWidget == this)
  31. state = BND_ACTIVE;
  32. bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
  33. }
  34. };
  35. ////////////////////
  36. // File
  37. ////////////////////
  38. struct NewItem : ui::MenuItem {
  39. void onAction(const event::Action &e) override {
  40. APP->patch->resetDialog();
  41. }
  42. };
  43. struct OpenItem : ui::MenuItem {
  44. void onAction(const event::Action &e) override {
  45. APP->patch->loadDialog();
  46. }
  47. };
  48. struct SaveItem : ui::MenuItem {
  49. void onAction(const event::Action &e) override {
  50. APP->patch->saveDialog();
  51. }
  52. };
  53. struct SaveAsItem : ui::MenuItem {
  54. void onAction(const event::Action &e) override {
  55. APP->patch->saveAsDialog();
  56. }
  57. };
  58. struct SaveTemplateItem : ui::MenuItem {
  59. void onAction(const event::Action &e) override {
  60. APP->patch->saveTemplateDialog();
  61. }
  62. };
  63. struct RevertItem : ui::MenuItem {
  64. void onAction(const event::Action &e) override {
  65. APP->patch->revertDialog();
  66. }
  67. };
  68. struct QuitItem : ui::MenuItem {
  69. void onAction(const event::Action &e) override {
  70. APP->window->close();
  71. }
  72. };
  73. struct FileButton : MenuButton {
  74. void onAction(const event::Action &e) override {
  75. ui::Menu *menu = createMenu();
  76. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  77. menu->box.size.x = box.size.x;
  78. NewItem *newItem = new NewItem;
  79. newItem->text = "New";
  80. newItem->rightText = RACK_MOD_CTRL_NAME "+N";
  81. menu->addChild(newItem);
  82. OpenItem *openItem = new OpenItem;
  83. openItem->text = "Open";
  84. openItem->rightText = RACK_MOD_CTRL_NAME "+O";
  85. menu->addChild(openItem);
  86. SaveItem *saveItem = new SaveItem;
  87. saveItem->text = "Save";
  88. saveItem->rightText = RACK_MOD_CTRL_NAME "+S";
  89. menu->addChild(saveItem);
  90. SaveAsItem *saveAsItem = new SaveAsItem;
  91. saveAsItem->text = "Save as";
  92. saveAsItem->rightText = RACK_MOD_CTRL_NAME "+Shift+S";
  93. menu->addChild(saveAsItem);
  94. SaveTemplateItem *saveTemplateItem = new SaveTemplateItem;
  95. saveTemplateItem->text = "Save template";
  96. menu->addChild(saveTemplateItem);
  97. RevertItem *revertItem = new RevertItem;
  98. revertItem->text = "Revert";
  99. menu->addChild(revertItem);
  100. QuitItem *quitItem = new QuitItem;
  101. quitItem->text = "Quit";
  102. quitItem->rightText = RACK_MOD_CTRL_NAME "+Q";
  103. menu->addChild(quitItem);
  104. }
  105. };
  106. ////////////////////
  107. // Edit
  108. ////////////////////
  109. struct UndoItem : ui::MenuItem {
  110. void onAction(const event::Action &e) override {
  111. APP->history->undo();
  112. }
  113. };
  114. struct RedoItem : ui::MenuItem {
  115. void onAction(const event::Action &e) override {
  116. APP->history->redo();
  117. }
  118. };
  119. struct DisconnectCablesItem : ui::MenuItem {
  120. void onAction(const event::Action &e) override {
  121. APP->patch->disconnectDialog();
  122. }
  123. };
  124. struct EditButton : MenuButton {
  125. void onAction(const event::Action &e) override {
  126. ui::Menu *menu = createMenu();
  127. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  128. menu->box.size.x = box.size.x;
  129. UndoItem *undoItem = new UndoItem;
  130. undoItem->text = "Undo " + APP->history->getUndoName();
  131. undoItem->rightText = RACK_MOD_CTRL_NAME "+Z";
  132. undoItem->disabled = !APP->history->canUndo();
  133. menu->addChild(undoItem);
  134. RedoItem *redoItem = new RedoItem;
  135. redoItem->text = "Redo " + APP->history->getRedoName();
  136. redoItem->rightText = RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+Z";
  137. redoItem->disabled = !APP->history->canRedo();
  138. menu->addChild(redoItem);
  139. DisconnectCablesItem *disconnectCablesItem = new DisconnectCablesItem;
  140. disconnectCablesItem->text = "Disconnect cables";
  141. menu->addChild(disconnectCablesItem);
  142. }
  143. };
  144. ////////////////////
  145. // View
  146. ////////////////////
  147. struct ZoomQuantity : Quantity {
  148. void setValue(float value) override {
  149. settings::zoom = math::clamp(value, getMinValue(), getMaxValue());
  150. }
  151. float getValue() override {
  152. return settings::zoom;
  153. }
  154. float getMinValue() override {return 0.25;}
  155. float getMaxValue() override {return 2.0;}
  156. float getDefaultValue() override {return 1.0;}
  157. float getDisplayValue() override {return std::round(getValue() * 100);}
  158. void setDisplayValue(float displayValue) override {setValue(displayValue / 100);}
  159. std::string getLabel() override {return "Zoom";}
  160. std::string getUnit() override {return "%";}
  161. };
  162. struct ZoomSlider : ui::Slider {
  163. ZoomSlider() {
  164. quantity = new ZoomQuantity;
  165. }
  166. ~ZoomSlider() {
  167. delete quantity;
  168. }
  169. };
  170. struct CableOpacityQuantity : Quantity {
  171. void setValue(float value) override {
  172. settings::cableOpacity = math::clamp(value, getMinValue(), getMaxValue());
  173. }
  174. float getValue() override {
  175. return settings::cableOpacity;
  176. }
  177. float getDefaultValue() override {return 0.5;}
  178. float getDisplayValue() override {return getValue() * 100;}
  179. void setDisplayValue(float displayValue) override {setValue(displayValue / 100);}
  180. std::string getLabel() override {return "Cable opacity";}
  181. std::string getUnit() override {return "%";}
  182. };
  183. struct CableOpacitySlider : ui::Slider {
  184. CableOpacitySlider() {
  185. quantity = new CableOpacityQuantity;
  186. }
  187. ~CableOpacitySlider() {
  188. delete quantity;
  189. }
  190. };
  191. struct CableTensionQuantity : Quantity {
  192. void setValue(float value) override {
  193. settings::cableTension = math::clamp(value, getMinValue(), getMaxValue());
  194. }
  195. float getValue() override {
  196. return settings::cableTension;
  197. }
  198. float getDefaultValue() override {return 0.5;}
  199. std::string getLabel() override {return "Cable tension";}
  200. int getDisplayPrecision() override {return 2;}
  201. };
  202. struct CableTensionSlider : ui::Slider {
  203. CableTensionSlider() {
  204. quantity = new CableTensionQuantity;
  205. }
  206. ~CableTensionSlider() {
  207. delete quantity;
  208. }
  209. };
  210. struct ParamTooltipItem : ui::MenuItem {
  211. void onAction(const event::Action &e) override {
  212. settings::paramTooltip ^= true;
  213. }
  214. };
  215. struct LockModulesItem : ui::MenuItem {
  216. void onAction(const event::Action &e) override {
  217. settings::lockModules ^= true;
  218. }
  219. };
  220. struct FullscreenItem : ui::MenuItem {
  221. void onAction(const event::Action &e) override {
  222. APP->window->setFullScreen(!APP->window->isFullScreen());
  223. }
  224. };
  225. struct ViewButton : MenuButton {
  226. void onAction(const event::Action &e) override {
  227. ui::Menu *menu = createMenu();
  228. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  229. menu->box.size.x = box.size.x;
  230. ParamTooltipItem *paramTooltipItem = new ParamTooltipItem;
  231. paramTooltipItem->text = "Parameter tooltips";
  232. paramTooltipItem->rightText = CHECKMARK(settings::paramTooltip);
  233. menu->addChild(paramTooltipItem);
  234. LockModulesItem *lockModulesItem = new LockModulesItem;
  235. lockModulesItem->text = "Lock modules";
  236. lockModulesItem->rightText = CHECKMARK(settings::lockModules);
  237. menu->addChild(lockModulesItem);
  238. ZoomSlider *zoomSlider = new ZoomSlider;
  239. zoomSlider->box.size.x = 200.0;
  240. menu->addChild(zoomSlider);
  241. CableOpacitySlider *cableOpacitySlider = new CableOpacitySlider;
  242. cableOpacitySlider->box.size.x = 200.0;
  243. menu->addChild(cableOpacitySlider);
  244. CableTensionSlider *cableTensionSlider = new CableTensionSlider;
  245. cableTensionSlider->box.size.x = 200.0;
  246. menu->addChild(cableTensionSlider);
  247. FullscreenItem *fullscreenItem = new FullscreenItem;
  248. fullscreenItem->text = "Fullscreen";
  249. fullscreenItem->rightText = "F11";
  250. if (APP->window->isFullScreen())
  251. fullscreenItem->rightText = CHECKMARK_STRING " " + fullscreenItem->rightText;
  252. menu->addChild(fullscreenItem);
  253. }
  254. };
  255. ////////////////////
  256. // Engine
  257. ////////////////////
  258. struct CpuMeterItem : ui::MenuItem {
  259. void onAction(const event::Action &e) override {
  260. settings::cpuMeter ^= true;
  261. }
  262. };
  263. struct EnginePauseItem : ui::MenuItem {
  264. void onAction(const event::Action &e) override {
  265. APP->engine->setPaused(!APP->engine->isPaused());
  266. }
  267. };
  268. struct SampleRateValueItem : ui::MenuItem {
  269. float sampleRate;
  270. void onAction(const event::Action &e) override {
  271. settings::sampleRate = sampleRate;
  272. APP->engine->setPaused(false);
  273. }
  274. };
  275. struct SampleRateItem : ui::MenuItem {
  276. ui::Menu *createChildMenu() override {
  277. ui::Menu *menu = new ui::Menu;
  278. EnginePauseItem *enginePauseItem = new EnginePauseItem;
  279. enginePauseItem->text = "Pause";
  280. enginePauseItem->rightText = CHECKMARK(APP->engine->isPaused());
  281. menu->addChild(enginePauseItem);
  282. for (int i = 0; i <= 4; i++) {
  283. for (int j = 0; j < 2; j++) {
  284. int oversample = 1 << i;
  285. float sampleRate = (j == 0) ? 44100.f : 48000.f;
  286. sampleRate *= oversample;
  287. SampleRateValueItem *item = new SampleRateValueItem;
  288. item->sampleRate = sampleRate;
  289. item->text = string::f("%g kHz", sampleRate / 1000.0);
  290. if (oversample > 1)
  291. item->rightText += string::f("(%dx)", oversample);
  292. item->rightText += " ";
  293. item->rightText += CHECKMARK(settings::sampleRate == sampleRate);
  294. menu->addChild(item);
  295. }
  296. }
  297. return menu;
  298. }
  299. };
  300. struct RealTimeItem : ui::MenuItem {
  301. void onAction(const event::Action &e) override {
  302. settings::realTime ^= true;
  303. }
  304. };
  305. struct ThreadCountValueItem : ui::MenuItem {
  306. int threadCount;
  307. void setThreadCount(int threadCount) {
  308. this->threadCount = threadCount;
  309. text = string::f("%d", threadCount);
  310. if (threadCount == system::getLogicalCoreCount() / 2)
  311. text += " (most modules)";
  312. else if (threadCount == 1)
  313. text += " (lowest CPU usage)";
  314. rightText = CHECKMARK(settings::threadCount == threadCount);
  315. }
  316. void onAction(const event::Action &e) override {
  317. settings::threadCount = threadCount;
  318. }
  319. };
  320. struct ThreadCountItem : ui::MenuItem {
  321. ui::Menu *createChildMenu() override {
  322. ui::Menu *menu = new ui::Menu;
  323. RealTimeItem *realTimeItem = new RealTimeItem;
  324. realTimeItem->text = "Real-time priority";
  325. realTimeItem->rightText = CHECKMARK(settings::realTime);
  326. menu->addChild(realTimeItem);
  327. int coreCount = system::getLogicalCoreCount();
  328. for (int i = 1; i <= coreCount; i++) {
  329. ThreadCountValueItem *item = new ThreadCountValueItem;
  330. item->setThreadCount(i);
  331. menu->addChild(item);
  332. }
  333. return menu;
  334. }
  335. };
  336. struct EngineButton : MenuButton {
  337. void onAction(const event::Action &e) override {
  338. ui::Menu *menu = createMenu();
  339. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  340. menu->box.size.x = box.size.x;
  341. CpuMeterItem *cpuMeterItem = new CpuMeterItem;
  342. cpuMeterItem->text = "CPU timer";
  343. cpuMeterItem->rightText = CHECKMARK(settings::cpuMeter);
  344. menu->addChild(cpuMeterItem);
  345. SampleRateItem *sampleRateItem = new SampleRateItem;
  346. sampleRateItem->text = "Sample rate";
  347. sampleRateItem->rightText = RIGHT_ARROW;
  348. menu->addChild(sampleRateItem);
  349. ThreadCountItem *threadCount = new ThreadCountItem;
  350. threadCount->text = "Threads";
  351. threadCount->rightText = RIGHT_ARROW;
  352. menu->addChild(threadCount);
  353. }
  354. };
  355. ////////////////////
  356. // Plugins
  357. ////////////////////
  358. static bool isLoggingIn = false;
  359. struct RegisterItem : ui::MenuItem {
  360. void onAction(const event::Action &e) override {
  361. std::thread t([]() {
  362. system::openBrowser("https://vcvrack.com/");
  363. });
  364. t.detach();
  365. }
  366. };
  367. struct AccountEmailField : ui::TextField {
  368. ui::TextField *passwordField;
  369. void onSelectKey(const event::SelectKey &e) override {
  370. if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) {
  371. APP->event->setSelected(passwordField);
  372. e.consume(this);
  373. }
  374. if (!e.getTarget())
  375. ui::TextField::onSelectKey(e);
  376. }
  377. };
  378. struct AccountPasswordField : ui::PasswordField {
  379. ui::MenuItem *logInItem;
  380. void onSelectKey(const event::SelectKey &e) override {
  381. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  382. logInItem->doAction();
  383. e.consume(this);
  384. }
  385. if (!e.getTarget())
  386. ui::PasswordField::onSelectKey(e);
  387. }
  388. };
  389. struct LogInItem : ui::MenuItem {
  390. ui::TextField *emailField;
  391. ui::TextField *passwordField;
  392. void onAction(const event::Action &e) override {
  393. isLoggingIn = true;
  394. std::string email = emailField->text;
  395. std::string password = passwordField->text;
  396. std::thread t([=]() {
  397. plugin::logIn(email, password);
  398. isLoggingIn = false;
  399. });
  400. t.detach();
  401. e.consume(NULL);
  402. }
  403. void step() override {
  404. disabled = isLoggingIn;
  405. text = "Log in";
  406. rightText = plugin::loginStatus;
  407. }
  408. };
  409. struct ManageItem : ui::MenuItem {
  410. void onAction(const event::Action &e) override {
  411. std::thread t([]() {
  412. system::openBrowser("https://vcvrack.com/plugins.html");
  413. });
  414. t.detach();
  415. }
  416. };
  417. struct SyncItem : ui::MenuItem {
  418. void onAction(const event::Action &e) override {
  419. std::thread t([=]() {
  420. plugin::syncUpdates();
  421. });
  422. t.detach();
  423. }
  424. };
  425. struct UpdateItem : ui::MenuItem {
  426. std::string changelogUrl;
  427. void onAction(const event::Action &e) override {
  428. std::thread t([=]() {
  429. system::openBrowser(changelogUrl);
  430. });
  431. t.detach();
  432. }
  433. };
  434. #if 0
  435. struct SyncButton : ui::Button {
  436. bool checked = false;
  437. /** Updates are available */
  438. bool available = false;
  439. /** Plugins have been updated */
  440. bool completed = false;
  441. void step() override {
  442. // Check for plugin update on first step()
  443. if (!checked) {
  444. std::thread t([this]() {
  445. if (plugin::sync(true))
  446. available = true;
  447. });
  448. t.detach();
  449. checked = true;
  450. }
  451. // Display message if we've completed updates
  452. if (completed) {
  453. if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been updated. Close Rack and re-launch it to load new updates.")) {
  454. APP->window->close();
  455. }
  456. completed = false;
  457. }
  458. }
  459. void onAction(const event::Action &e) override {
  460. available = false;
  461. std::thread t([this]() {
  462. if (plugin::sync(false))
  463. completed = true;
  464. });
  465. t.detach();
  466. }
  467. };
  468. #endif
  469. struct LogOutItem : ui::MenuItem {
  470. void onAction(const event::Action &e) override {
  471. plugin::logOut();
  472. }
  473. };
  474. struct DownloadQuantity : Quantity {
  475. float getValue() override {
  476. return plugin::downloadProgress;
  477. }
  478. float getDisplayValue() override {
  479. return getValue() * 100.f;
  480. }
  481. int getDisplayPrecision() override {return 0;}
  482. std::string getLabel() override {
  483. return "Downloading " + plugin::downloadName;
  484. }
  485. std::string getUnit() override {return "%";}
  486. };
  487. struct PluginsMenu : ui::Menu {
  488. int state = 0;
  489. PluginsMenu() {
  490. refresh();
  491. }
  492. void step() override {
  493. Menu::step();
  494. }
  495. void refresh() {
  496. clearChildren();
  497. {
  498. ui::MenuLabel *disabledLable = new ui::MenuLabel;
  499. disabledLable->text = "Server not yet available";
  500. addChild(disabledLable);
  501. return;
  502. }
  503. if (plugin::isLoggedIn()) {
  504. ManageItem *manageItem = new ManageItem;
  505. manageItem->text = "Manage";
  506. addChild(manageItem);
  507. LogOutItem *logOutItem = new LogOutItem;
  508. logOutItem->text = "Log out";
  509. addChild(logOutItem);
  510. SyncItem *syncItem = new SyncItem;
  511. syncItem->text = "Update all";
  512. syncItem->disabled = plugin::updates.empty();
  513. addChild(syncItem);
  514. if (!plugin::updates.empty()) {
  515. addChild(new ui::MenuEntry);
  516. ui::MenuLabel *updatesLabel = new ui::MenuLabel;
  517. updatesLabel->text = "Updates (click for changelog)";
  518. addChild(updatesLabel);
  519. for (const plugin::Update &update : plugin::updates) {
  520. UpdateItem *updateItem = new UpdateItem;
  521. updateItem->text = update.pluginSlug;
  522. plugin::Plugin *p = plugin::getPlugin(update.pluginSlug);
  523. if (p) {
  524. updateItem->rightText += "v" + p->version + " → ";
  525. }
  526. updateItem->rightText += "v" + update.version;
  527. updateItem->changelogUrl = update.changelogUrl;
  528. updateItem->disabled = update.changelogUrl.empty();
  529. addChild(updateItem);
  530. }
  531. }
  532. }
  533. else {
  534. RegisterItem *registerItem = new RegisterItem;
  535. registerItem->text = "Register VCV account";
  536. addChild(registerItem);
  537. AccountEmailField *emailField = new AccountEmailField;
  538. emailField->placeholder = "Email";
  539. emailField->box.size.x = 220.0;
  540. addChild(emailField);
  541. AccountPasswordField *passwordField = new AccountPasswordField;
  542. passwordField->placeholder = "Password";
  543. passwordField->box.size.x = 220.0;
  544. emailField->passwordField = passwordField;
  545. addChild(passwordField);
  546. LogInItem *logInItem = new LogInItem;
  547. logInItem->emailField = emailField;
  548. logInItem->passwordField = passwordField;
  549. passwordField->logInItem = logInItem;
  550. addChild(logInItem);
  551. }
  552. }
  553. };
  554. struct PluginsButton : MenuButton {
  555. void onAction(const event::Action &e) override {
  556. ui::Menu *menu = createMenu<PluginsMenu>();
  557. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  558. menu->box.size.x = box.size.x;
  559. }
  560. void draw(const DrawArgs &args) override {
  561. MenuButton::draw(args);
  562. if (0) {
  563. // Notification circle
  564. nvgBeginPath(args.vg);
  565. nvgCircle(args.vg, 4, 2, 4.0);
  566. nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0));
  567. nvgFill(args.vg);
  568. nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0));
  569. nvgStroke(args.vg);
  570. }
  571. }
  572. };
  573. ////////////////////
  574. // Help
  575. ////////////////////
  576. struct ManualItem : ui::MenuItem {
  577. void onAction(const event::Action &e) override {
  578. std::thread t(system::openBrowser, "https://vcvrack.com/manual/");
  579. t.detach();
  580. }
  581. };
  582. struct WebsiteItem : ui::MenuItem {
  583. void onAction(const event::Action &e) override {
  584. std::thread t(system::openBrowser, "https://vcvrack.com/");
  585. t.detach();
  586. }
  587. };
  588. struct CheckVersionItem : ui::MenuItem {
  589. void onAction(const event::Action &e) override {
  590. settings::checkVersion ^= true;
  591. }
  592. };
  593. struct UserFolderItem : ui::MenuItem {
  594. void onAction(const event::Action &e) override {
  595. std::thread t(system::openFolder, asset::user(""));
  596. t.detach();
  597. }
  598. };
  599. struct HelpButton : MenuButton {
  600. void onAction(const event::Action &e) override {
  601. ui::Menu *menu = createMenu();
  602. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  603. menu->box.size.x = box.size.x;
  604. ManualItem *manualItem = new ManualItem;
  605. manualItem->text = "Manual";
  606. manualItem->rightText = "F1";
  607. menu->addChild(manualItem);
  608. WebsiteItem *websiteItem = new WebsiteItem;
  609. websiteItem->text = "VCVRack.com";
  610. menu->addChild(websiteItem);
  611. CheckVersionItem *checkVersionItem = new CheckVersionItem;
  612. checkVersionItem->text = "Check version on launch";
  613. checkVersionItem->rightText = CHECKMARK(settings::checkVersion);
  614. menu->addChild(checkVersionItem);
  615. UserFolderItem *folderItem = new UserFolderItem;
  616. folderItem->text = "Open user folder";
  617. menu->addChild(folderItem);
  618. }
  619. };
  620. ////////////////////
  621. // MenuBar
  622. ////////////////////
  623. MenuBar::MenuBar() {
  624. const float margin = 5;
  625. box.size.y = BND_WIDGET_HEIGHT + 2*margin;
  626. ui::SequentialLayout *layout = new ui::SequentialLayout;
  627. layout->box.pos = math::Vec(margin, margin);
  628. layout->spacing = math::Vec(0, 0);
  629. addChild(layout);
  630. FileButton *fileButton = new FileButton;
  631. fileButton->text = "File";
  632. layout->addChild(fileButton);
  633. EditButton *editButton = new EditButton;
  634. editButton->text = "Edit";
  635. layout->addChild(editButton);
  636. ViewButton *viewButton = new ViewButton;
  637. viewButton->text = "View";
  638. layout->addChild(viewButton);
  639. EngineButton *engineButton = new EngineButton;
  640. engineButton->text = "Engine";
  641. layout->addChild(engineButton);
  642. PluginsButton *pluginsButton = new PluginsButton;
  643. pluginsButton->text = "Plugins";
  644. layout->addChild(pluginsButton);
  645. HelpButton *helpButton = new HelpButton;
  646. helpButton->text = "Help";
  647. layout->addChild(helpButton);
  648. }
  649. void MenuBar::draw(const DrawArgs &args) {
  650. bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL);
  651. bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y);
  652. Widget::draw(args);
  653. }
  654. } // namespace app
  655. } // namespace rack