polis
polis

Reputation: 805

JTabbedPane custom component layout

I'm looking at this example Using Swing Components; Examples. This example shows JTabbedPane with custom tab component which has close button in each tab.

If JTabbedPane is in WRAP_TAB_LAYOUT the tab title and the close button is in the middle of the wide tab. How to change this example that the tab title still would be visible in the center of the tab, but the close button would appear next to right tab border?

Here is a picture:

enter image description here

Upvotes: 1

Views: 579

Answers (1)

aterai
aterai

Reputation: 9808

  • Here is one possible implementation using UIManager.put("TabbedPane.tabInsets", insets) and JLayer
    • Paint a close button on the inner margin of the tab using the JLayer
    • I didn't test on NumbusLookAndFeel

enter image description here

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class RightCloseTabButtonTest {
  public JComponent makeUI() {
    JTabbedPane tabs = new JTabbedPane();
    tabs.addTab("Tab 1", new JLabel("aaa"));
    tabs.addTab("Tab 2", new JLabel("bbb"));
    tabs.addTab("Tab 3", new JLabel("ccc"));
    tabs.addTab("Tab 4", new JLabel("ddd"));
    return new JLayer<JTabbedPane>(tabs, new CloseableTabbedPaneLayerUI());
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      UIManager.put("TabbedPane.tabInsets", new Insets(2, 2 + 16, 2, 2 + 16));
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new RightCloseTabButtonTest().makeUI());
      f.setSize(300, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class CloseTabIcon implements Icon {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.translate(x, y);
    g2.setPaint(Color.BLACK);
    if (c instanceof AbstractButton) {
      ButtonModel m = ((AbstractButton) c).getModel();
      if (m.isRollover()) {
        g2.setPaint(Color.ORANGE);
      }
    }
    g2.drawLine(4,  4, 11, 11);
    g2.drawLine(4,  5, 10, 11);
    g2.drawLine(5,  4, 11, 10);
    g2.drawLine(11, 4,  4, 11);
    g2.drawLine(11, 5,  5, 11);
    g2.drawLine(10, 4,  4, 10);
    g2.dispose();
  }
  @Override public int getIconWidth() {
    return 16;
  }
  @Override public int getIconHeight() {
    return 16;
  }
}

class CloseableTabbedPaneLayerUI extends LayerUI<JTabbedPane> {
  private final JComponent rubberStamp = new JPanel();
  private final Point pt = new Point();
  private final JButton button = new JButton(new CloseTabIcon()) {
    @Override public void updateUI() {
      super.updateUI();
      setBorder(BorderFactory.createEmptyBorder());
      setFocusPainted(false);
      setBorderPainted(false);
      setContentAreaFilled(false);
      setRolloverEnabled(false);
    }
  };
  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer) {
      JLayer jlayer = (JLayer) c;
      JTabbedPane tabPane = (JTabbedPane) jlayer.getView();
      Dimension d = button.getPreferredSize();
      for (int i = 0; i < tabPane.getTabCount(); i++) {
        Rectangle rect = tabPane.getBoundsAt(i);
        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.getModel().setRollover(r.contains(pt));
        SwingUtilities.paintComponent(g, button, rubberStamp, r);
      }
    }
  }
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }
  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }
  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JTabbedPane> l) {
    if (e.getID() == MouseEvent.MOUSE_CLICKED) {
      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);
        }
      }
    }
  }
  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JTabbedPane> l) {
    pt.setLocation(e.getPoint());
    JTabbedPane t = (JTabbedPane) l.getView();
    if (t.indexAtLocation(pt.x, pt.y) >= 0) {
      Point loc = e.getPoint();
      loc.translate(-16, -16);
      l.repaint(new Rectangle(loc, new Dimension(32, 32)));
    }
  }
}

Upvotes: 1

Related Questions