mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-24 01:54:22 +00:00
Added a BurgerMenu component which will display your menus and menu items in a "burger" menu
This commit is contained in:
parent
ccbe16f7df
commit
e2a0759ab3
10 changed files with 561 additions and 34 deletions
|
|
@ -1275,6 +1275,72 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropDemo)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct BurgerMenuHeader : public Component,
|
||||
private Button::Listener
|
||||
{
|
||||
BurgerMenuHeader()
|
||||
{
|
||||
static const unsigned char burgerMenuPathData[]
|
||||
= { 110,109,0,0,128,64,0,0,32,65,108,0,0,224,65,0,0,32,65,98,254,212,232,65,0,0,32,65,0,0,240,65,252,
|
||||
169,17,65,0,0,240,65,0,0,0,65,98,0,0,240,65,8,172,220,64,254,212,232,65,0,0,192,64,0,0,224,65,0,0,
|
||||
192,64,108,0,0,128,64,0,0,192,64,98,16,88,57,64,0,0,192,64,0,0,0,64,8,172,220,64,0,0,0,64,0,0,0,65,
|
||||
98,0,0,0,64,252,169,17,65,16,88,57,64,0,0,32,65,0,0,128,64,0,0,32,65,99,109,0,0,224,65,0,0,96,65,108,
|
||||
0,0,128,64,0,0,96,65,98,16,88,57,64,0,0,96,65,0,0,0,64,4,86,110,65,0,0,0,64,0,0,128,65,98,0,0,0,64,
|
||||
254,212,136,65,16,88,57,64,0,0,144,65,0,0,128,64,0,0,144,65,108,0,0,224,65,0,0,144,65,98,254,212,232,
|
||||
65,0,0,144,65,0,0,240,65,254,212,136,65,0,0,240,65,0,0,128,65,98,0,0,240,65,4,86,110,65,254,212,232,
|
||||
65,0,0,96,65,0,0,224,65,0,0,96,65,99,109,0,0,224,65,0,0,176,65,108,0,0,128,64,0,0,176,65,98,16,88,57,
|
||||
64,0,0,176,65,0,0,0,64,2,43,183,65,0,0,0,64,0,0,192,65,98,0,0,0,64,254,212,200,65,16,88,57,64,0,0,208,
|
||||
65,0,0,128,64,0,0,208,65,108,0,0,224,65,0,0,208,65,98,254,212,232,65,0,0,208,65,0,0,240,65,254,212,
|
||||
200,65,0,0,240,65,0,0,192,65,98,0,0,240,65,2,43,183,65,254,212,232,65,0,0,176,65,0,0,224,65,0,0,176,
|
||||
65,99,101,0,0 };
|
||||
|
||||
Path p;
|
||||
p.loadPathFromData (burgerMenuPathData, sizeof (burgerMenuPathData));
|
||||
burgerButton.setShape (p, true, true, false);
|
||||
|
||||
burgerButton.addListener (this);
|
||||
addAndMakeVisible (burgerButton);
|
||||
}
|
||||
|
||||
~BurgerMenuHeader()
|
||||
{
|
||||
MainAppWindow::getSharedSidePanel().showOrHide (false);
|
||||
}
|
||||
|
||||
private:
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto titleBarBackgroundColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId)
|
||||
.darker();
|
||||
|
||||
g.setColour (titleBarBackgroundColour);
|
||||
g.fillRect (getLocalBounds());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
burgerButton.setBounds (r.removeFromRight (40).withSizeKeepingCentre (20, 20));
|
||||
|
||||
titleLabel.setFont (Font (getHeight() * 0.5f, Font::plain));
|
||||
titleLabel.setBounds (r);
|
||||
}
|
||||
|
||||
void buttonClicked (Button*) override
|
||||
{
|
||||
auto& panel = MainAppWindow::getSharedSidePanel();
|
||||
|
||||
panel.showOrHide (! panel.isPanelShowing());
|
||||
}
|
||||
|
||||
Label titleLabel { "titleLabel", "JUCE Demo" };
|
||||
ShapeButton burgerButton { "burgerButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuHeader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MenusDemo : public Component,
|
||||
public MenuBarModel,
|
||||
|
|
@ -1282,6 +1348,15 @@ class MenusDemo : public Component,
|
|||
private Button::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum MenuBarPosition
|
||||
{
|
||||
window,
|
||||
globalMenuBar,
|
||||
burger
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
MenusDemo()
|
||||
{
|
||||
addAndMakeVisible (menuBar = new MenuBarComponent (this));
|
||||
|
|
@ -1290,12 +1365,15 @@ public:
|
|||
popupButton.setTriggeredOnMouseDown (true);
|
||||
popupButton.addListener (this);
|
||||
addAndMakeVisible (popupButton);
|
||||
addChildComponent (menuHeader);
|
||||
|
||||
setApplicationCommandManagerToWatch (&MainAppWindow::getApplicationCommandManager());
|
||||
}
|
||||
|
||||
~MenusDemo()
|
||||
{
|
||||
MainAppWindow::getSharedSidePanel().setContent (nullptr, false);
|
||||
|
||||
#if JUCE_MAC
|
||||
MenuBarModel::setMacMainMenu (nullptr);
|
||||
#endif
|
||||
|
|
@ -1307,7 +1385,13 @@ public:
|
|||
void resized() override
|
||||
{
|
||||
Rectangle<int> area (getLocalBounds());
|
||||
menuBar->setBounds (area.removeFromTop (LookAndFeel::getDefaultLookAndFeel().getDefaultMenuBarHeight()));
|
||||
|
||||
{
|
||||
auto menuBarArea = area.removeFromTop (40);
|
||||
|
||||
menuBar->setBounds (menuBarArea.withHeight (LookAndFeel::getDefaultLookAndFeel().getDefaultMenuBarHeight()));
|
||||
menuHeader.setBounds (menuBarArea);
|
||||
}
|
||||
|
||||
area.removeFromTop (20);
|
||||
area = area.removeFromTop (33);
|
||||
|
|
@ -1317,7 +1401,7 @@ public:
|
|||
//==============================================================================
|
||||
StringArray getMenuBarNames() override
|
||||
{
|
||||
return { "Demo", "Look-and-feel", "Tabs", "Misc" };
|
||||
return { "Demo", "Look-and-feel", "Menus", "Tabs", "Misc" };
|
||||
}
|
||||
|
||||
PopupMenu getMenuForIndex (int menuIndex, const String& /*menuName*/) override
|
||||
|
|
@ -1350,10 +1434,6 @@ public:
|
|||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, MainAppWindow::useNativeTitleBar);
|
||||
|
||||
#if JUCE_MAC
|
||||
menu.addItem (6000, "Use Native Menu Bar");
|
||||
#endif
|
||||
|
||||
#if ! JUCE_LINUX
|
||||
menu.addCommandItem (commandManager, MainAppWindow::goToKioskMode);
|
||||
#endif
|
||||
|
|
@ -1372,6 +1452,14 @@ public:
|
|||
}
|
||||
}
|
||||
else if (menuIndex == 2)
|
||||
{
|
||||
menu.addItem (6000, "Inside Window", true, menuBarPosition == window);
|
||||
#if JUCE_MAC
|
||||
menu.addItem (6001, "Global Menu Bar", true, menuBarPosition == globalMenuBar);
|
||||
#endif
|
||||
menu.addItem (6002, "Burger Menu", true, menuBarPosition == burger);
|
||||
}
|
||||
else if (menuIndex == 3)
|
||||
{
|
||||
if (TabbedComponent* tabs = findParentComponentOfClass<TabbedComponent>())
|
||||
{
|
||||
|
|
@ -1381,7 +1469,7 @@ public:
|
|||
menu.addItem (3003, "Tabs on Right", true, tabs->getOrientation() == TabbedButtonBar::TabsAtRight);
|
||||
}
|
||||
}
|
||||
else if (menuIndex == 3)
|
||||
else if (menuIndex == 4)
|
||||
{
|
||||
return getDummyPopupMenu();
|
||||
}
|
||||
|
|
@ -1394,20 +1482,28 @@ public:
|
|||
// most of our menu items are invoked automatically as commands, but we can handle the
|
||||
// other special cases here..
|
||||
|
||||
if (menuItemID == 6000)
|
||||
if (menuItemID >= 6000 && menuItemID < 7000)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
if (MenuBarModel::getMacMainMenu() != nullptr)
|
||||
auto newPosition = static_cast<MenuBarPosition> (menuItemID - 6000);
|
||||
|
||||
if (newPosition != menuBarPosition)
|
||||
{
|
||||
MenuBarModel::setMacMainMenu (nullptr);
|
||||
menuBar->setModel (this);
|
||||
menuBarPosition = newPosition;
|
||||
|
||||
if (menuBarPosition != burger)
|
||||
MainAppWindow::getSharedSidePanel().showOrHide (false);
|
||||
|
||||
#if JUCE_MAC
|
||||
MenuBarModel::setMacMainMenu (menuBarPosition == globalMenuBar ? this : nullptr);
|
||||
#endif
|
||||
menuBar->setModel (menuBarPosition == window ? this : nullptr);
|
||||
menuBar->setVisible (menuBarPosition == window);
|
||||
burgerMenu.setModel (menuBarPosition == burger ? this : nullptr);
|
||||
menuHeader.setVisible (menuBarPosition == burger);
|
||||
|
||||
MainAppWindow::getSharedSidePanel().setContent (menuBarPosition == burger ? &burgerMenu : nullptr, false);
|
||||
menuItemsChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
menuBar->setModel (nullptr);
|
||||
MenuBarModel::setMacMainMenu (this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (menuItemID >= 3000 && menuItemID <= 3003)
|
||||
{
|
||||
|
|
@ -1431,6 +1527,9 @@ public:
|
|||
private:
|
||||
TextButton popupButton;
|
||||
ScopedPointer<MenuBarComponent> menuBar;
|
||||
BurgerMenuComponent burgerMenu;
|
||||
BurgerMenuHeader menuHeader;
|
||||
MenuBarPosition menuBarPosition = window;
|
||||
|
||||
PopupMenu getDummyPopupMenu()
|
||||
{
|
||||
|
|
@ -1443,21 +1542,24 @@ private:
|
|||
m.addCustomItem (5, new CustomMenuComponent());
|
||||
m.addSeparator();
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
if (menuBarPosition != burger)
|
||||
{
|
||||
PopupMenu subMenu;
|
||||
|
||||
for (int s = 0; s < 8; ++s)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
PopupMenu subSubMenu;
|
||||
PopupMenu subMenu;
|
||||
|
||||
for (int item = 0; item < 8; ++item)
|
||||
subSubMenu.addItem (1000 + (i * s * item), "Item " + String (item + 1));
|
||||
for (int s = 0; s < 8; ++s)
|
||||
{
|
||||
PopupMenu subSubMenu;
|
||||
|
||||
subMenu.addSubMenu ("Sub-sub menu " + String (s + 1), subSubMenu);
|
||||
for (int item = 0; item < 8; ++item)
|
||||
subSubMenu.addItem (1000 + (i * s * item), "Item " + String (item + 1));
|
||||
|
||||
subMenu.addSubMenu ("Sub-sub menu " + String (s + 1), subSubMenu);
|
||||
}
|
||||
|
||||
m.addSubMenu ("Sub menu " + String (i + 1), subMenu);
|
||||
}
|
||||
|
||||
m.addSubMenu ("Sub menu " + String (i + 1), subMenu);
|
||||
}
|
||||
|
||||
return m;
|
||||
|
|
@ -1508,9 +1610,11 @@ private:
|
|||
void timerCallback() override
|
||||
{
|
||||
Random random;
|
||||
blobPosition.setBounds ((float) random.nextInt (getWidth()),
|
||||
(float) random.nextInt (getHeight()),
|
||||
40.0f, 30.0f);
|
||||
|
||||
if (! getBounds().isEmpty())
|
||||
blobPosition.setBounds ((float) random.nextInt (getWidth()),
|
||||
(float) random.nextInt (getHeight()),
|
||||
40.0f, 30.0f);
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ public:
|
|||
mainWindow.reset();
|
||||
}
|
||||
|
||||
void backButtonPressed() override
|
||||
{
|
||||
MainAppWindow::getSharedSidePanel().showOrHide (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ public:
|
|||
demoList.getViewport()->setScrollOnDragEnabled (true);
|
||||
|
||||
addAndMakeVisible (demoList);
|
||||
addAndMakeVisible (sidePanel);
|
||||
sidePanel.setAlwaysOnTop (true);
|
||||
}
|
||||
|
||||
~ContentComponent()
|
||||
|
|
@ -246,9 +248,15 @@ public:
|
|||
return currentDemo != nullptr && currentDemo->getName().contains ("OpenGL 2D");
|
||||
}
|
||||
|
||||
SidePanel& getSharedSidePanel()
|
||||
{
|
||||
return sidePanel;
|
||||
}
|
||||
|
||||
private:
|
||||
ListBox demoList;
|
||||
ScopedPointer<Component> currentDemo;
|
||||
SidePanel sidePanel {"Menu", 300, false};
|
||||
|
||||
LookAndFeel_V1 lookAndFeelV1;
|
||||
LookAndFeel_V2 lookAndFeelV2;
|
||||
|
|
@ -745,6 +753,11 @@ int MainAppWindow::getActiveRenderingEngine() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
SidePanel& MainAppWindow::getSharedSidePanel()
|
||||
{
|
||||
return getMainAppWindow()->contentComponent->getSharedSidePanel();
|
||||
}
|
||||
|
||||
Path MainAppWindow::getJUCELogoPath()
|
||||
{
|
||||
return Drawable::parseSVGPath (
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ public:
|
|||
// (returns a shared AudioDeviceManager object that all the demos can use)
|
||||
static AudioDeviceManager& getSharedAudioDeviceManager();
|
||||
|
||||
static SidePanel& getSharedSidePanel();
|
||||
|
||||
StringArray getRenderingEngines() const;
|
||||
int getActiveRenderingEngine() const;
|
||||
void setRenderingEngine (int index);
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ namespace juce
|
|||
#include "lookandfeel/juce_LookAndFeel_V3.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel_V4.cpp"
|
||||
#include "menus/juce_MenuBarComponent.cpp"
|
||||
#include "menus/juce_BurgerMenuComponent.cpp"
|
||||
#include "menus/juce_MenuBarModel.cpp"
|
||||
#include "menus/juce_PopupMenu.cpp"
|
||||
#include "positioning/juce_MarkerList.cpp"
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ namespace juce
|
|||
#include "widgets/juce_ToolbarItemComponent.h"
|
||||
#include "widgets/juce_ToolbarItemFactory.h"
|
||||
#include "widgets/juce_ToolbarItemPalette.h"
|
||||
#include "menus/juce_BurgerMenuComponent.h"
|
||||
#include "buttons/juce_ToolbarButton.h"
|
||||
#include "misc/juce_DropShadower.h"
|
||||
#include "misc/juce_JUCESplashScreen.h"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ public:
|
|||
the caller must manage the lifetime of the component
|
||||
*/
|
||||
SidePanel (StringRef title, int width, bool positionOnLeft,
|
||||
Component* contentComponent = nullptr, bool deleteComponentWhenNoLongerNeeded = true);
|
||||
Component* contentComponent = nullptr,
|
||||
bool deleteComponentWhenNoLongerNeeded = true);
|
||||
|
||||
/** Destructor */
|
||||
~SidePanel();
|
||||
|
|
@ -156,7 +157,7 @@ private:
|
|||
OptionalScopedPointer<Component> contentComponent;
|
||||
|
||||
Label titleLabel;
|
||||
ShapeButton dismissButton {"dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white};
|
||||
ShapeButton dismissButton { "dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
|
||||
|
||||
Rectangle<int> shadowArea;
|
||||
|
||||
|
|
|
|||
294
modules/juce_gui_basics/menus/juce_BurgerMenuComponent.cpp
Normal file
294
modules/juce_gui_basics/menus/juce_BurgerMenuComponent.cpp
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-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);
|
||||
|
||||
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)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
listBox.setRowHeight (roundToInt (lf.getPopupMenuFont().getHeight() * 2.0f));
|
||||
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,
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
106
modules/juce_gui_basics/menus/juce_BurgerMenuComponent.h
Normal file
106
modules/juce_gui_basics/menus/juce_BurgerMenuComponent.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-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
|
||||
*/
|
||||
//==============================================================================
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Row
|
||||
{
|
||||
bool isMenuHeader;
|
||||
int topLevelMenuIndex;
|
||||
PopupMenu::Item item;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
@ -44,7 +44,7 @@ class MenuWindow;
|
|||
|
||||
static bool canBeTriggered (const PopupMenu::Item& item) noexcept { return item.isEnabled && item.itemID != 0 && ! item.isSectionHeader; }
|
||||
static bool hasActiveSubMenu (const PopupMenu::Item& item) noexcept { return item.isEnabled && item.subMenu != nullptr && item.subMenu->items.size() > 0; }
|
||||
static const Colour* getColour (const PopupMenu::Item& item) noexcept { return item.colour != Colour (0x00000000) ? &item.colour : nullptr; }
|
||||
static const Colour* getColour (const PopupMenu::Item& item) noexcept { return item.colour != Colour() ? &item.colour : nullptr; }
|
||||
static bool hasSubMenu (const PopupMenu::Item& item) noexcept { return item.subMenu != nullptr && (item.itemID == 0 || item.subMenu->getNumItems() > 0); }
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue