JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_MenuBarComponent.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
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.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30{
31public:
33 : owner (comp),
34 name (menuItemName)
35 {
36 setInterceptsMouseClicks (false, false);
37 }
38
39 const String& getName() const noexcept { return name; }
40
41private:
42 std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
43 {
45 {
46 public:
47 explicit ComponentHandler (AccessibleItemComponent& item)
49 AccessibilityRole::menuItem,
50 getAccessibilityActions (item)),
51 itemComponent (item)
52 {
53 }
54
55 String getTitle() const override { return itemComponent.name; }
56
57 private:
58 static AccessibilityActions getAccessibilityActions (AccessibleItemComponent& item)
59 {
60 auto showMenu = [&item] { item.owner.showMenu (item.owner.indexOfItemComponent (&item)); };
61
62 return AccessibilityActions().addAction (AccessibilityActionType::focus,
63 [&item] { item.owner.setItemUnderMouse (item.owner.indexOfItemComponent (&item)); })
64 .addAction (AccessibilityActionType::press, showMenu)
65 .addAction (AccessibilityActionType::showMenu, showMenu);
66 }
67
68 AccessibleItemComponent& itemComponent;
69 };
70
71 return std::make_unique<ComponentHandler> (*this);
72 }
73
74 MenuBarComponent& owner;
75 const String name;
76};
77
86
92
94{
95 return model;
96}
97
99{
100 if (model != newModel)
101 {
102 if (model != nullptr)
103 model->removeListener (this);
104
105 model = newModel;
106
107 if (model != nullptr)
108 model->addListener (this);
109
110 repaint();
111 menuBarItemsChanged (nullptr);
112 }
113}
114
115//==============================================================================
117{
118 const auto isMouseOverBar = (currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver());
119
120 getLookAndFeel().drawMenuBarBackground (g, getWidth(), getHeight(), isMouseOverBar, *this);
121
122 if (model == nullptr)
123 return;
124
125 for (size_t i = 0; i < itemComponents.size(); ++i)
126 {
127 const auto& itemComponent = itemComponents[i];
128 const auto itemBounds = itemComponent->getBounds();
129
131
132 g.setOrigin (itemBounds.getX(), 0);
133 g.reduceClipRegion (0, 0, itemBounds.getWidth(), itemBounds.getHeight());
134
135 getLookAndFeel().drawMenuBarItem (g,
136 itemBounds.getWidth(),
137 itemBounds.getHeight(),
138 (int) i,
139 itemComponent->getName(),
140 (int) i == itemUnderMouse,
141 (int) i == currentPopupIndex,
143 *this);
144 }
145}
146
148{
149 int x = 0;
150
151 for (size_t i = 0; i < itemComponents.size(); ++i)
152 {
153 auto& itemComponent = itemComponents[i];
154
155 auto w = getLookAndFeel().getMenuBarItemWidth (*this, (int) i, itemComponent->getName());
156 itemComponent->setBounds (x, 0, w, getHeight());
157 x += w;
158 }
159}
160
161int MenuBarComponent::getItemAt (Point<int> p)
162{
163 for (size_t i = 0; i < itemComponents.size(); ++i)
164 if (itemComponents[i]->getBounds().contains (p) && reallyContains (p, true))
165 return (int) i;
166
167 return -1;
168}
169
170void MenuBarComponent::repaintMenuItem (int index)
171{
172 if (isPositiveAndBelow (index, (int) itemComponents.size()))
173 {
174 auto itemBounds = itemComponents[(size_t) index]->getBounds();
175
176 repaint (itemBounds.getX() - 2,
177 0,
178 itemBounds.getWidth() + 4,
179 itemBounds.getHeight());
180 }
181}
182
183void MenuBarComponent::setItemUnderMouse (int index)
184{
185 if (itemUnderMouse == index)
186 return;
187
188 repaintMenuItem (itemUnderMouse);
189 itemUnderMouse = index;
190 repaintMenuItem (itemUnderMouse);
191
192 if (isPositiveAndBelow (itemUnderMouse, (int) itemComponents.size()))
193 if (auto* handler = itemComponents[(size_t) itemUnderMouse]->getAccessibilityHandler())
194 handler->grabFocus();
195}
196
197void MenuBarComponent::setOpenItem (int index)
198{
199 if (currentPopupIndex != index)
200 {
202 model->handleMenuBarActivate (true);
203 else if (currentPopupIndex >= 0 && index < 0)
204 model->handleMenuBarActivate (false);
205
206 repaintMenuItem (currentPopupIndex);
207 currentPopupIndex = index;
208 repaintMenuItem (currentPopupIndex);
209
211
212 if (index >= 0)
213 desktop.addGlobalMouseListener (this);
214 else
215 desktop.removeGlobalMouseListener (this);
216 }
217}
218
219void MenuBarComponent::updateItemUnderMouse (Point<int> p)
220{
221 setItemUnderMouse (getItemAt (p));
222}
223
225{
226 if (index == currentPopupIndex)
227 return;
228
229 const auto needToOpenNewSubMenu = isPositiveAndBelow (index, (int) itemComponents.size());
230
232 ++numActiveMenus;
233
235 menuBarItemsChanged (nullptr);
236
237 setOpenItem (index);
238 setItemUnderMouse (index);
239
241 {
242 const auto& itemComponent = itemComponents[(size_t) index];
243 auto m = model->getMenuForIndex (itemUnderMouse, itemComponent->getName());
244
245 if (m.lookAndFeel == nullptr)
247
248 auto itemBounds = itemComponent->getBounds();
249
250 const auto callback = [ref = SafePointer<MenuBarComponent> (this), index] (int result)
251 {
252 if (ref != nullptr)
253 ref->menuDismissed (index, result);
254 };
255
256 m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
258 .withMinimumWidth (itemBounds.getWidth()),
259 callback);
260 }
261}
262
263void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
264{
265 topLevelIndexDismissed = topLevelIndex;
266 --numActiveMenus;
267 postCommandMessage (itemId);
268}
269
271{
272 updateItemUnderMouse (getMouseXYRelative());
273
274 if (numActiveMenus == 0)
275 setOpenItem (-1);
276
277 if (commandId != 0 && model != nullptr)
278 model->menuItemSelected (commandId, topLevelIndexDismissed);
279}
280
281//==============================================================================
283{
284 if (e.eventComponent == this)
285 updateItemUnderMouse (e.getPosition());
286}
287
289{
290 if (e.eventComponent == this)
291 updateItemUnderMouse (e.getPosition());
292}
293
295{
296 if (currentPopupIndex < 0)
297 {
298 updateItemUnderMouse (e.getEventRelativeTo (this).getPosition());
299
300 currentPopupIndex = -2;
301 showMenu (itemUnderMouse);
302 }
303}
304
306{
307 const auto item = getItemAt (e.getEventRelativeTo (this).getPosition());
308
309 if (item >= 0)
310 showMenu (item);
311}
312
314{
315 const auto e2 = e.getEventRelativeTo (this);
316
317 updateItemUnderMouse (e2.getPosition());
318
319 if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
320 {
321 setOpenItem (-1);
323 }
324}
325
327{
328 const auto e2 = e.getEventRelativeTo (this);
329
330 if (lastMousePos != e2.getPosition())
331 {
332 if (currentPopupIndex >= 0)
333 {
334 const auto item = getItemAt (e2.getPosition());
335
336 if (item >= 0)
337 showMenu (item);
338 }
339 else
340 {
341 updateItemUnderMouse (e2.getPosition());
342 }
343
344 lastMousePos = e2.getPosition();
345 }
346}
347
349{
350 const auto numMenus = (int) itemComponents.size();
351
352 if (numMenus > 0)
353 {
354 const auto currentIndex = jlimit (0, numMenus - 1, currentPopupIndex);
355
357 {
359 return true;
360 }
361
363 {
365 return true;
366 }
367 }
368
369 return false;
370}
371
373{
375
376 if (model != nullptr)
377 newNames = model->getMenuBarNames();
378
379 auto itemsHaveChanged = [this, &newNames]
380 {
381 if ((int) itemComponents.size() != newNames.size())
382 return true;
383
384 for (size_t i = 0; i < itemComponents.size(); ++i)
385 if (itemComponents[i]->getName() != newNames[(int) i])
386 return true;
387
388 return false;
389 }();
390
392 {
393 updateItemComponents (newNames);
394
395 repaint();
396 resized();
397 }
398}
399
400void MenuBarComponent::updateItemComponents (const StringArray& menuNames)
401{
402 itemComponents.clear();
403
404 for (const auto& name : menuNames)
405 {
406 itemComponents.push_back (std::make_unique<AccessibleItemComponent> (*this, name));
407 addAndMakeVisible (*itemComponents.back());
408 }
409}
410
411int MenuBarComponent::indexOfItemComponent (AccessibleItemComponent* itemComponent) const
412{
413 const auto iter = std::find_if (itemComponents.cbegin(), itemComponents.cend(),
414 [itemComponent] (const std::unique_ptr<AccessibleItemComponent>& c) { return c.get() == itemComponent; });
415
416 if (iter != itemComponents.cend())
417 return (int) std::distance (itemComponents.cbegin(), iter);
418
420 return -1;
421}
422
424{
425 if (model == nullptr || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
426 return;
427
428 for (size_t i = 0; i < itemComponents.size(); ++i)
429 {
430 const auto menu = model->getMenuForIndex ((int) i, itemComponents[i]->getName());
431
432 if (menu.containsCommandItem (info.commandID))
433 {
434 setItemUnderMouse ((int) i);
435 startTimer (200);
436 break;
437 }
438 }
439}
440
441void MenuBarComponent::timerCallback()
442{
443 stopTimer();
444 updateItemUnderMouse (getMouseXYRelative());
445}
446
447//==============================================================================
449{
451 {
452 explicit MenuBarComponentAccessibilityHandler (MenuBarComponent& menuBarComponent)
453 : AccessibilityHandler (menuBarComponent, AccessibilityRole::menuBar)
454 {
455 }
456
457 AccessibleState getCurrentState() const override { return AccessibleState().withIgnored(); }
458 };
459
460 return std::make_unique<MenuBarComponentAccessibilityHandler> (*this);
461}
462
463} // namespace juce
A simple wrapper for building a collection of supported accessibility actions and corresponding callb...
AccessibilityActions & addAction(AccessibilityActionType type, std::function< void()> actionCallback)
Adds an action.
Base class for accessible Components.
Represents the state of an accessible UI element.
AccessibleState withIgnored() const noexcept
Sets the ignored flag and returns the new state.
The base class for all JUCE user-interface objects.
bool contains(Point< int > localPoint)
Returns true if a given point lies within this component or one of its children.
void setRepaintsOnMouseActivity(bool shouldRepaint) noexcept
Causes automatic repaints when the mouse enters or exits this component.
void setInterceptsMouseClicks(bool allowClicksOnThisComponent, bool allowClicksOnChildComponents) noexcept
Changes the default return value for the hitTest() method.
bool reallyContains(Point< int > localPoint, bool returnTrueIfWithinAChild)
Returns true if a given point lies in this component, taking any overlapping siblings into account.
int getHeight() const noexcept
Returns the component's height in pixels.
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
AccessibilityHandler * getAccessibilityHandler()
Returns the accessibility handler for this component, or nullptr if this component is not accessible.
void postCommandMessage(int commandId)
Dispatches a numbered message to this component.
Rectangle< int > getBounds() const noexcept
Returns this component's bounding box.
void repaint()
Marks the whole component as needing to be redrawn.
Point< int > getMouseXYRelative() const
Returns the mouse's current position, relative to this component.
Rectangle< int > localAreaToGlobal(Rectangle< int > localArea) const
Converts a rectangle from this component's coordinate space to a screen coordinate.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
bool isMouseOver(bool includeChildren=false) const
Returns true if the mouse is currently over this component.
void setMouseClickGrabsKeyboardFocus(bool shouldGrabFocus)
Chooses whether a click on this component automatically grabs the focus.
int getWidth() const noexcept
Returns the component's width in pixels.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
String getName() const noexcept
Returns the name of this component.
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
void removeGlobalMouseListener(MouseListener *listener)
Unregisters a MouseListener that was added with addGlobalMouseListener().
Uses RAII to save and restore the state of a graphics context.
A graphics context, used for drawing a component or image.
bool reduceClipRegion(int x, int y, int width, int height)
Intersects the current clipping region with another region.
void setOrigin(Point< int > newOrigin)
Moves the position of the context's origin.
Represents a key press, including any modifier keys that are needed.
bool isKeyCode(int keyCodeToCompare) const noexcept
Checks whether the KeyPress's key is the same as the one provided, without checking the modifiers.
static const int rightKey
key-code for the cursor-right key
static const int leftKey
key-code for the cursor-left key
A menu bar component.
void setModel(MenuBarModel *newModel)
Changes the model object to use to control the bar.
~MenuBarComponent() override
Destructor.
void menuBarItemsChanged(MenuBarModel *) override
This callback is made when items are changed in the menu bar model.
void mouseExit(const MouseEvent &) override
Called when the mouse moves out of a component.
bool keyPressed(const KeyPress &) override
Called when a key is pressed.
std::unique_ptr< AccessibilityHandler > createAccessibilityHandler() override
Override this method to return a custom AccessibilityHandler for this component.
void paint(Graphics &) override
Components can override this method to draw their content.
void handleCommandMessage(int commandId) override
Called to handle a command that was sent by postCommandMessage().
void mouseMove(const MouseEvent &) override
Called when the mouse moves inside a component.
void mouseDrag(const MouseEvent &) override
Called when the mouse is moved while a button is held down.
void resized() override
Called when this component's size has been changed.
void mouseUp(const MouseEvent &) override
Called when a mouse button is released.
MenuBarModel * getModel() const noexcept
Returns the current menu bar model being used.
void mouseEnter(const MouseEvent &) override
Called when the mouse first enters a component.
void menuCommandInvoked(MenuBarModel *, const ApplicationCommandTarget::InvocationInfo &) override
This callback is made when an application command is invoked that is represented by one of the items ...
void showMenu(int menuIndex)
Pops up one of the menu items.
MenuBarComponent(MenuBarModel *model=nullptr)
Creates a menu bar.
void mouseDown(const MouseEvent &) override
Called when a mouse button is pressed.
A class for controlling MenuBar components.
virtual PopupMenu getMenuForIndex(int topLevelMenuIndex, const String &menuName)=0
This should return the popup menu to display for a given top-level menu.
void removeListener(Listener *listenerToRemove)
Removes a listener.
virtual StringArray getMenuBarNames()=0
This method must return a list of the names of the menus.
void addListener(Listener *listenerToAdd)
Registers a listener for callbacks when the menu items in this model change.
virtual void menuItemSelected(int menuItemID, int topLevelMenuIndex)=0
This is called when a menu item has been clicked on.
Contains position and status information about a mouse event.
Point< int > getPosition() const noexcept
The position of the mouse when the event occurred.
MouseEvent getEventRelativeTo(Component *newComponent) const noexcept
Creates a version of this event that is relative to a different component.
Component *const eventComponent
The component that this event applies to.
A pair of (x, y) coordinates.
Definition juce_Point.h:42
Class used to create a set of options to pass to the show() method.
Options withTargetScreenArea(Rectangle< int > targetArea) const
Sets the region of the screen next to which the menu should be displayed.
Options withMinimumWidth(int minWidth) const
Sets the minimum width of the popup window.
Options withTargetComponent(Component *targetComponent) const
Sets the target component to use when displaying the menu.
void setLookAndFeel(LookAndFeel *newLookAndFeel)
Specifies a look-and-feel for the menu and any sub-menus that it has.
static bool JUCE_CALLTYPE dismissAllActiveMenus()
Closes any menus that are currently open.
A special array for holding a list of strings.
The JUCE String class!
Definition juce_String.h:53
void stopTimer() noexcept
Stops the timer.
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
T distance(T... args)
T find_if(T... args)
#define jassertfalse
This will always cause an assertion failure.
typedef int
JUCE Namespace.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
@ showMenu
Represents the user showing a contextual menu for a UI element.
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
@ dontTriggerVisualFeedback
If this flag is present and the command is invoked from a keypress, then any buttons or menus that ar...
Contains contextual details about the invocation of a command.
CommandID commandID
The UID of the command that should be performed.
typedef size_t