From 903c77b977eca8d5ffe54a59ccbe6405fdccbc11 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 27 Nov 2023 16:41:37 +0000 Subject: [PATCH] PopupMenu: Add accessor for top-level target component to Options class This allows the LookAndFeel of submenus to query the target component used for the top-level menu. getTargetComponent() isn't suitable for this because the target component is set to null for submenus, and this behaviour can't be changed without potentially breaking code that relies on the current behaviour. --- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 32 ++++++++++--------- .../juce_gui_basics/menus/juce_PopupMenu.h | 17 +++++++++- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 3c47a09482..d15f93a7c1 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -1195,22 +1195,19 @@ struct MenuWindow final : public Component { activeSubMenu.reset(); - if (childComp != nullptr - && hasActiveSubMenu (childComp->item)) - { - activeSubMenu.reset (new HelperClasses::MenuWindow (*(childComp->item.subMenu), this, - options.withTargetScreenArea (childComp->getScreenBounds()) - .withMinimumWidth (0) - .withTargetComponent (nullptr), - false, dismissOnMouseUp, managerOfChosenCommand, scaleFactor)); + if (childComp == nullptr || ! hasActiveSubMenu (childComp->item)) + return false; - activeSubMenu->setVisible (true); // (must be called before enterModalState on Windows to avoid DropShadower confusion) - activeSubMenu->enterModalState (false); - activeSubMenu->toFront (false); - return true; - } + activeSubMenu.reset (new HelperClasses::MenuWindow (*(childComp->item.subMenu), this, + options.forSubmenu() + .withTargetScreenArea (childComp->getScreenBounds()) + .withMinimumWidth (0), + false, dismissOnMouseUp, managerOfChosenCommand, scaleFactor)); - return false; + activeSubMenu->setVisible (true); // (must be called before enterModalState on Windows to avoid DropShadower confusion) + activeSubMenu->enterModalState (false); + activeSubMenu->toFront (false); + return true; } void triggerCurrentlyHighlightedItem() @@ -1975,7 +1972,7 @@ static PopupMenu::Options with (PopupMenu::Options options, Member&& member, Ite PopupMenu::Options PopupMenu::Options::withTargetComponent (Component* comp) const { - auto o = with (*this, &Options::targetComponent, comp); + auto o = with (with (*this, &Options::targetComponent, comp), &Options::topLevelTarget, comp); if (comp != nullptr) o.targetArea = comp->getScreenBounds(); @@ -2045,6 +2042,11 @@ PopupMenu::Options PopupMenu::Options::withInitiallySelectedItem (int idOfItemTo return with (*this, &Options::initiallySelectedItemId, idOfItemToBeSelected); } +PopupMenu::Options PopupMenu::Options::forSubmenu() const +{ + return with (*this, &Options::targetComponent, nullptr); +} + Component* PopupMenu::createWindow (const Options& options, ApplicationCommandManager** managerOfChosenCommand) const { diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.h b/modules/juce_gui_basics/menus/juce_PopupMenu.h index 6ca8c721d7..697ff1784b 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -562,6 +562,13 @@ public: */ [[nodiscard]] Options withInitiallySelectedItem (int idOfItemToBeSelected) const; + /** Returns a copy of these options with the target component set to null. The value of the + top-level target component will not be changed. + + @see getTargetComponent(), getTopLevelTargetComponent() + */ + [[nodiscard]] Options forSubmenu() const; + //============================================================================== /** Gets the parent component. This may be nullptr if the Component has been deleted. @@ -575,6 +582,14 @@ public: */ Component* getTargetComponent() const noexcept { return targetComponent; } + /** Gets the target component that was set for the top-level menu. + + When querying the options of a submenu, getTargetComponent() will always return + nullptr, while getTopLevelTargetComponent() will return the target passed to + withTargetComponent() when creating the top-level menu. + */ + Component* getTopLevelTargetComponent() const noexcept { return topLevelTarget; } + /** Returns true if the menu was watching a component, and that component has been deleted, and false otherwise. @see withDeletionCheck @@ -632,7 +647,7 @@ public: private: //============================================================================== Rectangle targetArea; - WeakReference targetComponent, parentComponent, componentToWatchForDeletion; + WeakReference targetComponent, parentComponent, componentToWatchForDeletion, topLevelTarget; int visibleItemID = 0, minWidth = 0, minColumns = 1, maxColumns = 0, standardHeight = 0, initiallySelectedItemId = 0; bool isWatchingForDeletion = false; PopupDirection preferredPopupDirection = PopupDirection::downwards;