Browse Source

Added clickable sub-menus ability to PopupMenu.

tags/2021-05-28
jules 13 years ago
parent
commit
19600a0556
2 changed files with 90 additions and 92 deletions
  1. +75
    -80
      modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  2. +15
    -12
      modules/juce_gui_basics/menus/juce_PopupMenu.h

+ 75
- 80
modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -26,36 +26,32 @@
class PopupMenu::Item
{
public:
Item()
: itemId (0), active (true), isSeparator (true), isTicked (false),
usesColour (false), commandManager (nullptr)
{
}
Item (const int itemId_,
const String& text_,
const bool active_,
const bool isTicked_,
Item() : itemID (0), isActive (true), isSeparator (true), isTicked (false),
usesColour (false), commandManager (nullptr)
{}
Item (const int itemId,
const String& name,
const bool active,
const bool ticked,
const Image& im,
const Colour& textColour_,
const bool usesColour_,
CustomComponent* const customComp_,
const PopupMenu* const subMenu_,
ApplicationCommandManager* const commandManager_)
: itemId (itemId_), text (text_), textColour (textColour_),
active (active_), isSeparator (false), isTicked (isTicked_),
usesColour (usesColour_), image (im), customComp (customComp_),
commandManager (commandManager_)
const Colour& colour,
const bool useColour,
CustomComponent* const custom,
const PopupMenu* const sub,
ApplicationCommandManager* const manager)
: itemID (itemId), text (name), textColour (colour),
isActive (active), isSeparator (false), isTicked (ticked),
usesColour (useColour), image (im), customComp (custom),
subMenu (createCopyIfNotNull (sub)), commandManager (manager)
{
if (subMenu_ != nullptr)
subMenu = new PopupMenu (*subMenu_);
if (commandManager_ != nullptr && itemId_ != 0)
if (commandManager != nullptr && itemID != 0)
{
String shortcutKey;
Array <KeyPress> keyPresses (commandManager_->getKeyMappings()
->getKeyPressesAssignedToCommand (itemId_));
const Array <KeyPress> keyPresses (commandManager->getKeyMappings()
->getKeyPressesAssignedToCommand (itemID));
for (int i = 0; i < keyPresses.size(); ++i)
{
@@ -78,29 +74,27 @@ public:
}
Item (const Item& other)
: itemId (other.itemId),
: itemID (other.itemID),
text (other.text),
textColour (other.textColour),
active (other.active),
isActive (other.isActive),
isSeparator (other.isSeparator),
isTicked (other.isTicked),
usesColour (other.usesColour),
image (other.image),
customComp (other.customComp),
subMenu (createCopyIfNotNull (other.subMenu.get())),
commandManager (other.commandManager)
{
if (other.subMenu != nullptr)
subMenu = new PopupMenu (*(other.subMenu));
}
{}
bool canBeTriggered() const noexcept { return active && ! (isSeparator || (subMenu != nullptr)); }
bool hasActiveSubMenu() const noexcept { return active && subMenu != nullptr && subMenu->items.size() > 0; }
bool canBeTriggered() const noexcept { return isActive && itemID != 0; }
bool hasActiveSubMenu() const noexcept { return isActive && subMenu != nullptr && subMenu->items.size() > 0; }
//==============================================================================
const int itemId;
const int itemID;
String text;
const Colour textColour;
const bool active, isSeparator, isTicked, usesColour;
const bool isActive, isSeparator, isTicked, usesColour;
Image image;
ReferenceCountedObjectPtr <CustomComponent> customComp;
ScopedPointer <PopupMenu> subMenu;
@@ -117,8 +111,8 @@ private:
class PopupMenu::ItemComponent : public Component
{
public:
ItemComponent (const PopupMenu::Item& itemInfo_, int standardItemHeight, Component* const parent)
: itemInfo (itemInfo_),
ItemComponent (const PopupMenu::Item& info, int standardItemHeight, Component* const parent)
: itemInfo (info),
isHighlighted (false)
{
addAndMakeVisible (itemInfo.customComp);
@@ -165,10 +159,10 @@ public:
getLookAndFeel()
.drawPopupMenuItem (g, getWidth(), getHeight(),
itemInfo.isSeparator,
itemInfo.active,
itemInfo.isActive,
isHighlighted,
itemInfo.isTicked,
itemInfo.subMenu != nullptr,
itemInfo.subMenu != nullptr && (itemInfo.itemID == 0 || itemInfo.subMenu->getNumItems() > 0),
mainText, endText,
itemInfo.image.isValid() ? &itemInfo.image : nullptr,
itemInfo.usesColour ? &(itemInfo.textColour) : nullptr);
@@ -184,7 +178,7 @@ public:
void setHighlighted (bool shouldBeHighlighted)
{
shouldBeHighlighted = shouldBeHighlighted && itemInfo.active;
shouldBeHighlighted = shouldBeHighlighted && itemInfo.isActive;
if (isHighlighted != shouldBeHighlighted)
{
@@ -222,22 +216,22 @@ class PopupMenu::Window : public Component,
private Timer
{
public:
Window (const PopupMenu& menu, Window* const owner_,
const Options& options_,
Window (const PopupMenu& menu, Window* const window,
const Options& opts,
const bool alignToRectangle,
const bool dismissOnMouseUp_,
ApplicationCommandManager** const managerOfChosenCommand_)
const bool shouldDismissOnMouseUp,
ApplicationCommandManager** const manager)
: Component ("menu"),
owner (owner_),
options (options_),
owner (window),
options (opts),
activeSubMenu (nullptr),
managerOfChosenCommand (managerOfChosenCommand_),
managerOfChosenCommand (manager),
componentAttachedTo (options.targetComponent),
isOver (false),
hasBeenOver (false),
isDown (false),
needsToScroll (false),
dismissOnMouseUp (dismissOnMouseUp_),
dismissOnMouseUp (shouldDismissOnMouseUp),
hideOnExit (false),
disableMouseMoves (false),
hasAnyJuceCompHadFocus (false),
@@ -332,12 +326,12 @@ public:
if (item != nullptr
&& item->commandManager != nullptr
&& item->itemId != 0)
&& item->itemID != 0)
{
*managerOfChosenCommand = item->commandManager;
}
exitModalState (item != nullptr ? item->itemId : 0);
exitModalState (item != nullptr ? item->itemID : 0);
if (makeInvisible && (deletionChecker != nullptr))
setVisible (false);
@@ -774,16 +768,16 @@ private:
return totalW;
}
void ensureItemIsVisible (const int itemId, int wantedY)
void ensureItemIsVisible (const int itemID, int wantedY)
{
jassert (itemId != 0)
jassert (itemID != 0)
for (int i = items.size(); --i >= 0;)
{
PopupMenu::ItemComponent* const m = items.getUnchecked(i);
if (m != nullptr
&& m->itemInfo.itemId == itemId
&& m->itemInfo.itemID == itemID
&& windowPos.getHeight() > PopupMenuSettings::scrollZone * 4)
{
const int currentY = m->getY();
@@ -1181,14 +1175,14 @@ void PopupMenu::clear()
items.clear();
}
void PopupMenu::addItem (const int itemResultId, const String& itemText,
void PopupMenu::addItem (const int itemResultID, const String& itemText,
const bool isActive, const bool isTicked, const Image& iconToUse)
{
jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user
jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user
// didn't pick anything, so you shouldn't use it as the id
// for an item..
items.add (new Item (itemResultId, itemText, isActive, isTicked, iconToUse,
items.add (new Item (itemResultID, itemText, isActive, isTicked, iconToUse,
Colours::black, false, nullptr, nullptr, nullptr));
}
@@ -1218,29 +1212,29 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager,
}
}
void PopupMenu::addColouredItem (const int itemResultId,
void PopupMenu::addColouredItem (const int itemResultID,
const String& itemText,
const Colour& itemTextColour,
const bool isActive,
const bool isTicked,
const Image& iconToUse)
{
jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user
jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user
// didn't pick anything, so you shouldn't use it as the id
// for an item..
items.add (new Item (itemResultId, itemText, isActive, isTicked, iconToUse,
items.add (new Item (itemResultID, itemText, isActive, isTicked, iconToUse,
itemTextColour, true, nullptr, nullptr, nullptr));
}
//==============================================================================
void PopupMenu::addCustomItem (const int itemResultId, CustomComponent* const customComponent)
void PopupMenu::addCustomItem (const int itemResultID, CustomComponent* const customComponent)
{
jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user
jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user
// didn't pick anything, so you shouldn't use it as the id
// for an item..
items.add (new Item (itemResultId, String::empty, true, false, Image::null,
items.add (new Item (itemResultID, String::empty, true, false, Image::null,
Colours::black, false, customComponent, nullptr, nullptr));
}
@@ -1273,12 +1267,12 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NormalComponentWrapper);
};
void PopupMenu::addCustomItem (const int itemResultId,
void PopupMenu::addCustomItem (const int itemResultID,
Component* customComponent,
int idealWidth, int idealHeight,
const bool triggerMenuItemAutomaticallyWhenClicked)
{
addCustomItem (itemResultId,
addCustomItem (itemResultID,
new NormalComponentWrapper (customComponent, idealWidth, idealHeight,
triggerMenuItemAutomaticallyWhenClicked));
}
@@ -1288,9 +1282,10 @@ void PopupMenu::addSubMenu (const String& subMenuName,
const PopupMenu& subMenu,
const bool isActive,
const Image& iconToUse,
const bool isTicked)
const bool isTicked,
const int itemResultID)
{
items.add (new Item (0, subMenuName, isActive && (subMenu.getNumItems() > 0), isTicked,
items.add (new Item (itemResultID, subMenuName, isActive && (itemResultID != 0 || subMenu.getNumItems() > 0), isTicked,
iconToUse, Colours::black, false, nullptr, &subMenu, nullptr));
}
@@ -1495,12 +1490,12 @@ void PopupMenu::showMenuAsync (const Options& options, ModalComponentManager::Ca
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED
int PopupMenu::show (const int itemIdThatMustBeVisible,
int PopupMenu::show (const int itemIDThatMustBeVisible,
const int minimumWidth, const int maximumNumColumns,
const int standardItemHeight,
ModalComponentManager::Callback* callback)
{
return showWithOptionalCallback (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible)
return showWithOptionalCallback (Options().withItemThatMustBeVisible (itemIDThatMustBeVisible)
.withMinimumWidth (minimumWidth)
.withMaximumNumColumns (maximumNumColumns)
.withStandardItemHeight (standardItemHeight),
@@ -1508,13 +1503,13 @@ int PopupMenu::show (const int itemIdThatMustBeVisible,
}
int PopupMenu::showAt (const Rectangle<int>& screenAreaToAttachTo,
const int itemIdThatMustBeVisible,
const int itemIDThatMustBeVisible,
const int minimumWidth, const int maximumNumColumns,
const int standardItemHeight,
ModalComponentManager::Callback* callback)
{
return showWithOptionalCallback (Options().withTargetScreenArea (screenAreaToAttachTo)
.withItemThatMustBeVisible (itemIdThatMustBeVisible)
.withItemThatMustBeVisible (itemIDThatMustBeVisible)
.withMinimumWidth (minimumWidth)
.withMaximumNumColumns (maximumNumColumns)
.withStandardItemHeight (standardItemHeight),
@@ -1522,12 +1517,12 @@ int PopupMenu::showAt (const Rectangle<int>& screenAreaToAttachTo,
}
int PopupMenu::showAt (Component* componentToAttachTo,
const int itemIdThatMustBeVisible,
const int itemIDThatMustBeVisible,
const int minimumWidth, const int maximumNumColumns,
const int standardItemHeight,
ModalComponentManager::Callback* callback)
{
Options options (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible)
Options options (Options().withItemThatMustBeVisible (itemIDThatMustBeVisible)
.withMinimumWidth (minimumWidth)
.withMaximumNumColumns (maximumNumColumns)
.withStandardItemHeight (standardItemHeight));
@@ -1573,7 +1568,7 @@ bool PopupMenu::containsCommandItem (const int commandID) const
{
const Item* const mi = items.getUnchecked (i);
if ((mi->itemId == commandID && mi->commandManager != nullptr)
if ((mi->itemID == commandID && mi->commandManager != nullptr)
|| (mi->subMenu != nullptr && mi->subMenu->containsCommandItem (commandID)))
{
return true;
@@ -1594,7 +1589,7 @@ bool PopupMenu::containsAnyActiveItems() const noexcept
if (mi->subMenu->containsAnyActiveItems())
return true;
}
else if (mi->active)
else if (mi->isActive)
{
return true;
}
@@ -1609,9 +1604,9 @@ void PopupMenu::setLookAndFeel (LookAndFeel* const newLookAndFeel)
}
//==============================================================================
PopupMenu::CustomComponent::CustomComponent (const bool isTriggeredAutomatically_)
PopupMenu::CustomComponent::CustomComponent (const bool isTriggeredAutomatically)
: isHighlighted (false),
triggeredAutomatically (isTriggeredAutomatically_)
triggeredAutomatically (isTriggeredAutomatically)
{
}
@@ -1652,7 +1647,7 @@ void PopupMenu::CustomComponent::triggerMenuItem()
}
//==============================================================================
PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& menu_)
PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& m)
: subMenu (nullptr),
itemId (0),
isSeparator (false),
@@ -1661,7 +1656,7 @@ PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& menu_)
isCustomComponent (false),
isSectionHeader (false),
customColour (nullptr),
menu (menu_),
menu (m),
index (0)
{
}
@@ -1683,10 +1678,10 @@ bool PopupMenu::MenuItemIterator::next()
itemName = item->customComp != nullptr ? item->customComp->getName() : item->text;
subMenu = item->subMenu;
itemId = item->itemId;
itemId = item->itemID;
isSeparator = item->isSeparator;
isTicked = item->isTicked;
isEnabled = item->active;
isEnabled = item->isActive;
isSectionHeader = dynamic_cast <HeaderItemComponent*> (static_cast <CustomComponent*> (item->customComp)) != nullptr;
isCustomComponent = (! isSectionHeader) && item->customComp != nullptr;
customColour = item->usesColour ? &(item->textColour) : nullptr;


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

@@ -105,7 +105,7 @@ public:
/** Appends a new text item for this menu to show.
@param itemResultId the number that will be returned from the show() method
@param itemResultID the number that will be returned from the show() method
if the user picks this item. The value should never be
zero, because that's used to indicate that the user didn't
select anything.
@@ -119,7 +119,7 @@ public:
@see addSeparator, addColouredItem, addCustomItem, addSubMenu
*/
void addItem (int itemResultId,
void addItem (int itemResultID,
const String& itemText,
bool isEnabled = true,
bool isTicked = false,
@@ -144,7 +144,7 @@ public:
text, which will override the default colours that are used by the
current look-and-feel. See addItem() for a description of the parameters.
*/
void addColouredItem (int itemResultId,
void addColouredItem (int itemResultID,
const String& itemText,
const Colour& itemTextColour,
bool isEnabled = true,
@@ -161,12 +161,12 @@ public:
if triggerMenuItemAutomaticallyWhenClicked is true, the menu itself will handle
detection of a mouse-click on your component, and use that to trigger the
menu ID specified in itemResultId. If this is false, the menu item can't
be triggered, so itemResultId is not used.
menu ID specified in itemResultID. If this is false, the menu item can't
be triggered, so itemResultID is not used.
@see CustomComponent
*/
void addCustomItem (int itemResultId,
void addCustomItem (int itemResultID,
Component* customComponent,
int idealWidth, int idealHeight,
bool triggerMenuItemAutomaticallyWhenClicked);
@@ -174,12 +174,15 @@ public:
/** Appends a sub-menu.
If the menu that's passed in is empty, it will appear as an inactive item.
If the itemResultID argument is non-zero, then the sub-menu item itself can be
clicked to trigger it as a command.
*/
void addSubMenu (const String& subMenuName,
const PopupMenu& subMenu,
bool isEnabled = true,
const Image& iconToUse = Image::null,
bool isTicked = false);
bool isTicked = false,
int itemResultID = 0);
/** Appends a separator to the menu, to help break it up into sections.
@@ -254,7 +257,7 @@ public:
on where this point is on the screen, the menu will appear above, below or
to the side of the point.
@param itemIdThatMustBeVisible if you set this to the ID of one of the menu items,
@param itemIDThatMustBeVisible if you set this to the ID of one of the menu items,
then when the menu first appears, it will make sure
that this item is visible. So if the menu has too many
items to fit on the screen, it will be scrolled to a
@@ -276,7 +279,7 @@ public:
pointers that it uses are safely within scope.
@see showAt
*/
int show (int itemIdThatMustBeVisible = 0,
int show (int itemIDThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
int standardItemHeight = 0,
@@ -297,7 +300,7 @@ public:
@see show()
*/
int showAt (const Rectangle<int>& screenAreaToAttachTo,
int itemIdThatMustBeVisible = 0,
int itemIDThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
int standardItemHeight = 0,
@@ -310,7 +313,7 @@ public:
things like buttons that trigger a pop-up menu.
*/
int showAt (Component* componentToAttachTo,
int itemIdThatMustBeVisible = 0,
int itemIDThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
int standardItemHeight = 0,
@@ -472,7 +475,7 @@ public:
@see CustomComponent
*/
void addCustomItem (int itemResultId, CustomComponent* customComponent);
void addCustomItem (int itemResultID, CustomComponent* customComponent);
private:
//==============================================================================


Loading…
Cancel
Save