How to change the pupup menu with LookAndFeel

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;
1 Like

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);

            baseColour.darker(0.1f), 0.f, height / 2 - 2,
            baseColour.darker(0.2f), 0.f, height / 2 + 2, false));


        if (isButtonDown)
            g.setColour(baseColour.brighter(isButtonDown ? 0.1f : 0.01f));

        constexpr auto outlineAlpha = 0.05f;

        g.strokePath(outline, juce::PathStrokeType(1.f));

        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)

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;


    if (isSeparator)
        const auto separatorRect = Rectangle<int>(10, area.getHeight() / 2 - 0.5f, area.getWidth() - 20, 1);
        if (isHighlighted)
            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);

        if (isTicked)
            g.fillEllipse(10, (area.getHeight() - 5) / 2, 8, 8);

Note that this includes some statics that I have defined, pick what suits your project.

1 Like