diff --git a/include/element/settings.hpp b/include/element/settings.hpp index d4c56f151..91376049e 100644 --- a/include/element/settings.hpp +++ b/include/element/settings.hpp @@ -39,6 +39,7 @@ class Settings : public juce::ApplicationProperties, static const char* oscHostPortKey; static const char* oscHostEnabledKey; static const char* systrayKey; + static const char* startHiddenKey; static const char* midiOutLatencyKey; static const char* desktopScaleKey; static const char* mainContentTypeKey; @@ -55,7 +56,11 @@ class Settings : public juce::ApplicationProperties, static const char* transportStartStopContinue; bool getBool (std::string_view key, bool fallback = false) const noexcept; + int getInt (std::string_view key, int fallback = 0) const noexcept; + double getDouble (std::string_view key, double fallback = 0.0) const noexcept; + juce::String getString (std::string_view key, const juce::String& fallback = {}) const; + /** Stores a value. Does nothing if the stored value is already equal. */ void set (std::string_view key, const juce::var& value); std::unique_ptr getLastGraph() const; @@ -89,7 +94,7 @@ class Settings : public juce::ApplicationProperties, void setPluginWindowsOnTop (const bool); /** True if the user should be prompted to save when exiting the app */ - bool askToSaveSession(); + bool askToSaveSession() const; void setAskToSaveSession (const bool); const juce::File getDefaultNewSessionFile() const; @@ -118,6 +123,11 @@ class Settings : public juce::ApplicationProperties, bool isSystrayEnabled() const; void setSystrayEnabled (bool); + /** True if the main window should start hidden in the system tray. + Only meaningful when the system tray is enabled and available. */ + bool isStartHiddenEnabled() const; + void setStartHiddenEnabled (bool); + double getMidiOutLatency() const; void setMidiOutLatency (double latencyMs); diff --git a/src/devicemanager.cpp b/src/devicemanager.cpp index 87aa9b3c6..452d56e96 100644 --- a/src/devicemanager.cpp +++ b/src/devicemanager.cpp @@ -5,15 +5,9 @@ #include #include "engine/jack.hpp" +#include "win32.hpp" #if JUCE_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include #include #endif diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index bb2a2b29f..1dee63352 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -30,10 +30,10 @@ #include extern char* program_invocation_name; +#include "win32.hpp" + #if JUCE_WINDOWS -#define WIN32_LEAN_AND_MEAN #include -#include #else #include #include diff --git a/src/services.cpp b/src/services.cpp index 232632026..538eb5354 100644 --- a/src/services.cpp +++ b/src/services.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "engine/graphmanager.hpp" #include "presetmanager.hpp" @@ -182,14 +183,11 @@ void Services::run() { gui->stabilizeContent(); const Node graph (session->getCurrentGraph()); - auto* const props = context().settings().getUserSettings(); + auto* const window = gui->getMainWindow(); - if (graph.isValid()) - { - // don't show plugin windows on load if the UI was hidden - if (props->getBoolValue ("mainWindowVisible", true)) - gui->showPluginWindowsFor (graph); - } + // don't show plugin windows on load if the UI is hidden + if (graph.isValid() && window != nullptr && window->isOnDesktop()) + gui->showPluginWindowsFor (graph); } } diff --git a/src/services/guiservice.cpp b/src/services/guiservice.cpp index 35f7ed683..9669a12b0 100644 --- a/src/services/guiservice.cpp +++ b/src/services/guiservice.cpp @@ -28,6 +28,7 @@ #include "ui/systemtray.hpp" #include "ui/virtualkeyboardview.hpp" #include "ui/windowmanager.hpp" +#include "win32.hpp" #ifndef ELEMENT_USE_SYSTEM_TRAY #define ELEMENT_USE_SYSTEM_TRAY 1 @@ -35,6 +36,32 @@ namespace element { +/** Returns true if the main window should not be shown at launch and instead + wait in the system tray. Honours the "start hidden" setting, a `--hidden` + command line flag, and on Windows the shortcut "Run: Minimized" option. */ +static bool shouldStartHidden (Settings& settings) +{ + if (! SystemTray::isAvailable() || ! settings.isSystrayEnabled()) + return false; + + if (settings.isStartHiddenEnabled()) + return true; + + if (JUCEApplicationBase::getCommandLineParameterArray().contains ("--hidden")) + return true; + +#if JUCE_WINDOWS + STARTUPINFOW info = {}; + info.cb = sizeof (info); + GetStartupInfoW (&info); + if ((info.dwFlags & STARTF_USESHOWWINDOW) != 0 + && (info.wShowWindow == SW_SHOWMINIMIZED || info.wShowWindow == SW_SHOWMINNOACTIVE || info.wShowWindow == SW_MINIMIZE)) + return true; +#endif + + return false; +} + //============================================================================= class DefaultContentFactory : public ContentFactory { @@ -343,7 +370,6 @@ void GuiService::saveProperties (PropertiesFile* props) { props->setValue ("mainWindowState", mainWindow->getWindowStateAsString()); props->setValue ("mainWindowFullScreen", mainWindow->isFullScreen()); - props->setValue ("mainWindowVisible", mainWindow->isOnDesktop() && mainWindow->isVisible()); } if (_content) @@ -673,17 +699,24 @@ void GuiService::run() mainWindow->setContentNonOwned (content(), true); mainWindow->centreWithSize (_content->getWidth(), _content->getHeight()); - mainWindow->restoreWindowStateFromString (pf->getValue ("mainWindowState")); mainWindow->addKeyListener (keys.get()); mainWindow->addKeyListener (commands().getKeyMappings()); _content->restoreState (pf); - if (pf->getBoolValue ("mainWindowVisible", true)) + const auto windowState = pf->getValue ("mainWindowState"); + if (! shouldStartHidden (settings)) { + // Create the native peer before restoring bounds so the window frame is + // known and the title bar is kept clear of docked taskbars. + mainWindow->addToDesktop(); + mainWindow->restoreWindowStateFromString (windowState); mainWindow->setVisible (true); if (pf->getBoolValue ("mainWindowFullScreen", false)) mainWindow->setFullScreen (true); - mainWindow->addToDesktop(); + } + else + { + mainWindow->restoreWindowStateFromString (windowState); } sibling()->resetChanges(); @@ -996,6 +1029,9 @@ bool GuiService::perform (const InvocationInfo& info) else { window->addToDesktop(); + // Re-run the constrainer now the native frame is known so + // the title bar isn't left under a docked taskbar. + window->setBoundsConstrained (window->getBounds()); window->toFront (true); if (session) showPluginWindowsFor (session->getActiveGraph(), true, false); diff --git a/src/settings.cpp b/src/settings.cpp index 14a15b4fd..c816dfab6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -29,6 +29,7 @@ const char* Settings::midiEngineKey = "midiEngine"; const char* Settings::oscHostPortKey = "oscHostPortKey"; const char* Settings::oscHostEnabledKey = "oscHostEnabledKey"; const char* Settings::systrayKey = "systrayKey"; +const char* Settings::startHiddenKey = "startHidden"; const char* Settings::midiOutLatencyKey = "midiOutLatency"; const char* Settings::desktopScaleKey = "desktopScale"; const char* Settings::mainContentTypeKey = "mainContentType"; @@ -113,31 +114,33 @@ Settings::Settings() Settings::~Settings() {} //============================================================================= -bool Settings::checkForUpdates() const +PropertiesFile* Settings::getProps() const { - if (auto* props = getProps()) - return props->getBoolValue (checkForUpdatesKey, true); - return false; + return (const_cast (this))->getUserSettings(); } -void Settings::setCheckForUpdates (const bool shouldCheck) +bool Settings::getBool (std::string_view key, bool fallback) const noexcept { - if (shouldCheck == checkForUpdates()) - return; - if (auto* p = getProps()) - p->setValue (checkForUpdatesKey, shouldCheck); + auto p = getProps(); + return p != nullptr ? p->getBoolValue (key.data(), fallback) : fallback; } -//============================================================================= -PropertiesFile* Settings::getProps() const +int Settings::getInt (std::string_view key, int fallback) const noexcept { - return (const_cast (this))->getUserSettings(); + auto p = getProps(); + return p != nullptr ? p->getIntValue (key.data(), fallback) : fallback; } -bool Settings::getBool (std::string_view key, bool fallback) const noexcept +double Settings::getDouble (std::string_view key, double fallback) const noexcept { auto p = getProps(); - return p != nullptr ? p->getBoolValue (key.data(), fallback) : fallback; + return p != nullptr ? p->getDoubleValue (key.data(), fallback) : fallback; +} + +String Settings::getString (std::string_view key, const String& fallback) const +{ + auto p = getProps(); + return p != nullptr ? p->getValue (key.data(), fallback) : fallback; } void Settings::set (std::string_view key, const var& value) @@ -147,10 +150,13 @@ void Settings::set (std::string_view key, const var& value) } //============================================================================= +bool Settings::checkForUpdates() const { return getBool (checkForUpdatesKey, true); } +void Settings::setCheckForUpdates (const bool shouldCheck) { set (checkForUpdatesKey, shouldCheck); } + std::unique_ptr Settings::getLastGraph() const { if (auto* p = getProps()) - return p->getXmlValue ("lastGraph"); + return p->getXmlValue (lastGraphKey); return nullptr; } @@ -161,129 +167,41 @@ void Settings::setLastGraph (const ValueTree& data) return; if (auto* p = getProps()) if (auto xml = data.createXml()) - p->setValue ("lastGraph", xml.get()); -} - -//============================================================================= -bool Settings::scanForPluginsOnStartup() const -{ - if (auto* p = getProps()) - return p->getBoolValue (scanForPluginsOnStartKey, false); - return false; -} - -void Settings::setScanForPluginsOnStartup (const bool shouldScan) -{ - if (shouldScan == scanForPluginsOnStartup()) - return; - if (auto* p = getProps()) - p->setValue (scanForPluginsOnStartKey, shouldScan); -} - -//============================================================================= -bool Settings::showPluginWindowsWhenAdded() const -{ - if (auto* p = getProps()) - return p->getBoolValue (showPluginWindowsKey, false); - return false; -} - -void Settings::setShowPluginWindowsWhenAdded (const bool shouldShow) -{ - if (shouldShow == showPluginWindowsWhenAdded()) - return; - if (auto* p = getProps()) - p->setValue (showPluginWindowsKey, shouldShow); -} - -//============================================================================= -bool Settings::openLastUsedSession() const -{ - if (auto* p = getProps()) - return p->getBoolValue (openLastUsedSessionKey, true); - return true; -} - -void Settings::setOpenLastUsedSession (const bool shouldOpen) -{ - if (shouldOpen == openLastUsedSession()) - return; - if (auto* p = getProps()) - p->setValue (openLastUsedSessionKey, shouldOpen); -} - -//============================================================================= -void Settings::setGenerateMidiClock (const bool generate) -{ - if (auto* p = getProps()) - return p->setValue (generateMidiClockKey, generate); + p->setValue (lastGraphKey, xml.get()); } -bool Settings::generateMidiClock() const -{ - if (auto* p = getProps()) - return p->getBoolValue (generateMidiClockKey, false); - return false; -} +bool Settings::scanForPluginsOnStartup() const { return getBool (scanForPluginsOnStartKey, false); } +void Settings::setScanForPluginsOnStartup (const bool shouldScan) { set (scanForPluginsOnStartKey, shouldScan); } -bool Settings::pluginWindowsOnTop() const -{ - if (auto* p = getProps()) - return p->getBoolValue (pluginWindowOnTopDefault, true); - return false; -} +bool Settings::showPluginWindowsWhenAdded() const { return getBool (showPluginWindowsKey, false); } +void Settings::setShowPluginWindowsWhenAdded (const bool shouldShow) { set (showPluginWindowsKey, shouldShow); } -bool Settings::askToSaveSession() -{ - if (auto* props = getProps()) - return props->getBoolValue (askToSaveSessionKey, true); - return false; -} +bool Settings::openLastUsedSession() const { return getBool (openLastUsedSessionKey, true); } +void Settings::setOpenLastUsedSession (const bool shouldOpen) { set (openLastUsedSessionKey, shouldOpen); } -void Settings::setAskToSaveSession (const bool value) -{ - if (auto* props = getProps()) - props->setValue (askToSaveSessionKey, value); -} +bool Settings::generateMidiClock() const { return getBool (generateMidiClockKey, false); } +void Settings::setGenerateMidiClock (const bool generate) { set (generateMidiClockKey, generate); } -bool Settings::sendMidiClockToInput() const -{ - if (auto* props = getProps()) - return props->getBoolValue (sendMidiClockToInputKey, false); - return false; -} +bool Settings::pluginWindowsOnTop() const { return getBool (pluginWindowOnTopDefault, true); } +void Settings::setPluginWindowsOnTop (const bool onTop) { set (pluginWindowOnTopDefault, onTop); } -void Settings::setSendMidiClockToInput (const bool value) -{ - if (auto* props = getProps()) - props->setValue (sendMidiClockToInputKey, value); -} +bool Settings::askToSaveSession() const { return getBool (askToSaveSessionKey, true); } +void Settings::setAskToSaveSession (const bool value) { set (askToSaveSessionKey, value); } -void Settings::setPluginWindowsOnTop (const bool onTop) -{ - if (onTop == pluginWindowsOnTop()) - return; - if (auto* p = getProps()) - p->setValue (pluginWindowOnTopDefault, onTop); -} +bool Settings::sendMidiClockToInput() const { return getBool (sendMidiClockToInputKey, false); } +void Settings::setSendMidiClockToInput (const bool value) { set (sendMidiClockToInputKey, value); } const File Settings::getDefaultNewSessionFile() const { - if (auto* p = getProps()) - { - const auto value = p->getValue (defaultNewSessionFile); - if (value.isNotEmpty() && File::isAbsolutePath (value)) - return File (value); - } - + const auto value = getString (defaultNewSessionFile); + if (value.isNotEmpty() && File::isAbsolutePath (value)) + return File (value); return File(); } void Settings::setDefaultNewSessionFile (const File& file) { - if (auto* p = getProps()) - p->setValue (defaultNewSessionFile, - file.existsAsFile() ? file.getFullPathName() : ""); + set (defaultNewSessionFile, file.existsAsFile() ? file.getFullPathName() : String()); } bool Settings::hidePluginWindowsWhenFocusLost() const @@ -293,158 +211,62 @@ bool Settings::hidePluginWindowsWhenFocusLost() const #else const bool fallback = true; #endif - - if (auto* p = getProps()) - return p->getBoolValue (hidePluginWindowsWhenFocusLostKey, fallback); - return fallback; + return getBool (hidePluginWindowsWhenFocusLostKey, fallback); } -void Settings::setHidePluginWindowsWhenFocusLost (const bool hideThem) -{ - if (hideThem == hidePluginWindowsWhenFocusLost()) - return; - if (auto* p = getProps()) - p->setValue (hidePluginWindowsWhenFocusLostKey, hideThem); -} +void Settings::setHidePluginWindowsWhenFocusLost (const bool hideThem) { set (hidePluginWindowsWhenFocusLostKey, hideThem); } -bool Settings::useLegacyInterface() const -{ - if (auto* p = getProps()) - return p->getBoolValue (legacyInterfaceKey, false); - return false; -} - -void Settings::setUseLegacyInterface (const bool useLegacy) -{ - if (useLegacy == useLegacyInterface()) - return; - if (auto* p = getProps()) - p->setValue (legacyInterfaceKey, useLegacy); -} - -bool Settings::isOscHostEnabled() const -{ - if (auto* p = getProps()) - return p->getBoolValue (oscHostEnabledKey, false); - return false; -} - -void Settings::setOscHostEnabled (bool enabled) -{ - if (isOscHostEnabled() == enabled) - return; - if (auto* p = getProps()) - p->setValue (oscHostEnabledKey, enabled); -} +bool Settings::useLegacyInterface() const { return getBool (legacyInterfaceKey, false); } +void Settings::setUseLegacyInterface (const bool useLegacy) { set (legacyInterfaceKey, useLegacy); } //============================================================================= -int Settings::getOscHostPort() const -{ - if (auto* p = getProps()) - return p->getIntValue (oscHostPortKey, 9000); - return 9000; -} +bool Settings::isOscHostEnabled() const { return getBool (oscHostEnabledKey, false); } +void Settings::setOscHostEnabled (bool enabled) { set (oscHostEnabledKey, enabled); } -void Settings::setOscHostPort (int port) -{ - if (getOscHostPort() == port) - return; - if (auto* p = getProps()) - p->setValue (oscHostPortKey, port); -} +int Settings::getOscHostPort() const { return getInt (oscHostPortKey, 9000); } +void Settings::setOscHostPort (int port) { set (oscHostPortKey, port); } //============================================================================= bool Settings::isSystrayEnabled() const { #if JUCE_LINUX - const bool defaultSysTrayEnabled = false; + const bool fallback = false; #else - const bool defaultSysTrayEnabled = true; + const bool fallback = true; #endif - - if (auto* p = getProps()) - return p->getBoolValue (systrayKey, defaultSysTrayEnabled); - return defaultSysTrayEnabled; + return getBool (systrayKey, fallback); } -void Settings::setSystrayEnabled (bool enabled) -{ - if (isSystrayEnabled() == enabled) - return; - if (auto* p = getProps()) - p->setValue (systrayKey, enabled); -} +void Settings::setSystrayEnabled (bool enabled) { set (systrayKey, enabled); } -//============================================================================= -double Settings::getMidiOutLatency() const -{ - if (auto* p = getProps()) - return p->getDoubleValue (midiOutLatencyKey, true); - return 0.0; -} - -void Settings::setMidiOutLatency (double latencyMs) -{ - if (latencyMs == getMidiOutLatency()) - return; - if (auto* p = getProps()) - p->setValue (midiOutLatencyKey, latencyMs); -} +bool Settings::isStartHiddenEnabled() const { return getBool (startHiddenKey, false); } +void Settings::setStartHiddenEnabled (bool enabled) { set (startHiddenKey, enabled); } //============================================================================= -double Settings::getDesktopScale() const -{ - if (auto* p = getProps()) - return p->getDoubleValue (desktopScaleKey, 1.0); - return 1.0; -} +double Settings::getMidiOutLatency() const { return getDouble (midiOutLatencyKey, 0.0); } +void Settings::setMidiOutLatency (double latencyMs) { set (midiOutLatencyKey, latencyMs); } -void Settings::setDesktopScale (double scale) -{ - if (scale == getDesktopScale()) - return; - scale = jlimit (0.1, 8.0, scale); - if (auto* p = getProps()) - p->setValue (desktopScaleKey, scale); -} +double Settings::getDesktopScale() const { return getDouble (desktopScaleKey, 1.0); } +void Settings::setDesktopScale (double scale) { set (desktopScaleKey, jlimit (0.1, 8.0, scale)); } //============================================================================= -String Settings::getMainContentType() const -{ - return "standard"; -} +String Settings::getMainContentType() const { return "standard"; } +void Settings::setMainContentType (const String& tp) { ignoreUnused (tp); } -void Settings::setMainContentType (const String& tp) -{ - ignoreUnused (tp); -} +String Settings::getClockSource() const { return getString (clockSourceKey, "internal"); } -//============================================================================= -juce::String Settings::getClockSource() const -{ - if (auto* p = getProps()) - return p->getValue (clockSourceKey, "internal"); - return "internal"; -} - -void Settings::setClockSource (const juce::String& src) +void Settings::setClockSource (const String& src) { if (src != "internal" && src != "midiClock") { jassertfalse; return; } - - if (auto p = getProps()) - p->setValue (clockSourceKey, src); + set (clockSourceKey, src); } -juce::String Settings::getUpdateKeyType() const -{ - if (auto* p = getProps()) - return p->getValue (updateKeyTypeKey, "element-v1"); - return "element-v1"; -} +//============================================================================= +String Settings::getUpdateKeyType() const { return getString (updateKeyTypeKey, "element-v1"); } void Settings::setUpdateKeyType (const String& slug) { @@ -453,113 +275,58 @@ void Settings::setUpdateKeyType (const String& slug) jassertfalse; return; } - - if (auto p = getProps()) - p->setValue (updateKeyTypeKey, slug); + set (updateKeyTypeKey, slug); } -juce::String Settings::getUpdateKeyUser() const -{ - if (auto* p = getProps()) - return p->getValue (updateKeyUserKey, ""); - return ""; -} +String Settings::getUpdateKeyUser() const { return getString (updateKeyUserKey); } +void Settings::setUpdateKeyUser (const String& user) { set (updateKeyUserKey, user.trim()); } -void Settings::setUpdateKeyUser (const String& user) -{ - if (auto p = getProps()) - p->setValue (updateKeyUserKey, user.trim()); -} - -juce::String Settings::getUpdateKey() const -{ - if (auto* p = getProps()) - return p->getValue (updateKeyKey, ""); - return ""; -} +String Settings::getUpdateKey() const { return getString (updateKeyKey); } void Settings::setUpdateKey (const String& slug) { - if (auto p = getProps()) - p->setValue (updateKeyKey, slug.trim()); + set (updateKeyKey, slug.trim()); sendChangeMessage(); } -juce::String Settings::getUpdateChannel() const -{ - if (auto* p = getProps()) - return p->getValue (updateChannelKey, ""); - return ""; -} - -void Settings::setUpdateChannel (const String& channel) -{ - if (auto p = getProps()) - p->setValue (updateChannelKey, channel.trim()); -} +String Settings::getUpdateChannel() const { return getString (updateChannelKey); } +void Settings::setUpdateChannel (const String& channel) { set (updateChannelKey, channel.trim()); } -bool Settings::getAuthPreviewUpdates() const -{ - if (auto* p = getProps()) - return p->getBoolValue (authPreviewUpdatesKey, false); - return false; -} +bool Settings::getAuthPreviewUpdates() const { return getBool (authPreviewUpdatesKey, false); } void Settings::setAuthPreviewUpdates (bool enabled) { - if (auto* p = getProps()) - p->setValue (authPreviewUpdatesKey, enabled); + set (authPreviewUpdatesKey, enabled); sendChangeMessage(); } -juce::String Settings::getAuthAppcastUrl() const -{ - if (auto* p = getProps()) - return p->getValue (authAppcastUrlKey); - return {}; -} +String Settings::getAuthAppcastUrl() const { return getString (authAppcastUrlKey); } -void Settings::setAuthAppcastUrl (const juce::String& url) +void Settings::setAuthAppcastUrl (const String& url) { - if (auto* p = getProps()) - p->setValue (authAppcastUrlKey, url); + set (authAppcastUrlKey, url); sendChangeMessage(); } +//============================================================================= void Settings::setMidiPanicParams (MidiPanicParams params) { - if (auto p = getProps()) - { - p->setValue ("midiPanicCCEnabled", params.enabled); - p->setValue ("midiPanicCCNumber", params.ccNumber); - p->setValue ("midiPanicChannel", params.channel); - } + set ("midiPanicCCEnabled", params.enabled); + set ("midiPanicCCNumber", params.ccNumber); + set ("midiPanicChannel", params.channel); } MidiPanicParams Settings::getMidiPanicParams() const { MidiPanicParams params; - if (auto p = getProps()) - { - params.enabled = p->getBoolValue ("midiPanicCCEnabled", false); - params.ccNumber = p->getIntValue ("midiPanicCCNumber", -1); - params.channel = p->getIntValue ("midiPanicChannel", 1); - } + params.enabled = getBool ("midiPanicCCEnabled", false); + params.ccNumber = getInt ("midiPanicCCNumber", -1); + params.channel = getInt ("midiPanicChannel", 1); return params; } -void Settings::setTransportRespondToStartStopContinue (bool shouldRespond) -{ - if (auto p = getProps()) - p->setValue (transportStartStopContinue, shouldRespond); -} - -bool Settings::transportRespondToStartStopContinue() const -{ - if (auto* p = getProps()) - return p->getBoolValue (transportStartStopContinue, false); - return false; -} +bool Settings::transportRespondToStartStopContinue() const { return getBool (transportStartStopContinue, false); } +void Settings::setTransportRespondToStartStopContinue (bool shouldRespond) { set (transportStartStopContinue, shouldRespond); } //============================================================================= void Settings::addItemsToMenu (Context& world, PopupMenu& menu) diff --git a/src/spinlock.cpp b/src/spinlock.cpp index d399a3bc7..a2cfc9822 100644 --- a/src/spinlock.cpp +++ b/src/spinlock.cpp @@ -2,15 +2,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include "win32.hpp" -#if _WIN32 -#define WINDOWS_LWAN -#define WIN32_LEAN_AND_MEAN 1 -#include -#undef WIN32_LEAN_AND_MEAN -#undef min -#undef max -#else +#if ! JUCE_WINDOWS #include #endif diff --git a/src/ui/preferences.cpp b/src/ui/preferences.cpp index 255df17e7..560d45003 100644 --- a/src/ui/preferences.cpp +++ b/src/ui/preferences.cpp @@ -93,6 +93,44 @@ class Preferences::PageList : public ListBox, String page; }; +//============================================================================== +/** A labelled on/off setting: a bold label on the left and a SettingButton on + the right. Owned by a SettingsPage and laid out with layoutSetting(). */ +class BoolSettingRow +{ +public: + /** Adds the label and button to the parent and wires the change callback. + + @param parent The page that owns this row. + @param text The label text. + @param initial The initial toggle state. + @param onChange Called after the user toggles the button. + */ + void init (Component& parent, const String& text, bool initial, std::function onChange) + { + parent.addAndMakeVisible (label); + label.setText (text, dontSendNotification); + label.setFont (Font (FontOptions (12.0, Font::bold))); + parent.addAndMakeVisible (button); + button.setClickingTogglesState (true); + button.setToggleState (initial, dontSendNotification); + button.onClick = std::move (onChange); + } + + bool get() const { return button.getToggleState(); } + void set (bool state) { button.setToggleState (state, dontSendNotification); } + void setYesNoText (const String& yes, const String& no) { button.setYesNoText (yes, no); } + + void setEnabled (bool enabled) + { + label.setEnabled (enabled); + button.setEnabled (enabled); + } + + Label label; + SettingButton button; +}; + //============================================================================== class SettingsPage : public Component { @@ -101,6 +139,11 @@ class SettingsPage : public Component virtual ~SettingsPage() {} protected: + void layoutSetting (Rectangle& r, BoolSettingRow& row) + { + layoutSetting (r, row.label, row.button); + } + virtual void layoutSetting (Rectangle& r, Label& label, Component& setting, const int valueWidth = -1, const int keyWidth = -1) { const int spacingBetweenSections = 6; @@ -128,17 +171,11 @@ class OSCSettingsPage : public SettingsPage, : world (w), gui (g) { auto& settings = world.settings(); - addAndMakeVisible (enabledLabel); - enabledLabel.setFont (Font (FontOptions (12.0, Font::bold))); - enabledLabel.setText ("OSC Host Enabled?", dontSendNotification); - addAndMakeVisible (enabledButton); - enabledButton.setYesNoText ("Yes", "No"); - enabledButton.setClickingTogglesState (true); - enabledButton.setToggleState (settings.isOscHostEnabled(), dontSendNotification); - enabledButton.onClick = [this]() { + enabled.init (*this, "OSC Host Enabled?", settings.isOscHostEnabled(), [this]() { updateEnablement(); triggerAsyncUpdate(); - }; + }); + enabled.setYesNoText ("Yes", "No"); addAndMakeVisible (hostLabel); hostLabel.setFont (Font (FontOptions (12.0, Font::bold))); @@ -169,7 +206,7 @@ class OSCSettingsPage : public SettingsPage, void resized() override { auto r = getLocalBounds(); - layoutSetting (r, enabledLabel, enabledButton); + layoutSetting (r, enabled); layoutSetting (r, hostLabel, hostField, getWidth() / 2); layoutSetting (r, portLabel, portSlider, getWidth() / 4); } @@ -177,8 +214,7 @@ class OSCSettingsPage : public SettingsPage, private: Context& world; GuiService& gui; - Label enabledLabel; - SettingButton enabledButton; + BoolSettingRow enabled; Label hostLabel; TextEditor hostField; Label portLabel; @@ -197,9 +233,9 @@ class OSCSettingsPage : public SettingsPage, void updateEnablement() { - world.settings().setOscHostEnabled (enabledButton.getToggleState()); - hostField.setEnabled (enabledButton.getToggleState()); - portSlider.setEnabled (enabledButton.getToggleState()); + world.settings().setOscHostEnabled (enabled.get()); + hostField.setEnabled (enabled.get()); + portSlider.setEnabled (enabled.get()); } }; @@ -316,8 +352,7 @@ class PluginSettingsComponent : public SettingsPage, //============================================================================== class GeneralSettingsPage : public SettingsPage, public Value::Listener, - public FilenameComponentListener, - public Button::Listener + public FilenameComponentListener { public: enum ComboBoxIDs @@ -338,6 +373,12 @@ class GeneralSettingsPage : public SettingsPage, engine (world.audio()), gui (g) { +#if ! ELEMENT_SE + const String sessionStr = "session"; +#else + const String sessionStr = "graph"; +#endif + addAndMakeVisible (clockSourceLabel); clockSourceLabel.setText ("Clock Source", dontSendNotification); clockSourceLabel.setFont (Font (FontOptions (12.0, Font::bold))); @@ -345,79 +386,48 @@ class GeneralSettingsPage : public SettingsPage, clockSourceBox.addItem ("Internal", ClockSourceInternal); clockSourceBox.addItem ("MIDI Clock", ClockSourceMidiClock); clockSource.referTo (clockSourceBox.getSelectedIdAsValue()); -#if ELEMENT_UPDATER - addAndMakeVisible (checkForUpdatesLabel); - checkForUpdatesLabel.setText ("Check for updates on startup", dontSendNotification); - checkForUpdatesLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (checkForUpdates); - checkForUpdates.setClickingTogglesState (true); - checkForUpdates.setToggleState (settings.checkForUpdates(), dontSendNotification); - checkForUpdates.getToggleStateValue().addListener (this); -#endif - addAndMakeVisible (scanForPlugsLabel); - scanForPlugsLabel.setText ("Scan plugins on startup", dontSendNotification); - scanForPlugsLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (scanForPlugins); - scanForPlugins.setClickingTogglesState (true); - scanForPlugins.setToggleState (settings.scanForPluginsOnStartup(), dontSendNotification); - scanForPlugins.getToggleStateValue().addListener (this); - - addAndMakeVisible (showPluginWindowsLabel); - showPluginWindowsLabel.setText ("Automatically show plugin windows", dontSendNotification); - showPluginWindowsLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (showPluginWindows); - showPluginWindows.setClickingTogglesState (true); - showPluginWindows.setToggleState (settings.showPluginWindowsWhenAdded(), dontSendNotification); - showPluginWindows.getToggleStateValue().addListener (this); - - addAndMakeVisible (pluginWindowsOnTopLabel); - pluginWindowsOnTopLabel.setText ("Plugin windows on top by default", dontSendNotification); - pluginWindowsOnTopLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (pluginWindowsOnTop); - pluginWindowsOnTop.setClickingTogglesState (true); - pluginWindowsOnTop.setToggleState (settings.pluginWindowsOnTop(), dontSendNotification); - pluginWindowsOnTop.getToggleStateValue().addListener (this); - - addAndMakeVisible (hidePluginWindowsLabel); - hidePluginWindowsLabel.setText ("Hide plugin windows when app inactive", dontSendNotification); - hidePluginWindowsLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (hidePluginWindows); - hidePluginWindows.setClickingTogglesState (true); - hidePluginWindows.setToggleState (settings.hidePluginWindowsWhenFocusLost(), dontSendNotification); - hidePluginWindows.getToggleStateValue().addListener (this); - - addAndMakeVisible (openLastSessionLabel); -#if ! ELEMENT_SE - const String sessionStr = "session"; -#else - const String sessionStr = "graph"; +#if ELEMENT_UPDATER + checkForUpdates.init (*this, "Check for updates on startup", settings.checkForUpdates(), [this]() { + settings.setCheckForUpdates (checkForUpdates.get()); + settingChanged(); + }); #endif - - openLastSessionLabel.setText (String ("Open last used XXX").replace ("XXX", sessionStr), - dontSendNotification); - openLastSessionLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (openLastSession); - openLastSession.setClickingTogglesState (true); - openLastSession.setToggleState (settings.openLastUsedSession(), dontSendNotification); - openLastSession.getToggleStateValue().addListener (this); - - addAndMakeVisible (askToSaveSessionLabel); - askToSaveSessionLabel.setText (String ("Ask to save XXXs on exit").replace ("XXX", sessionStr), - dontSendNotification); - askToSaveSessionLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (askToSaveSession); - askToSaveSession.setClickingTogglesState (true); - askToSaveSession.setToggleState (settings.askToSaveSession(), dontSendNotification); - askToSaveSession.getToggleStateValue().addListener (this); - - addAndMakeVisible (systrayLabel); - systrayLabel.setText ("Show system tray", dontSendNotification); - systrayLabel.setFont (Font (FontOptions (12.0, Font::bold))); - addAndMakeVisible (systray); - systray.setClickingTogglesState (true); - systray.setToggleState (settings.isSystrayEnabled(), dontSendNotification); - systray.getToggleStateValue().addListener (this); + scanForPlugins.init (*this, "Scan plugins on startup", settings.scanForPluginsOnStartup(), [this]() { + settings.setScanForPluginsOnStartup (scanForPlugins.get()); + settingChanged(); + }); + showPluginWindows.init (*this, "Automatically show plugin windows", settings.showPluginWindowsWhenAdded(), [this]() { + settings.setShowPluginWindowsWhenAdded (showPluginWindows.get()); + settingChanged(); + }); + pluginWindowsOnTop.init (*this, "Plugin windows on top by default", settings.pluginWindowsOnTop(), [this]() { + settings.setPluginWindowsOnTop (pluginWindowsOnTop.get()); + settingChanged(); + }); + hidePluginWindows.init (*this, "Hide plugin windows when app inactive", settings.hidePluginWindowsWhenFocusLost(), [this]() { + settings.setHidePluginWindowsWhenFocusLost (hidePluginWindows.get()); + settingChanged(); + }); + openLastSession.init (*this, "Open last used " + sessionStr, settings.openLastUsedSession(), [this]() { + settings.setOpenLastUsedSession (openLastSession.get()); + settingChanged(); + }); + askToSaveSession.init (*this, "Ask to save " + sessionStr + "s on exit", settings.askToSaveSession(), [this]() { + settings.setAskToSaveSession (askToSaveSession.get()); + settingChanged(); + }); + systray.init (*this, "Show system tray", settings.isSystrayEnabled(), [this]() { + settings.setSystrayEnabled (systray.get()); + startHidden.setEnabled (systray.get()); + gui.refreshSystemTray(); + settingChanged(); + }); + startHidden.init (*this, "Start hidden in system tray", settings.isStartHiddenEnabled(), [this]() { + settings.setStartHiddenEnabled (startHidden.get()); + settingChanged(); + }); + startHidden.setEnabled (systray.get()); addAndMakeVisible (desktopScaleLabel); desktopScaleLabel.setText ("Desktop scale", dontSendNotification); @@ -449,7 +459,9 @@ class GeneralSettingsPage : public SettingsPage, defaultSessionFile.addListener (this); addAndMakeVisible (defaultSessionClearButton); defaultSessionClearButton.setButtonText ("X"); - defaultSessionClearButton.addListener (this); + defaultSessionClearButton.onClick = [this]() { + defaultSessionFile.setCurrentFile (File(), false, sendNotificationAsync); + }; #if ELEMENT_SE defaultSessionFileLabel.setVisible (false); defaultSessionFile.setVisible (false); @@ -467,13 +479,8 @@ class GeneralSettingsPage : public SettingsPage, mainContentLabel.setFont (Font (FontOptions (12.0, Font::bold))); addAndMakeVisible (mainContentBox); mainContentBox.addItem ("Standard", 1); - // mainContentBox.addItem ("Workspace", 2); - if (settings.getMainContentType() == "standard") - mainContentBox.setSelectedId (1, dontSendNotification); - else - { - jassertfalse; - } // invalid content type + jassert (settings.getMainContentType() == "standard"); + mainContentBox.setSelectedId (1, dontSendNotification); mainContentBox.getSelectedIdAsValue().addListener (this); } @@ -496,48 +503,26 @@ class GeneralSettingsPage : public SettingsPage, settings.saveIfNeeded(); } - void buttonClicked (Button* b) override - { - if (b == &defaultSessionClearButton) - defaultSessionFile.setCurrentFile (File(), false, sendNotificationAsync); - } - void resized() override { const int spacingBetweenSections = 6; const int settingHeight = 22; - const int toggleWidth = 40; - const int toggleHeight = 18; + const int comboWidth = getWidth() / 2; Rectangle r (getLocalBounds()); - auto r2 = r.removeFromTop (settingHeight); - clockSourceLabel.setBounds (r2.removeFromLeft (getWidth() / 2)); - clockSourceBox.setBounds (r2.withSizeKeepingCentre (r2.getWidth(), settingHeight)); + layoutSetting (r, clockSourceLabel, clockSourceBox, comboWidth); #if ELEMENT_UPDATER - r.removeFromTop (spacingBetweenSections); - r2 = r.removeFromTop (settingHeight); - checkForUpdatesLabel.setBounds (r2.removeFromLeft (getWidth() / 2)); - checkForUpdates.setBounds (r2.removeFromLeft (toggleWidth) - .withSizeKeepingCentre (toggleWidth, toggleHeight)); + layoutSetting (r, checkForUpdates); #endif - r.removeFromTop (spacingBetweenSections); - r2 = r.removeFromTop (settingHeight); - scanForPlugsLabel.setBounds (r2.removeFromLeft (getWidth() / 2)); - scanForPlugins.setBounds (r2.removeFromLeft (toggleWidth) - .withSizeKeepingCentre (toggleWidth, toggleHeight)); - - layoutSetting (r, showPluginWindowsLabel, showPluginWindows); - layoutSetting (r, pluginWindowsOnTopLabel, pluginWindowsOnTop); - layoutSetting (r, hidePluginWindowsLabel, hidePluginWindows); - layoutSetting (r, openLastSessionLabel, openLastSession); - layoutSetting (r, askToSaveSessionLabel, askToSaveSession); - - r.removeFromTop (spacingBetweenSections); - r2 = r.removeFromTop (settingHeight); - mainContentLabel.setBounds (r2.removeFromLeft (getWidth() / 2)); - mainContentBox.setBounds (r2.withSizeKeepingCentre (r2.getWidth(), settingHeight)); - - layoutSetting (r, systrayLabel, systray); + layoutSetting (r, scanForPlugins); + layoutSetting (r, showPluginWindows); + layoutSetting (r, pluginWindowsOnTop); + layoutSetting (r, hidePluginWindows); + layoutSetting (r, openLastSession); + layoutSetting (r, askToSaveSession); + layoutSetting (r, mainContentLabel, mainContentBox, comboWidth); + layoutSetting (r, systray); + layoutSetting (r, startHidden); layoutSetting (r, desktopScaleLabel, desktopScale, getWidth() / 4); #if ! ELEMENT_SE @@ -557,18 +542,6 @@ class GeneralSettingsPage : public SettingsPage, void valueChanged (Value& value) override { -#if ELEMENT_UPDATER - if (value.refersToSameSourceAs (checkForUpdates.getToggleStateValue())) - { - settings.setCheckForUpdates (checkForUpdates.getToggleState()); - jassert (settings.checkForUpdates() == checkForUpdates.getToggleState()); - settings.saveIfNeeded(); - gui.stabilizeViews(); - gui.refreshMainMenu(); - return; - } -#endif - // clock source if (value.refersToSameSourceAs (clockSource)) { const var val = ClockSourceInternal == (int) clockSource.getValue() ? "internal" : "midiClock"; @@ -577,60 +550,17 @@ class GeneralSettingsPage : public SettingsPage, if (auto* cc = ViewHelpers::findContentComponent()) cc->refreshToolbar(); } - - else if (value.refersToSameSourceAs (scanForPlugins.getToggleStateValue())) - { - settings.setScanForPluginsOnStartup (scanForPlugins.getToggleState()); - } - else if (value.refersToSameSourceAs (showPluginWindows.getToggleStateValue())) - { - settings.setShowPluginWindowsWhenAdded (showPluginWindows.getToggleState()); - } - else if (value.refersToSameSourceAs (openLastSession.getToggleStateValue())) - { - settings.setOpenLastUsedSession (openLastSession.getToggleState()); - } - else if (value.refersToSameSourceAs (pluginWindowsOnTop.getToggleStateValue())) - { - settings.setPluginWindowsOnTop (pluginWindowsOnTop.getToggleState()); - } - else if (value.refersToSameSourceAs (askToSaveSession.getToggleStateValue())) - { - settings.setAskToSaveSession (askToSaveSession.getToggleState()); - } - else if (value.refersToSameSourceAs (hidePluginWindows.getToggleStateValue())) - { - settings.setHidePluginWindowsWhenFocusLost (hidePluginWindows.getToggleState()); - } - else if (value.refersToSameSourceAs (systray.getToggleStateValue())) - { - settings.setSystrayEnabled (systray.getToggleState()); - gui.refreshSystemTray(); - } else if (value.refersToSameSourceAs (mainContentBox.getSelectedIdAsValue())) { - auto uitype = settings.getMainContentType(); - if (1 == mainContentBox.getSelectedId()) - uitype = "standard"; - + const String uitype = "standard"; if (uitype != settings.getMainContentType()) { - bool changeType = true; - if (changeType) - { - settings.setMainContentType (uitype); - ViewHelpers::postMessageFor (this, new ReloadMainContentMessage()); - } - else - { - mainContentBox.setSelectedId (1, dontSendNotification); - } + settings.setMainContentType (uitype); + ViewHelpers::postMessageFor (this, new ReloadMainContentMessage()); } } - settings.saveIfNeeded(); - gui.stabilizeViews(); - gui.refreshMainMenu(); + settingChanged(); } private: @@ -638,35 +568,23 @@ class GeneralSettingsPage : public SettingsPage, ComboBox clockSourceBox; Value clockSource; - Label checkForUpdatesLabel; - SettingButton checkForUpdates; - - Label scanForPlugsLabel; - SettingButton scanForPlugins; + BoolSettingRow checkForUpdates; + BoolSettingRow scanForPlugins; PluginSettingsComponent pluginSettings; - Label showPluginWindowsLabel; - SettingButton showPluginWindows; - - Label pluginWindowsOnTopLabel; - SettingButton pluginWindowsOnTop; - - Label hidePluginWindowsLabel; - SettingButton hidePluginWindows; - - Label openLastSessionLabel; - SettingButton openLastSession; - - Label askToSaveSessionLabel; - SettingButton askToSaveSession; + BoolSettingRow showPluginWindows; + BoolSettingRow pluginWindowsOnTop; + BoolSettingRow hidePluginWindows; + BoolSettingRow openLastSession; + BoolSettingRow askToSaveSession; Label defaultSessionFileLabel; FilenameComponent defaultSessionFile; TextButton defaultSessionClearButton; - Label systrayLabel; - SettingButton systray; + BoolSettingRow systray; + BoolSettingRow startHidden; Label desktopScaleLabel; Slider desktopScale; @@ -677,6 +595,13 @@ class GeneralSettingsPage : public SettingsPage, Settings& settings; AudioEnginePtr engine; GuiService& gui; + + void settingChanged() + { + settings.saveIfNeeded(); + gui.stabilizeViews(); + gui.refreshMainMenu(); + } }; //============================================================================== @@ -707,7 +632,6 @@ class AudioSettingsComponent : public SettingsPage //============================================================================== class MidiSettingsPage : public SettingsPage, public ComboBox::Listener, - public Button::Listener, public ChangeListener, public Timer { @@ -749,23 +673,19 @@ class MidiSettingsPage : public SettingsPage, midiOutLatency.setEnabled (false); #endif - addAndMakeVisible (generateClockLabel); - generateClockLabel.setFont (Font (FontOptions (12.0, Font::bold))); - generateClockLabel.setText ("Generate MIDI Clock", dontSendNotification); - addAndMakeVisible (generateClock); + generateClock.init (*this, "Generate MIDI Clock", settings.generateMidiClock(), [this]() { + settings.setGenerateMidiClock (generateClock.get()); + generateClock.set (settings.generateMidiClock()); + applyEngineSettings(); + }); generateClock.setYesNoText ("Yes", "No"); - generateClock.setClickingTogglesState (true); - generateClock.setToggleState (settings.generateMidiClock(), dontSendNotification); - generateClock.addListener (this); - - addAndMakeVisible (sendClockToInputLabel); - sendClockToInputLabel.setFont (Font (FontOptions (12.0, Font::bold))); - sendClockToInputLabel.setText ("Send Clock to MIDI Input?", dontSendNotification); - addAndMakeVisible (sendClockToInput); + + sendClockToInput.init (*this, "Send Clock to MIDI Input?", settings.sendMidiClockToInput(), [this]() { + settings.setSendMidiClockToInput (sendClockToInput.get()); + sendClockToInput.set (settings.sendMidiClockToInput()); + applyEngineSettings(); + }); sendClockToInput.setYesNoText ("Yes", "No"); - sendClockToInput.setClickingTogglesState (true); - sendClockToInput.setToggleState (settings.sendMidiClockToInput(), dontSendNotification); - sendClockToInput.addListener (this); addAndMakeVisible (panicLabel); panicLabel.setFont (Font (FontOptions (12.0, Font::bold))); @@ -773,16 +693,12 @@ class MidiSettingsPage : public SettingsPage, addAndMakeVisible (panic); panic.stabilize(); - addAndMakeVisible (startStopContLabel); - startStopContLabel.setFont (Font (FontOptions (12.0, Font::bold))); - startStopContLabel.setText (TRANS ("Transport: MIDI Start/Stop"), - juce::dontSendNotification); - addAndMakeVisible (startStopCont); + startStopCont.init (*this, TRANS ("Transport: MIDI Start/Stop"), settings.transportRespondToStartStopContinue(), [this]() { + settings.setTransportRespondToStartStopContinue (startStopCont.get()); + startStopCont.set (settings.transportRespondToStartStopContinue()); + applyEngineSettings(); + }); startStopCont.setYesNoText ("Yes", "No"); - startStopCont.setClickingTogglesState (true); - startStopCont.setToggleState (settings.transportRespondToStartStopContinue(), - dontSendNotification); - startStopCont.addListener (this); addAndMakeVisible (midiInputHeader); midiInputHeader.setText ("Active MIDI Inputs", dontSendNotification); @@ -801,7 +717,6 @@ class MidiSettingsPage : public SettingsPage, ~MidiSettingsPage() { - startStopCont.removeListener (this); devices.removeChangeListener (this); midiInputs = nullptr; midiOutput.removeListener (this); @@ -825,9 +740,9 @@ class MidiSettingsPage : public SettingsPage, midiOutputLabel.setBounds (r2.removeFromLeft (getWidth() / 2)); midiOutput.setBounds (r2.withSizeKeepingCentre (r2.getWidth(), settingHeight)); layoutSetting (r, midiOutLatencyLabel, midiOutLatency, getWidth() / 4); - layoutSetting (r, generateClockLabel, generateClock); - layoutSetting (r, sendClockToInputLabel, sendClockToInput); - layoutSetting (r, startStopContLabel, startStopCont); + layoutSetting (r, generateClock); + layoutSetting (r, sendClockToInput); + layoutSetting (r, startStopCont); layoutSetting (r, panicLabel, panic, getWidth() / 2); r.removeFromTop (roundToInt ((double) spacingBetweenSections * 1.5)); @@ -837,35 +752,10 @@ class MidiSettingsPage : public SettingsPage, midiInputs->updateSize(); } - void buttonClicked (Button* button) override + void applyEngineSettings() { - bool sendChanges = true; - - if (button == &generateClock) - { - settings.setGenerateMidiClock (generateClock.getToggleState()); - generateClock.setToggleState (settings.generateMidiClock(), dontSendNotification); - } - else if (button == &sendClockToInput) - { - settings.setSendMidiClockToInput (sendClockToInput.getToggleState()); - sendClockToInput.setToggleState (settings.sendMidiClockToInput(), - dontSendNotification); - } - else if (button == &startStopCont) - { - settings.setTransportRespondToStartStopContinue (startStopCont.getToggleState()); - startStopCont.setToggleState (settings.transportRespondToStartStopContinue(), - dontSendNotification); - } - else - { - sendChanges = false; - } - - if (sendChanges) - if (auto engine = world.audio()) - engine->applySettings (settings); + if (auto engine = world.audio()) + engine->applySettings (settings); } void comboBoxChanged (ComboBox* box) override @@ -893,12 +783,9 @@ class MidiSettingsPage : public SettingsPage, ComboBox midiOutput; Label midiOutLatencyLabel; Slider midiOutLatency; - Label generateClockLabel; - SettingButton generateClock; - Label sendClockToInputLabel; - SettingButton sendClockToInput; - Label startStopContLabel; - SettingButton startStopCont; + BoolSettingRow generateClock; + BoolSettingRow sendClockToInput; + BoolSettingRow startStopCont; Label midiInputHeader; Array outputs; diff --git a/src/ui/systemtray.cpp b/src/ui/systemtray.cpp index ee9ce4376..03f357f6e 100644 --- a/src/ui/systemtray.cpp +++ b/src/ui/systemtray.cpp @@ -71,9 +71,14 @@ void SystemTray::init (GuiService& gui) canUseSystemTray = gui.getRunMode() == RunMode::Standalone; } +bool SystemTray::isAvailable() +{ + return initialized && canUseSystemTray && ! element::Util::isRunningInWine(); +} + void SystemTray::setEnabled (bool enabled) { - if (element::Util::isRunningInWine() || ! initialized || ! canUseSystemTray) + if (! isAvailable()) return; if (enabled) diff --git a/src/ui/systemtray.hpp b/src/ui/systemtray.hpp index 056c2289c..3c3c76671 100644 --- a/src/ui/systemtray.hpp +++ b/src/ui/systemtray.hpp @@ -13,6 +13,10 @@ class SystemTray : public SystemTrayIconComponent, static SystemTray* getInstance() { return instance; } static void setEnabled (bool enabled); + /** Returns true if a tray icon can be shown in this run mode and + environment, regardless of whether the user has enabled it. */ + static bool isAvailable(); + void mouseDown (const MouseEvent&) override; void mouseUp (const MouseEvent&) override; diff --git a/src/urlhandler.cpp b/src/urlhandler.cpp index 20ab472c6..b7fe9a9a8 100644 --- a/src/urlhandler.cpp +++ b/src/urlhandler.cpp @@ -7,14 +7,7 @@ #include #include "log.hpp" - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include +#include "win32.hpp" using namespace juce; diff --git a/src/utils.cpp b/src/utils.cpp index 608e8d516..79216b813 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -2,10 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "utils.hpp" - -#if JUCE_WINDOWS -#include -#endif +#include "win32.hpp" namespace element { namespace Util { diff --git a/src/win32.hpp b/src/win32.hpp new file mode 100644 index 000000000..eebe19b08 --- /dev/null +++ b/src/win32.hpp @@ -0,0 +1,23 @@ +// Copyright 2026 Kushview, LLC +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +/** Includes with WIN32_LEAN_AND_MEAN and NOMINMAX defined. + + Include this instead of directly so every translation unit + gets the same configuration. It is a no-op on other platforms, so it can + be included unconditionally. +*/ + +#include + +#if JUCE_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif