I am new to JUCE, and am trying to display a menu on top of the window using MenuBarModel.
If the menu worked, it would have these options:

I have checked all relevant examples such as MenusDemo.h, but cannot get the menu to display.
Here is my code in case I am missing something obvious:
/*
==============================================================================
This is PluginEditor.h
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "PluginProcessor.h"
class MyMenu : public juce::Component,
public juce::ApplicationCommandTarget,
public juce::MenuBarModel
{
public:
enum CommandIDs
{
newProject = 1,
openProject,
saveProject,
importFile,
exportFile,
undo,
redo,
option1,
option2
};
MyMenu()
{
menuComponent.reset(new juce::MenuBarComponent(this));
addAndMakeVisible(menuComponent.get());
setApplicationCommandManagerToWatch(&commandManager);
commandManager.registerAllCommandsForTarget(this);
// this ensures that commands invoked on the DemoRunner application are correctly forwarded to this demo
commandManager.setFirstCommandTarget(this);
// this lets the command manager use keypresses that arrive in our window to send out commands
addKeyListener(commandManager.getKeyMappings());
setWantsKeyboardFocus(true);
//setSize(500, 500);
#if JUCE_MAC
MenuBarModel::setMacMainMenu(this);
#endif
}
~MyMenu() override
{
#if JUCE_MAC
MenuBarModel::setMacMainMenu(nullptr);
#endif
commandManager.setFirstCommandTarget(nullptr);
}
juce::StringArray getMenuBarNames() override;
juce::PopupMenu getMenuForIndex(int index, const juce::String& name) override;
void menuItemSelected(int menuID, int index) override;
ApplicationCommandTarget* getNextCommandTarget() override;
void getAllCommands(juce::Array<juce::CommandID>& c) override;
void getCommandInfo(juce::CommandID commandID, juce::ApplicationCommandInfo& result) override;
bool perform(const InvocationInfo& info) override;
private:
#if JUCE_DEMO_RUNNER
juce::ApplicationCommandManager& commandManager = getGlobalCommandManager();
#else
juce::ApplicationCommandManager commandManager;
#endif
std::unique_ptr<juce::MenuBarComponent> menuComponent;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyMenu)
};
//==============================================================================
/**
*/
class MyAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
MyAudioProcessorEditor (MyAudioProcessor&);
~MyAudioProcessorEditor() override;
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
MyAudioProcessor& audioProcessor;
std::unique_ptr<MyMenu> menuBar;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyAudioProcessorEditor)
};
/*
==============================================================================
This is PluginEditor.cpp
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
juce::StringArray MyMenu::getMenuBarNames()
{
return { "File", "Edit", "Align" };
}
juce::PopupMenu MyMenu::getMenuForIndex(int index, const juce::String& name)
{
juce::PopupMenu menu;
switch (index) {
case 0: // File menu
menu.addCommandItem(&commandManager, CommandIDs::newProject);
menu.addCommandItem(&commandManager, CommandIDs::openProject);
menu.addCommandItem(&commandManager, CommandIDs::saveProject);
menu.addCommandItem(&commandManager, CommandIDs::importFile);
menu.addCommandItem(&commandManager, CommandIDs::exportFile);
break;
case 1: // Edit menu
menu.addCommandItem(&commandManager, CommandIDs::undo);
menu.addCommandItem(&commandManager, CommandIDs::redo);
break;
case 2: // Align menu
menu.addCommandItem(&commandManager, CommandIDs::option1);
menu.addCommandItem(&commandManager, CommandIDs::option2);
break;
}
return menu;
}
void MyMenu::menuItemSelected(int menuID, int index) {} // todo
juce::ApplicationCommandTarget* MyMenu::getNextCommandTarget()
{
return this; // findFirstTargetParentComponent()
}
void MyMenu::getAllCommands(juce::Array<juce::CommandID>& c)
{
juce::Array<juce::CommandID> commands
{
CommandIDs::newProject,
CommandIDs::openProject,
CommandIDs::saveProject,
CommandIDs::importFile,
CommandIDs::exportFile,
CommandIDs::undo,
CommandIDs::redo,
CommandIDs::option1,
CommandIDs::option2
};
c.addArray(commands);
}
void MyMenu::getCommandInfo(juce::CommandID commandID, juce::ApplicationCommandInfo& result)
{
switch (commandID)
{
case CommandIDs::newProject:
result.setInfo("New", "TODO", "File", 0);
result.addDefaultKeypress('n', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::openProject:
result.setInfo("Open", "TODO", "File", 0);
result.addDefaultKeypress('o', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::saveProject:
result.setInfo("Save", "TODO", "File", 0);
result.addDefaultKeypress('s', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::importFile:
result.setInfo("Import", "TODO", "File", 0);
result.addDefaultKeypress('i', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::exportFile:
result.setInfo("Export", "TODO", "File", 0);
result.addDefaultKeypress('e', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::undo:
result.setInfo("Undo", "TODO", "Edit", 0);
result.addDefaultKeypress('z', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::redo:
result.setInfo("Redo", "TODO", "Edit", 0);
result.addDefaultKeypress('y', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::option1:
result.setInfo("Option 1", "TODO", "Align", 0);
result.addDefaultKeypress('g', juce::ModifierKeys::ctrlModifier);
break;
case CommandIDs::option2:
result.setInfo("Option 2", "TODO", "Align", 0);
result.addDefaultKeypress('w', juce::ModifierKeys::ctrlModifier);
break;
}
}
bool MyMenu::perform(const InvocationInfo& info)
{
// todo: make commands perform actions
return true;
}
//==============================================================================
MyAudioProcessorEditor::MyAudioProcessorEditor (MyAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
// Make sure that before the constructor has finished, you've set the editor's size to whatever you need it to be.
menuBar.reset(new MyMenu());
addAndMakeVisible(menuBar.get());
setSize (1280, 720);
}
MyAudioProcessorEditor::~MyAudioProcessorEditor() {}
//==============================================================================
void MyAudioProcessorEditor::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setColour (juce::Colours::white);
g.setFont (16.0f);
g.drawFittedText ("Where is the menu", getLocalBounds(), juce::Justification::centred, 1);
}
void MyAudioProcessorEditor::resized()
{
// This is generally where you'll want to lay out the positions of any subcomponents in your editor..
auto bounds = getLocalBounds();
//menuBar->setBounds(50, 50, getWidth(), 20);
menuBar->setBounds(bounds.removeFromTop(getLookAndFeel().getDefaultMenuBarHeight()));
}
I am compiling on Windows, so setMacMainMenu is not the cause of the problem.
I use addAndMakeVisible on both the MenuBarComponent and MyMenu.
I don’t know if both are necessary, but there is no difference if either are removed.
I have no idea what I’m doing wrong, so any help would be extremely useful!
