Java Swing How to - Create Closeable JTabbedPane - alignment of the close button








Question

We would like to know how to create Closeable JTabbedPane - alignment of the close button.

Answer

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
/*from  w  ww.jav a  2  s  .  c o  m*/
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.plaf.LayerUI;

public class Main {
  public JComponent makeUI() {
    UIManager.put("TabbedPane.tabInsets", new Insets(2, 2, 2, 50));
    final JTabbedPane tabbedPane = new JTabbedPane();
    tabbedPane.addTab("A", new JPanel());

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JLayer<JTabbedPane>(tabbedPane, new CloseableTabbedPaneLayerUI()));
    p.add(new JButton(new AbstractAction("add tab") {
      @Override
      public void actionPerformed(ActionEvent e) {
        tabbedPane.addTab("test", new JPanel());
      }
    }), BorderLayout.SOUTH);
    return p;
  }

  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new Main().makeUI());
    f.setSize(320, 240);
    f.setVisible(true);
  }
}

class MyCloseButton extends JButton {
  public MyCloseButton() {
    super("x");
    setBorder(BorderFactory.createEmptyBorder());
    setFocusPainted(false);
    setBorderPainted(false);
    setContentAreaFilled(false);
    setRolloverEnabled(false);
  }
  @Override
  public Dimension getPreferredSize() {
    return new Dimension(16, 16);
  }
};

class CloseableTabbedPaneLayerUI extends LayerUI<JTabbedPane> {
  JPanel p = new JPanel();
  Point pt = new Point(-100, -100);
  MyCloseButton button = new MyCloseButton();

  public CloseableTabbedPaneLayerUI() {
    super();
  }

  @Override
  public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer == false) {
      return;
    }
    JLayer jlayer = (JLayer) c;
    JTabbedPane tabPane = (JTabbedPane) jlayer.getView();
    for (int i = 0; i < tabPane.getTabCount(); i++) {
      Rectangle rect = tabPane.getBoundsAt(i);
      Dimension d = button.getPreferredSize();
      int x = rect.x + rect.width - d.width - 2;
      int y = rect.y + (rect.height - d.height) / 2;
      Rectangle r = new Rectangle(x, y, d.width, d.height);
      button.setForeground(r.contains(pt) ? Color.RED : Color.BLACK);
      SwingUtilities.paintComponent(g, button, p, r);
    }
  }

  @Override
  public void installUI(JComponent c) {
    super.installUI(c);
    ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK
        | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  }

  @Override
  public void uninstallUI(JComponent c) {
    ((JLayer) c).setLayerEventMask(0);
    super.uninstallUI(c);
  }

  @Override
  protected void processMouseEvent(MouseEvent e, JLayer<? extends JTabbedPane> l) {
    if (e.getID() != MouseEvent.MOUSE_CLICKED) {
      return;
    }
    pt.setLocation(e.getPoint());
    JTabbedPane tabbedPane = (JTabbedPane) l.getView();
    int index = tabbedPane.indexAtLocation(pt.x, pt.y);
    if (index >= 0) {
      Rectangle rect = tabbedPane.getBoundsAt(index);
      Dimension d = button.getPreferredSize();
      int x = rect.x + rect.width - d.width - 2;
      int y = rect.y + (rect.height - d.height) / 2;
      Rectangle r = new Rectangle(x, y, d.width, d.height);
      if (r.contains(pt)) {
        tabbedPane.removeTabAt(index);
      }
    }
    l.getView().repaint();
  }

  @Override
  protected void processMouseMotionEvent(MouseEvent e,
      JLayer<? extends JTabbedPane> l) {
    pt.setLocation(e.getPoint());
    JTabbedPane tabbedPane = (JTabbedPane) l.getView();
    int index = tabbedPane.indexAtLocation(pt.x, pt.y);
    if (index >= 0) {
      tabbedPane.repaint(tabbedPane.getBoundsAt(index));
    } else {
      tabbedPane.repaint();
    }
  }
}