2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 //==============================================================================
30 struct CustomMenuBarItemHolder
: public Component
32 CustomMenuBarItemHolder (const ReferenceCountedObjectPtr
<PopupMenu::CustomComponent
>& customComponent
)
34 setInterceptsMouseClicks (false, true);
35 update (customComponent
);
38 void update (const ReferenceCountedObjectPtr
<PopupMenu::CustomComponent
>& newComponent
)
40 jassert (newComponent
!= nullptr);
42 if (newComponent
!= custom
)
44 if (custom
!= nullptr)
45 removeChildComponent (custom
.get());
47 custom
= newComponent
;
48 addAndMakeVisible (*custom
);
53 void resized() override
55 custom
->setBounds (getLocalBounds());
58 ReferenceCountedObjectPtr
<PopupMenu::CustomComponent
> custom
;
60 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomMenuBarItemHolder
)
63 //==============================================================================
64 BurgerMenuComponent::BurgerMenuComponent (MenuBarModel
* modelToUse
)
67 listBox
.addMouseListener (this, true);
69 setModel (modelToUse
);
70 addAndMakeVisible (listBox
);
73 BurgerMenuComponent::~BurgerMenuComponent()
76 model
->removeListener (this);
79 void BurgerMenuComponent::setModel (MenuBarModel
* newModel
)
81 if (newModel
!= model
)
84 model
->removeListener (this);
89 model
->addListener (this);
92 listBox
.updateContent();
96 MenuBarModel
* BurgerMenuComponent::getModel() const noexcept
101 void BurgerMenuComponent::refresh()
103 lastRowClicked
= inputSourceIndexOfLastClick
= -1;
107 if (model
!= nullptr)
109 auto menuBarNames
= model
->getMenuBarNames();
111 for (auto menuIdx
= 0; menuIdx
< menuBarNames
.size(); ++menuIdx
)
113 PopupMenu::Item menuItem
;
114 menuItem
.text
= menuBarNames
[menuIdx
];
117 auto menu
= model
->getMenuForIndex (menuIdx
, ignore
);
119 rows
.add (Row
{ true, menuIdx
, menuItem
});
120 addMenuBarItemsForMenu (menu
, menuIdx
);
125 void BurgerMenuComponent::addMenuBarItemsForMenu (PopupMenu
& menu
, int menuIdx
)
127 for (PopupMenu::MenuItemIterator
it (menu
); it
.next();)
129 auto& item
= it
.getItem();
131 if (item
.isSeparator
)
134 if (hasSubMenu (item
))
135 addMenuBarItemsForMenu (*item
.subMenu
, menuIdx
);
137 rows
.add (Row
{false, menuIdx
, it
.getItem()});
141 int BurgerMenuComponent::getNumRows()
146 void BurgerMenuComponent::paint (Graphics
& g
)
148 getLookAndFeel().drawPopupMenuBackground (g
, getWidth(), getHeight());
151 void BurgerMenuComponent::paintListBoxItem (int rowIndex
, Graphics
& g
, int w
, int h
, bool highlight
)
153 auto& lf
= getLookAndFeel();
154 Rectangle
<int> r (w
, h
);
156 auto row
= (rowIndex
< rows
.size() ? rows
.getReference (rowIndex
)
157 : Row
{ true, 0, {} });
159 g
.fillAll (findColour (PopupMenu::backgroundColourId
));
161 if (row
.isMenuHeader
)
163 lf
.drawPopupMenuSectionHeader (g
, r
.reduced (20, 0), row
.item
.text
);
164 g
.setColour (Colours::grey
);
165 g
.fillRect (r
.withHeight (1));
169 auto& item
= row
.item
;
170 auto* colour
= item
.colour
!= Colour() ? &item
.colour
: nullptr;
172 if (item
.customComponent
== nullptr)
173 lf
.drawPopupMenuItem (g
, r
.reduced (20, 0),
180 item
.shortcutKeyDescription
,
186 bool BurgerMenuComponent::hasSubMenu (const PopupMenu::Item
& item
)
188 return item
.subMenu
!= nullptr && (item
.itemID
== 0 || item
.subMenu
->getNumItems() > 0);
191 void BurgerMenuComponent::listBoxItemClicked (int rowIndex
, const MouseEvent
& e
)
193 auto row
= rowIndex
< rows
.size() ? rows
.getReference (rowIndex
)
194 : Row
{ true, 0, {} };
196 if (! row
.isMenuHeader
)
198 lastRowClicked
= rowIndex
;
199 inputSourceIndexOfLastClick
= e
.source
.getIndex();
203 Component
* BurgerMenuComponent::refreshComponentForRow (int rowIndex
, bool isRowSelected
, Component
* existing
)
205 auto row
= rowIndex
< rows
.size() ? rows
.getReference (rowIndex
)
206 : Row
{ true, 0, {} };
208 auto hasCustomComponent
= (row
.item
.customComponent
!= nullptr);
210 if (existing
== nullptr && hasCustomComponent
)
211 return new CustomMenuBarItemHolder (row
.item
.customComponent
);
213 if (existing
!= nullptr)
215 auto* componentToUpdate
= dynamic_cast<CustomMenuBarItemHolder
*> (existing
);
216 jassert (componentToUpdate
!= nullptr);
218 if (hasCustomComponent
&& componentToUpdate
!= nullptr)
220 row
.item
.customComponent
->setHighlighted (isRowSelected
);
221 componentToUpdate
->update (row
.item
.customComponent
);
233 void BurgerMenuComponent::resized()
235 listBox
.setBounds (getLocalBounds());
238 void BurgerMenuComponent::menuBarItemsChanged (MenuBarModel
* menuBarModel
)
240 setModel (menuBarModel
);
243 void BurgerMenuComponent::menuCommandInvoked (MenuBarModel
*, const ApplicationCommandTarget::InvocationInfo
&)
247 void BurgerMenuComponent::mouseUp (const MouseEvent
& event
)
249 auto rowIndex
= listBox
.getSelectedRow();
251 if (rowIndex
== lastRowClicked
&& rowIndex
< rows
.size()
252 && event
.source
.getIndex() == inputSourceIndexOfLastClick
)
254 auto& row
= rows
.getReference (rowIndex
);
256 if (! row
.isMenuHeader
)
258 listBox
.selectRow (-1);
261 inputSourceIndexOfLastClick
= -1;
263 topLevelIndexClicked
= row
.topLevelMenuIndex
;
264 auto& item
= row
.item
;
266 if (auto* managerOfChosenCommand
= item
.commandManager
)
268 ApplicationCommandTarget::InvocationInfo
info (item
.itemID
);
269 info
.invocationMethod
= ApplicationCommandTarget::InvocationInfo::fromMenu
;
271 managerOfChosenCommand
->invoke (info
, true);
274 postCommandMessage (item
.itemID
);
279 void BurgerMenuComponent::handleCommandMessage (int commandID
)
281 if (model
!= nullptr)
283 model
->menuItemSelected (commandID
, topLevelIndexClicked
);
284 topLevelIndexClicked
= -1;
287 listBox
.updateContent();
291 void BurgerMenuComponent::lookAndFeelChanged()
293 listBox
.setRowHeight (roundToInt (getLookAndFeel().getPopupMenuFont().getHeight() * 2.0f
));
296 //==============================================================================
297 std::unique_ptr
<AccessibilityHandler
> BurgerMenuComponent::createAccessibilityHandler()
299 return std::make_unique
<AccessibilityHandler
> (*this, AccessibilityRole::menuBar
);