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);
}
Component* LookAndFeel_V2::getParentComponentForMenuOptions (const PopupMenu::Options& options)
{
return options.getParentComponent();
}
//==============================================================================
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,
MenuBarComponent&) override;
Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) override;
//==============================================================================
void drawComboBox (Graphics&, int width, int height, bool isButtonDown,
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),
managerOfChosenCommand (manager),
componentAttachedTo (options.targetComponent),
parentComponent (nullptr),
hasBeenOver (false),
needsToScroll (false),
dismissOnMouseUp (shouldDismissOnMouseUp),
@@ -218,6 +219,8 @@ public:
setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel())
: menu.lookAndFeel.get());
parentComponent = getLookAndFeel().getParentComponentForMenuOptions (options);
setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque()
|| ! Desktop::canUseSemiTransparentWindows());
@@ -235,18 +238,30 @@ public:
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,
isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1);
}
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()
@@ -268,10 +283,13 @@ public:
void paintOverChildren (Graphics& g) override
{
LookAndFeel& lf = getLookAndFeel();
if (parentComponent != nullptr)
lf.drawResizableFrame (g, getWidth(), getHeight(), BorderSize<int> (PopupMenuSettings::borderSize));
if (canScroll())
{
LookAndFeel& lf = getLookAndFeel();
if (isTopScrollZoneActive())
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;
layoutMenuItems (mon.getWidth() - 24, maxMenuHeight, widthToUse, heightToUse);
layoutMenuItems (parentArea.getWidth() - 24, maxMenuHeight, widthToUse, heightToUse);
if (alignToRectangle)
{
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)
y = target.getBottom();
@@ -580,7 +614,7 @@ public:
}
else
{
bool tendTowardsRight = target.getCentreX() < mon.getCentreX();
bool tendTowardsRight = target.getCentreX() < parentArea.getCentreX();
if (parent != nullptr)
{
@@ -589,19 +623,19 @@ public:
const bool parentGoingRight = (parent->getX() + 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;
else if ((! parentGoingRight) && target.getX() > widthToUse + 4)
tendTowardsRight = false;
}
else if (target.getRight() + widthToUse < mon.getRight() - 32)
else if (target.getRight() + widthToUse < parentArea.getRight() - 32)
{
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)
{
@@ -610,21 +644,21 @@ public:
if (numColumns > 1)
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)
x = jmin (mon.getRight() - widthToUse - 4, target.getRight());
x = jmin (parentArea.getRight() - widthToUse - 4, target.getRight());
else
x = jmax (mon.getX() + 4, target.getX() - widthToUse);
x = jmax (parentArea.getX() + 4, target.getX() - widthToUse);
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);
@@ -695,9 +729,12 @@ public:
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)
columnWidths.set (0, totalW / numColumns);
@@ -727,16 +764,15 @@ public:
windowPos.getHeight() - (PopupMenuSettings::scrollZone + m->getHeight())),
currentY);
const Rectangle<int> mon (Desktop::getInstance().getDisplays()
.getDisplayContaining (windowPos.getPosition()).userArea);
const Rectangle<int> parantArea = getParentArea (windowPos.getPosition());
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);
deltaY -= newY - windowPos.getY();
@@ -912,6 +948,7 @@ public:
OwnedArray<ItemComponent> items;
ApplicationCommandManager** managerOfChosenCommand;
WeakReference<Component> componentAttachedTo;
Component* parentComponent;
Rectangle<int> windowPos;
bool hasBeenOver, needsToScroll;
bool dismissOnMouseUp, hideOnExit, disableMouseMoves, hasAnyJuceCompHadFocus;
@@ -1428,6 +1465,7 @@ void PopupMenu::addSectionHeader (const String& title)
//==============================================================================
PopupMenu::Options::Options()
: targetComponent (nullptr),
parentComponent (nullptr),
visibleItemID (0),
minWidth (0),
maxColumns (0),
@@ -1482,6 +1520,13 @@ PopupMenu::Options PopupMenu::Options::withItemThatMustBeVisible (int idOfItemTo
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,
ApplicationCommandManager** managerOfChosenCommand) const
{


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

@@ -380,18 +380,31 @@ public:
public:
Options();
//==============================================================================
Options withTargetComponent (Component* targetComponent) const noexcept;
Options withTargetScreenArea (const Rectangle<int>& targetArea) const noexcept;
Options withMinimumWidth (int minWidth) const noexcept;
Options withMaximumNumColumns (int maxNumColumns) const noexcept;
Options withStandardItemHeight (int standardHeight) 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:
//==============================================================================
friend class PopupMenu;
friend class PopupMenu::Window;
Rectangle<int> targetArea;
Component* targetComponent;
Component* parentComponent;
int visibleItemID, minWidth, maxColumns, standardHeight;
};
@@ -668,6 +681,8 @@ public:
bool isMenuOpen,
bool isMouseOverBar,
MenuBarComponent&) = 0;
virtual Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) = 0;
};
private:


Loading…
Cancel
Save