I have a problem with the LookAndFeel class. I don’t know how to change the look of the popup menu, and I’m unsure about which version of LookAndFeel to us
e.
I have a problem with the LookAndFeel class. I don’t know how to change the look of the popup menu, and I’m unsure about which version of LookAndFeel to us
Use LookAndFeel_V4.
These are some of the LookAndFeel methods that I override to control the drop down menus in my ComboBoxes:
// PopupMenu
Font getPopupMenuFont() override;
int getPopupMenuColumnSeparatorWidthWithOptions(const PopupMenu::Options&) override; // KJ#0130
void drawPopupMenuColumnSeparatorWithOptions(Graphics& g,
const Rectangle<int >& bounds, const PopupMenu::Options &) override; // KJ#0085.2
void getIdealPopupMenuItemSize (const String& text, const bool isSeparator,
int standardMenuItemHeight, int& idealWidth, int& idealHeight) override;
void 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,
const Drawable* icon, const Colour* const textColourToUse) override;
void drawPopupMenuBackground (Graphics& g, int width, int height) override;
void drawPopupMenuSectionHeader (Graphics& g, const Rectangle<int>& area, const String& sectionName) override; // KJ#0137
bool shouldPopupMenuScaleWithTargetComponent(const PopupMenu::Options& options) override;
I have been working on some code that may help you get you in the direction you want to go. For a combobox/popup that look like this:
You can use the below code in your LookAndFeel.cpp overrides:
void PluginLookAndFeel::drawComboBox(juce::Graphics& g, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& comboBox)
{
const auto baseColour = g_secondaryColour.withMultipliedAlpha(comboBox.isEnabled() ? 0.75f : 0.3f);
if (width > 0 && height > 0)
{
const float cornerSize = 1.f;
juce::Path outline;
outline.addRoundedRectangle(0.f, 0.f,
width, height, cornerSize, cornerSize,
true, true, true, true);
g.setGradientFill(juce::ColourGradient(
baseColour.darker(0.1f), 0.f, height / 2 - 2,
baseColour.darker(0.2f), 0.f, height / 2 + 2, false));
g.fillPath(outline);
if (isButtonDown)
{
g.setColour(baseColour.brighter(isButtonDown ? 0.1f : 0.01f));
g.fillPath(outline);
}
constexpr auto outlineAlpha = 0.05f;
g.setColour(juce::Colours::white.withAlpha(outlineAlpha));
g.strokePath(outline, juce::PathStrokeType(1.f));
g.setColour(juce::Colours::black.withAlpha(outlineAlpha));
g.strokePath(outline, juce::PathStrokeType(1.f),
juce::AffineTransform::translation(0.f, 1.f).scaled(1.f, (height + 2.f) / height));
}
}
void PluginLookAndFeel::positionComboBoxText(juce::ComboBox& cb, juce::Label& labelToPosition)
{
labelToPosition.setBounds(cb.getLocalBounds().reduced(cb.getHeight() / 2, 0));
}
void PluginLookAndFeel::drawPopupMenuBackground(juce::Graphics& g, int width, int height)
{
g.fillAll(g_secondaryColour);
}
void PluginLookAndFeel::drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area, bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu, const juce::String& text, const juce::String& shortcutKeyText, const juce::Drawable* icon, const juce::Colour* textColour)
{
using namespace juce;
g.setColour(g_secondaryColour);
g.fillRect(area);
g.setFont(juce::Font(g_fontSize));
if (isSeparator)
{
g.setColour(juce::Colour(0xFFFFFF).withAlpha(0.2f));
const auto separatorRect = Rectangle<int>(10, area.getHeight() / 2 - 0.5f, area.getWidth() - 20, 1);
g.fillRect(separatorRect);
}
else
{
if (isHighlighted)
{
g.setColour(g_secondaryColour);
g.fillRect(area.reduced(2, 2));
}
g.setColour(juce::Colour(0xFFFFFF).withAlpha(isActive ? 0.8f : 0.2f));
g.drawText(text, area.reduced(25, 0), Justification::left);
if (hasSubMenu)
{
constexpr int triWidth = 6;
constexpr int triHeight = 8;
const int x0 = area.getRight() - triWidth - 10;
const int x1 = x0 + triWidth;
const int y0 = area.getY() + area.getHeight() / 2 - triHeight / 2;
const int y1 = y0 + triHeight / 2;
const int y2 = y1 + triHeight / 2;
Path triangle;
triangle.addTriangle(x0, y0, x0, y2, x1, y1);
g.fillPath(triangle);
}
if (isTicked)
{
g.setColour(g_primaryColour);
g.fillEllipse(10, (area.getHeight() - 5) / 2, 8, 8);
}
}
}
Note that this includes some statics that I have defined, pick what suits your project.