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.

762 lines
19KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. /**
  18. * This file is an edited version of VCVRack's MenuBar.cpp
  19. * Copyright (C) 2016-2021 VCV.
  20. *
  21. * This program is free software: you can redistribute it and/or
  22. * modify it under the terms of the GNU General Public License as
  23. * published by the Free Software Foundation; either version 3 of
  24. * the License, or (at your option) any later version.
  25. */
  26. #include <thread>
  27. #include <utility>
  28. #include <app/MenuBar.hpp>
  29. #include <app/TipWindow.hpp>
  30. #include <widget/OpaqueWidget.hpp>
  31. #include <ui/Button.hpp>
  32. #include <ui/MenuItem.hpp>
  33. #include <ui/MenuSeparator.hpp>
  34. #include <ui/SequentialLayout.hpp>
  35. #include <ui/Slider.hpp>
  36. #include <ui/TextField.hpp>
  37. #include <ui/PasswordField.hpp>
  38. #include <ui/ProgressBar.hpp>
  39. #include <ui/Label.hpp>
  40. #include <engine/Engine.hpp>
  41. #include <window/Window.hpp>
  42. #include <asset.hpp>
  43. #include <context.hpp>
  44. #include <settings.hpp>
  45. #include <helpers.hpp>
  46. #include <system.hpp>
  47. #include <plugin.hpp>
  48. #include <patch.hpp>
  49. #include <library.hpp>
  50. #ifdef NDEBUG
  51. # undef DEBUG
  52. #endif
  53. // for finding home dir
  54. #ifndef ARCH_WIN
  55. # include <pwd.h>
  56. # include <unistd.h>
  57. #endif
  58. #ifdef HAVE_LIBLO
  59. # include <lo/lo.h>
  60. #endif
  61. #include <Window.hpp>
  62. #include "../AsyncDialog.hpp"
  63. #include "../PluginContext.hpp"
  64. // #define REMOTE_HOST "localhost"
  65. #define REMOTE_HOST "192.168.51.1"
  66. #define REMOTE_HOST_PORT "2228"
  67. namespace rack {
  68. namespace app {
  69. namespace menuBar {
  70. struct MenuButton : ui::Button {
  71. void step() override {
  72. box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0;
  73. Widget::step();
  74. }
  75. void draw(const DrawArgs& args) override {
  76. BNDwidgetState state = BND_DEFAULT;
  77. if (APP->event->hoveredWidget == this)
  78. state = BND_HOVER;
  79. if (APP->event->draggedWidget == this)
  80. state = BND_ACTIVE;
  81. bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
  82. Widget::draw(args);
  83. }
  84. };
  85. ////////////////////
  86. // File
  87. ////////////////////
  88. static void promptClear(const char* const message, const std::function<void()> action)
  89. {
  90. if (APP->history->isSaved() || APP->scene->rack->hasModules())
  91. return action();
  92. asyncDialog::create(message, action);
  93. }
  94. static std::string homeDir()
  95. {
  96. #ifdef ARCH_WIN
  97. if (const char* const userprofile = getenv("USERPROFILE"))
  98. {
  99. return userprofile;
  100. }
  101. else if (const char* const homedrive = getenv("HOMEDRIVE"))
  102. {
  103. if (const char* const homepath = getenv("HOMEPATH"))
  104. return system::join(homedrive, homepath);
  105. }
  106. #else
  107. if (const char* const home = getenv("HOME"))
  108. return home;
  109. else if (struct passwd* const pwd = getpwuid(getuid()))
  110. return pwd->pw_dir;
  111. #endif
  112. return {};
  113. }
  114. struct FileButton : MenuButton {
  115. CardinalBaseUI* const ui;
  116. const bool isStandalone;
  117. #ifdef HAVE_LIBLO
  118. bool oscConnected = false;
  119. lo_server oscServer = nullptr;
  120. static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self)
  121. {
  122. d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc);
  123. if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') {
  124. d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s);
  125. if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0)
  126. static_cast<FileButton*>(self)->oscConnected = true;
  127. }
  128. return 0;
  129. }
  130. ~FileButton() {
  131. lo_server_free(oscServer);
  132. }
  133. #endif
  134. FileButton(CardinalBaseUI* const ui2, const bool standalone)
  135. : MenuButton(), ui(ui2), isStandalone(standalone) {}
  136. void onAction(const ActionEvent& e) override {
  137. ui::Menu* menu = createMenu();
  138. menu->cornerFlags = BND_CORNER_TOP;
  139. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  140. menu->addChild(createMenuItem("New", ""/*RACK_MOD_CTRL_NAME "+N"*/, []() {
  141. // see APP->patch->loadTemplateDialog();
  142. promptClear("The current patch is unsaved. Clear it and start a new patch?", []() {
  143. APP->patch->loadTemplate();
  144. });
  145. }));
  146. menu->addChild(createMenuItem("Open / Import...", ""/*RACK_MOD_CTRL_NAME "+O"*/, [this]() {
  147. // see APP->patch->loadDialog();
  148. promptClear("The current patch is unsaved. Clear it and open a new patch?", [this]() {
  149. std::string dir;
  150. if (! APP->patch->path.empty())
  151. dir = system::getDirectory(APP->patch->path);
  152. else
  153. dir = homeDir();
  154. Window::FileBrowserOptions opts;
  155. opts.startDir = dir.c_str();
  156. opts.saving = ui->saving = false;
  157. ui->openFileBrowser(opts);
  158. });
  159. }));
  160. menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() {
  161. APP->patch->saveDialog();
  162. }, APP->patch->path.empty()));
  163. menu->addChild(createMenuItem("Save as / Export...", ""/*RACK_MOD_CTRL_NAME "+Shift+S"*/, [this]() {
  164. // see APP->patch->saveAsDialog();
  165. std::string dir;
  166. if (! APP->patch->path.empty())
  167. dir = system::getDirectory(APP->patch->path);
  168. else
  169. dir = homeDir();
  170. Window::FileBrowserOptions opts;
  171. opts.startDir = dir.c_str();
  172. opts.saving = ui->saving = true;
  173. ui->openFileBrowser(opts);
  174. }));
  175. #ifdef HAVE_LIBLO
  176. if (oscServer == nullptr || !oscConnected) {
  177. menu->addChild(createMenuItem("Connect to MOD", "", [this]() {
  178. if (oscServer == nullptr) {
  179. oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr);
  180. DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,);
  181. lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, this);
  182. }
  183. const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  184. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  185. lo_send(addr, "/hello", "");
  186. lo_address_free(addr);
  187. }));
  188. } else {
  189. menu->addChild(createMenuItem("Deploy to MOD", "", []() {
  190. const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
  191. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  192. APP->engine->prepareSave();
  193. APP->patch->saveAutosave();
  194. APP->patch->cleanAutosave();
  195. std::vector<uint8_t> data(rack::system::archiveDirectory(APP->patch->autosavePath, 1));
  196. if (const lo_blob blob = lo_blob_new(data.size(), data.data()))
  197. {
  198. lo_send(addr, "/load", "b", blob);
  199. lo_blob_free(blob);
  200. }
  201. lo_address_free(addr);
  202. }));
  203. }
  204. #endif
  205. menu->addChild(createMenuItem("Revert", ""/*RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O"*/, []() {
  206. // APP->patch->revertDialog();
  207. if (APP->patch->path.empty())
  208. return;
  209. promptClear("Revert patch to the last saved state?", []{
  210. APP->patch->loadAction(APP->patch->path);
  211. });
  212. }, APP->patch->path.empty()));
  213. if (isStandalone) {
  214. menu->addChild(new ui::MenuSeparator);
  215. menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
  216. APP->window->close();
  217. }));
  218. };
  219. }
  220. #ifdef HAVE_LIBLO
  221. void step() override {
  222. MenuButton::step();
  223. if (oscServer != nullptr) {
  224. while (lo_server_recv_noblock(oscServer, 0) != 0) {}
  225. }
  226. }
  227. #endif
  228. };
  229. ////////////////////
  230. // Edit
  231. ////////////////////
  232. struct EditButton : MenuButton {
  233. void onAction(const ActionEvent& e) override {
  234. ui::Menu* menu = createMenu();
  235. menu->cornerFlags = BND_CORNER_TOP;
  236. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  237. struct UndoItem : ui::MenuItem {
  238. void step() override {
  239. text = "Undo " + APP->history->getUndoName();
  240. disabled = !APP->history->canUndo();
  241. MenuItem::step();
  242. }
  243. void onAction(const ActionEvent& e) override {
  244. APP->history->undo();
  245. }
  246. };
  247. menu->addChild(createMenuItem<UndoItem>("", RACK_MOD_CTRL_NAME "+Z"));
  248. struct RedoItem : ui::MenuItem {
  249. void step() override {
  250. text = "Redo " + APP->history->getRedoName();
  251. disabled = !APP->history->canRedo();
  252. MenuItem::step();
  253. }
  254. void onAction(const ActionEvent& e) override {
  255. APP->history->redo();
  256. }
  257. };
  258. menu->addChild(createMenuItem<RedoItem>("", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+Z"));
  259. menu->addChild(createMenuItem("Clear cables", "", [=]() {
  260. APP->patch->disconnectDialog();
  261. }));
  262. menu->addChild(new ui::MenuSeparator);
  263. APP->scene->rack->appendSelectionContextMenu(menu);
  264. }
  265. };
  266. ////////////////////
  267. // View
  268. ////////////////////
  269. struct ZoomQuantity : Quantity {
  270. void setValue(float value) override {
  271. APP->scene->rackScroll->setZoom(std::pow(2.f, value));
  272. }
  273. float getValue() override {
  274. return std::log2(APP->scene->rackScroll->getZoom());
  275. }
  276. float getMinValue() override {
  277. return -2.f;
  278. }
  279. float getMaxValue() override {
  280. return 2.f;
  281. }
  282. float getDefaultValue() override {
  283. return 0.0;
  284. }
  285. float getDisplayValue() override {
  286. return std::round(std::pow(2.f, getValue()) * 100);
  287. }
  288. void setDisplayValue(float displayValue) override {
  289. setValue(std::log2(displayValue / 100));
  290. }
  291. std::string getLabel() override {
  292. return "Zoom";
  293. }
  294. std::string getUnit() override {
  295. return "%";
  296. }
  297. };
  298. struct ZoomSlider : ui::Slider {
  299. ZoomSlider() {
  300. quantity = new ZoomQuantity;
  301. }
  302. ~ZoomSlider() {
  303. delete quantity;
  304. }
  305. };
  306. struct CableOpacityQuantity : Quantity {
  307. void setValue(float value) override {
  308. settings::cableOpacity = math::clamp(value, getMinValue(), getMaxValue());
  309. }
  310. float getValue() override {
  311. return settings::cableOpacity;
  312. }
  313. float getDefaultValue() override {
  314. return 0.5;
  315. }
  316. float getDisplayValue() override {
  317. return getValue() * 100;
  318. }
  319. void setDisplayValue(float displayValue) override {
  320. setValue(displayValue / 100);
  321. }
  322. std::string getLabel() override {
  323. return "Cable opacity";
  324. }
  325. std::string getUnit() override {
  326. return "%";
  327. }
  328. };
  329. struct CableOpacitySlider : ui::Slider {
  330. CableOpacitySlider() {
  331. quantity = new CableOpacityQuantity;
  332. }
  333. ~CableOpacitySlider() {
  334. delete quantity;
  335. }
  336. };
  337. struct CableTensionQuantity : Quantity {
  338. void setValue(float value) override {
  339. settings::cableTension = math::clamp(value, getMinValue(), getMaxValue());
  340. }
  341. float getValue() override {
  342. return settings::cableTension;
  343. }
  344. float getDefaultValue() override {
  345. return 0.5;
  346. }
  347. std::string getLabel() override {
  348. return "Cable tension";
  349. }
  350. int getDisplayPrecision() override {
  351. return 2;
  352. }
  353. };
  354. struct CableTensionSlider : ui::Slider {
  355. CableTensionSlider() {
  356. quantity = new CableTensionQuantity;
  357. }
  358. ~CableTensionSlider() {
  359. delete quantity;
  360. }
  361. };
  362. struct RackBrightnessQuantity : Quantity {
  363. void setValue(float value) override {
  364. settings::rackBrightness = math::clamp(value, getMinValue(), getMaxValue());
  365. }
  366. float getValue() override {
  367. return settings::rackBrightness;
  368. }
  369. float getDefaultValue() override {
  370. return 1.0;
  371. }
  372. float getDisplayValue() override {
  373. return getValue() * 100;
  374. }
  375. void setDisplayValue(float displayValue) override {
  376. setValue(displayValue / 100);
  377. }
  378. std::string getUnit() override {
  379. return "%";
  380. }
  381. std::string getLabel() override {
  382. return "Room brightness";
  383. }
  384. int getDisplayPrecision() override {
  385. return 3;
  386. }
  387. };
  388. struct RackBrightnessSlider : ui::Slider {
  389. RackBrightnessSlider() {
  390. quantity = new RackBrightnessQuantity;
  391. }
  392. ~RackBrightnessSlider() {
  393. delete quantity;
  394. }
  395. };
  396. struct HaloBrightnessQuantity : Quantity {
  397. void setValue(float value) override {
  398. settings::haloBrightness = math::clamp(value, getMinValue(), getMaxValue());
  399. }
  400. float getValue() override {
  401. return settings::haloBrightness;
  402. }
  403. float getDefaultValue() override {
  404. return 0.25;
  405. }
  406. float getDisplayValue() override {
  407. return getValue() * 100;
  408. }
  409. void setDisplayValue(float displayValue) override {
  410. setValue(displayValue / 100);
  411. }
  412. std::string getUnit() override {
  413. return "%";
  414. }
  415. std::string getLabel() override {
  416. return "Light bloom";
  417. }
  418. int getDisplayPrecision() override {
  419. return 3;
  420. }
  421. };
  422. struct HaloBrightnessSlider : ui::Slider {
  423. HaloBrightnessSlider() {
  424. quantity = new HaloBrightnessQuantity;
  425. }
  426. ~HaloBrightnessSlider() {
  427. delete quantity;
  428. }
  429. };
  430. struct KnobScrollSensitivityQuantity : Quantity {
  431. void setValue(float value) override {
  432. value = math::clamp(value, getMinValue(), getMaxValue());
  433. settings::knobScrollSensitivity = std::pow(2.f, value);
  434. }
  435. float getValue() override {
  436. return std::log2(settings::knobScrollSensitivity);
  437. }
  438. float getMinValue() override {
  439. return std::log2(1e-4f);
  440. }
  441. float getMaxValue() override {
  442. return std::log2(1e-2f);
  443. }
  444. float getDefaultValue() override {
  445. return std::log2(1e-3f);
  446. }
  447. float getDisplayValue() override {
  448. return std::pow(2.f, getValue() - getDefaultValue());
  449. }
  450. void setDisplayValue(float displayValue) override {
  451. setValue(std::log2(displayValue) + getDefaultValue());
  452. }
  453. std::string getLabel() override {
  454. return "Scroll wheel knob sensitivity";
  455. }
  456. int getDisplayPrecision() override {
  457. return 2;
  458. }
  459. };
  460. struct KnobScrollSensitivitySlider : ui::Slider {
  461. KnobScrollSensitivitySlider() {
  462. quantity = new KnobScrollSensitivityQuantity;
  463. }
  464. ~KnobScrollSensitivitySlider() {
  465. delete quantity;
  466. }
  467. };
  468. struct ViewButton : MenuButton {
  469. void onAction(const ActionEvent& e) override {
  470. ui::Menu* menu = createMenu();
  471. menu->cornerFlags = BND_CORNER_TOP;
  472. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  473. menu->addChild(createBoolPtrMenuItem("Show tooltips", "", &settings::tooltips));
  474. ZoomSlider* zoomSlider = new ZoomSlider;
  475. zoomSlider->box.size.x = 250.0;
  476. menu->addChild(zoomSlider);
  477. CableOpacitySlider* cableOpacitySlider = new CableOpacitySlider;
  478. cableOpacitySlider->box.size.x = 250.0;
  479. menu->addChild(cableOpacitySlider);
  480. CableTensionSlider* cableTensionSlider = new CableTensionSlider;
  481. cableTensionSlider->box.size.x = 250.0;
  482. menu->addChild(cableTensionSlider);
  483. RackBrightnessSlider* rackBrightnessSlider = new RackBrightnessSlider;
  484. rackBrightnessSlider->box.size.x = 250.0;
  485. menu->addChild(rackBrightnessSlider);
  486. HaloBrightnessSlider* haloBrightnessSlider = new HaloBrightnessSlider;
  487. haloBrightnessSlider->box.size.x = 250.0;
  488. menu->addChild(haloBrightnessSlider);
  489. menu->addChild(new ui::MenuSeparator);
  490. // menu->addChild(createBoolPtrMenuItem("Hide cursor while dragging", "", &settings::allowCursorLock));
  491. static const std::vector<std::string> knobModeLabels = {
  492. "Linear",
  493. "Scaled linear",
  494. "Absolute rotary",
  495. "Relative rotary",
  496. };
  497. static const std::vector<int> knobModes = {0, 2, 3};
  498. menu->addChild(createSubmenuItem("Knob mode", knobModeLabels[settings::knobMode], [=](ui::Menu* menu) {
  499. for (int knobMode : knobModes) {
  500. menu->addChild(createCheckMenuItem(knobModeLabels[knobMode], "",
  501. [=]() {return settings::knobMode == knobMode;},
  502. [=]() {settings::knobMode = (settings::KnobMode) knobMode;}
  503. ));
  504. }
  505. }));
  506. menu->addChild(createBoolPtrMenuItem("Scroll wheel knob control", "", &settings::knobScroll));
  507. KnobScrollSensitivitySlider* knobScrollSensitivitySlider = new KnobScrollSensitivitySlider;
  508. knobScrollSensitivitySlider->box.size.x = 250.0;
  509. menu->addChild(knobScrollSensitivitySlider);
  510. menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
  511. }
  512. };
  513. ////////////////////
  514. // Engine
  515. ////////////////////
  516. struct EngineButton : MenuButton {
  517. void onAction(const ActionEvent& e) override {
  518. ui::Menu* menu = createMenu();
  519. menu->cornerFlags = BND_CORNER_TOP;
  520. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  521. std::string cpuMeterText = "F3";
  522. if (settings::cpuMeter)
  523. cpuMeterText += " " CHECKMARK_STRING;
  524. menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() {
  525. settings::cpuMeter ^= true;
  526. }));
  527. }
  528. };
  529. ////////////////////
  530. // Help
  531. ////////////////////
  532. struct HelpButton : MenuButton {
  533. void onAction(const ActionEvent& e) override {
  534. ui::Menu* menu = createMenu();
  535. menu->cornerFlags = BND_CORNER_TOP;
  536. menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
  537. menu->addChild(createMenuItem("Tips", "", [=]() {
  538. APP->scene->addChild(tipWindowCreate());
  539. }));
  540. menu->addChild(createMenuItem("VCV User manual", "F1", [=]() {
  541. system::openBrowser("https://vcvrack.com/manual/");
  542. }));
  543. menu->addChild(createMenuItem("Cardinal Project page", "", [=]() {
  544. system::openBrowser("https://github.com/DISTRHO/Cardinal/");
  545. }));
  546. menu->addChild(new ui::MenuSeparator);
  547. menu->addChild(createMenuLabel(APP_EDITION + " " + APP_EDITION_NAME));
  548. menu->addChild(createMenuLabel("VCVRack " + APP_VERSION + " Compatible"));
  549. }
  550. };
  551. ////////////////////
  552. // MenuBar
  553. ////////////////////
  554. struct MeterLabel : ui::Label {
  555. int frameIndex = 0;
  556. double frameDurationTotal = 0.0;
  557. double frameDurationAvg = 0.0;
  558. double uiLastTime = 0.0;
  559. double uiLastThreadTime = 0.0;
  560. double uiFrac = 0.0;
  561. void step() override {
  562. // Compute frame rate
  563. double frameDuration = APP->window->getLastFrameDuration();
  564. frameDurationTotal += frameDuration;
  565. frameIndex++;
  566. if (frameDurationTotal >= 1.0) {
  567. frameDurationAvg = frameDurationTotal / frameIndex;
  568. frameDurationTotal = 0.0;
  569. frameIndex = 0;
  570. }
  571. // Compute UI thread CPU
  572. // double time = system::getTime();
  573. // double uiDuration = time - uiLastTime;
  574. // if (uiDuration >= 1.0) {
  575. // double threadTime = system::getThreadTime();
  576. // uiFrac = (threadTime - uiLastThreadTime) / uiDuration;
  577. // uiLastThreadTime = threadTime;
  578. // uiLastTime = time;
  579. // }
  580. double meterAverage = APP->engine->getMeterAverage();
  581. double meterMax = APP->engine->getMeterMax();
  582. text = string::f("%.1f fps %.1f%% avg %.1f%% max", 1.0 / frameDurationAvg, meterAverage * 100, meterMax * 100);
  583. Label::step();
  584. }
  585. };
  586. struct MenuBar : widget::OpaqueWidget {
  587. // CardinalPluginContext* const context;
  588. MeterLabel* meterLabel;
  589. MenuBar(CardinalBaseUI* const ui, const bool isStandalone)
  590. : widget::OpaqueWidget()
  591. // : context(ctx)
  592. {
  593. const float margin = 5;
  594. box.size.y = BND_WIDGET_HEIGHT + 2 * margin;
  595. ui::SequentialLayout* layout = new ui::SequentialLayout;
  596. layout->margin = math::Vec(margin, margin);
  597. layout->spacing = math::Vec(0, 0);
  598. addChild(layout);
  599. FileButton* fileButton = new FileButton(ui, isStandalone);
  600. fileButton->text = "File";
  601. layout->addChild(fileButton);
  602. EditButton* editButton = new EditButton;
  603. editButton->text = "Edit";
  604. layout->addChild(editButton);
  605. ViewButton* viewButton = new ViewButton;
  606. viewButton->text = "View";
  607. layout->addChild(viewButton);
  608. EngineButton* engineButton = new EngineButton;
  609. engineButton->text = "Engine";
  610. layout->addChild(engineButton);
  611. HelpButton* helpButton = new HelpButton;
  612. helpButton->text = "Help";
  613. layout->addChild(helpButton);
  614. // ui::Label* titleLabel = new ui::Label;
  615. // titleLabel->color.a = 0.5;
  616. // layout->addChild(titleLabel);
  617. meterLabel = new MeterLabel;
  618. meterLabel->box.pos.y = margin;
  619. meterLabel->box.size.x = 300;
  620. meterLabel->alignment = ui::Label::RIGHT_ALIGNMENT;
  621. meterLabel->color.a = 0.5;
  622. addChild(meterLabel);
  623. }
  624. void draw(const DrawArgs& args) override {
  625. bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL);
  626. bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y);
  627. Widget::draw(args);
  628. }
  629. void step() override {
  630. meterLabel->box.pos.x = box.size.x - meterLabel->box.size.x - 5;
  631. Widget::step();
  632. }
  633. };
  634. } // namespace menuBar
  635. widget::Widget* createMenuBar() {
  636. return new widget::Widget;
  637. }
  638. widget::Widget* createMenuBar(CardinalBaseUI* const ui, const bool isStandalone) {
  639. menuBar::MenuBar* menuBar = new menuBar::MenuBar(ui, isStandalone);
  640. return menuBar;
  641. }
  642. } // namespace app
  643. } // namespace rack