VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_basics / menus / juce_BurgerMenuComponent.cpp
blobdc9f961a8a18b46d1f0912d0ad0bd5e9e4abb8e7
1 /*
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
8 licensing.
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
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
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);
49 resized();
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)
66 lookAndFeelChanged();
67 listBox.addMouseListener (this, true);
69 setModel (modelToUse);
70 addAndMakeVisible (listBox);
73 BurgerMenuComponent::~BurgerMenuComponent()
75 if (model != nullptr)
76 model->removeListener (this);
79 void BurgerMenuComponent::setModel (MenuBarModel* newModel)
81 if (newModel != model)
83 if (model != nullptr)
84 model->removeListener (this);
86 model = newModel;
88 if (model != nullptr)
89 model->addListener (this);
91 refresh();
92 listBox.updateContent();
96 MenuBarModel* BurgerMenuComponent::getModel() const noexcept
98 return model;
101 void BurgerMenuComponent::refresh()
103 lastRowClicked = inputSourceIndexOfLastClick = -1;
105 rows.clear();
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];
116 String ignore;
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)
132 continue;
134 if (hasSubMenu (item))
135 addMenuBarItemsForMenu (*item.subMenu, menuIdx);
136 else
137 rows.add (Row {false, menuIdx, it.getItem()});
141 int BurgerMenuComponent::getNumRows()
143 return rows.size();
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));
167 else
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),
174 item.isSeparator,
175 item.isEnabled,
176 highlight,
177 item.isTicked,
178 hasSubMenu (item),
179 item.text,
180 item.shortcutKeyDescription,
181 item.image.get(),
182 colour);
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);
223 else
225 delete existing;
226 existing = nullptr;
230 return existing;
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);
260 lastRowClicked = -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;
286 refresh();
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);
302 } // namespace juce