From f866b4ff5e6269eb39fe59d022730f26b5ed57d8 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 19 Jun 2017 16:27:47 +0100 Subject: [PATCH] Fixed a problem where popup menus triggered from the taskbar could get stuck, and tidied up some internal PopupMenu code --- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 196 ++++++++---------- .../native/juce_mac_SystemTrayIcon.cpp | 17 +- 2 files changed, 92 insertions(+), 121 deletions(-) diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index ff847a6e0a..4e3082ea32 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -159,12 +159,11 @@ private: && item.shortcutKeyDescription.isEmpty()) { String shortcutKey; - const Array keyPresses (item.commandManager->getKeyMappings() - ->getKeyPressesAssignedToCommand (item.itemID)); - for (int i = 0; i < keyPresses.size(); ++i) + for (auto& keypress : item.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (item.itemID)) { - const String key (keyPresses.getReference (i).getTextDescriptionWithIcons()); + auto key = keypress.getTextDescriptionWithIcons(); if (shortcutKey.isNotEmpty()) shortcutKey << ", "; @@ -192,26 +191,15 @@ private: class MenuWindow : public Component { public: - MenuWindow (const PopupMenu& menu, MenuWindow* const parentWindow, - const Options& opts, - const bool alignToRectangle, - const bool shouldDismissOnMouseUp, - ApplicationCommandManager** const manager) + MenuWindow (const PopupMenu& menu, MenuWindow* parentWindow, + const Options& opts, bool alignToRectangle, bool shouldDismissOnMouseUp, + ApplicationCommandManager** manager) : Component ("menu"), parent (parentWindow), options (opts), managerOfChosenCommand (manager), componentAttachedTo (options.targetComponent), - parentComponent (nullptr), - hasBeenOver (false), - needsToScroll (false), dismissOnMouseUp (shouldDismissOnMouseUp), - hideOnExit (false), - disableMouseMoves (false), - hasAnyJuceCompHadFocus (false), - numColumns (0), - contentHeight (0), - childYOffset (0), windowCreationTime (Time::getMillisecondCounter()), lastFocusedTime (windowCreationTime), timeEnteredCurrentChildComp (windowCreationTime) @@ -223,7 +211,7 @@ public: setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel()) : menu.lookAndFeel.get()); - LookAndFeel& lf = getLookAndFeel(); + auto& lf = getLookAndFeel(); parentComponent = lf.getParentComponentForMenuOptions (options); @@ -232,7 +220,7 @@ public: for (int i = 0; i < menu.items.size(); ++i) { - PopupMenu::Item* const item = menu.items.getUnchecked (i); + auto item = menu.items.getUnchecked (i); if (i < menu.items.size() - 1 || ! item->isSeparator) items.add (new ItemComponent (*item, options.standardHeight, *this)); @@ -244,11 +232,10 @@ public: if (options.visibleItemID != 0) { - const Point targetPosition = - (parentComponent != nullptr ? parentComponent->getLocalPoint (nullptr, options.targetArea.getTopLeft()) - : options.targetArea.getTopLeft()); + auto targetPosition = parentComponent != nullptr ? parentComponent->getLocalPoint (nullptr, options.targetArea.getTopLeft()) + : options.targetArea.getTopLeft(); - const int y = targetPosition.getY() - windowPos.getY(); + auto y = targetPosition.getY() - windowPos.getY(); ensureItemIsVisible (options.visibleItemID, isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1); } @@ -270,6 +257,8 @@ public: } lf.preparePopupMenuWindow (*this); + + getMouseState (Desktop::getInstance().getMainMouseSource()); // forces creation of a mouse source watcher for the main mouse } ~MenuWindow() @@ -291,7 +280,7 @@ public: void paintOverChildren (Graphics& g) override { - LookAndFeel& lf = getLookAndFeel(); + auto& lf = getLookAndFeel(); if (parentComponent != nullptr) lf.drawResizableFrame (g, getWidth(), getHeight(), BorderSize (PopupMenuSettings::borderSize)); @@ -311,7 +300,7 @@ public: //============================================================================== // hide this and all sub-comps - void hide (const PopupMenu::Item* const item, const bool makeInvisible) + void hide (const PopupMenu::Item* item, bool makeInvisible) { if (isVisible()) { @@ -339,7 +328,7 @@ public: if (item == nullptr) return 0; - if (CustomCallback* cc = item->customCallback) + if (auto* cc = item->customCallback.get()) if (! cc->menuItemTriggered()) return 0; @@ -357,7 +346,7 @@ public: if (item != nullptr) { // need a copy of this on the stack as the one passed in will get deleted during this call - const PopupMenu::Item mi (*item); + auto mi (*item); hide (&mi, false); } else @@ -431,9 +420,9 @@ public: { WeakReference deletionChecker (this); - for (int i = mouseSourceStates.size(); --i >= 0;) + for (auto* ms : mouseSourceStates) { - mouseSourceStates.getUnchecked (i)->timerCallback(); + ms->timerCallback(); if (deletionChecker == nullptr) return; @@ -449,7 +438,7 @@ public: // as they'll expect the menu to go away, and in fact it'll just // come back. So only dismiss synchronously if they're not on the original // comp that we're attached to. - const Point mousePos (componentAttachedTo->getMouseXYRelative()); + auto mousePos = componentAttachedTo->getMouseXYRelative(); if (componentAttachedTo->reallyContains (mousePos, true)) { @@ -497,7 +486,7 @@ public: return false; } - if (MenuWindow* currentlyModalWindow = dynamic_cast (Component::getCurrentlyModalComponent())) + if (auto* currentlyModalWindow = dynamic_cast (Component::getCurrentlyModalComponent())) if (! treeContains (currentlyModalWindow)) return false; @@ -512,14 +501,11 @@ public: MouseSourceState& getMouseState (MouseInputSource source) { - for (int i = mouseSourceStates.size(); --i >= 0;) - { - MouseSourceState& ms = *mouseSourceStates.getUnchecked (i); - if (ms.source == source) - return ms; - } + for (auto* ms : mouseSourceStates) + if (ms->source == source) + return *ms; - MouseSourceState* ms = new MouseSourceState (*this, source); + auto ms = new MouseSourceState (*this, source); mouseSourceStates.add (ms); return *ms; } @@ -539,8 +525,8 @@ public: bool isAnyMouseOver() const { - for (int i = 0; i < mouseSourceStates.size(); ++i) - if (mouseSourceStates.getUnchecked (i)->isOver()) + for (auto* ms : mouseSourceStates) + if (ms->isOver()) return true; return false; @@ -591,13 +577,12 @@ public: //============================================================================== Rectangle getParentArea (Point targetPoint) { - Rectangle parentArea (Desktop::getInstance().getDisplays() - .getDisplayContaining (targetPoint) - #if JUCE_MAC - .userArea); - #else - .totalArea); // on windows, don't stop the menu overlapping the taskbar - #endif + auto parentArea = Desktop::getInstance().getDisplays().getDisplayContaining (targetPoint) + #if JUCE_MAC + .userArea; + #else + .totalArea; // on windows, don't stop the menu overlapping the taskbar + #endif if (parentComponent == nullptr) return parentArea; @@ -610,7 +595,7 @@ public: void calculateWindowPos (Rectangle target, const bool alignToRectangle) { - const Rectangle parentArea = getParentArea (target.getCentre()); + auto parentArea = getParentArea (target.getCentre()); if (parentComponent != nullptr) target = parentComponent->getLocalArea (nullptr, target).getIntersection (parentArea); @@ -706,10 +691,9 @@ public: workOutBestSize (maxMenuW); // to update col widths break; } - else if (totalW > maxMenuW / 2 || contentHeight < maxMenuH) - { + + if (totalW > maxMenuW / 2 || contentHeight < maxMenuH) break; - } } while (numColumns < maximumNumColumns); @@ -769,12 +753,12 @@ public: for (int i = items.size(); --i >= 0;) { - if (ItemComponent* const m = items.getUnchecked (i)) + if (auto* m = items.getUnchecked (i)) { if (m->item.itemID == itemID && windowPos.getHeight() > PopupMenuSettings::scrollZone * 4) { - const int currentY = m->getY(); + auto currentY = m->getY(); if (wantedY > 0 || currentY < 0 || m->getBottom() > windowPos.getHeight()) { @@ -784,16 +768,16 @@ public: windowPos.getHeight() - (PopupMenuSettings::scrollZone + m->getHeight())), currentY); - const Rectangle parantArea = getParentArea (windowPos.getPosition()); + auto parentArea = getParentArea (windowPos.getPosition()); int deltaY = wantedY - currentY; - windowPos.setSize (jmin (windowPos.getWidth(), parantArea.getWidth()), - jmin (windowPos.getHeight(), parantArea.getHeight())); + windowPos.setSize (jmin (windowPos.getWidth(), parentArea.getWidth()), + jmin (windowPos.getHeight(), parentArea.getHeight())); - const int newY = jlimit (parantArea.getY(), - parantArea.getBottom() - windowPos.getHeight(), - windowPos.getY() + deltaY); + auto newY = jlimit (parentArea.getY(), + parentArea.getBottom() - windowPos.getHeight(), + windowPos.getY() + deltaY); deltaY -= newY - windowPos.getY(); @@ -811,7 +795,7 @@ public: void resizeToBestWindowPos() { - Rectangle r (windowPos); + auto r = windowPos; if (childYOffset < 0) { @@ -868,7 +852,7 @@ public: for (int i = 0; i < numChildren; ++i) { - Component* const c = items.getUnchecked (childNum + i); + auto* c = items.getUnchecked (childNum + i); c->setBounds (x, y, colW, c->getHeight()); y += c->getHeight(); } @@ -939,7 +923,7 @@ public: { start += delta; - if (ItemComponent* mic = items.getUnchecked ((start + items.size()) % items.size())) + if (auto* mic = items.getUnchecked ((start + items.size()) % items.size())) { if (canBeTriggered (mic->item) || hasActiveSubMenu (mic->item)) { @@ -968,11 +952,11 @@ public: OwnedArray items; ApplicationCommandManager** managerOfChosenCommand; WeakReference componentAttachedTo; - Component* parentComponent; + Component* parentComponent = nullptr; Rectangle windowPos; - bool hasBeenOver, needsToScroll; - bool dismissOnMouseUp, hideOnExit, disableMouseMoves, hasAnyJuceCompHadFocus; - int numColumns, contentHeight, childYOffset; + bool hasBeenOver = false, needsToScroll = false; + bool dismissOnMouseUp, hideOnExit = false, disableMouseMoves = false, hasAnyJuceCompHadFocus = false; + int numColumns = 0, contentHeight = 0, childYOffset = 0; Component::SafePointer currentChild; ScopedPointer activeSubMenu; Array columnWidths; @@ -987,10 +971,9 @@ class MouseSourceState : private Timer { public: MouseSourceState (MenuWindow& w, MouseInputSource s) - : window (w), source (s), scrollAcceleration (1.0), - lastScrollTime (Time::getMillisecondCounter()), - lastMouseMoveTime (0), isDown (false) + : window (w), source (s), lastScrollTime (Time::getMillisecondCounter()) { + startTimerHz (20); } void handleMouseEvent (const MouseEvent& e) @@ -1018,15 +1001,14 @@ public: private: Point lastMousePos; - double scrollAcceleration; - uint32 lastScrollTime, lastMouseMoveTime; - bool isDown; + double scrollAcceleration = 0; + uint32 lastScrollTime, lastMouseMoveTime = 0; + bool isDown = false; void handleMousePosition (Point globalMousePos) { - const Point localMousePos (window.getLocalPoint (nullptr, globalMousePos)); - - const uint32 timeNow = Time::getMillisecondCounter(); + auto localMousePos = window.getLocalPoint (nullptr, globalMousePos); + auto timeNow = Time::getMillisecondCounter(); if (timeNow > window.timeEnteredCurrentChildComp + 100 && window.reallyContains (localMousePos, true) @@ -1106,11 +1088,11 @@ private: if (! isMovingTowardsMenu) { - Component* c = window.getComponentAt (localMousePos); + auto* c = window.getComponentAt (localMousePos); if (c == &window) c = nullptr; - ItemComponent* itemUnderMouse = dynamic_cast (c); + auto* itemUnderMouse = dynamic_cast (c); if (itemUnderMouse == nullptr && c != nullptr) itemUnderMouse = c->findParentComponentOfClass(); @@ -1139,10 +1121,10 @@ private: // submenu. To do this, look at whether the mouse stays inside a triangular region that // extends from the last mouse pos to the submenu's rectangle.. - const Rectangle itemScreenBounds (window.activeSubMenu->getScreenBounds()); - float subX = (float) itemScreenBounds.getX(); + auto itemScreenBounds = window.activeSubMenu->getScreenBounds(); + auto subX = (float) itemScreenBounds.getX(); - Point oldGlobalPos (lastMousePos); + auto oldGlobalPos = lastMousePos; if (itemScreenBounds.getX() > window.getX()) { @@ -1202,8 +1184,7 @@ private: //============================================================================== struct NormalComponentWrapper : public PopupMenu::CustomComponent { - NormalComponentWrapper (Component* const comp, const int w, const int h, - const bool triggerMenuItemAutomaticallyWhenClicked) + NormalComponentWrapper (Component* comp, int w, int h, bool triggerMenuItemAutomaticallyWhenClicked) : PopupMenu::CustomComponent (triggerMenuItemAutomaticallyWhenClicked), width (w), height (h) { @@ -1218,7 +1199,7 @@ struct NormalComponentWrapper : public PopupMenu::CustomComponent void resized() override { - if (Component* const child = getChildComponent (0)) + if (auto* child = getChildComponent (0)) child->setBounds (getLocalBounds()); } @@ -1349,7 +1330,7 @@ static Drawable* createDrawableFromImage (const Image& im) { if (im.isValid()) { - DrawableImage* d = new DrawableImage(); + auto d = new DrawableImage(); d->setImage (im); return d; } @@ -1380,10 +1361,10 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, { jassert (commandManager != nullptr && commandID != 0); - if (const ApplicationCommandInfo* const registeredInfo = commandManager->getCommandForID (commandID)) + if (auto* registeredInfo = commandManager->getCommandForID (commandID)) { ApplicationCommandInfo info (*registeredInfo); - ApplicationCommandTarget* const target = commandManager->getTargetForCommand (commandID, info); + auto* target = commandManager->getTargetForCommand (commandID, info); Item i; i.text = displayName.isNotEmpty() ? displayName : info.shortName; @@ -1564,8 +1545,7 @@ Component* PopupMenu::createWindow (const Options& options, struct PopupMenuCompletionCallback : public ModalComponentManager::Callback { PopupMenuCompletionCallback() - : managerOfChosenCommand (nullptr), - prevFocused (Component::getCurrentlyFocusedComponent()), + : prevFocused (Component::getCurrentlyFocusedComponent()), prevTopLevel (prevFocused != nullptr ? prevFocused->getTopLevelComponent() : nullptr) { PopupMenuSettings::menuWasHiddenBecauseOfAppChange = false; @@ -1594,7 +1574,7 @@ struct PopupMenuCompletionCallback : public ModalComponentManager::Callback } } - ApplicationCommandManager* managerOfChosenCommand; + ApplicationCommandManager* managerOfChosenCommand = nullptr; ScopedPointer component; WeakReference prevFocused, prevTopLevel; @@ -1607,7 +1587,7 @@ int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentM ScopedPointer userCallbackDeleter (userCallback); ScopedPointer callback (new PopupMenuCompletionCallback()); - if (Component* window = createWindow (options, &(callback->managerOfChosenCommand))) + if (auto* window = createWindow (options, &(callback->managerOfChosenCommand))) { callback->component = window; @@ -1695,11 +1675,11 @@ int PopupMenu::showAt (Component* componentToAttachTo, bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() { - const Array& windows = HelperClasses::MenuWindow::getActiveWindows(); - const int numWindows = windows.size(); + auto& windows = HelperClasses::MenuWindow::getActiveWindows(); + auto numWindows = windows.size(); for (int i = numWindows; --i >= 0;) - if (HelperClasses::MenuWindow* const pmw = windows[i]) + if (auto* pmw = windows[i]) pmw->dismissMenu (nullptr); return numWindows > 0; @@ -1710,8 +1690,8 @@ int PopupMenu::getNumItems() const noexcept { int num = 0; - for (int i = items.size(); --i >= 0;) - if (! items.getUnchecked (i)->isSeparator) + for (auto* mi : items) + if (! mi->isSeparator) ++num; return num; @@ -1719,30 +1699,24 @@ int PopupMenu::getNumItems() const noexcept bool PopupMenu::containsCommandItem (const int commandID) const { - for (int i = items.size(); --i >= 0;) - { - const Item& mi = *items.getUnchecked (i); - - if ((mi.itemID == commandID && mi.commandManager != nullptr) - || (mi.subMenu != nullptr && mi.subMenu->containsCommandItem (commandID))) + for (auto* mi : items) + if ((mi->itemID == commandID && mi->commandManager != nullptr) + || (mi->subMenu != nullptr && mi->subMenu->containsCommandItem (commandID))) return true; - } return false; } bool PopupMenu::containsAnyActiveItems() const noexcept { - for (int i = items.size(); --i >= 0;) + for (auto* mi : items) { - const Item& mi = *items.getUnchecked (i); - - if (mi.subMenu != nullptr) + if (mi->subMenu != nullptr) { - if (mi.subMenu->containsAnyActiveItems()) + if (mi->subMenu->containsAnyActiveItems()) return true; } - else if (mi.isEnabled) + else if (mi->isEnabled) { return true; } @@ -1775,9 +1749,9 @@ void PopupMenu::CustomComponent::setHighlighted (bool shouldBeHighlighted) void PopupMenu::CustomComponent::triggerMenuItem() { - if (HelperClasses::ItemComponent* const mic = findParentComponentOfClass()) + if (auto* mic = findParentComponentOfClass()) { - if (HelperClasses::MenuWindow* const pmw = mic->findParentComponentOfClass()) + if (auto* pmw = mic->findParentComponentOfClass()) { pmw->dismissMenu (&mic->item); } diff --git a/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp index 8c59ff0c8c..49cde5eb2f 100644 --- a/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp @@ -36,9 +36,7 @@ class SystemTrayIconComponent::Pimpl : private Timer { public: Pimpl (SystemTrayIconComponent& iconComp, const Image& im) - : owner (iconComp), statusItem (nil), - statusIcon (MouseCursorHelpers::createNSImage (im)), - view (nil), isHighlighted (false) + : owner (iconComp), statusIcon (MouseCursorHelpers::createNSImage (im)) { static SystemTrayViewClass cls; view = [cls.createInstance() init]; @@ -105,8 +103,7 @@ public: eventMods = eventMods.withFlags (ModifierKeys::commandModifier); auto now = Time::getCurrentTime(); - - MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource(); + auto mouseSource = Desktop::getInstance().getMainMouseSource(); auto pressure = (float) e.pressure; if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up @@ -148,12 +145,12 @@ public: } SystemTrayIconComponent& owner; - NSStatusItem* statusItem; + NSStatusItem* statusItem = nil; private: - NSImage* statusIcon; - NSControl* view; - bool isHighlighted; + NSImage* statusIcon = nil; + NSControl* view = nil; + bool isHighlighted = false; void setIconSize() { @@ -188,7 +185,7 @@ private: static void frameChanged (id self, SEL, NSNotification*) { - if (Pimpl* const owner = getOwner (self)) + if (auto* owner = getOwner (self)) { NSRect r = [[[owner->statusItem view] window] frame]; NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame];