diff --git a/Source/components/EffectTypeGridComponent.cpp b/Source/components/EffectTypeGridComponent.cpp index 134dda5..54e5dec 100644 --- a/Source/components/EffectTypeGridComponent.cpp +++ b/Source/components/EffectTypeGridComponent.cpp @@ -62,12 +62,16 @@ void EffectTypeGridComponent::setupEffectItems() }; // Hover preview: request temporary preview of this effect while hovered item->onHoverStart = [this](const juce::String& effectId) { - juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); - audioProcessor.setPreviewEffectId(effectId); + if (audioProcessor.getGlobalBoolValue("previewEffectOnHover", true)) { + juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); + audioProcessor.setPreviewEffectId(effectId); + } }; item->onHoverEnd = [this]() { - juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); - audioProcessor.clearPreviewEffect(); + if (audioProcessor.getGlobalBoolValue("previewEffectOnHover", true)) { + juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); + audioProcessor.clearPreviewEffect(); + } }; effectItems.add(item); diff --git a/Source/components/MainMenuBarModel.cpp b/Source/components/MainMenuBarModel.cpp index 2dff617..e07787b 100644 --- a/Source/components/MainMenuBarModel.cpp +++ b/Source/components/MainMenuBarModel.cpp @@ -6,12 +6,17 @@ MainMenuBarModel::~MainMenuBarModel() {} void MainMenuBarModel::addTopLevelMenu(const juce::String& name) { topLevelMenuNames.add(name); - menuItems.push_back(std::vector>>()); + menuItems.push_back({}); menuItemsChanged(); } void MainMenuBarModel::addMenuItem(int topLevelMenuIndex, const juce::String& name, std::function action) { - menuItems[topLevelMenuIndex].push_back(std::make_pair(name, action)); + menuItems[topLevelMenuIndex].push_back({ name, std::move(action), {}, false }); + menuItemsChanged(); +} + +void MainMenuBarModel::addToggleMenuItem(int topLevelMenuIndex, const juce::String& name, std::function action, std::function isTicked) { + menuItems[topLevelMenuIndex].push_back({ name, std::move(action), std::move(isTicked), true }); menuItemsChanged(); } @@ -26,8 +31,13 @@ juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const j customMenuLogic(menu, topLevelMenuIndex); } - for (int i = 0; i < menuItems[topLevelMenuIndex].size(); i++) { - menu.addItem(i + 1, menuItems[topLevelMenuIndex][i].first); + for (int i = 0; i < (int) menuItems[topLevelMenuIndex].size(); i++) { + auto& mi = menuItems[topLevelMenuIndex][i]; + juce::PopupMenu::Item item(mi.name); + item.itemID = i + 1; + if (mi.hasTick && mi.isTicked) + item.setTicked(mi.isTicked()); + menu.addItem(item); } return menu; @@ -37,7 +47,9 @@ void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) { if (customMenuSelectedLogic && customMenuSelectedLogic(menuItemID, topLevelMenuIndex)) { return; } - menuItems[topLevelMenuIndex][menuItemID - 1].second(); + auto& mi = menuItems[topLevelMenuIndex][menuItemID - 1]; + if (mi.action) + mi.action(); } void MainMenuBarModel::menuBarActivated(bool isActive) {} diff --git a/Source/components/MainMenuBarModel.h b/Source/components/MainMenuBarModel.h index 5e80fd8..ea9f993 100644 --- a/Source/components/MainMenuBarModel.h +++ b/Source/components/MainMenuBarModel.h @@ -8,6 +8,8 @@ public: void addTopLevelMenu(const juce::String& name); void addMenuItem(int topLevelMenuIndex, const juce::String& name, std::function action); + // Adds a toggle (ticked) menu item whose tick state is provided dynamically via isTicked() + void addToggleMenuItem(int topLevelMenuIndex, const juce::String& name, std::function action, std::function isTicked); void resetMenuItems(); std::function customMenuLogic; @@ -19,6 +21,14 @@ private: void menuItemSelected(int menuItemID, int topLevelMenuIndex) override; void menuBarActivated(bool isActive); + struct MenuItem + { + juce::String name; + std::function action; + std::function isTicked; // optional tick state + bool hasTick = false; + }; + juce::StringArray topLevelMenuNames; - std::vector>>> menuItems; + std::vector> menuItems; }; diff --git a/Source/components/OsciMainMenuBarModel.cpp b/Source/components/OsciMainMenuBarModel.cpp index 74e9f5a..d5add6f 100644 --- a/Source/components/OsciMainMenuBarModel.cpp +++ b/Source/components/OsciMainMenuBarModel.cpp @@ -10,12 +10,13 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend void OsciMainMenuBarModel::resetMenuItems() { MainMenuBarModel::resetMenuItems(); - addTopLevelMenu("File"); - addTopLevelMenu("About"); - addTopLevelMenu("Video"); + addTopLevelMenu("File"); // index 0 + addTopLevelMenu("About"); // index 1 + addTopLevelMenu("Video"); // index 2 if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) { - addTopLevelMenu("Audio"); + addTopLevelMenu("Audio"); // index 3 (only if standalone) } + addTopLevelMenu("Interface"); // index 3 (if not standalone) or 4 (if standalone) addMenuItem(0, "Open Project", [this] { editor.openProject(); }); addMenuItem(0, "Save Project", [this] { editor.saveProject(); }); @@ -88,6 +89,20 @@ void OsciMainMenuBarModel::resetMenuItems() { editor.openAudioSettings(); }); } + + // Interface menu index depends on whether Audio menu exists + int interfaceMenuIndex = (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) ? 4 : 3; + addToggleMenuItem(interfaceMenuIndex, "Preview effect on hover", [this] { + bool current = audioProcessor.getGlobalBoolValue("previewEffectOnHover", true); + bool newValue = ! current; + audioProcessor.setGlobalValue("previewEffectOnHover", newValue); + audioProcessor.saveGlobalSettings(); + if (! newValue) { + juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); + audioProcessor.clearPreviewEffect(); + } + resetMenuItems(); // update tick state + }, [this] { return audioProcessor.getGlobalBoolValue("previewEffectOnHover", true); }); } #if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM