Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display buttons in JComboBox items

I currently have a JComboBox which I'm using as an audio playlist - what I'd like to achieve is a little "remove" button on the right hand side of each item that I can use to remove it from the underlying model, where the circle is:

What would be the best way of achieving this?

I'd like the button to be the same for all items in the JComboBox.

demo screenshot

like image 458
Michael Berry Avatar asked Jun 16 '12 16:06

Michael Berry


1 Answers

Let me start by saying that this is an interesting question (+1 a while ago).

I had to quickly try and see for myself how difficult it is to achieve the wanted result with JComboBox. The conclusion I got (as @trashgod says in the comment above) was that this object was never designed to have other components or at least it feels like this to me.

Below is a sample that does something what you want. You could use it as a start, but to be honest you should forget about using the JComboBox for this problem.

For no means the sample below presents the right way of approaching the problem. It simply shows the result of my attempts of approaching the problem. The code below doesn't preserve good practice rules e.g. it mixes presentation with functionality (the renderer removes the elements). This is in fact just a hack not a real solution.

import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class ButtonCombo {

    private JPanel getContent() throws MalformedURLException {
        String[] ids = {"north", "west", "south", "east"};
        JComboBox combo = new JComboBox(ids);
        Icon removeIcon = new ImageIcon(new URL("http://filesharefreak.org/images/red_x.png"));
        combo.setRenderer(new ButtonComboRenderer(removeIcon, combo));
        JPanel panel = new JPanel();
        panel.add(combo);
        return panel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    JFrame f = new JFrame();
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    JPanel panel = new JPanel();
                    panel.add(new ButtonCombo().getContent());
                    JButton button = new JButton("OKOKO");
                    panel.add(button);
                    f.setContentPane(panel);
                    f.setSize(300, 160);
                    f.setLocation(200, 200);
                    f.setVisible(true);
                } catch (MalformedURLException ex) {
                    Logger.getLogger(ButtonCombo.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }
}

class ButtonComboRenderer implements ListCellRenderer {
    Icon icon;
    JPanel panel;
    JLabel label;
    JButton button;

    public ButtonComboRenderer(Icon removeIcon, final JComboBox combo) {
        icon = removeIcon;
        label = new JLabel();
        button = new JButton(icon);
        button.setPreferredSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
        panel = new JPanel(new BorderLayout());
        panel.add(label);
        panel.add(button, BorderLayout.EAST);
        panel.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {
                if (button.getX() < e.getX()) {
                    System.out.println("button contains the click remove the item");
                    combo.removeItem(label.getText());
                }
            }
        });
    }
    //so we will install the mouse listener once
    boolean isFirst = true;

    @Override
    public Component getListCellRendererComponent(JList list,
            Object value,
            int index,
            boolean isSelected,
            boolean cellHasFocus) {
        if (isFirst) {
            isFirst = false;
            list.addMouseListener(new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent e) {
                    panel.dispatchEvent(e);
                    e.consume();
                }
            });
        }
        String text = (String) value;
        label.setText(text);
        if(text == null)
            button.setIcon(null);
        else if(button.getIcon() == null)
            button.setIcon(icon);
        panel.setBackground(isSelected ? Color.red : Color.white);
        panel.setForeground(isSelected ? Color.white : Color.black);
        return panel;
    }
}

My final recommendation and the way I would do it is: BUILD YOUR OWN COMPONENT. Make it extensible and modifiable by separating it from the trigger and presentation, where both use JComponents as they come as oppose to using a renderer. In this way you would be able to capture and serve events on the components rather than as in this case all events are captured by the JList used for rendering.

Below is a sample that should help you start. It is not the final solution but it presents lots of the important issues involved in making such component. You should use the presented functionality and wrap it all accordingly in a single component:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class MockJComboBox {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final JPanel popupContent = new JPanel(new GridLayout(0, 1));
                popupContent.setBackground(Color.GREEN);
                popupContent.add(new JLabel("Content of popupContent panel"));
                popupContent.add(new JLabel("Content of popupContent panel"));
                popupContent.add(new JLabel("Content of popupContent panel"));
                popupContent.add(new JLabel("Content of popupContent panel"));
                popupContent.add(new JLabel("Content of popupContent panel"));
                popupContent.add(new JComboBox(new Object[]{"Content of popupContent panel"}));
                final JButton popupCloseButton = new JButton("X");
                popupContent.add(popupCloseButton);

                final JScrollPane s = new JScrollPane(popupContent);
                s.setPreferredSize(new Dimension(popupContent.getPreferredSize().width + s.getVerticalScrollBar().getPreferredSize().width
                        + s.getBorder().getBorderInsets(s).left
                        + s.getBorder().getBorderInsets(s).right, 100));

                JPanel panel = new JPanel();
                panel.setPreferredSize(new Dimension(200, 200));
                final JButton popupOpenButton = new JButton();
                panel.add(popupOpenButton);
                final JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setContentPane(panel);
                final PopupFactory popupFactory = PopupFactory.getSharedInstance();
                popupOpenButton.setAction(new AbstractAction("Open") {
                    private Popup popup;
                    private boolean isShown = false;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (isShown) {
                            popup.hide();
                        } else {
                            popup = popupFactory.getPopup(popupOpenButton, s,
                                    popupOpenButton.getLocationOnScreen().x, popupOpenButton.getLocationOnScreen().y + popupOpenButton.getHeight());
                            popupCloseButton.setAction(new AbstractAction(popupCloseButton.getText()) {

                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    isShown = false;
                                    popup.hide();
                                }
                            });
                            popup.show();
                        }
                        isShown = !isShown;
                    }
                });
                f.pack();
                f.setVisible(true);
            }
        });
    }
}
like image 89
Boro Avatar answered Oct 01 '22 13:10

Boro