Seems like nothing’s been done about this, I had the same problem.
I thought I would share a little hack I developed for this issue. Not sure I’m going to use it, but it seems to work.
This does involve modifying the JUCE code. What it does is, when the parentComponent is specified, is put the menuWindow into a wrapper component that is then expanded enough to draw a dropShadow. The following image (Mac) shows the normal appearance (no drop shadow), the result of the hack, and the expanded wrapper layer that is used to hold the PopupMenu shaded in green:
So, to do this, you modify juce_PopupMenu.cpp in 3 places:
At the end of struct MenuWindow : public Component, add this private struct PopupWrapper:
// start hack ===========================
// wrapper for PopupMenu
struct PopupWrapper : public Component
{
PopupWrapper (MenuWindow& inWind)
: menuWind (inWind)
{
setInterceptsMouseClicks(false, true);
addAndMakeVisible(menuWind);
}
void paint(Graphics& g) override
{
// put in to see the wrapper component bounds:
// g.fillAll(Colours::green.withAlpha(0.3f));
DropShadow shadow;
shadow.radius = shadowRadius;
shadow.colour = Colours::black.withAlpha(0.5f);
auto area = getLocalBounds();
area = area.reduced (2 * shadow.radius);
shadow.drawForRectangle (g, area);
}
void resized () override
{
menuWind.setTopLeftPosition(shadowRadius * 2, shadowRadius * 2);
}
MenuWindow& menuWind;
};
static const int shadowRadius = 10;
std::unique_ptr<PopupWrapper> pw;
// end hack ================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuWindow)
};
And then, in the MenuWindow constructor, two mods near the beginning and end, to add the PopupWrapper containing the MenuWindow to the parent component, and set the bounds to an expanded version of it:
struct MenuWindow : public Component
{
MenuWindow (const PopupMenu& menu, MenuWindow* parentWindow,
Options opts, bool alignToRectangle, bool shouldDismissOnMouseUp,
ApplicationCommandManager** manager, float parentScaleFactor = 1.0f)
: Component ("menu"),
parent (parentWindow),
options (std::move (opts)),
managerOfChosenCommand (manager),
componentAttachedTo (options.getTargetComponent()),
dismissOnMouseUp (shouldDismissOnMouseUp),
windowCreationTime (Time::getMillisecondCounter()),
lastFocusedTime (windowCreationTime),
timeEnteredCurrentChildComp (windowCreationTime),
scaleFactor (parentWindow != nullptr ? parentScaleFactor : 1.0f)
{
setWantsKeyboardFocus (false);
setMouseClickGrabsKeyboardFocus (false);
setAlwaysOnTop (true);
setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel())
: menu.lookAndFeel.get());
auto& lf = getLookAndFeel();
parentComponent = lf.getParentComponentForMenuOptions (options);
const_cast<Options&>(options) = options.withParentComponent (parentComponent);
if (parentComponent != nullptr)
{
// start hack =========================
// instead of adding the MenuWindow, add a wrapper containing the MenuWindow
pw.reset(new PopupWrapper(*this));
parentComponent->addAndMakeVisible (pw.get());
//parentComponent->addChildComponent (this);
// end hack ===========================
}
else
{
addToDesktop (ComponentPeer::windowIsTemporary
| ComponentPeer::windowIgnoresKeyPresses
| lf.getMenuWindowFlags());
Desktop::getInstance().addGlobalMouseListener (this);
}
if (parentComponent == nullptr && parentWindow == nullptr && lf.shouldPopupMenuScaleWithTargetComponent (options))
if (auto* targetComponent = options.getTargetComponent())
scaleFactor = Component::getApproximateScaleFactorForComponent (targetComponent);
setOpaque (lf.findColour (PopupMenu::backgroundColourId).isOpaque()
|| ! Desktop::canUseSemiTransparentWindows());
const auto initialSelectedId = options.getInitiallySelectedItemId();
for (int i = 0; i < menu.items.size(); ++i)
{
auto& item = menu.items.getReference (i);
if (i + 1 < menu.items.size() || ! item.isSeparator)
{
auto* child = items.add (new ItemComponent (item, options, *this));
if (initialSelectedId != 0 && item.itemID == initialSelectedId)
setCurrentlyHighlightedChild (child);
}
}
auto targetArea = options.getTargetScreenArea() / scaleFactor;
calculateWindowPos (targetArea, alignToRectangle);
setTopLeftPosition (windowPos.getPosition());
updateYPositions();
if (auto visibleID = options.getItemThatMustBeVisible())
{
auto targetPosition = parentComponent != nullptr ? parentComponent->getLocalPoint (nullptr, targetArea.getTopLeft())
: targetArea.getTopLeft();
auto y = targetPosition.getY() - windowPos.getY();
ensureItemIsVisible (visibleID, isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1);
}
resizeToBestWindowPos();
// start hack ========================
// set the bounds of the wrapper to be expanded for the dropShadow
if (parentComponent != nullptr)
pw->setBounds(windowPos.expanded(shadowRadius * 2, shadowRadius * 2));
// end hack ==========================
getActiveWindows().add (this);
lf.preparePopupMenuWindow (*this);
getMouseState (Desktop::getInstance().getMainMouseSource()); // forces creation of a mouse source watcher for the main mouse
}