migrating to the latest JUCE version
This commit is contained in:
		@@ -1,302 +1,302 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct CustomMenuBarItemHolder    : public Component
 | 
			
		||||
{
 | 
			
		||||
    CustomMenuBarItemHolder (const ReferenceCountedObjectPtr<PopupMenu::CustomComponent>& customComponent)
 | 
			
		||||
    {
 | 
			
		||||
        setInterceptsMouseClicks (false, true);
 | 
			
		||||
        update (customComponent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update (const ReferenceCountedObjectPtr<PopupMenu::CustomComponent>& newComponent)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (newComponent != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (newComponent != custom)
 | 
			
		||||
        {
 | 
			
		||||
            if (custom != nullptr)
 | 
			
		||||
                removeChildComponent (custom.get());
 | 
			
		||||
 | 
			
		||||
            custom = newComponent;
 | 
			
		||||
            addAndMakeVisible (*custom);
 | 
			
		||||
            resized();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        custom->setBounds (getLocalBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ReferenceCountedObjectPtr<PopupMenu::CustomComponent> custom;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomMenuBarItemHolder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
BurgerMenuComponent::BurgerMenuComponent (MenuBarModel* modelToUse)
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
    listBox.addMouseListener (this, true);
 | 
			
		||||
 | 
			
		||||
    setModel (modelToUse);
 | 
			
		||||
    addAndMakeVisible (listBox);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BurgerMenuComponent::~BurgerMenuComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
        model->removeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::setModel (MenuBarModel* newModel)
 | 
			
		||||
{
 | 
			
		||||
    if (newModel != model)
 | 
			
		||||
    {
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        model = newModel;
 | 
			
		||||
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->addListener (this);
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
        listBox.updateContent();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel* BurgerMenuComponent::getModel() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::refresh()
 | 
			
		||||
{
 | 
			
		||||
    lastRowClicked = inputSourceIndexOfLastClick = -1;
 | 
			
		||||
 | 
			
		||||
    rows.clear();
 | 
			
		||||
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto menuBarNames = model->getMenuBarNames();
 | 
			
		||||
 | 
			
		||||
        for (auto menuIdx = 0; menuIdx < menuBarNames.size(); ++menuIdx)
 | 
			
		||||
        {
 | 
			
		||||
            PopupMenu::Item menuItem;
 | 
			
		||||
            menuItem.text = menuBarNames[menuIdx];
 | 
			
		||||
 | 
			
		||||
            String ignore;
 | 
			
		||||
            auto menu = model->getMenuForIndex (menuIdx, ignore);
 | 
			
		||||
 | 
			
		||||
            rows.add (Row { true, menuIdx, menuItem });
 | 
			
		||||
            addMenuBarItemsForMenu (menu, menuIdx);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::addMenuBarItemsForMenu (PopupMenu& menu, int menuIdx)
 | 
			
		||||
{
 | 
			
		||||
    for (PopupMenu::MenuItemIterator it (menu); it.next();)
 | 
			
		||||
    {
 | 
			
		||||
        auto& item = it.getItem();
 | 
			
		||||
 | 
			
		||||
        if (item.isSeparator)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (hasSubMenu (item))
 | 
			
		||||
            addMenuBarItemsForMenu (*item.subMenu, menuIdx);
 | 
			
		||||
        else
 | 
			
		||||
            rows.add (Row {false, menuIdx, it.getItem()});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BurgerMenuComponent::getNumRows()
 | 
			
		||||
{
 | 
			
		||||
    return rows.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawPopupMenuBackground (g, getWidth(), getHeight());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::paintListBoxItem (int rowIndex, Graphics& g, int w, int h, bool highlight)
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
    Rectangle<int> r (w, h);
 | 
			
		||||
 | 
			
		||||
    auto row = (rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                       : Row { true, 0, {} });
 | 
			
		||||
 | 
			
		||||
    g.fillAll (findColour (PopupMenu::backgroundColourId));
 | 
			
		||||
 | 
			
		||||
    if (row.isMenuHeader)
 | 
			
		||||
    {
 | 
			
		||||
        lf.drawPopupMenuSectionHeader (g, r.reduced (20, 0), row.item.text);
 | 
			
		||||
        g.setColour (Colours::grey);
 | 
			
		||||
        g.fillRect (r.withHeight (1));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        auto& item = row.item;
 | 
			
		||||
        auto* colour = item.colour != Colour() ? &item.colour : nullptr;
 | 
			
		||||
 | 
			
		||||
        if (item.customComponent == nullptr)
 | 
			
		||||
            lf.drawPopupMenuItem (g, r.reduced (20, 0),
 | 
			
		||||
                                  item.isSeparator,
 | 
			
		||||
                                  item.isEnabled,
 | 
			
		||||
                                  highlight,
 | 
			
		||||
                                  item.isTicked,
 | 
			
		||||
                                  hasSubMenu (item),
 | 
			
		||||
                                  item.text,
 | 
			
		||||
                                  item.shortcutKeyDescription,
 | 
			
		||||
                                  item.image.get(),
 | 
			
		||||
                                  colour);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BurgerMenuComponent::hasSubMenu (const PopupMenu::Item& item)
 | 
			
		||||
{
 | 
			
		||||
    return item.subMenu != nullptr && (item.itemID == 0 || item.subMenu->getNumItems() > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::listBoxItemClicked (int rowIndex, const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto row = rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                      : Row { true, 0, {} };
 | 
			
		||||
 | 
			
		||||
    if (! row.isMenuHeader)
 | 
			
		||||
    {
 | 
			
		||||
        lastRowClicked = rowIndex;
 | 
			
		||||
        inputSourceIndexOfLastClick = e.source.getIndex();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* BurgerMenuComponent::refreshComponentForRow (int rowIndex, bool isRowSelected, Component* existing)
 | 
			
		||||
{
 | 
			
		||||
    auto row = rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                      : Row { true, 0, {} };
 | 
			
		||||
 | 
			
		||||
    auto hasCustomComponent = (row.item.customComponent != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (existing == nullptr && hasCustomComponent)
 | 
			
		||||
        return new CustomMenuBarItemHolder (row.item.customComponent);
 | 
			
		||||
 | 
			
		||||
    if (existing != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto* componentToUpdate = dynamic_cast<CustomMenuBarItemHolder*> (existing);
 | 
			
		||||
        jassert (componentToUpdate != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (hasCustomComponent && componentToUpdate != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            row.item.customComponent->setHighlighted (isRowSelected);
 | 
			
		||||
            componentToUpdate->update (row.item.customComponent);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            delete existing;
 | 
			
		||||
            existing = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return existing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    listBox.setBounds (getLocalBounds());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::menuBarItemsChanged (MenuBarModel* menuBarModel)
 | 
			
		||||
{
 | 
			
		||||
    setModel (menuBarModel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::mouseUp (const MouseEvent& event)
 | 
			
		||||
{
 | 
			
		||||
    auto rowIndex = listBox.getSelectedRow();
 | 
			
		||||
 | 
			
		||||
    if (rowIndex == lastRowClicked && rowIndex < rows.size()
 | 
			
		||||
         && event.source.getIndex() == inputSourceIndexOfLastClick)
 | 
			
		||||
    {
 | 
			
		||||
        auto& row = rows.getReference (rowIndex);
 | 
			
		||||
 | 
			
		||||
        if (! row.isMenuHeader)
 | 
			
		||||
        {
 | 
			
		||||
            listBox.selectRow (-1);
 | 
			
		||||
 | 
			
		||||
            lastRowClicked = -1;
 | 
			
		||||
            inputSourceIndexOfLastClick = -1;
 | 
			
		||||
 | 
			
		||||
            topLevelIndexClicked = row.topLevelMenuIndex;
 | 
			
		||||
            auto& item = row.item;
 | 
			
		||||
 | 
			
		||||
            if (auto* managerOfChosenCommand = item.commandManager)
 | 
			
		||||
            {
 | 
			
		||||
                ApplicationCommandTarget::InvocationInfo info (item.itemID);
 | 
			
		||||
                info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
 | 
			
		||||
 | 
			
		||||
                managerOfChosenCommand->invoke (info, true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            postCommandMessage (item.itemID);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::handleCommandMessage (int commandID)
 | 
			
		||||
{
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        model->menuItemSelected (commandID, topLevelIndexClicked);
 | 
			
		||||
        topLevelIndexClicked = -1;
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
        listBox.updateContent();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    listBox.setRowHeight (roundToInt (getLookAndFeel().getPopupMenuFont().getHeight() * 2.0f));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> BurgerMenuComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::menuBar);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct CustomMenuBarItemHolder    : public Component
 | 
			
		||||
{
 | 
			
		||||
    CustomMenuBarItemHolder (const ReferenceCountedObjectPtr<PopupMenu::CustomComponent>& customComponent)
 | 
			
		||||
    {
 | 
			
		||||
        setInterceptsMouseClicks (false, true);
 | 
			
		||||
        update (customComponent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update (const ReferenceCountedObjectPtr<PopupMenu::CustomComponent>& newComponent)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (newComponent != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (newComponent != custom)
 | 
			
		||||
        {
 | 
			
		||||
            if (custom != nullptr)
 | 
			
		||||
                removeChildComponent (custom.get());
 | 
			
		||||
 | 
			
		||||
            custom = newComponent;
 | 
			
		||||
            addAndMakeVisible (*custom);
 | 
			
		||||
            resized();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        custom->setBounds (getLocalBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ReferenceCountedObjectPtr<PopupMenu::CustomComponent> custom;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomMenuBarItemHolder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
BurgerMenuComponent::BurgerMenuComponent (MenuBarModel* modelToUse)
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
    listBox.addMouseListener (this, true);
 | 
			
		||||
 | 
			
		||||
    setModel (modelToUse);
 | 
			
		||||
    addAndMakeVisible (listBox);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BurgerMenuComponent::~BurgerMenuComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
        model->removeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::setModel (MenuBarModel* newModel)
 | 
			
		||||
{
 | 
			
		||||
    if (newModel != model)
 | 
			
		||||
    {
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        model = newModel;
 | 
			
		||||
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->addListener (this);
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
        listBox.updateContent();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel* BurgerMenuComponent::getModel() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::refresh()
 | 
			
		||||
{
 | 
			
		||||
    lastRowClicked = inputSourceIndexOfLastClick = -1;
 | 
			
		||||
 | 
			
		||||
    rows.clear();
 | 
			
		||||
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto menuBarNames = model->getMenuBarNames();
 | 
			
		||||
 | 
			
		||||
        for (auto menuIdx = 0; menuIdx < menuBarNames.size(); ++menuIdx)
 | 
			
		||||
        {
 | 
			
		||||
            PopupMenu::Item menuItem;
 | 
			
		||||
            menuItem.text = menuBarNames[menuIdx];
 | 
			
		||||
 | 
			
		||||
            String ignore;
 | 
			
		||||
            auto menu = model->getMenuForIndex (menuIdx, ignore);
 | 
			
		||||
 | 
			
		||||
            rows.add (Row { true, menuIdx, menuItem });
 | 
			
		||||
            addMenuBarItemsForMenu (menu, menuIdx);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::addMenuBarItemsForMenu (PopupMenu& menu, int menuIdx)
 | 
			
		||||
{
 | 
			
		||||
    for (PopupMenu::MenuItemIterator it (menu); it.next();)
 | 
			
		||||
    {
 | 
			
		||||
        auto& item = it.getItem();
 | 
			
		||||
 | 
			
		||||
        if (item.isSeparator)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (hasSubMenu (item))
 | 
			
		||||
            addMenuBarItemsForMenu (*item.subMenu, menuIdx);
 | 
			
		||||
        else
 | 
			
		||||
            rows.add (Row {false, menuIdx, it.getItem()});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BurgerMenuComponent::getNumRows()
 | 
			
		||||
{
 | 
			
		||||
    return rows.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawPopupMenuBackground (g, getWidth(), getHeight());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::paintListBoxItem (int rowIndex, Graphics& g, int w, int h, bool highlight)
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
    Rectangle<int> r (w, h);
 | 
			
		||||
 | 
			
		||||
    auto row = (rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                       : Row { true, 0, {} });
 | 
			
		||||
 | 
			
		||||
    g.fillAll (findColour (PopupMenu::backgroundColourId));
 | 
			
		||||
 | 
			
		||||
    if (row.isMenuHeader)
 | 
			
		||||
    {
 | 
			
		||||
        lf.drawPopupMenuSectionHeader (g, r.reduced (20, 0), row.item.text);
 | 
			
		||||
        g.setColour (Colours::grey);
 | 
			
		||||
        g.fillRect (r.withHeight (1));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        auto& item = row.item;
 | 
			
		||||
        auto* colour = item.colour != Colour() ? &item.colour : nullptr;
 | 
			
		||||
 | 
			
		||||
        if (item.customComponent == nullptr)
 | 
			
		||||
            lf.drawPopupMenuItem (g, r.reduced (20, 0),
 | 
			
		||||
                                  item.isSeparator,
 | 
			
		||||
                                  item.isEnabled,
 | 
			
		||||
                                  highlight,
 | 
			
		||||
                                  item.isTicked,
 | 
			
		||||
                                  hasSubMenu (item),
 | 
			
		||||
                                  item.text,
 | 
			
		||||
                                  item.shortcutKeyDescription,
 | 
			
		||||
                                  item.image.get(),
 | 
			
		||||
                                  colour);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BurgerMenuComponent::hasSubMenu (const PopupMenu::Item& item)
 | 
			
		||||
{
 | 
			
		||||
    return item.subMenu != nullptr && (item.itemID == 0 || item.subMenu->getNumItems() > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::listBoxItemClicked (int rowIndex, const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto row = rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                      : Row { true, 0, {} };
 | 
			
		||||
 | 
			
		||||
    if (! row.isMenuHeader)
 | 
			
		||||
    {
 | 
			
		||||
        lastRowClicked = rowIndex;
 | 
			
		||||
        inputSourceIndexOfLastClick = e.source.getIndex();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* BurgerMenuComponent::refreshComponentForRow (int rowIndex, bool isRowSelected, Component* existing)
 | 
			
		||||
{
 | 
			
		||||
    auto row = rowIndex < rows.size() ? rows.getReference (rowIndex)
 | 
			
		||||
                                      : Row { true, 0, {} };
 | 
			
		||||
 | 
			
		||||
    auto hasCustomComponent = (row.item.customComponent != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (existing == nullptr && hasCustomComponent)
 | 
			
		||||
        return new CustomMenuBarItemHolder (row.item.customComponent);
 | 
			
		||||
 | 
			
		||||
    if (existing != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto* componentToUpdate = dynamic_cast<CustomMenuBarItemHolder*> (existing);
 | 
			
		||||
        jassert (componentToUpdate != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (hasCustomComponent && componentToUpdate != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            row.item.customComponent->setHighlighted (isRowSelected);
 | 
			
		||||
            componentToUpdate->update (row.item.customComponent);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            delete existing;
 | 
			
		||||
            existing = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return existing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    listBox.setBounds (getLocalBounds());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::menuBarItemsChanged (MenuBarModel* menuBarModel)
 | 
			
		||||
{
 | 
			
		||||
    setModel (menuBarModel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::mouseUp (const MouseEvent& event)
 | 
			
		||||
{
 | 
			
		||||
    auto rowIndex = listBox.getSelectedRow();
 | 
			
		||||
 | 
			
		||||
    if (rowIndex == lastRowClicked && rowIndex < rows.size()
 | 
			
		||||
         && event.source.getIndex() == inputSourceIndexOfLastClick)
 | 
			
		||||
    {
 | 
			
		||||
        auto& row = rows.getReference (rowIndex);
 | 
			
		||||
 | 
			
		||||
        if (! row.isMenuHeader)
 | 
			
		||||
        {
 | 
			
		||||
            listBox.selectRow (-1);
 | 
			
		||||
 | 
			
		||||
            lastRowClicked = -1;
 | 
			
		||||
            inputSourceIndexOfLastClick = -1;
 | 
			
		||||
 | 
			
		||||
            topLevelIndexClicked = row.topLevelMenuIndex;
 | 
			
		||||
            auto& item = row.item;
 | 
			
		||||
 | 
			
		||||
            if (auto* managerOfChosenCommand = item.commandManager)
 | 
			
		||||
            {
 | 
			
		||||
                ApplicationCommandTarget::InvocationInfo info (item.itemID);
 | 
			
		||||
                info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
 | 
			
		||||
 | 
			
		||||
                managerOfChosenCommand->invoke (info, true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            postCommandMessage (item.itemID);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::handleCommandMessage (int commandID)
 | 
			
		||||
{
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        model->menuItemSelected (commandID, topLevelIndexClicked);
 | 
			
		||||
        topLevelIndexClicked = -1;
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
        listBox.updateContent();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BurgerMenuComponent::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    listBox.setRowHeight (roundToInt (getLookAndFeel().getPopupMenuFont().getHeight() * 2.0f));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> BurgerMenuComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::menuBar);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,110 +1,110 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component which lists all menu items and groups them into categories
 | 
			
		||||
    by their respective parent menus. This kind of component is often used
 | 
			
		||||
    for so-called "burger" menus in mobile apps.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class BurgerMenuComponent     : public Component,
 | 
			
		||||
                                private ListBoxModel,
 | 
			
		||||
                                private MenuBarModel::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a burger menu component.
 | 
			
		||||
 | 
			
		||||
        @param model    the model object to use to control this burger menu. You can
 | 
			
		||||
                        set the parameter or pass nullptr into this if you like,
 | 
			
		||||
                        and set the model later using the setModel() method.
 | 
			
		||||
 | 
			
		||||
        @see setModel
 | 
			
		||||
     */
 | 
			
		||||
    BurgerMenuComponent (MenuBarModel* model = nullptr);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~BurgerMenuComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the model object to use to control the burger menu.
 | 
			
		||||
 | 
			
		||||
        This can be a nullptr, in which case the bar will be empty. This object will not be
 | 
			
		||||
        owned by the BurgerMenuComponent so it is up to you to manage its lifetime.
 | 
			
		||||
        Don't delete the object that is passed-in while it's still being used by this MenuBar.
 | 
			
		||||
        Any submenus in your MenuBarModel will be recursively flattened and added to the
 | 
			
		||||
        top-level burger menu section.
 | 
			
		||||
     */
 | 
			
		||||
    void setModel (MenuBarModel* newModel);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current burger menu model being used. */
 | 
			
		||||
    MenuBarModel* getModel() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct Row
 | 
			
		||||
    {
 | 
			
		||||
        bool isMenuHeader;
 | 
			
		||||
        int topLevelMenuIndex;
 | 
			
		||||
        PopupMenu::Item item;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void refresh();
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    int getNumRows() override;
 | 
			
		||||
    void paintListBoxItem (int, Graphics&, int, int, bool) override;
 | 
			
		||||
    void listBoxItemClicked (int, const MouseEvent&) override;
 | 
			
		||||
    Component* refreshComponentForRow (int, bool, Component*) override;
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    void menuBarItemsChanged (MenuBarModel*) override;
 | 
			
		||||
    void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    void handleCommandMessage (int) override;
 | 
			
		||||
    void addMenuBarItemsForMenu (PopupMenu&, int);
 | 
			
		||||
    static bool hasSubMenu (const PopupMenu::Item&);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel* model = nullptr;
 | 
			
		||||
    ListBox listBox    {"BurgerMenuListBox", this};
 | 
			
		||||
    Array<Row> rows;
 | 
			
		||||
 | 
			
		||||
    int lastRowClicked = -1, inputSourceIndexOfLastClick = -1, topLevelIndexClicked = -1;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component which lists all menu items and groups them into categories
 | 
			
		||||
    by their respective parent menus. This kind of component is often used
 | 
			
		||||
    for so-called "burger" menus in mobile apps.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class BurgerMenuComponent     : public Component,
 | 
			
		||||
                                private ListBoxModel,
 | 
			
		||||
                                private MenuBarModel::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a burger menu component.
 | 
			
		||||
 | 
			
		||||
        @param model    the model object to use to control this burger menu. You can
 | 
			
		||||
                        set the parameter or pass nullptr into this if you like,
 | 
			
		||||
                        and set the model later using the setModel() method.
 | 
			
		||||
 | 
			
		||||
        @see setModel
 | 
			
		||||
     */
 | 
			
		||||
    BurgerMenuComponent (MenuBarModel* model = nullptr);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~BurgerMenuComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the model object to use to control the burger menu.
 | 
			
		||||
 | 
			
		||||
        This can be a nullptr, in which case the bar will be empty. This object will not be
 | 
			
		||||
        owned by the BurgerMenuComponent so it is up to you to manage its lifetime.
 | 
			
		||||
        Don't delete the object that is passed-in while it's still being used by this MenuBar.
 | 
			
		||||
        Any submenus in your MenuBarModel will be recursively flattened and added to the
 | 
			
		||||
        top-level burger menu section.
 | 
			
		||||
     */
 | 
			
		||||
    void setModel (MenuBarModel* newModel);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current burger menu model being used. */
 | 
			
		||||
    MenuBarModel* getModel() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct Row
 | 
			
		||||
    {
 | 
			
		||||
        bool isMenuHeader;
 | 
			
		||||
        int topLevelMenuIndex;
 | 
			
		||||
        PopupMenu::Item item;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void refresh();
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    int getNumRows() override;
 | 
			
		||||
    void paintListBoxItem (int, Graphics&, int, int, bool) override;
 | 
			
		||||
    void listBoxItemClicked (int, const MouseEvent&) override;
 | 
			
		||||
    Component* refreshComponentForRow (int, bool, Component*) override;
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    void menuBarItemsChanged (MenuBarModel*) override;
 | 
			
		||||
    void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    void handleCommandMessage (int) override;
 | 
			
		||||
    void addMenuBarItemsForMenu (PopupMenu&, int);
 | 
			
		||||
    static bool hasSubMenu (const PopupMenu::Item&);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel* model = nullptr;
 | 
			
		||||
    ListBox listBox    {"BurgerMenuListBox", this};
 | 
			
		||||
    Array<Row> rows;
 | 
			
		||||
 | 
			
		||||
    int lastRowClicked = -1, inputSourceIndexOfLastClick = -1, topLevelIndexClicked = -1;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,451 +1,463 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class MenuBarComponent::AccessibleItemComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AccessibleItemComponent (MenuBarComponent& comp, const String& menuItemName)
 | 
			
		||||
        : owner (comp),
 | 
			
		||||
          name (menuItemName)
 | 
			
		||||
    {
 | 
			
		||||
        setInterceptsMouseClicks (false, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const String& getName() const noexcept    { return name; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 | 
			
		||||
    {
 | 
			
		||||
        class ComponentHandler  : public AccessibilityHandler
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            explicit ComponentHandler (AccessibleItemComponent& item)
 | 
			
		||||
                : AccessibilityHandler (item,
 | 
			
		||||
                                        AccessibilityRole::menuItem,
 | 
			
		||||
                                        getAccessibilityActions (item)),
 | 
			
		||||
                  itemComponent (item)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            String getTitle() const override  { return itemComponent.name; }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            static AccessibilityActions getAccessibilityActions (AccessibleItemComponent& item)
 | 
			
		||||
            {
 | 
			
		||||
                auto showMenu = [&item] { item.owner.showMenu (item.owner.indexOfItemComponent (&item)); };
 | 
			
		||||
 | 
			
		||||
                return AccessibilityActions().addAction (AccessibilityActionType::focus,
 | 
			
		||||
                                                         [&item] { item.owner.setItemUnderMouse (item.owner.indexOfItemComponent (&item)); })
 | 
			
		||||
                                             .addAction (AccessibilityActionType::press,    showMenu)
 | 
			
		||||
                                             .addAction (AccessibilityActionType::showMenu, showMenu);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AccessibleItemComponent& itemComponent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return std::make_unique<ComponentHandler> (*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MenuBarComponent& owner;
 | 
			
		||||
    const String name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MenuBarComponent::MenuBarComponent (MenuBarModel* m)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setWantsKeyboardFocus (false);
 | 
			
		||||
    setMouseClickGrabsKeyboardFocus (false);
 | 
			
		||||
 | 
			
		||||
    setModel (m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarComponent::~MenuBarComponent()
 | 
			
		||||
{
 | 
			
		||||
    setModel (nullptr);
 | 
			
		||||
    Desktop::getInstance().removeGlobalMouseListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel* MenuBarComponent::getModel() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setModel (MenuBarModel* const newModel)
 | 
			
		||||
{
 | 
			
		||||
    if (model != newModel)
 | 
			
		||||
    {
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        model = newModel;
 | 
			
		||||
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->addListener (this);
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
        menuBarItemsChanged (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    const auto isMouseOverBar = (currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver());
 | 
			
		||||
 | 
			
		||||
    getLookAndFeel().drawMenuBarBackground (g, getWidth(), getHeight(), isMouseOverBar, *this);
 | 
			
		||||
 | 
			
		||||
    if (model == nullptr)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        const auto& itemComponent = itemComponents[i];
 | 
			
		||||
        const auto itemBounds = itemComponent->getBounds();
 | 
			
		||||
 | 
			
		||||
        Graphics::ScopedSaveState ss (g);
 | 
			
		||||
 | 
			
		||||
        g.setOrigin (itemBounds.getX(), 0);
 | 
			
		||||
        g.reduceClipRegion (0, 0, itemBounds.getWidth(), itemBounds.getHeight());
 | 
			
		||||
 | 
			
		||||
        getLookAndFeel().drawMenuBarItem (g,
 | 
			
		||||
                                          itemBounds.getWidth(),
 | 
			
		||||
                                          itemBounds.getHeight(),
 | 
			
		||||
                                          (int) i,
 | 
			
		||||
                                          itemComponent->getName(),
 | 
			
		||||
                                          (int) i == itemUnderMouse,
 | 
			
		||||
                                          (int) i == currentPopupIndex,
 | 
			
		||||
                                          isMouseOverBar,
 | 
			
		||||
                                          *this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    int x = 0;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto& itemComponent = itemComponents[i];
 | 
			
		||||
 | 
			
		||||
        auto w = getLookAndFeel().getMenuBarItemWidth (*this, (int) i, itemComponent->getName());
 | 
			
		||||
        itemComponent->setBounds (x, 0, w, getHeight());
 | 
			
		||||
        x += w;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MenuBarComponent::getItemAt (Point<int> p)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
        if (itemComponents[i]->getBounds().contains (p) && reallyContains (p, true))
 | 
			
		||||
            return (int) i;
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::repaintMenuItem (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (isPositiveAndBelow (index, (int) itemComponents.size()))
 | 
			
		||||
    {
 | 
			
		||||
        auto itemBounds = itemComponents[(size_t) index]->getBounds();
 | 
			
		||||
 | 
			
		||||
        repaint (itemBounds.getX() - 2,
 | 
			
		||||
                 0,
 | 
			
		||||
                 itemBounds.getWidth() + 4,
 | 
			
		||||
                 itemBounds.getHeight());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setItemUnderMouse (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (itemUnderMouse == index)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    repaintMenuItem (itemUnderMouse);
 | 
			
		||||
    itemUnderMouse = index;
 | 
			
		||||
    repaintMenuItem (itemUnderMouse);
 | 
			
		||||
 | 
			
		||||
    if (isPositiveAndBelow (itemUnderMouse, (int) itemComponents.size()))
 | 
			
		||||
        if (auto* handler = itemComponents[(size_t) itemUnderMouse]->getAccessibilityHandler())
 | 
			
		||||
            handler->grabFocus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setOpenItem (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (currentPopupIndex != index)
 | 
			
		||||
    {
 | 
			
		||||
        if (currentPopupIndex < 0 && index >= 0)
 | 
			
		||||
            model->handleMenuBarActivate (true);
 | 
			
		||||
        else if (currentPopupIndex >= 0 && index < 0)
 | 
			
		||||
            model->handleMenuBarActivate (false);
 | 
			
		||||
 | 
			
		||||
        repaintMenuItem (currentPopupIndex);
 | 
			
		||||
        currentPopupIndex = index;
 | 
			
		||||
        repaintMenuItem (currentPopupIndex);
 | 
			
		||||
 | 
			
		||||
        auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            desktop.addGlobalMouseListener (this);
 | 
			
		||||
        else
 | 
			
		||||
            desktop.removeGlobalMouseListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::updateItemUnderMouse (Point<int> p)
 | 
			
		||||
{
 | 
			
		||||
    setItemUnderMouse (getItemAt (p));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::showMenu (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (index != currentPopupIndex)
 | 
			
		||||
    {
 | 
			
		||||
        PopupMenu::dismissAllActiveMenus();
 | 
			
		||||
        menuBarItemsChanged (nullptr);
 | 
			
		||||
 | 
			
		||||
        setOpenItem (index);
 | 
			
		||||
        setItemUnderMouse (index);
 | 
			
		||||
 | 
			
		||||
        if (isPositiveAndBelow (index, (int) itemComponents.size()))
 | 
			
		||||
        {
 | 
			
		||||
            const auto& itemComponent = itemComponents[(size_t) index];
 | 
			
		||||
            auto m = model->getMenuForIndex (itemUnderMouse, itemComponent->getName());
 | 
			
		||||
 | 
			
		||||
            if (m.lookAndFeel == nullptr)
 | 
			
		||||
                m.setLookAndFeel (&getLookAndFeel());
 | 
			
		||||
 | 
			
		||||
            auto itemBounds = itemComponent->getBounds();
 | 
			
		||||
 | 
			
		||||
            m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
 | 
			
		||||
                                                 .withTargetScreenArea (localAreaToGlobal (itemBounds))
 | 
			
		||||
                                                 .withMinimumWidth (itemBounds.getWidth()),
 | 
			
		||||
                             [this, index] (int result) { menuDismissed (index, result); });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
 | 
			
		||||
{
 | 
			
		||||
    topLevelIndexClicked = topLevelIndex;
 | 
			
		||||
    postCommandMessage (itemId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::handleCommandMessage (int commandId)
 | 
			
		||||
{
 | 
			
		||||
    updateItemUnderMouse (getMouseXYRelative());
 | 
			
		||||
 | 
			
		||||
    if (currentPopupIndex == topLevelIndexClicked)
 | 
			
		||||
        setOpenItem (-1);
 | 
			
		||||
 | 
			
		||||
    if (commandId != 0 && model != nullptr)
 | 
			
		||||
        model->menuItemSelected (commandId, topLevelIndexClicked);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarComponent::mouseEnter (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (e.eventComponent == this)
 | 
			
		||||
        updateItemUnderMouse (e.getPosition());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseExit (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (e.eventComponent == this)
 | 
			
		||||
        updateItemUnderMouse (e.getPosition());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (currentPopupIndex < 0)
 | 
			
		||||
    {
 | 
			
		||||
        updateItemUnderMouse (e.getEventRelativeTo (this).getPosition());
 | 
			
		||||
 | 
			
		||||
        currentPopupIndex = -2;
 | 
			
		||||
        showMenu (itemUnderMouse);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto item = getItemAt (e.getEventRelativeTo (this).getPosition());
 | 
			
		||||
 | 
			
		||||
    if (item >= 0)
 | 
			
		||||
        showMenu (item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseUp (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto e2 = e.getEventRelativeTo (this);
 | 
			
		||||
 | 
			
		||||
    updateItemUnderMouse (e2.getPosition());
 | 
			
		||||
 | 
			
		||||
    if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
 | 
			
		||||
    {
 | 
			
		||||
        setOpenItem (-1);
 | 
			
		||||
        PopupMenu::dismissAllActiveMenus();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseMove (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto e2 = e.getEventRelativeTo (this);
 | 
			
		||||
 | 
			
		||||
    if (lastMousePos != e2.getPosition())
 | 
			
		||||
    {
 | 
			
		||||
        if (currentPopupIndex >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            const auto item = getItemAt (e2.getPosition());
 | 
			
		||||
 | 
			
		||||
            if (item >= 0)
 | 
			
		||||
                showMenu (item);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            updateItemUnderMouse (e2.getPosition());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lastMousePos = e2.getPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MenuBarComponent::keyPressed (const KeyPress& key)
 | 
			
		||||
{
 | 
			
		||||
    const auto numMenus = (int) itemComponents.size();
 | 
			
		||||
 | 
			
		||||
    if (numMenus > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const auto currentIndex = jlimit (0, numMenus - 1, currentPopupIndex);
 | 
			
		||||
 | 
			
		||||
        if (key.isKeyCode (KeyPress::leftKey))
 | 
			
		||||
        {
 | 
			
		||||
            showMenu ((currentIndex + numMenus - 1) % numMenus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key.isKeyCode (KeyPress::rightKey))
 | 
			
		||||
        {
 | 
			
		||||
            showMenu ((currentIndex + 1) % numMenus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuBarItemsChanged (MenuBarModel*)
 | 
			
		||||
{
 | 
			
		||||
    StringArray newNames;
 | 
			
		||||
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
        newNames = model->getMenuBarNames();
 | 
			
		||||
 | 
			
		||||
    auto itemsHaveChanged = [this, &newNames]
 | 
			
		||||
    {
 | 
			
		||||
        if ((int) itemComponents.size() != newNames.size())
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
            if (itemComponents[i]->getName() != newNames[(int) i])
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    if (itemsHaveChanged)
 | 
			
		||||
    {
 | 
			
		||||
        updateItemComponents (newNames);
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::updateItemComponents (const StringArray& menuNames)
 | 
			
		||||
{
 | 
			
		||||
    itemComponents.clear();
 | 
			
		||||
 | 
			
		||||
    for (const auto& name : menuNames)
 | 
			
		||||
    {
 | 
			
		||||
        itemComponents.push_back (std::make_unique<AccessibleItemComponent> (*this, name));
 | 
			
		||||
        addAndMakeVisible (*itemComponents.back());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MenuBarComponent::indexOfItemComponent (AccessibleItemComponent* itemComponent) const
 | 
			
		||||
{
 | 
			
		||||
    const auto iter = std::find_if (itemComponents.cbegin(), itemComponents.cend(),
 | 
			
		||||
                                    [itemComponent] (const std::unique_ptr<AccessibleItemComponent>& c) { return c.get() == itemComponent; });
 | 
			
		||||
 | 
			
		||||
    if (iter != itemComponents.cend())
 | 
			
		||||
        return (int) std::distance (itemComponents.cbegin(), iter);
 | 
			
		||||
 | 
			
		||||
    jassertfalse;
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    if (model == nullptr || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        const auto menu = model->getMenuForIndex ((int) i, itemComponents[i]->getName());
 | 
			
		||||
 | 
			
		||||
        if (menu.containsCommandItem (info.commandID))
 | 
			
		||||
        {
 | 
			
		||||
            setItemUnderMouse ((int) i);
 | 
			
		||||
            startTimer (200);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    stopTimer();
 | 
			
		||||
    updateItemUnderMouse (getMouseXYRelative());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> MenuBarComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    struct MenuBarComponentAccessibilityHandler  : public AccessibilityHandler
 | 
			
		||||
    {
 | 
			
		||||
        explicit MenuBarComponentAccessibilityHandler (MenuBarComponent& menuBarComponent)
 | 
			
		||||
            : AccessibilityHandler (menuBarComponent, AccessibilityRole::menuBar)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AccessibleState getCurrentState() const override  { return AccessibleState().withIgnored(); }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return std::make_unique<MenuBarComponentAccessibilityHandler> (*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class MenuBarComponent::AccessibleItemComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AccessibleItemComponent (MenuBarComponent& comp, const String& menuItemName)
 | 
			
		||||
        : owner (comp),
 | 
			
		||||
          name (menuItemName)
 | 
			
		||||
    {
 | 
			
		||||
        setInterceptsMouseClicks (false, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const String& getName() const noexcept    { return name; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 | 
			
		||||
    {
 | 
			
		||||
        class ComponentHandler  : public AccessibilityHandler
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            explicit ComponentHandler (AccessibleItemComponent& item)
 | 
			
		||||
                : AccessibilityHandler (item,
 | 
			
		||||
                                        AccessibilityRole::menuItem,
 | 
			
		||||
                                        getAccessibilityActions (item)),
 | 
			
		||||
                  itemComponent (item)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            String getTitle() const override  { return itemComponent.name; }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            static AccessibilityActions getAccessibilityActions (AccessibleItemComponent& item)
 | 
			
		||||
            {
 | 
			
		||||
                auto showMenu = [&item] { item.owner.showMenu (item.owner.indexOfItemComponent (&item)); };
 | 
			
		||||
 | 
			
		||||
                return AccessibilityActions().addAction (AccessibilityActionType::focus,
 | 
			
		||||
                                                         [&item] { item.owner.setItemUnderMouse (item.owner.indexOfItemComponent (&item)); })
 | 
			
		||||
                                             .addAction (AccessibilityActionType::press,    showMenu)
 | 
			
		||||
                                             .addAction (AccessibilityActionType::showMenu, showMenu);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AccessibleItemComponent& itemComponent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return std::make_unique<ComponentHandler> (*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MenuBarComponent& owner;
 | 
			
		||||
    const String name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MenuBarComponent::MenuBarComponent (MenuBarModel* m)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setWantsKeyboardFocus (false);
 | 
			
		||||
    setMouseClickGrabsKeyboardFocus (false);
 | 
			
		||||
 | 
			
		||||
    setModel (m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarComponent::~MenuBarComponent()
 | 
			
		||||
{
 | 
			
		||||
    setModel (nullptr);
 | 
			
		||||
    Desktop::getInstance().removeGlobalMouseListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel* MenuBarComponent::getModel() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setModel (MenuBarModel* const newModel)
 | 
			
		||||
{
 | 
			
		||||
    if (model != newModel)
 | 
			
		||||
    {
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        model = newModel;
 | 
			
		||||
 | 
			
		||||
        if (model != nullptr)
 | 
			
		||||
            model->addListener (this);
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
        menuBarItemsChanged (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    const auto isMouseOverBar = (currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver());
 | 
			
		||||
 | 
			
		||||
    getLookAndFeel().drawMenuBarBackground (g, getWidth(), getHeight(), isMouseOverBar, *this);
 | 
			
		||||
 | 
			
		||||
    if (model == nullptr)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        const auto& itemComponent = itemComponents[i];
 | 
			
		||||
        const auto itemBounds = itemComponent->getBounds();
 | 
			
		||||
 | 
			
		||||
        Graphics::ScopedSaveState ss (g);
 | 
			
		||||
 | 
			
		||||
        g.setOrigin (itemBounds.getX(), 0);
 | 
			
		||||
        g.reduceClipRegion (0, 0, itemBounds.getWidth(), itemBounds.getHeight());
 | 
			
		||||
 | 
			
		||||
        getLookAndFeel().drawMenuBarItem (g,
 | 
			
		||||
                                          itemBounds.getWidth(),
 | 
			
		||||
                                          itemBounds.getHeight(),
 | 
			
		||||
                                          (int) i,
 | 
			
		||||
                                          itemComponent->getName(),
 | 
			
		||||
                                          (int) i == itemUnderMouse,
 | 
			
		||||
                                          (int) i == currentPopupIndex,
 | 
			
		||||
                                          isMouseOverBar,
 | 
			
		||||
                                          *this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    int x = 0;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto& itemComponent = itemComponents[i];
 | 
			
		||||
 | 
			
		||||
        auto w = getLookAndFeel().getMenuBarItemWidth (*this, (int) i, itemComponent->getName());
 | 
			
		||||
        itemComponent->setBounds (x, 0, w, getHeight());
 | 
			
		||||
        x += w;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MenuBarComponent::getItemAt (Point<int> p)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
        if (itemComponents[i]->getBounds().contains (p) && reallyContains (p, true))
 | 
			
		||||
            return (int) i;
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::repaintMenuItem (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (isPositiveAndBelow (index, (int) itemComponents.size()))
 | 
			
		||||
    {
 | 
			
		||||
        auto itemBounds = itemComponents[(size_t) index]->getBounds();
 | 
			
		||||
 | 
			
		||||
        repaint (itemBounds.getX() - 2,
 | 
			
		||||
                 0,
 | 
			
		||||
                 itemBounds.getWidth() + 4,
 | 
			
		||||
                 itemBounds.getHeight());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setItemUnderMouse (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (itemUnderMouse == index)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    repaintMenuItem (itemUnderMouse);
 | 
			
		||||
    itemUnderMouse = index;
 | 
			
		||||
    repaintMenuItem (itemUnderMouse);
 | 
			
		||||
 | 
			
		||||
    if (isPositiveAndBelow (itemUnderMouse, (int) itemComponents.size()))
 | 
			
		||||
        if (auto* handler = itemComponents[(size_t) itemUnderMouse]->getAccessibilityHandler())
 | 
			
		||||
            handler->grabFocus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::setOpenItem (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (currentPopupIndex != index)
 | 
			
		||||
    {
 | 
			
		||||
        if (currentPopupIndex < 0 && index >= 0)
 | 
			
		||||
            model->handleMenuBarActivate (true);
 | 
			
		||||
        else if (currentPopupIndex >= 0 && index < 0)
 | 
			
		||||
            model->handleMenuBarActivate (false);
 | 
			
		||||
 | 
			
		||||
        repaintMenuItem (currentPopupIndex);
 | 
			
		||||
        currentPopupIndex = index;
 | 
			
		||||
        repaintMenuItem (currentPopupIndex);
 | 
			
		||||
 | 
			
		||||
        auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            desktop.addGlobalMouseListener (this);
 | 
			
		||||
        else
 | 
			
		||||
            desktop.removeGlobalMouseListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::updateItemUnderMouse (Point<int> p)
 | 
			
		||||
{
 | 
			
		||||
    setItemUnderMouse (getItemAt (p));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::showMenu (int index)
 | 
			
		||||
{
 | 
			
		||||
    if (index == currentPopupIndex)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    const auto needToOpenNewSubMenu = isPositiveAndBelow (index, (int) itemComponents.size());
 | 
			
		||||
 | 
			
		||||
    if (needToOpenNewSubMenu)
 | 
			
		||||
        ++numActiveMenus;
 | 
			
		||||
 | 
			
		||||
    PopupMenu::dismissAllActiveMenus();
 | 
			
		||||
    menuBarItemsChanged (nullptr);
 | 
			
		||||
 | 
			
		||||
    setOpenItem (index);
 | 
			
		||||
    setItemUnderMouse (index);
 | 
			
		||||
 | 
			
		||||
    if (needToOpenNewSubMenu)
 | 
			
		||||
    {
 | 
			
		||||
        const auto& itemComponent = itemComponents[(size_t) index];
 | 
			
		||||
        auto m = model->getMenuForIndex (itemUnderMouse, itemComponent->getName());
 | 
			
		||||
 | 
			
		||||
        if (m.lookAndFeel == nullptr)
 | 
			
		||||
            m.setLookAndFeel (&getLookAndFeel());
 | 
			
		||||
 | 
			
		||||
        auto itemBounds = itemComponent->getBounds();
 | 
			
		||||
 | 
			
		||||
        const auto callback = [ref = SafePointer<MenuBarComponent> (this), index] (int result)
 | 
			
		||||
        {
 | 
			
		||||
            if (ref != nullptr)
 | 
			
		||||
                ref->menuDismissed (index, result);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
 | 
			
		||||
                                             .withTargetScreenArea (localAreaToGlobal (itemBounds))
 | 
			
		||||
                                             .withMinimumWidth (itemBounds.getWidth()),
 | 
			
		||||
                         callback);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
 | 
			
		||||
{
 | 
			
		||||
    topLevelIndexDismissed = topLevelIndex;
 | 
			
		||||
    --numActiveMenus;
 | 
			
		||||
    postCommandMessage (itemId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::handleCommandMessage (int commandId)
 | 
			
		||||
{
 | 
			
		||||
    updateItemUnderMouse (getMouseXYRelative());
 | 
			
		||||
 | 
			
		||||
    if (numActiveMenus == 0)
 | 
			
		||||
        setOpenItem (-1);
 | 
			
		||||
 | 
			
		||||
    if (commandId != 0 && model != nullptr)
 | 
			
		||||
        model->menuItemSelected (commandId, topLevelIndexDismissed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarComponent::mouseEnter (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (e.eventComponent == this)
 | 
			
		||||
        updateItemUnderMouse (e.getPosition());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseExit (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (e.eventComponent == this)
 | 
			
		||||
        updateItemUnderMouse (e.getPosition());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (currentPopupIndex < 0)
 | 
			
		||||
    {
 | 
			
		||||
        updateItemUnderMouse (e.getEventRelativeTo (this).getPosition());
 | 
			
		||||
 | 
			
		||||
        currentPopupIndex = -2;
 | 
			
		||||
        showMenu (itemUnderMouse);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto item = getItemAt (e.getEventRelativeTo (this).getPosition());
 | 
			
		||||
 | 
			
		||||
    if (item >= 0)
 | 
			
		||||
        showMenu (item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseUp (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto e2 = e.getEventRelativeTo (this);
 | 
			
		||||
 | 
			
		||||
    updateItemUnderMouse (e2.getPosition());
 | 
			
		||||
 | 
			
		||||
    if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
 | 
			
		||||
    {
 | 
			
		||||
        setOpenItem (-1);
 | 
			
		||||
        PopupMenu::dismissAllActiveMenus();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::mouseMove (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const auto e2 = e.getEventRelativeTo (this);
 | 
			
		||||
 | 
			
		||||
    if (lastMousePos != e2.getPosition())
 | 
			
		||||
    {
 | 
			
		||||
        if (currentPopupIndex >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            const auto item = getItemAt (e2.getPosition());
 | 
			
		||||
 | 
			
		||||
            if (item >= 0)
 | 
			
		||||
                showMenu (item);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            updateItemUnderMouse (e2.getPosition());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lastMousePos = e2.getPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MenuBarComponent::keyPressed (const KeyPress& key)
 | 
			
		||||
{
 | 
			
		||||
    const auto numMenus = (int) itemComponents.size();
 | 
			
		||||
 | 
			
		||||
    if (numMenus > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const auto currentIndex = jlimit (0, numMenus - 1, currentPopupIndex);
 | 
			
		||||
 | 
			
		||||
        if (key.isKeyCode (KeyPress::leftKey))
 | 
			
		||||
        {
 | 
			
		||||
            showMenu ((currentIndex + numMenus - 1) % numMenus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key.isKeyCode (KeyPress::rightKey))
 | 
			
		||||
        {
 | 
			
		||||
            showMenu ((currentIndex + 1) % numMenus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuBarItemsChanged (MenuBarModel*)
 | 
			
		||||
{
 | 
			
		||||
    StringArray newNames;
 | 
			
		||||
 | 
			
		||||
    if (model != nullptr)
 | 
			
		||||
        newNames = model->getMenuBarNames();
 | 
			
		||||
 | 
			
		||||
    auto itemsHaveChanged = [this, &newNames]
 | 
			
		||||
    {
 | 
			
		||||
        if ((int) itemComponents.size() != newNames.size())
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
            if (itemComponents[i]->getName() != newNames[(int) i])
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    if (itemsHaveChanged)
 | 
			
		||||
    {
 | 
			
		||||
        updateItemComponents (newNames);
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::updateItemComponents (const StringArray& menuNames)
 | 
			
		||||
{
 | 
			
		||||
    itemComponents.clear();
 | 
			
		||||
 | 
			
		||||
    for (const auto& name : menuNames)
 | 
			
		||||
    {
 | 
			
		||||
        itemComponents.push_back (std::make_unique<AccessibleItemComponent> (*this, name));
 | 
			
		||||
        addAndMakeVisible (*itemComponents.back());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MenuBarComponent::indexOfItemComponent (AccessibleItemComponent* itemComponent) const
 | 
			
		||||
{
 | 
			
		||||
    const auto iter = std::find_if (itemComponents.cbegin(), itemComponents.cend(),
 | 
			
		||||
                                    [itemComponent] (const std::unique_ptr<AccessibleItemComponent>& c) { return c.get() == itemComponent; });
 | 
			
		||||
 | 
			
		||||
    if (iter != itemComponents.cend())
 | 
			
		||||
        return (int) std::distance (itemComponents.cbegin(), iter);
 | 
			
		||||
 | 
			
		||||
    jassertfalse;
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    if (model == nullptr || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < itemComponents.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        const auto menu = model->getMenuForIndex ((int) i, itemComponents[i]->getName());
 | 
			
		||||
 | 
			
		||||
        if (menu.containsCommandItem (info.commandID))
 | 
			
		||||
        {
 | 
			
		||||
            setItemUnderMouse ((int) i);
 | 
			
		||||
            startTimer (200);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarComponent::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    stopTimer();
 | 
			
		||||
    updateItemUnderMouse (getMouseXYRelative());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> MenuBarComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    struct MenuBarComponentAccessibilityHandler  : public AccessibilityHandler
 | 
			
		||||
    {
 | 
			
		||||
        explicit MenuBarComponentAccessibilityHandler (MenuBarComponent& menuBarComponent)
 | 
			
		||||
            : AccessibilityHandler (menuBarComponent, AccessibilityRole::menuBar)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AccessibleState getCurrentState() const override  { return AccessibleState().withIgnored(); }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return std::make_unique<MenuBarComponentAccessibilityHandler> (*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,127 +1,128 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A menu bar component.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MenuBarComponent  : public Component,
 | 
			
		||||
                                    private MenuBarModel::Listener,
 | 
			
		||||
                                    private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a menu bar.
 | 
			
		||||
 | 
			
		||||
        @param model    the model object to use to control this bar. You can
 | 
			
		||||
                        pass omit the parameter or pass nullptr into this if you like,
 | 
			
		||||
                        and set the model later using the setModel() method.
 | 
			
		||||
    */
 | 
			
		||||
    MenuBarComponent (MenuBarModel* model = nullptr);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MenuBarComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the model object to use to control the bar.
 | 
			
		||||
 | 
			
		||||
        This can be a null pointer, in which case the bar will be empty. Don't delete the object
 | 
			
		||||
        that is passed-in while it's still being used by this MenuBar.
 | 
			
		||||
    */
 | 
			
		||||
    void setModel (MenuBarModel* newModel);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current menu bar model being used. */
 | 
			
		||||
    MenuBarModel* getModel() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Pops up one of the menu items.
 | 
			
		||||
 | 
			
		||||
        This lets you manually open one of the menus - it could be triggered by a
 | 
			
		||||
        key shortcut, for example.
 | 
			
		||||
    */
 | 
			
		||||
    void showMenu (int menuIndex);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseEnter (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseExit (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseMove (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleCommandMessage (int commandId) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void menuBarItemsChanged (MenuBarModel*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AccessibleItemComponent;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
 | 
			
		||||
    int getItemAt (Point<int>);
 | 
			
		||||
    void setItemUnderMouse (int);
 | 
			
		||||
    void setOpenItem (int);
 | 
			
		||||
    void updateItemUnderMouse (Point<int>);
 | 
			
		||||
    void repaintMenuItem (int);
 | 
			
		||||
    void menuDismissed (int, int);
 | 
			
		||||
 | 
			
		||||
    void updateItemComponents (const StringArray&);
 | 
			
		||||
    int indexOfItemComponent (AccessibleItemComponent*) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel* model = nullptr;
 | 
			
		||||
    std::vector<std::unique_ptr<AccessibleItemComponent>> itemComponents;
 | 
			
		||||
 | 
			
		||||
    Point<int> lastMousePos;
 | 
			
		||||
    int itemUnderMouse = -1, currentPopupIndex = -1, topLevelIndexClicked = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A menu bar component.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MenuBarComponent  : public Component,
 | 
			
		||||
                                    private MenuBarModel::Listener,
 | 
			
		||||
                                    private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a menu bar.
 | 
			
		||||
 | 
			
		||||
        @param model    the model object to use to control this bar. You can
 | 
			
		||||
                        pass omit the parameter or pass nullptr into this if you like,
 | 
			
		||||
                        and set the model later using the setModel() method.
 | 
			
		||||
    */
 | 
			
		||||
    MenuBarComponent (MenuBarModel* model = nullptr);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MenuBarComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the model object to use to control the bar.
 | 
			
		||||
 | 
			
		||||
        This can be a null pointer, in which case the bar will be empty. Don't delete the object
 | 
			
		||||
        that is passed-in while it's still being used by this MenuBar.
 | 
			
		||||
    */
 | 
			
		||||
    void setModel (MenuBarModel* newModel);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current menu bar model being used. */
 | 
			
		||||
    MenuBarModel* getModel() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Pops up one of the menu items.
 | 
			
		||||
 | 
			
		||||
        This lets you manually open one of the menus - it could be triggered by a
 | 
			
		||||
        key shortcut, for example.
 | 
			
		||||
    */
 | 
			
		||||
    void showMenu (int menuIndex);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseEnter (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseExit (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseMove (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleCommandMessage (int commandId) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void menuBarItemsChanged (MenuBarModel*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AccessibleItemComponent;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
 | 
			
		||||
    int getItemAt (Point<int>);
 | 
			
		||||
    void setItemUnderMouse (int);
 | 
			
		||||
    void setOpenItem (int);
 | 
			
		||||
    void updateItemUnderMouse (Point<int>);
 | 
			
		||||
    void repaintMenuItem (int);
 | 
			
		||||
    void menuDismissed (int, int);
 | 
			
		||||
 | 
			
		||||
    void updateItemComponents (const StringArray&);
 | 
			
		||||
    int indexOfItemComponent (AccessibleItemComponent*) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel* model = nullptr;
 | 
			
		||||
    std::vector<std::unique_ptr<AccessibleItemComponent>> itemComponents;
 | 
			
		||||
 | 
			
		||||
    Point<int> lastMousePos;
 | 
			
		||||
    int itemUnderMouse = -1, currentPopupIndex = -1, topLevelIndexDismissed = 0;
 | 
			
		||||
    int numActiveMenus = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,99 +1,99 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
MenuBarModel::MenuBarModel() noexcept
 | 
			
		||||
    : manager (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel::~MenuBarModel()
 | 
			
		||||
{
 | 
			
		||||
    setApplicationCommandManagerToWatch (nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarModel::menuItemsChanged()
 | 
			
		||||
{
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::setApplicationCommandManagerToWatch (ApplicationCommandManager* newManager)
 | 
			
		||||
{
 | 
			
		||||
    if (manager != newManager)
 | 
			
		||||
    {
 | 
			
		||||
        if (manager != nullptr)
 | 
			
		||||
            manager->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        manager = newManager;
 | 
			
		||||
 | 
			
		||||
        if (manager != nullptr)
 | 
			
		||||
            manager->addListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::addListener (Listener* newListener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (newListener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::removeListener (Listener* listenerToRemove)
 | 
			
		||||
{
 | 
			
		||||
    // Trying to remove a listener that isn't on the list!
 | 
			
		||||
    // If this assertion happens because this object is a dangling pointer, make sure you've not
 | 
			
		||||
    // deleted this menu model while it's still being used by something (e.g. by a MenuBarComponent)
 | 
			
		||||
    jassert (listeners.contains (listenerToRemove));
 | 
			
		||||
 | 
			
		||||
    listeners.remove (listenerToRemove);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarModel::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([this] (Listener& l) { l.menuBarItemsChanged (this); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([this, &info] (Listener& l) { l.menuCommandInvoked (this, info); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::applicationCommandListChanged()
 | 
			
		||||
{
 | 
			
		||||
    menuItemsChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::handleMenuBarActivate (bool isActive)
 | 
			
		||||
{
 | 
			
		||||
    menuBarActivated (isActive);
 | 
			
		||||
    listeners.call ([this, isActive] (Listener& l) { l.menuBarActivated (this, isActive); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::menuBarActivated (bool) {}
 | 
			
		||||
void MenuBarModel::Listener::menuBarActivated (MenuBarModel*, bool) {}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
MenuBarModel::MenuBarModel() noexcept
 | 
			
		||||
    : manager (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MenuBarModel::~MenuBarModel()
 | 
			
		||||
{
 | 
			
		||||
    setApplicationCommandManagerToWatch (nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarModel::menuItemsChanged()
 | 
			
		||||
{
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::setApplicationCommandManagerToWatch (ApplicationCommandManager* newManager)
 | 
			
		||||
{
 | 
			
		||||
    if (manager != newManager)
 | 
			
		||||
    {
 | 
			
		||||
        if (manager != nullptr)
 | 
			
		||||
            manager->removeListener (this);
 | 
			
		||||
 | 
			
		||||
        manager = newManager;
 | 
			
		||||
 | 
			
		||||
        if (manager != nullptr)
 | 
			
		||||
            manager->addListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::addListener (Listener* newListener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (newListener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::removeListener (Listener* listenerToRemove)
 | 
			
		||||
{
 | 
			
		||||
    // Trying to remove a listener that isn't on the list!
 | 
			
		||||
    // If this assertion happens because this object is a dangling pointer, make sure you've not
 | 
			
		||||
    // deleted this menu model while it's still being used by something (e.g. by a MenuBarComponent)
 | 
			
		||||
    jassert (listeners.contains (listenerToRemove));
 | 
			
		||||
 | 
			
		||||
    listeners.remove (listenerToRemove);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void MenuBarModel::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([this] (Listener& l) { l.menuBarItemsChanged (this); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([this, &info] (Listener& l) { l.menuCommandInvoked (this, info); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::applicationCommandListChanged()
 | 
			
		||||
{
 | 
			
		||||
    menuItemsChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::handleMenuBarActivate (bool isActive)
 | 
			
		||||
{
 | 
			
		||||
    menuBarActivated (isActive);
 | 
			
		||||
    listeners.call ([this, isActive] (Listener& l) { l.menuBarActivated (this, isActive); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBarModel::menuBarActivated (bool) {}
 | 
			
		||||
void MenuBarModel::Listener::menuBarActivated (MenuBarModel*, bool) {}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,192 +1,192 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class for controlling MenuBar components.
 | 
			
		||||
 | 
			
		||||
    This class is used to tell a MenuBar what menus to show, and to respond
 | 
			
		||||
    to a menu being selected.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel::Listener, MenuBarComponent, PopupMenu
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MenuBarModel      : private AsyncUpdater,
 | 
			
		||||
                                    private ApplicationCommandManagerListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MenuBarModel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Call this when some of your menu items have changed.
 | 
			
		||||
 | 
			
		||||
        This method will cause a callback to any MenuBarListener objects that
 | 
			
		||||
        are registered with this model.
 | 
			
		||||
 | 
			
		||||
        If this model is displaying items from an ApplicationCommandManager, you
 | 
			
		||||
        can use the setApplicationCommandManagerToWatch() method to cause
 | 
			
		||||
        change messages to be sent automatically when the ApplicationCommandManager
 | 
			
		||||
        is changed.
 | 
			
		||||
 | 
			
		||||
        @see addListener, removeListener, MenuBarListener
 | 
			
		||||
    */
 | 
			
		||||
    void menuItemsChanged();
 | 
			
		||||
 | 
			
		||||
    /** Tells the menu bar to listen to the specified command manager, and to update
 | 
			
		||||
        itself when the commands change.
 | 
			
		||||
 | 
			
		||||
        This will also allow it to flash a menu name when a command from that menu
 | 
			
		||||
        is invoked using a keystroke.
 | 
			
		||||
    */
 | 
			
		||||
    void setApplicationCommandManagerToWatch (ApplicationCommandManager* manager);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A class to receive callbacks when a MenuBarModel changes.
 | 
			
		||||
 | 
			
		||||
        @see MenuBarModel::addListener, MenuBarModel::removeListener, MenuBarModel::menuItemsChanged
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** This callback is made when items are changed in the menu bar model. */
 | 
			
		||||
        virtual void menuBarItemsChanged (MenuBarModel* menuBarModel) = 0;
 | 
			
		||||
 | 
			
		||||
        /** This callback is made when an application command is invoked that
 | 
			
		||||
            is represented by one of the items in the menu bar model.
 | 
			
		||||
        */
 | 
			
		||||
        virtual void menuCommandInvoked (MenuBarModel* menuBarModel,
 | 
			
		||||
                                         const ApplicationCommandTarget::InvocationInfo& info) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Called when the menu bar is first activated or when the user finished interacting
 | 
			
		||||
            with the menu bar. */
 | 
			
		||||
        virtual void menuBarActivated (MenuBarModel* menuBarModel, bool isActive);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Registers a listener for callbacks when the menu items in this model change.
 | 
			
		||||
 | 
			
		||||
        The listener object will get callbacks when this object's menuItemsChanged()
 | 
			
		||||
        method is called.
 | 
			
		||||
 | 
			
		||||
        @see removeListener
 | 
			
		||||
    */
 | 
			
		||||
    void addListener (Listener* listenerToAdd);
 | 
			
		||||
 | 
			
		||||
    /** Removes a listener.
 | 
			
		||||
        @see addListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeListener (Listener* listenerToRemove);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This method must return a list of the names of the menus. */
 | 
			
		||||
    virtual StringArray getMenuBarNames() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This should return the popup menu to display for a given top-level menu.
 | 
			
		||||
 | 
			
		||||
        @param topLevelMenuIndex    the index of the top-level menu to show
 | 
			
		||||
        @param menuName             the name of the top-level menu item to show
 | 
			
		||||
    */
 | 
			
		||||
    virtual PopupMenu getMenuForIndex (int topLevelMenuIndex,
 | 
			
		||||
                                       const String& menuName) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is called when a menu item has been clicked on.
 | 
			
		||||
 | 
			
		||||
        @param menuItemID           the item ID of the PopupMenu item that was selected
 | 
			
		||||
        @param topLevelMenuIndex    the index of the top-level menu from which the item was
 | 
			
		||||
                                    chosen (just in case you've used duplicate ID numbers
 | 
			
		||||
                                    on more than one of the popup menus)
 | 
			
		||||
    */
 | 
			
		||||
    virtual void menuItemSelected (int menuItemID,
 | 
			
		||||
                                   int topLevelMenuIndex) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is called when the user starts/stops navigating the menu bar.
 | 
			
		||||
 | 
			
		||||
        @param isActive              true when the user starts navigating the menu bar
 | 
			
		||||
    */
 | 
			
		||||
    virtual void menuBarActivated (bool isActive);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MAC || DOXYGEN
 | 
			
		||||
    /** OSX ONLY - Sets the model that is currently being shown as the main
 | 
			
		||||
        menu bar at the top of the screen on the Mac.
 | 
			
		||||
 | 
			
		||||
        You can pass nullptr to stop the current model being displayed. Be careful
 | 
			
		||||
        not to delete a model while it is being used.
 | 
			
		||||
 | 
			
		||||
        An optional extra menu can be specified, containing items to add to the top of
 | 
			
		||||
        the apple menu. (Confusingly, the 'apple' menu isn't the one with a picture of
 | 
			
		||||
        an apple, it's the one next to it, with your application's name at the top
 | 
			
		||||
        and the services menu etc on it). When one of these items is selected, the
 | 
			
		||||
        menu bar model will be used to invoke it, and in the menuItemSelected() callback
 | 
			
		||||
        the topLevelMenuIndex parameter will be -1. If you pass in an extraAppleMenuItems
 | 
			
		||||
        object then newMenuBarModel must be non-null.
 | 
			
		||||
 | 
			
		||||
        If the recentItemsMenuName parameter is non-empty, then any sub-menus with this
 | 
			
		||||
        name will be replaced by OSX's special recent-files menu.
 | 
			
		||||
    */
 | 
			
		||||
    static void setMacMainMenu (MenuBarModel* newMenuBarModel,
 | 
			
		||||
                                const PopupMenu* extraAppleMenuItems = nullptr,
 | 
			
		||||
                                const String& recentItemsMenuName = String());
 | 
			
		||||
 | 
			
		||||
    /** OSX ONLY - Returns the menu model that is currently being shown as
 | 
			
		||||
        the main menu bar.
 | 
			
		||||
    */
 | 
			
		||||
    static MenuBarModel* getMacMainMenu();
 | 
			
		||||
 | 
			
		||||
    /** OSX ONLY - Returns the menu that was last passed as the extraAppleMenuItems
 | 
			
		||||
        argument to setMacMainMenu(), or nullptr if none was specified.
 | 
			
		||||
    */
 | 
			
		||||
    static const PopupMenu* getMacExtraAppleItemsMenu();
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void applicationCommandListChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleMenuBarActivate (bool isActive);
 | 
			
		||||
private:
 | 
			
		||||
    ApplicationCommandManager* manager;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarModel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class for controlling MenuBar components.
 | 
			
		||||
 | 
			
		||||
    This class is used to tell a MenuBar what menus to show, and to respond
 | 
			
		||||
    to a menu being selected.
 | 
			
		||||
 | 
			
		||||
    @see MenuBarModel::Listener, MenuBarComponent, PopupMenu
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MenuBarModel      : private AsyncUpdater,
 | 
			
		||||
                                    private ApplicationCommandManagerListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MenuBarModel() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MenuBarModel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Call this when some of your menu items have changed.
 | 
			
		||||
 | 
			
		||||
        This method will cause a callback to any MenuBarListener objects that
 | 
			
		||||
        are registered with this model.
 | 
			
		||||
 | 
			
		||||
        If this model is displaying items from an ApplicationCommandManager, you
 | 
			
		||||
        can use the setApplicationCommandManagerToWatch() method to cause
 | 
			
		||||
        change messages to be sent automatically when the ApplicationCommandManager
 | 
			
		||||
        is changed.
 | 
			
		||||
 | 
			
		||||
        @see addListener, removeListener, MenuBarListener
 | 
			
		||||
    */
 | 
			
		||||
    void menuItemsChanged();
 | 
			
		||||
 | 
			
		||||
    /** Tells the menu bar to listen to the specified command manager, and to update
 | 
			
		||||
        itself when the commands change.
 | 
			
		||||
 | 
			
		||||
        This will also allow it to flash a menu name when a command from that menu
 | 
			
		||||
        is invoked using a keystroke.
 | 
			
		||||
    */
 | 
			
		||||
    void setApplicationCommandManagerToWatch (ApplicationCommandManager* manager);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A class to receive callbacks when a MenuBarModel changes.
 | 
			
		||||
 | 
			
		||||
        @see MenuBarModel::addListener, MenuBarModel::removeListener, MenuBarModel::menuItemsChanged
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** This callback is made when items are changed in the menu bar model. */
 | 
			
		||||
        virtual void menuBarItemsChanged (MenuBarModel* menuBarModel) = 0;
 | 
			
		||||
 | 
			
		||||
        /** This callback is made when an application command is invoked that
 | 
			
		||||
            is represented by one of the items in the menu bar model.
 | 
			
		||||
        */
 | 
			
		||||
        virtual void menuCommandInvoked (MenuBarModel* menuBarModel,
 | 
			
		||||
                                         const ApplicationCommandTarget::InvocationInfo& info) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Called when the menu bar is first activated or when the user finished interacting
 | 
			
		||||
            with the menu bar. */
 | 
			
		||||
        virtual void menuBarActivated (MenuBarModel* menuBarModel, bool isActive);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Registers a listener for callbacks when the menu items in this model change.
 | 
			
		||||
 | 
			
		||||
        The listener object will get callbacks when this object's menuItemsChanged()
 | 
			
		||||
        method is called.
 | 
			
		||||
 | 
			
		||||
        @see removeListener
 | 
			
		||||
    */
 | 
			
		||||
    void addListener (Listener* listenerToAdd);
 | 
			
		||||
 | 
			
		||||
    /** Removes a listener.
 | 
			
		||||
        @see addListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeListener (Listener* listenerToRemove);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This method must return a list of the names of the menus. */
 | 
			
		||||
    virtual StringArray getMenuBarNames() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This should return the popup menu to display for a given top-level menu.
 | 
			
		||||
 | 
			
		||||
        @param topLevelMenuIndex    the index of the top-level menu to show
 | 
			
		||||
        @param menuName             the name of the top-level menu item to show
 | 
			
		||||
    */
 | 
			
		||||
    virtual PopupMenu getMenuForIndex (int topLevelMenuIndex,
 | 
			
		||||
                                       const String& menuName) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is called when a menu item has been clicked on.
 | 
			
		||||
 | 
			
		||||
        @param menuItemID           the item ID of the PopupMenu item that was selected
 | 
			
		||||
        @param topLevelMenuIndex    the index of the top-level menu from which the item was
 | 
			
		||||
                                    chosen (just in case you've used duplicate ID numbers
 | 
			
		||||
                                    on more than one of the popup menus)
 | 
			
		||||
    */
 | 
			
		||||
    virtual void menuItemSelected (int menuItemID,
 | 
			
		||||
                                   int topLevelMenuIndex) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is called when the user starts/stops navigating the menu bar.
 | 
			
		||||
 | 
			
		||||
        @param isActive              true when the user starts navigating the menu bar
 | 
			
		||||
    */
 | 
			
		||||
    virtual void menuBarActivated (bool isActive);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MAC || DOXYGEN
 | 
			
		||||
    /** OSX ONLY - Sets the model that is currently being shown as the main
 | 
			
		||||
        menu bar at the top of the screen on the Mac.
 | 
			
		||||
 | 
			
		||||
        You can pass nullptr to stop the current model being displayed. Be careful
 | 
			
		||||
        not to delete a model while it is being used.
 | 
			
		||||
 | 
			
		||||
        An optional extra menu can be specified, containing items to add to the top of
 | 
			
		||||
        the apple menu. (Confusingly, the 'apple' menu isn't the one with a picture of
 | 
			
		||||
        an apple, it's the one next to it, with your application's name at the top
 | 
			
		||||
        and the services menu etc on it). When one of these items is selected, the
 | 
			
		||||
        menu bar model will be used to invoke it, and in the menuItemSelected() callback
 | 
			
		||||
        the topLevelMenuIndex parameter will be -1. If you pass in an extraAppleMenuItems
 | 
			
		||||
        object then newMenuBarModel must be non-null.
 | 
			
		||||
 | 
			
		||||
        If the recentItemsMenuName parameter is non-empty, then any sub-menus with this
 | 
			
		||||
        name will be replaced by OSX's special recent-files menu.
 | 
			
		||||
    */
 | 
			
		||||
    static void setMacMainMenu (MenuBarModel* newMenuBarModel,
 | 
			
		||||
                                const PopupMenu* extraAppleMenuItems = nullptr,
 | 
			
		||||
                                const String& recentItemsMenuName = String());
 | 
			
		||||
 | 
			
		||||
    /** OSX ONLY - Returns the menu model that is currently being shown as
 | 
			
		||||
        the main menu bar.
 | 
			
		||||
    */
 | 
			
		||||
    static MenuBarModel* getMacMainMenu();
 | 
			
		||||
 | 
			
		||||
    /** OSX ONLY - Returns the menu that was last passed as the extraAppleMenuItems
 | 
			
		||||
        argument to setMacMainMenu(), or nullptr if none was specified.
 | 
			
		||||
    */
 | 
			
		||||
    static const PopupMenu* getMacExtraAppleItemsMenu();
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void applicationCommandListChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void handleMenuBarActivate (bool isActive);
 | 
			
		||||
private:
 | 
			
		||||
    ApplicationCommandManager* manager;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarModel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2079
									
								
								deps/juce/modules/juce_gui_basics/menus/juce_PopupMenu.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2079
									
								
								deps/juce/modules/juce_gui_basics/menus/juce_PopupMenu.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user