303 lines
8.6 KiB
C++
303 lines
8.6 KiB
C++
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
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
|