Browse Source

Add support for creating popup menus inside parent components

tags/2021-05-28
hogliux 9 years ago
parent
commit
ac9973f185
4 changed files with 107 additions and 40 deletions
  1. +5
    -0
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp
  2. +2
    -0
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h
  3. +85
    -40
      modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  4. +15
    -0
      modules/juce_gui_basics/menus/juce_PopupMenu.h

+ 5
- 0
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp View File

@@ -1075,6 +1075,11 @@ void LookAndFeel_V2::drawMenuBarItem (Graphics& g, int width, int height,
g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1); g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1);
} }
Component* LookAndFeel_V2::getParentComponentForMenuOptions (const PopupMenu::Options& options)
{
return options.getParentComponent();
}
//============================================================================== //==============================================================================
void LookAndFeel_V2::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, TextEditor& textEditor) void LookAndFeel_V2::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, TextEditor& textEditor)
{ {


+ 2
- 0
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h View File

@@ -173,6 +173,8 @@ public:
bool isMouseOverItem, bool isMenuOpen, bool isMouseOverBar, bool isMouseOverItem, bool isMenuOpen, bool isMouseOverBar,
MenuBarComponent&) override; MenuBarComponent&) override;
Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) override;
//============================================================================== //==============================================================================
void drawComboBox (Graphics&, int width, int height, bool isButtonDown, void drawComboBox (Graphics&, int width, int height, bool isButtonDown,
int buttonX, int buttonY, int buttonW, int buttonH, int buttonX, int buttonY, int buttonW, int buttonH,


+ 85
- 40
modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -198,6 +198,7 @@ public:
options (opts), options (opts),
managerOfChosenCommand (manager), managerOfChosenCommand (manager),
componentAttachedTo (options.targetComponent), componentAttachedTo (options.targetComponent),
parentComponent (nullptr),
hasBeenOver (false), hasBeenOver (false),
needsToScroll (false), needsToScroll (false),
dismissOnMouseUp (shouldDismissOnMouseUp), dismissOnMouseUp (shouldDismissOnMouseUp),
@@ -218,6 +219,8 @@ public:
setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel()) setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel())
: menu.lookAndFeel.get()); : menu.lookAndFeel.get());
parentComponent = getLookAndFeel().getParentComponentForMenuOptions (options);
setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque() setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque()
|| ! Desktop::canUseSemiTransparentWindows()); || ! Desktop::canUseSemiTransparentWindows());
@@ -235,18 +238,30 @@ public:
if (options.visibleItemID != 0) if (options.visibleItemID != 0)
{ {
const int y = options.targetArea.getY() - windowPos.getY();
const Point<int> targetPosition =
(parentComponent != nullptr ? parentComponent->getLocalPoint (nullptr, options.targetArea.getTopLeft())
: options.targetArea.getTopLeft());
const int y = targetPosition.getY() - windowPos.getY();
ensureItemIsVisible (options.visibleItemID, ensureItemIsVisible (options.visibleItemID,
isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1); isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1);
} }
resizeToBestWindowPos(); resizeToBestWindowPos();
addToDesktop (ComponentPeer::windowIsTemporary
| ComponentPeer::windowIgnoresKeyPresses
| getLookAndFeel().getMenuWindowFlags());
getActiveWindows().add (this);
Desktop::getInstance().addGlobalMouseListener (this);
if (parentComponent != nullptr)
{
parentComponent->addChildComponent (this);
}
else
{
addToDesktop (ComponentPeer::windowIsTemporary
| ComponentPeer::windowIgnoresKeyPresses
| getLookAndFeel().getMenuWindowFlags());
getActiveWindows().add (this);
Desktop::getInstance().addGlobalMouseListener (this);
}
} }
~MenuWindow() ~MenuWindow()
@@ -268,10 +283,13 @@ public:
void paintOverChildren (Graphics& g) override void paintOverChildren (Graphics& g) override
{ {
LookAndFeel& lf = getLookAndFeel();
if (parentComponent != nullptr)
lf.drawResizableFrame (g, getWidth(), getHeight(), BorderSize<int> (PopupMenuSettings::borderSize));
if (canScroll()) if (canScroll())
{ {
LookAndFeel& lf = getLookAndFeel();
if (isTopScrollZoneActive()) if (isTopScrollZoneActive())
lf.drawPopupMenuUpDownArrow (g, getWidth(), PopupMenuSettings::scrollZone, true); lf.drawPopupMenuUpDownArrow (g, getWidth(), PopupMenuSettings::scrollZone, true);
@@ -551,27 +569,43 @@ public:
} }
//============================================================================== //==============================================================================
void calculateWindowPos (const Rectangle<int>& target, const bool alignToRectangle)
Rectangle<int> getParentArea (Point<int> targetPoint)
{ {
const Rectangle<int> mon (Desktop::getInstance().getDisplays()
.getDisplayContaining (target.getCentre())
#if JUCE_MAC
.userArea);
#else
.totalArea); // on windows, don't stop the menu overlapping the taskbar
#endif
Rectangle<int> parentArea (Desktop::getInstance().getDisplays()
.getDisplayContaining (targetPoint)
#if JUCE_MAC
.userArea);
#else
.totalArea); // on windows, don't stop the menu overlapping the taskbar
#endif
const int maxMenuHeight = mon.getHeight() - 24;
if (parentComponent == nullptr)
return parentArea;
return parentComponent->getLocalArea (nullptr,
parentComponent->getScreenBounds()
.reduced (PopupMenuSettings::borderSize)
.getIntersection (parentArea));
}
void calculateWindowPos (Rectangle<int> target, const bool alignToRectangle)
{
const Rectangle<int> parentArea = getParentArea (target.getCentre());
if (parentComponent != nullptr)
target = parentComponent->getLocalArea (nullptr, target).getIntersection (parentArea);
const int maxMenuHeight = parentArea.getHeight() - 24;
int x, y, widthToUse, heightToUse; int x, y, widthToUse, heightToUse;
layoutMenuItems (mon.getWidth() - 24, maxMenuHeight, widthToUse, heightToUse);
layoutMenuItems (parentArea.getWidth() - 24, maxMenuHeight, widthToUse, heightToUse);
if (alignToRectangle) if (alignToRectangle)
{ {
x = target.getX(); x = target.getX();
const int spaceUnder = mon.getHeight() - (target.getBottom() - mon.getY());
const int spaceOver = target.getY() - mon.getY();
const int spaceUnder = parentArea.getHeight() - (target.getBottom() - parentArea.getY());
const int spaceOver = target.getY() - parentArea.getY();
if (heightToUse < spaceUnder - 30 || spaceUnder >= spaceOver) if (heightToUse < spaceUnder - 30 || spaceUnder >= spaceOver)
y = target.getBottom(); y = target.getBottom();
@@ -580,7 +614,7 @@ public:
} }
else else
{ {
bool tendTowardsRight = target.getCentreX() < mon.getCentreX();
bool tendTowardsRight = target.getCentreX() < parentArea.getCentreX();
if (parent != nullptr) if (parent != nullptr)
{ {
@@ -589,19 +623,19 @@ public:
const bool parentGoingRight = (parent->getX() + parent->getWidth() / 2 const bool parentGoingRight = (parent->getX() + parent->getWidth() / 2
> parent->parent->getX() + parent->parent->getWidth() / 2); > parent->parent->getX() + parent->parent->getWidth() / 2);
if (parentGoingRight && target.getRight() + widthToUse < mon.getRight() - 4)
if (parentGoingRight && target.getRight() + widthToUse < parentArea.getRight() - 4)
tendTowardsRight = true; tendTowardsRight = true;
else if ((! parentGoingRight) && target.getX() > widthToUse + 4) else if ((! parentGoingRight) && target.getX() > widthToUse + 4)
tendTowardsRight = false; tendTowardsRight = false;
} }
else if (target.getRight() + widthToUse < mon.getRight() - 32)
else if (target.getRight() + widthToUse < parentArea.getRight() - 32)
{ {
tendTowardsRight = true; tendTowardsRight = true;
} }
} }
const int biggestSpace = jmax (mon.getRight() - target.getRight(),
target.getX() - mon.getX()) - 32;
const int biggestSpace = jmax (parentArea.getRight() - target.getRight(),
target.getX() - parentArea.getX()) - 32;
if (biggestSpace < widthToUse) if (biggestSpace < widthToUse)
{ {
@@ -610,21 +644,21 @@ public:
if (numColumns > 1) if (numColumns > 1)
layoutMenuItems (biggestSpace - 4, maxMenuHeight, widthToUse, heightToUse); layoutMenuItems (biggestSpace - 4, maxMenuHeight, widthToUse, heightToUse);
tendTowardsRight = (mon.getRight() - target.getRight()) >= (target.getX() - mon.getX());
tendTowardsRight = (parentArea.getRight() - target.getRight()) >= (target.getX() - parentArea.getX());
} }
if (tendTowardsRight) if (tendTowardsRight)
x = jmin (mon.getRight() - widthToUse - 4, target.getRight());
x = jmin (parentArea.getRight() - widthToUse - 4, target.getRight());
else else
x = jmax (mon.getX() + 4, target.getX() - widthToUse);
x = jmax (parentArea.getX() + 4, target.getX() - widthToUse);
y = target.getY(); y = target.getY();
if (target.getCentreY() > mon.getCentreY())
y = jmax (mon.getY(), target.getBottom() - heightToUse);
if (target.getCentreY() > parentArea.getCentreY())
y = jmax (parentArea.getY(), target.getBottom() - heightToUse);
} }
x = jmax (mon.getX() + 1, jmin (mon.getRight() - (widthToUse + 6), x));
y = jmax (mon.getY() + 1, jmin (mon.getBottom() - (heightToUse + 6), y));
x = jmax (parentArea.getX() + 1, jmin (parentArea.getRight() - (widthToUse + 6), x));
y = jmax (parentArea.getY() + 1, jmin (parentArea.getBottom() - (heightToUse + 6), y));
windowPos.setBounds (x, y, widthToUse, heightToUse); windowPos.setBounds (x, y, widthToUse, heightToUse);
@@ -695,9 +729,12 @@ public:
childNum += numChildren; childNum += numChildren;
} }
if (totalW < options.minWidth)
// width must never be larger than the screen
const int minWidth = jmin (maxMenuW, options.minWidth);
if (totalW < minWidth)
{ {
totalW = options.minWidth;
totalW = minWidth;
for (int col = 0; col < numColumns; ++col) for (int col = 0; col < numColumns; ++col)
columnWidths.set (0, totalW / numColumns); columnWidths.set (0, totalW / numColumns);
@@ -727,16 +764,15 @@ public:
windowPos.getHeight() - (PopupMenuSettings::scrollZone + m->getHeight())), windowPos.getHeight() - (PopupMenuSettings::scrollZone + m->getHeight())),
currentY); currentY);
const Rectangle<int> mon (Desktop::getInstance().getDisplays()
.getDisplayContaining (windowPos.getPosition()).userArea);
const Rectangle<int> parantArea = getParentArea (windowPos.getPosition());
int deltaY = wantedY - currentY; int deltaY = wantedY - currentY;
windowPos.setSize (jmin (windowPos.getWidth(), mon.getWidth()),
jmin (windowPos.getHeight(), mon.getHeight()));
windowPos.setSize (jmin (windowPos.getWidth(), parantArea.getWidth()),
jmin (windowPos.getHeight(), parantArea.getHeight()));
const int newY = jlimit (mon.getY(),
mon.getBottom() - windowPos.getHeight(),
const int newY = jlimit (parantArea.getY(),
parantArea.getBottom() - windowPos.getHeight(),
windowPos.getY() + deltaY); windowPos.getY() + deltaY);
deltaY -= newY - windowPos.getY(); deltaY -= newY - windowPos.getY();
@@ -912,6 +948,7 @@ public:
OwnedArray<ItemComponent> items; OwnedArray<ItemComponent> items;
ApplicationCommandManager** managerOfChosenCommand; ApplicationCommandManager** managerOfChosenCommand;
WeakReference<Component> componentAttachedTo; WeakReference<Component> componentAttachedTo;
Component* parentComponent;
Rectangle<int> windowPos; Rectangle<int> windowPos;
bool hasBeenOver, needsToScroll; bool hasBeenOver, needsToScroll;
bool dismissOnMouseUp, hideOnExit, disableMouseMoves, hasAnyJuceCompHadFocus; bool dismissOnMouseUp, hideOnExit, disableMouseMoves, hasAnyJuceCompHadFocus;
@@ -1428,6 +1465,7 @@ void PopupMenu::addSectionHeader (const String& title)
//============================================================================== //==============================================================================
PopupMenu::Options::Options() PopupMenu::Options::Options()
: targetComponent (nullptr), : targetComponent (nullptr),
parentComponent (nullptr),
visibleItemID (0), visibleItemID (0),
minWidth (0), minWidth (0),
maxColumns (0), maxColumns (0),
@@ -1482,6 +1520,13 @@ PopupMenu::Options PopupMenu::Options::withItemThatMustBeVisible (int idOfItemTo
return o; return o;
} }
PopupMenu::Options PopupMenu::Options::withParentComponent (Component* parent) const noexcept
{
Options o (*this);
o.parentComponent = parent;
return o;
}
Component* PopupMenu::createWindow (const Options& options, Component* PopupMenu::createWindow (const Options& options,
ApplicationCommandManager** managerOfChosenCommand) const ApplicationCommandManager** managerOfChosenCommand) const
{ {


+ 15
- 0
modules/juce_gui_basics/menus/juce_PopupMenu.h View File

@@ -380,18 +380,31 @@ public:
public: public:
Options(); Options();
//==============================================================================
Options withTargetComponent (Component* targetComponent) const noexcept; Options withTargetComponent (Component* targetComponent) const noexcept;
Options withTargetScreenArea (const Rectangle<int>& targetArea) const noexcept; Options withTargetScreenArea (const Rectangle<int>& targetArea) const noexcept;
Options withMinimumWidth (int minWidth) const noexcept; Options withMinimumWidth (int minWidth) const noexcept;
Options withMaximumNumColumns (int maxNumColumns) const noexcept; Options withMaximumNumColumns (int maxNumColumns) const noexcept;
Options withStandardItemHeight (int standardHeight) const noexcept; Options withStandardItemHeight (int standardHeight) const noexcept;
Options withItemThatMustBeVisible (int idOfItemToBeVisible) const noexcept; Options withItemThatMustBeVisible (int idOfItemToBeVisible) const noexcept;
Options withParentComponent (Component* parentComponent) const noexcept;
//==============================================================================
Component* getParentComponent() const noexcept { return parentComponent; }
Component* getTargetComponent() const noexcept { return targetComponent; }
Rectangle<int> getTargetScreenArea() const noexcept { return targetArea; }
int getMinimumWidth() const noexcept { return minWidth; }
int getMaximumNumColumns() const noexcept { return maxColumns; }
int getStandardItemHeight() const noexcept { return standardHeight; }
int getItemThatMustBeVisible() const noexcept { return visibleItemID; }
private: private:
//==============================================================================
friend class PopupMenu; friend class PopupMenu;
friend class PopupMenu::Window; friend class PopupMenu::Window;
Rectangle<int> targetArea; Rectangle<int> targetArea;
Component* targetComponent; Component* targetComponent;
Component* parentComponent;
int visibleItemID, minWidth, maxColumns, standardHeight; int visibleItemID, minWidth, maxColumns, standardHeight;
}; };
@@ -668,6 +681,8 @@ public:
bool isMenuOpen, bool isMenuOpen,
bool isMouseOverBar, bool isMouseOverBar,
MenuBarComponent&) = 0; MenuBarComponent&) = 0;
virtual Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) = 0;
}; };
private: private:


Loading…
Cancel
Save