1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Refactored LookAndFeel::drawPopupMenuItem() and some PopupMenu methods to allow menus to use Drawables for their icons as well as just Images.

This commit is contained in:
jules 2013-11-24 19:30:04 +00:00
parent 06629a3967
commit 28dbc839b1
6 changed files with 210 additions and 93 deletions

View file

@ -900,24 +900,23 @@ void LookAndFeel_V2::drawPopupMenuUpDownArrow (Graphics& g, int width, int heigh
g.fillPath (p);
}
void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height,
void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, const Rectangle<int>& area,
const bool isSeparator, const bool isActive,
const bool isHighlighted, const bool isTicked,
const bool hasSubMenu, const String& text,
const String& shortcutKeyText,
Image* image, const Colour* const textColourToUse)
const Drawable* icon, const Colour* const textColourToUse)
{
const float halfH = height * 0.5f;
if (isSeparator)
{
const float separatorIndent = 5.5f;
Rectangle<int> r (area.reduced (5, 0));
r.removeFromTop (r.getHeight() / 2 - 1);
g.setColour (Colour (0x33000000));
g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH);
g.fillRect (r.removeFromTop (1));
g.setColour (Colour (0x66ffffff));
g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f);
g.fillRect (r.removeFromTop (1));
}
else
{
@ -926,10 +925,12 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height,
if (textColourToUse != nullptr)
textColour = *textColourToUse;
Rectangle<int> r (area.reduced (1));
if (isHighlighted)
{
g.setColour (findColour (PopupMenu::highlightedBackgroundColourId));
g.fillRect (1, 1, width - 2, height - 2);
g.fillRect (r);
g.setColour (findColour (PopupMenu::highlightedTextColourId));
}
@ -943,33 +944,42 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height,
Font font (getPopupMenuFont());
if (font.getHeight() > height / 1.3f)
font.setHeight (height / 1.3f);
const float maxFontHeight = area.getHeight() / 1.3f;
if (font.getHeight() > maxFontHeight)
font.setHeight (maxFontHeight);
g.setFont (font);
const int leftBorder = (height * 5) / 4;
const int rightBorder = 4;
Rectangle<float> iconArea (r.removeFromLeft ((r.getHeight() * 5) / 4).reduced (3).toFloat());
if (image != nullptr)
if (icon != nullptr)
{
g.drawImageWithin (*image,
2, 1, leftBorder - 4, height - 2,
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false);
icon->drawWithin (g, iconArea, RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f);
}
else if (isTicked)
{
const Path tick (getTickShape (1.0f));
const float th = font.getAscent();
const float ty = halfH - th * 0.5f;
g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4),
th, true));
g.fillPath (tick, tick.getTransformToScaleToFit (iconArea, true));
}
g.drawFittedText (text,
leftBorder, 0, width - (leftBorder + rightBorder), height,
Justification::centredLeft, 1);
if (hasSubMenu)
{
const float arrowH = 0.6f * getPopupMenuFont().getAscent();
const float x = (float) r.removeFromRight ((int) arrowH).getX();
const float halfH = (float) r.getCentreY();
Path p;
p.addTriangle (x, halfH - arrowH * 0.5f,
x, halfH + arrowH * 0.5f,
x + arrowH * 0.6f, halfH);
g.fillPath (p);
}
r.removeFromRight (3);
g.drawFittedText (text, r, Justification::centredLeft, 1);
if (shortcutKeyText.isNotEmpty())
{
@ -978,23 +988,7 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height,
f2.setHorizontalScale (0.95f);
g.setFont (f2);
g.drawText (shortcutKeyText,
leftBorder, 0, width - (leftBorder + rightBorder + 4), height,
Justification::centredRight,
true);
}
if (hasSubMenu)
{
const float arrowH = 0.6f * getPopupMenuFont().getAscent();
const float x = width - height * 0.6f;
Path p;
p.addTriangle (x, halfH - arrowH * 0.5f,
x, halfH + arrowH * 0.5f,
x + arrowH * 0.6f, halfH);
g.fillPath (p);
g.drawText (shortcutKeyText, r, Justification::centredRight, true);
}
}
}

View file

@ -133,10 +133,10 @@ public:
//==============================================================================
void drawPopupMenuBackground (Graphics&, int width, int height) override;
void drawPopupMenuItem (Graphics&, int width, int height,
void drawPopupMenuItem (Graphics&, const Rectangle<int>& area,
bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu,
const String& text, const String& shortcutKeyText,
Image* image, const Colour* textColour) override;
const Drawable* icon, const Colour* textColour) override;
Font getPopupMenuFont() override;

View file

@ -604,3 +604,27 @@ Button* LookAndFeel_V3::createDocumentWindowButton (int buttonType)
jassertfalse;
return nullptr;
}
Path LookAndFeel_V3::getTickShape (const float height)
{
static const unsigned char pathData[]
= { 110,109,32,210,202,64,126,183,148,64,108,39,244,247,64,245,76,124,64,108,178,131,27,65,246,76,252,64,108,175,242,4,65,246,76,252,
64,108,236,5,68,65,0,0,160,180,108,240,150,90,65,21,136,52,63,108,48,59,16,65,0,0,32,65,108,32,210,202,64,126,183,148,64, 99,101,0,0 };
Path p;
p.loadPathFromData (pathData, sizeof (pathData));
p.scaleToFit (0, 0, height * 2.0f, height, true);
return p;
}
Path LookAndFeel_V3::getCrossShape (const float height)
{
static const unsigned char pathData[]
= { 110,109,88,57,198,65,29,90,171,65,108,63,53,154,65,8,172,126,65,108,76,55,198,65,215,163,38,65,108,141,151,175,65,82,184,242,64,108,117,147,131,65,90,100,81,65,108,184,30,47,65,82,184,242,64,108,59,223,1,65,215,163,38,65,108,84,227,89,65,8,172,126,65,
108,35,219,1,65,29,90,171,65,108,209,34,47,65,231,251,193,65,108,117,147,131,65,207,247,149,65,108,129,149,175,65,231,251,193,65,99,101,0,0 };
Path p;
p.loadPathFromData (pathData, sizeof (pathData));
p.scaleToFit (0, 0, height * 2.0f, height, true);
return p;
}

View file

@ -82,6 +82,9 @@ public:
void drawConcertinaPanelHeader (Graphics&, const Rectangle<int>& area, bool isMouseOver, bool isMouseDown,
ConcertinaPanel&, Component&) override;
Path getTickShape (float height) override;
Path getCrossShape (float height) override;
static void createTabTextLayout (const TabBarButton& button, float length, float depth, Colour colour, TextLayout&);
private:

View file

@ -45,7 +45,7 @@ public:
const String& name,
const bool active,
const bool ticked,
const Image& im,
Drawable* drawable,
const Colour colour,
const bool useColour,
CustomComponent* const custom,
@ -54,8 +54,8 @@ public:
: itemID (itemId), text (name), textColour (colour),
isActive (active), isSeparator (false), isTicked (ticked),
usesColour (useColour), image (im), customComp (custom),
subMenu (createCopyIfNotNull (sub)), commandManager (manager)
usesColour (useColour), iconDrawable (drawable),
customComp (custom), subMenu (createCopyIfNotNull (sub)), commandManager (manager)
{
if (commandManager != nullptr && itemID != 0)
{
@ -92,7 +92,7 @@ public:
isSeparator (other.isSeparator),
isTicked (other.isTicked),
usesColour (other.usesColour),
image (other.image),
iconDrawable (other.iconDrawable != nullptr ? other.iconDrawable->createCopy() : nullptr),
customComp (other.customComp),
subMenu (createCopyIfNotNull (other.subMenu.get())),
commandManager (other.commandManager)
@ -106,7 +106,7 @@ public:
String text;
const Colour textColour;
const bool isActive, isSeparator, isTicked, usesColour;
Image image;
ScopedPointer<Drawable> iconDrawable;
ReferenceCountedObjectPtr <CustomComponent> customComp;
ScopedPointer <PopupMenu> subMenu;
ApplicationCommandManager* const commandManager;
@ -175,14 +175,14 @@ public:
}
getLookAndFeel()
.drawPopupMenuItem (g, getWidth(), getHeight(),
.drawPopupMenuItem (g, getLocalBounds(),
itemInfo.isSeparator,
itemInfo.isActive,
isHighlighted,
itemInfo.isTicked,
itemInfo.subMenu != nullptr && (itemInfo.itemID == 0 || itemInfo.subMenu->getNumItems() > 0),
mainText, endText,
itemInfo.image.isValid() ? &itemInfo.image : nullptr,
itemInfo.iconDrawable,
itemInfo.usesColour ? &(itemInfo.textColour) : nullptr);
}
}
@ -1288,8 +1288,40 @@ void PopupMenu::clear()
items.clear();
}
void PopupMenu::addItem (const int itemResultID, const String& itemText,
const bool isActive, const bool isTicked, const Image& iconToUse)
void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked)
{
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, nullptr,
Colours::black, false, nullptr, nullptr, nullptr));
}
static Drawable* createDrawableFromImage (const Image& im)
{
if (im.isValid())
{
DrawableImage* d = new DrawableImage();
d->setImage (im);
return d;
}
return nullptr;
}
void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked, const Image& iconToUse)
{
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, createDrawableFromImage (iconToUse),
Colours::black, false, nullptr, nullptr, nullptr));
}
void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked, Drawable* iconToUse)
{
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
@ -1315,7 +1347,7 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager,
: info.shortName,
target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0,
(info.flags & ApplicationCommandInfo::isTicked) != 0,
Image::null,
nullptr,
Colours::black,
false,
nullptr, nullptr,
@ -1323,50 +1355,49 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager,
}
}
void PopupMenu::addColouredItem (const int itemResultID,
const String& itemText,
Colour itemTextColour,
const bool isActive,
const bool isTicked,
const Image& iconToUse)
void PopupMenu::addColouredItem (int itemResultID, const String& itemText, Colour itemTextColour,
bool isActive, bool isTicked, const Image& iconToUse)
{
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, createDrawableFromImage (iconToUse),
itemTextColour, true, nullptr, nullptr, nullptr));
}
void PopupMenu::addCustomItem (const int itemID, CustomComponent* const cc, const PopupMenu* subMenu)
void PopupMenu::addCustomItem (int itemID, CustomComponent* cc, const PopupMenu* subMenu)
{
jassert (itemID != 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 (itemID, String::empty, true, false, Image::null,
items.add (new Item (itemID, String::empty, true, false, nullptr,
Colours::black, false, cc, subMenu, nullptr));
}
void PopupMenu::addCustomItem (const int itemResultID,
Component* customComponent,
int idealWidth, int idealHeight,
const bool triggerMenuItemAutomaticallyWhenClicked,
const PopupMenu* subMenu)
void PopupMenu::addCustomItem (int itemResultID, Component* customComponent, int idealWidth, int idealHeight,
bool triggerMenuItemAutomaticallyWhenClicked, const PopupMenu* subMenu)
{
items.add (new Item (itemResultID, String::empty, true, false, Image::null,
Colours::black, false,
items.add (new Item (itemResultID, String::empty, true, false, nullptr, Colours::black, false,
new HelperClasses::NormalComponentWrapper (customComponent, idealWidth, idealHeight,
triggerMenuItemAutomaticallyWhenClicked),
subMenu, nullptr));
}
void PopupMenu::addSubMenu (const String& subMenuName,
const PopupMenu& subMenu,
const bool isActive,
const Image& iconToUse,
const bool isTicked,
const int itemResultID)
void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive)
{
addSubMenu (subMenuName, subMenu, isActive, nullptr, false, 0);
}
void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive,
const Image& iconToUse, bool isTicked, int itemResultID)
{
addSubMenu (subMenuName, subMenu, isActive, createDrawableFromImage (iconToUse), isTicked, itemResultID);
}
void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive,
Drawable* iconToUse, bool isTicked, int itemResultID)
{
items.add (new Item (itemResultID, subMenuName, isActive && (itemResultID != 0 || subMenu.getNumItems() > 0), isTicked,
iconToUse, Colours::black, false, nullptr, &subMenu, nullptr));
@ -1673,7 +1704,7 @@ void PopupMenu::CustomComponent::triggerMenuItem()
{
if (HelperClasses::ItemComponent* const mic = dynamic_cast<HelperClasses::ItemComponent*> (getParentComponent()))
{
if (HelperClasses::MenuWindow* const pmw = dynamic_cast <HelperClasses::MenuWindow*> (mic->getParentComponent()))
if (HelperClasses::MenuWindow* const pmw = dynamic_cast<HelperClasses::MenuWindow*> (mic->getParentComponent()))
{
pmw->dismissMenu (&mic->itemInfo);
}
@ -1727,10 +1758,10 @@ bool PopupMenu::MenuItemIterator::next()
isSeparator = item->isSeparator;
isTicked = item->isTicked;
isEnabled = item->isActive;
isSectionHeader = dynamic_cast <HelperClasses::HeaderItemComponent*> (static_cast <CustomComponent*> (item->customComp)) != nullptr;
isSectionHeader = dynamic_cast<HelperClasses::HeaderItemComponent*> (static_cast<CustomComponent*> (item->customComp)) != nullptr;
isCustomComponent = (! isSectionHeader) && item->customComp != nullptr;
customColour = item->usesColour ? &(item->textColour) : nullptr;
customImage = item->image;
icon = item->iconDrawable;
commandManager = item->commandManager;
return true;
@ -1738,8 +1769,7 @@ bool PopupMenu::MenuItemIterator::next()
void PopupMenu::MenuItemIterator::addItemTo (PopupMenu& targetMenu)
{
targetMenu.items.add (new Item (itemId, itemName, isEnabled, isTicked, customImage,
customColour != nullptr ? *customColour : Colours::black, customColour != nullptr,
nullptr,
subMenu, commandManager));
targetMenu.items.add (new Item (itemId, itemName, isEnabled, isTicked, icon != nullptr ? icon->createCopy() : nullptr,
customColour != nullptr ? *customColour : Colours::black,
customColour != nullptr, nullptr, subMenu, commandManager));
}

View file

@ -111,18 +111,52 @@ public:
@param itemText the text to show.
@param isEnabled if false, the item will be shown 'greyed-out' and can't be picked
@param isTicked if true, the item will be shown with a tick next to it
@param iconToUse if this is non-zero, it should be an image that will be
displayed to the left of the item. This method will take its
own copy of the image passed-in, so there's no need to keep
it hanging around.
@see addSeparator, addColouredItem, addCustomItem, addSubMenu
*/
void addItem (int itemResultID,
const String& itemText,
bool isEnabled = true,
bool isTicked = false,
const Image& iconToUse = Image::null);
bool isTicked = false);
/** Appends a new item with an icon.
@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.
@param itemText the text to show.
@param isEnabled if false, the item will be shown 'greyed-out' and can't be picked
@param isTicked if true, the item will be shown with a tick next to it
@param iconToUse if this is a valid image, it will be displayed to the left of the item.
@see addSeparator, addColouredItem, addCustomItem, addSubMenu
*/
void addItem (int itemResultID,
const String& itemText,
bool isEnabled,
bool isTicked,
const Image& iconToUse);
/** Appends a new item with an icon.
@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.
@param itemText the text to show.
@param isEnabled if false, the item will be shown 'greyed-out' and can't be picked
@param isTicked if true, the item will be shown with a tick next to it
@param iconToUse a Drawable object to use as the icon to the left of the item.
The menu will take ownership of this drawable object and will
delete it later when no longer needed
@see addSeparator, addColouredItem, addCustomItem, addSubMenu
*/
void addItem (int itemResultID,
const String& itemText,
bool isEnabled,
bool isTicked,
Drawable* iconToUse);
/** Adds an item that represents one of the commands in a command manager object.
@ -177,8 +211,35 @@ public:
*/
void addSubMenu (const String& subMenuName,
const PopupMenu& subMenu,
bool isEnabled = true,
const Image& iconToUse = Image::null,
bool isEnabled = true);
/** Appends a sub-menu with an icon.
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,
const Image& iconToUse,
bool isTicked = false,
int itemResultID = 0);
/** Appends a sub-menu with an icon.
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.
The iconToUse parameter is a Drawable object to use as the icon to the left of
the item. The menu will take ownership of this drawable object and will delete it
later when no longer needed
*/
void addSubMenu (const String& subMenuName,
const PopupMenu& subMenu,
bool isEnabled,
Drawable* iconToUse,
bool isTicked = false,
int itemResultID = 0);
@ -408,7 +469,7 @@ public:
bool isCustomComponent;
bool isSectionHeader;
const Colour* customColour;
Image customImage;
const Drawable* icon;
ApplicationCommandManager* commandManager;
private:
@ -493,12 +554,12 @@ public:
virtual void drawPopupMenuBackground (Graphics&, int width, int height) = 0;
/** Draws one of the items in a popup menu. */
virtual void drawPopupMenuItem (Graphics&, int width, int height,
virtual void drawPopupMenuItem (Graphics&, const Rectangle<int>& area,
bool isSeparator, bool isActive, bool isHighlighted,
bool isTicked, bool hasSubMenu,
const String& text,
const String& shortcutKeyText,
Image* icon,
const Drawable* icon,
const Colour* textColour) = 0;
/** Returns the size and style of font to use in popup menus. */
@ -549,6 +610,11 @@ private:
Component* createWindow (const Options&, ApplicationCommandManager**) const;
int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// These methods have new implementations now - see its new definition
int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; }
#endif
JUCE_LEAK_DETECTOR (PopupMenu)
};