diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc38bbb6..197a00ce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - CLAP plugins now appear in the Unverified plugin menu and can be added to a graph without a prior scan. - Graph Mixer: channel strips can be hidden via right-click and restored from the mixer background's right-click menu. +- "Menu-bar only" UI Type in Preferences: the main window starts hidden and Element is reachable exclusively through the system tray icon. The systray menu now includes a Preferences item. ### Changed - Disconnected audio devices are no longer silently replaced with another device: Element closes the device, shows its status in the status bar, and automatically restores it when it reconnects. Double-click the status label to open audio settings. @@ -15,6 +16,9 @@ - Note names throughout the UI now use scientific pitch notation (middle C = C4), matching the convention used by most DAWs. ### Fixed +- The Preferences UI Type dropdown is now wired up: selecting an option persists and is applied. Previously the value was silently ignored. +- Switching UI Type no longer rebuilds the main content, which previously reset graph layout and node positions. +- In Menu-bar only mode, closing the main window now hides to the tray instead of quitting the app. - Freeze on Windows when an ASIO audio interface is disconnected. Reconnection is now driven by system hardware notifications instead of repeatedly probing the driver. - Blocks embedded in the graph return to their embedded state when the plugin window is closed. - MIDI Monitor note names displayed two octaves too high. diff --git a/include/element/ui.hpp b/include/element/ui.hpp index 50d8a6fd2..4a916cf7a 100644 --- a/include/element/ui.hpp +++ b/include/element/ui.hpp @@ -125,6 +125,9 @@ class GuiService : public Service, /** Clears the current content component */ void clearContentComponent(); + /** Show or hide the main window according to the persisted UI Type. */ + void applyUiTypeToMainWindow(); + // TODO: content manager on selected nodes Node getSelectedNode() const { return selectedNode; } // TODO: content manager on selected nodes. diff --git a/src/services/guiservice.cpp b/src/services/guiservice.cpp index 9669a12b0..3d4c29207 100644 --- a/src/services/guiservice.cpp +++ b/src/services/guiservice.cpp @@ -41,7 +41,18 @@ namespace element { command line flag, and on Windows the shortcut "Run: Minimized" option. */ static bool shouldStartHidden (Settings& settings) { - if (! SystemTray::isAvailable() || ! settings.isSystrayEnabled()) + if (! SystemTray::isAvailable()) + return false; + + if (settings.getMainContentType() == "menubarOnly") + { + // Auto-enable the tray so the mode is actually reachable. + if (! settings.isSystrayEnabled()) + settings.setSystrayEnabled (true); + return true; + } + + if (! settings.isSystrayEnabled()) return false; if (settings.isStartHiddenEnabled()) @@ -1179,6 +1190,31 @@ void GuiService::refreshSystemTray() #endif } +void GuiService::applyUiTypeToMainWindow() +{ + if (! mainWindow) + return; + + const bool menubarOnly = context().settings().getMainContentType() == "menubarOnly"; + + if (menubarOnly) + { + if (mainWindow->isOnDesktop()) + { + mainWindow->removeFromDesktop(); + closeAllPluginWindows (true); + } + } + else if (! mainWindow->isOnDesktop()) + { + mainWindow->addToDesktop(); + mainWindow->setBoundsConstrained (mainWindow->getBounds()); + mainWindow->toFront (true); + if (auto session = context().session()) + showPluginWindowsFor (session->getActiveGraph(), true, false); + } +} + void GuiService::setMainWindowTitler (std::function f) { if (mainWindow) @@ -1262,6 +1298,7 @@ bool GuiService::handleMessage (const AppMessage& msg) stabilizeContent(); refreshMainMenu(); refreshSystemTray(); + applyUiTypeToMainWindow(); } return true; diff --git a/src/settings.cpp b/src/settings.cpp index c816dfab6..accf25a62 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -250,8 +250,23 @@ double Settings::getDesktopScale() const { return getDouble (desktopScaleKey, 1. void Settings::setDesktopScale (double scale) { set (desktopScaleKey, jlimit (0.1, 8.0, scale)); } //============================================================================= -String Settings::getMainContentType() const { return "standard"; } -void Settings::setMainContentType (const String& tp) { ignoreUnused (tp); } +String Settings::getMainContentType() const +{ + auto value = getString (mainContentTypeKey, "standard"); + if (value != "standard" && value != "menubarOnly") + return "standard"; + return value; +} + +void Settings::setMainContentType (const String& tp) +{ + if (tp != "standard" && tp != "menubarOnly") + { + jassertfalse; + return; + } + set (mainContentTypeKey, tp); +} String Settings::getClockSource() const { return getString (clockSourceKey, "internal"); } diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index d7dd0e08f..bbc1d5079 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -119,6 +119,14 @@ void MainWindow::nameChangedSession() void MainWindow::closeButtonPressed() { + auto& settings = world.settings(); + if (settings.getMainContentType() == "menubarOnly") + { + auto& gui = *world.services().find(); + gui.commands().invokeDirectly (Commands::toggleUserInterface, true); + return; + } + JUCEApplication* app (JUCEApplication::getInstance()); app->systemRequestedQuit(); } diff --git a/src/ui/preferences.cpp b/src/ui/preferences.cpp index 560d45003..6213e5a23 100644 --- a/src/ui/preferences.cpp +++ b/src/ui/preferences.cpp @@ -479,8 +479,12 @@ class GeneralSettingsPage : public SettingsPage, mainContentLabel.setFont (Font (FontOptions (12.0, Font::bold))); addAndMakeVisible (mainContentBox); mainContentBox.addItem ("Standard", 1); - jassert (settings.getMainContentType() == "standard"); - mainContentBox.setSelectedId (1, dontSendNotification); + mainContentBox.addItem ("Menu-bar only", 2); + { + const auto uitype = settings.getMainContentType(); + const int selectedId = uitype == "menubarOnly" ? 2 : 1; + mainContentBox.setSelectedId (selectedId, dontSendNotification); + } mainContentBox.getSelectedIdAsValue().addListener (this); } @@ -552,11 +556,16 @@ class GeneralSettingsPage : public SettingsPage, } else if (value.refersToSameSourceAs (mainContentBox.getSelectedIdAsValue())) { - const String uitype = "standard"; + const String uitype = mainContentBox.getSelectedId() == 2 + ? String ("menubarOnly") + : String ("standard"); if (uitype != settings.getMainContentType()) { settings.setMainContentType (uitype); - ViewHelpers::postMessageFor (this, new ReloadMainContentMessage()); + if (uitype == "menubarOnly" && ! settings.isSystrayEnabled()) + settings.setSystrayEnabled (true); + gui.applyUiTypeToMainWindow(); + gui.refreshSystemTray(); } } diff --git a/src/ui/systemtray.cpp b/src/ui/systemtray.cpp index 03f357f6e..fe1de1f83 100644 --- a/src/ui/systemtray.cpp +++ b/src/ui/systemtray.cpp @@ -110,6 +110,7 @@ void SystemTray::mouseUp (const MouseEvent& ev) { PopupMenu menu; menu.addCommandItem (cmd, Commands::toggleUserInterface, "Show/Hide"); + menu.addCommandItem (cmd, Commands::showPreferences, "Preferences..."); menu.addSeparator(); menu.addCommandItem (cmd, Commands::quit, "Exit"); #if JUCE_MAC @@ -139,6 +140,7 @@ void SystemTray::runMenu() PopupMenu menu; menu.addCommandItem (cmd, Commands::toggleUserInterface, "Show/Hide"); + menu.addCommandItem (cmd, Commands::showPreferences, "Preferences..."); menu.addSeparator(); menu.addCommandItem (cmd, Commands::quit, "Exit"); #if JUCE_MAC