#include "app/Toolbar.hpp" #include "window.hpp" #include "engine/Engine.hpp" #include "asset.hpp" #include "ui/Button.hpp" #include "ui/MenuItem.hpp" #include "ui/SequentialLayout.hpp" #include "ui/Slider.hpp" #include "ui/TextField.hpp" #include "ui/PasswordField.hpp" #include "ui/ProgressBar.hpp" #include "app.hpp" #include "settings.hpp" #include "helpers.hpp" #include "system.hpp" #include "plugin.hpp" #include "patch.hpp" #include namespace rack { namespace app { struct MenuButton : ui::Button { void step() override { box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0; widget::Widget::step(); } void draw(const DrawArgs &args) override { bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); } }; struct NewItem : ui::MenuItem { NewItem() { text = "New"; rightText = WINDOW_MOD_CTRL_NAME "+N"; } void onAction(const event::Action &e) override { APP->patch->resetDialog(); } }; struct OpenItem : ui::MenuItem { OpenItem() { text = "Open"; rightText = WINDOW_MOD_CTRL_NAME "+O"; } void onAction(const event::Action &e) override { APP->patch->loadDialog(); } }; struct SaveItem : ui::MenuItem { SaveItem() { text = "Save"; rightText = WINDOW_MOD_CTRL_NAME "+S"; } void onAction(const event::Action &e) override { APP->patch->saveDialog(); } }; struct SaveAsItem : ui::MenuItem { SaveAsItem() { text = "Save as"; rightText = WINDOW_MOD_CTRL_NAME "+Shift+S"; } void onAction(const event::Action &e) override { APP->patch->saveAsDialog(); } }; struct SaveTemplateItem : ui::MenuItem { SaveTemplateItem() { text = "Save template"; } void onAction(const event::Action &e) override { APP->patch->saveTemplateDialog(); } }; struct RevertItem : ui::MenuItem { RevertItem() { text = "Revert"; } void onAction(const event::Action &e) override { APP->patch->revertDialog(); } }; struct DisconnectCablesItem : ui::MenuItem { DisconnectCablesItem() { text = "Disconnect cables"; } void onAction(const event::Action &e) override { APP->patch->disconnectDialog(); } }; struct QuitItem : ui::MenuItem { QuitItem() { text = "Quit"; rightText = WINDOW_MOD_CTRL_NAME "+Q"; } void onAction(const event::Action &e) override { APP->window->close(); } }; struct FileButton : MenuButton { FileButton() { text = "File"; } void onAction(const event::Action &e) override { ui::Menu *menu = createMenu(); menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); menu->box.size.x = box.size.x; menu->addChild(new NewItem); menu->addChild(new OpenItem); menu->addChild(new SaveItem); menu->addChild(new SaveAsItem); menu->addChild(new SaveTemplateItem); menu->addChild(new RevertItem); menu->addChild(new DisconnectCablesItem); menu->addChild(new QuitItem); } }; struct UndoItem : ui::MenuItem { UndoItem() { text = "Undo " + APP->history->getUndoName(); rightText = WINDOW_MOD_CTRL_NAME "+Z"; disabled = !APP->history->canUndo(); } void onAction(const event::Action &e) override { APP->history->undo(); } }; struct RedoItem : ui::MenuItem { RedoItem() { text = "Redo " + APP->history->getRedoName(); rightText = WINDOW_MOD_CTRL_NAME "+" WINDOW_MOD_SHIFT_NAME "+Z"; disabled = !APP->history->canRedo(); } void onAction(const event::Action &e) override { APP->history->redo(); } }; struct EditButton : MenuButton { EditButton() { text = "Edit"; } void onAction(const event::Action &e) override { ui::Menu *menu = createMenu(); menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); menu->box.size.x = box.size.x; menu->addChild(new UndoItem); menu->addChild(new RedoItem); } }; struct ZoomQuantity : ui::Quantity { void setValue(float value) override { settings.zoom = math::clamp(value, getMinValue(), getMaxValue()); } float getValue() override { return settings.zoom; } float getMinValue() override {return 0.25;} float getMaxValue() override {return 2.0;} float getDefaultValue() override {return 1.0;} float getDisplayValue() override {return std::round(getValue() * 100);} void setDisplayValue(float displayValue) override {setValue(displayValue / 100);} std::string getLabel() override {return "Zoom";} std::string getUnit() override {return "%";} }; struct CableOpacityQuantity : ui::Quantity { void setValue(float value) override { settings.cableOpacity = math::clamp(value, getMinValue(), getMaxValue()); } float getValue() override { return settings.cableOpacity; } float getDefaultValue() override {return 0.5;} float getDisplayValue() override {return getValue() * 100;} void setDisplayValue(float displayValue) override {setValue(displayValue / 100);} std::string getLabel() override {return "Cable opacity";} std::string getUnit() override {return "%";} }; struct CableTensionQuantity : ui::Quantity { void setValue(float value) override { settings.cableTension = math::clamp(value, getMinValue(), getMaxValue()); } float getValue() override { return settings.cableTension; } float getDefaultValue() override {return 0.5;} std::string getLabel() override {return "Cable tension";} int getDisplayPrecision() override {return 2;} }; struct CpuMeterItem : ui::MenuItem { CpuMeterItem() { text = "CPU meter"; rightText = CHECKMARK(settings.cpuMeter); } void onAction(const event::Action &e) override { settings.cpuMeter ^= true; } }; struct ParamTooltipItem : ui::MenuItem { ParamTooltipItem() { text = "Parameter tooltips"; rightText = CHECKMARK(settings.paramTooltip); } void onAction(const event::Action &e) override { settings.paramTooltip ^= true; } }; struct LockModulesItem : ui::MenuItem { LockModulesItem() { text = "Lock modules"; rightText = CHECKMARK(settings.lockModules); } void onAction(const event::Action &e) override { settings.lockModules ^= true; } }; struct EnginePauseItem : ui::MenuItem { EnginePauseItem() { text = "Pause engine"; rightText = CHECKMARK(APP->engine->isPaused()); } void onAction(const event::Action &e) override { APP->engine->setPaused(!APP->engine->isPaused()); } }; struct SampleRateValueItem : ui::MenuItem { float sampleRate; void setSampleRate(float sampleRate) { this->sampleRate = sampleRate; text = string::f("%.0f Hz", sampleRate); rightText = CHECKMARK(APP->engine->getSampleRate() == sampleRate); } void onAction(const event::Action &e) override { APP->engine->setSampleRate(sampleRate); APP->engine->setPaused(false); } }; struct SampleRateItem : ui::MenuItem { SampleRateItem() { text = "Engine sample rate"; } ui::Menu *createChildMenu() override { ui::Menu *menu = new ui::Menu; menu->addChild(new EnginePauseItem); for (int i = 0; i <= 4; i++) { int oversample = 1 << i; SampleRateValueItem *item = new SampleRateValueItem; item->setSampleRate(44100.f * oversample); if (oversample > 1) item->text += string::f(" (%dx)", oversample); menu->addChild(item); item = new SampleRateValueItem; item->setSampleRate(48000.f * oversample); if (oversample > 1) item->text += string::f(" (%dx)", oversample); menu->addChild(item); } return menu; } }; struct ThreadCountValueItem : ui::MenuItem { int threadCount; void setThreadCount(int threadCount) { this->threadCount = threadCount; text = string::f("%d", threadCount); if (threadCount == system::getLogicalCoreCount() / 2) text += " (most modules)"; else if (threadCount == 1) text += " (lowest CPU usage)"; rightText = CHECKMARK(APP->engine->getThreadCount() == threadCount); } void onAction(const event::Action &e) override { APP->engine->setThreadCount(threadCount); } }; struct ThreadCount : ui::MenuItem { ThreadCount() { text = "Thread count"; } ui::Menu *createChildMenu() override { ui::Menu *menu = new ui::Menu; int coreCount = system::getLogicalCoreCount(); for (int i = 1; i <= coreCount; i++) { ThreadCountValueItem *item = new ThreadCountValueItem; item->setThreadCount(i); menu->addChild(item); } return menu; } }; struct FullscreenItem : ui::MenuItem { FullscreenItem() { text = "Fullscreen"; rightText = "F11"; if (APP->window->isFullScreen()) rightText = CHECKMARK_STRING " " + rightText; } void onAction(const event::Action &e) override { APP->window->setFullScreen(!APP->window->isFullScreen()); } }; struct SettingsButton : MenuButton { SettingsButton() { text = "Settings"; } void onAction(const event::Action &e) override { ui::Menu *menu = createMenu(); menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); menu->box.size.x = box.size.x; menu->addChild(new ParamTooltipItem); menu->addChild(new CpuMeterItem); menu->addChild(new LockModulesItem); menu->addChild(new SampleRateItem); menu->addChild(new ThreadCount); menu->addChild(new FullscreenItem); ui::Slider *zoomSlider = new ui::Slider; zoomSlider->box.size.x = 200.0; zoomSlider->quantity = new ZoomQuantity; menu->addChild(zoomSlider); ui::Slider *cableOpacitySlider = new ui::Slider; cableOpacitySlider->box.size.x = 200.0; cableOpacitySlider->quantity = new CableOpacityQuantity; menu->addChild(cableOpacitySlider); ui::Slider *cableTensionSlider = new ui::Slider; cableTensionSlider->box.size.x = 200.0; cableTensionSlider->quantity = new CableTensionQuantity; menu->addChild(cableTensionSlider); } }; struct RegisterItem : ui::MenuItem { RegisterItem() { text = "Register VCV account"; } void onAction(const event::Action &e) override { std::thread t([&]() { system::openBrowser("https://vcvrack.com/"); }); t.detach(); } }; struct AccountEmailField : ui::TextField { ui::TextField *passwordField; AccountEmailField() { placeholder = "Email"; } void onSelectKey(const event::SelectKey &e) override { if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) { APP->event->selectedWidget = passwordField; e.consume(this); } if (!e.getConsumed()) ui::TextField::onSelectKey(e); } }; struct AccountPasswordField : ui::PasswordField { ui::MenuItem *logInItem; AccountPasswordField() { placeholder = "Password"; } void onSelectKey(const event::SelectKey &e) override { if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) { logInItem->doAction(); e.consume(this); } if (!e.getConsumed()) ui::PasswordField::onSelectKey(e); } }; struct LogInItem : ui::MenuItem { ui::TextField *emailField; ui::TextField *passwordField; LogInItem() { text = "Log in"; } void onAction(const event::Action &e) override { std::string email = emailField->text; std::string password = passwordField->text; std::thread t([&, email, password]() { plugin::logIn(email, password); }); t.detach(); } }; struct ManageItem : ui::MenuItem { ManageItem() { text = "Manage plugins"; } void onAction(const event::Action &e) override { std::thread t([&]() { system::openBrowser("https://vcvrack.com/plugins.html"); }); t.detach(); } }; struct SyncItem : ui::MenuItem { SyncItem() { text = "Sync plugins"; disabled = true; } void onAction(const event::Action &e) override { } }; // struct SyncButton : ui::Button { // bool checked = false; // /** Updates are available */ // bool available = false; // /** Plugins have been updated */ // bool completed = false; // void step() override { // // Check for plugin update on first step() // if (!checked) { // std::thread t([this]() { // if (plugin::sync(true)) // available = true; // }); // t.detach(); // checked = true; // } // // Display message if we've completed updates // if (completed) { // if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been updated. Close Rack and re-launch it to load new updates.")) { // APP->window->close(); // } // completed = false; // } // } // void onAction(const event::Action &e) override { // available = false; // std::thread t([this]() { // if (plugin::sync(false)) // completed = true; // }); // t.detach(); // } // }; struct LogOutItem : ui::MenuItem { LogOutItem() { text = "Log out"; } void onAction(const event::Action &e) override { plugin::logOut(); } }; struct DownloadQuantity : ui::Quantity { float getValue() override { return plugin::downloadProgress; } float getDisplayValue() override { return getValue() * 100.f; } int getDisplayPrecision() override {return 0;} std::string getLabel() override { return "Downloading " + plugin::downloadName; } std::string getUnit() override {return "%";} }; struct PluginsButton : MenuButton { PluginsButton() { text = "Plugins"; } void onAction(const event::Action &e) override { ui::Menu *menu = createMenu(); menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); menu->box.size.x = box.size.x; // TODO Design dialog box for plugin syncing if (plugin::isDownloading) { ui::ProgressBar *downloadProgressBar = new ui::ProgressBar; downloadProgressBar->quantity = new DownloadQuantity; menu->addChild(downloadProgressBar); } else if (plugin::isLoggedIn()) { menu->addChild(new ManageItem); menu->addChild(new SyncItem); menu->addChild(new LogOutItem); } else { menu->addChild(new RegisterItem); AccountEmailField *emailField = new AccountEmailField; emailField->box.size.x = 200.0; menu->addChild(emailField); AccountPasswordField *passwordField = new AccountPasswordField; passwordField->box.size.x = 200.0; emailField->passwordField = passwordField; menu->addChild(passwordField); LogInItem *logInItem = new LogInItem; logInItem->emailField = emailField; logInItem->passwordField = passwordField; passwordField->logInItem = logInItem; menu->addChild(logInItem); } } void draw(const DrawArgs &args) override { MenuButton::draw(args); // if (1) { // // Notification circle // nvgBeginPath(args.vg); // nvgCircle(args.vg, box.size.x - 3, 3, 4.0); // nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0)); // nvgFill(args.vg); // nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0)); // nvgStroke(args.vg); // } } }; struct ManualItem : ui::MenuItem { ManualItem() { text = "Manual"; rightText = "F1"; } void onAction(const event::Action &e) override { std::thread t([&]() { system::openBrowser("https://vcvrack.com/manual/"); }); t.detach(); } }; struct WebsiteItem : ui::MenuItem { WebsiteItem() { text = "VCVRack.com"; } void onAction(const event::Action &e) override { std::thread t([&]() { system::openBrowser("https://vcvrack.com/"); }); t.detach(); } }; struct CheckVersionItem : ui::MenuItem { CheckVersionItem() { text = "Check version on launch"; rightText = CHECKMARK(settings.checkVersion); } void onAction(const event::Action &e) override { settings.checkVersion ^= true; } }; struct HelpButton : MenuButton { HelpButton() { text = "Help"; } void onAction(const event::Action &e) override { ui::Menu *menu = createMenu(); menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); menu->box.size.x = box.size.x; menu->addChild(new ManualItem); menu->addChild(new WebsiteItem); menu->addChild(new CheckVersionItem); } }; Toolbar::Toolbar() { const float margin = 5; box.size.y = BND_WIDGET_HEIGHT + 2*margin; ui::SequentialLayout *layout = new ui::SequentialLayout; layout->box.pos = math::Vec(margin, margin); layout->spacing = math::Vec(0, 0); addChild(layout); FileButton *fileButton = new FileButton; layout->addChild(fileButton); EditButton *editButton = new EditButton; layout->addChild(editButton); SettingsButton *settingsButton = new SettingsButton; layout->addChild(settingsButton); PluginsButton *pluginsButton = new PluginsButton; layout->addChild(pluginsButton); HelpButton *helpButton = new HelpButton; layout->addChild(helpButton); } void Toolbar::draw(const DrawArgs &args) { bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL); bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y); widget::Widget::draw(args); } } // namespace app } // namespace rack