概要

JTabbedPaneのタブエリアレイアウトを変更して余白や右端にタブの一覧表示ボタンなどを追加します。

スクリーンショット

Swing/TabAreaLayout.png

サンプルコード

protected CardLayoutTabbedPane() {
  super(new BorderLayout());
  setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
  setBackground(new Color(16, 16, 16));
  tabPanel.setInheritsPopupMenu(true);
  hiddenTabs.addActionListener(e -> {
    JButton b = (JButton) e.getSource();
    Point p = b.getLocation();
    JPopupMenu popup = new JPopupMenu();
    for (int i = 0; i < tabPanel.getComponentCount(); i++) {
      JMenuItem mi = makeRadioMenuItem(tabPanel, i);
      if (mi != null) {
        popup.add(mi);
      }
    }
    p.x += b.getWidth() - popup.getPreferredSize().width - 1;
    p.y += b.getHeight();
    popup.show(b.getParent(), p.x, p.y);
  });
  JPanel buttons = new JPanel(new GridBagLayout());
  buttons.add(hiddenTabs);
  JPanel header = new JPanel(new BorderLayout());
  header.add(new JLayer<>(tabArea, new HorizontalScrollLayerUI()));
  header.add(buttons, BorderLayout.EAST);
  add(header, BorderLayout.NORTH);
  add(contentsPanel);
}

private JMenuItem makeRadioMenuItem(Container c, int i) {
  JToggleButton tab = (JToggleButton) c.getComponent(i);
  JViewport viewport = tabArea.getViewport();
  Rectangle r = tab.getBounds();
  if (viewport.getViewRect().contains(r)) {
    return null;
  }
  JLabel label = (JLabel) tab.getComponent(0);
  String title = label.getText();
  JMenuItem mi = new JRadioButtonMenuItem(title);
  mi.addActionListener(e -> {
    tab.setSelected(true);
    cardLayout.show(contentsPanel, title);
    viewport.scrollRectToVisible(SwingUtilities.convertRectangle(c, r, viewport));
  });
  return mi;
}
View in GitHub: Java, Kotlin

解説

  • 上: JTabbedPane + Box + OverlayLayout
    • タブエリアの右端に余白を設定したJTabbedPaneと、右端にJButtonを配置した透明なBoxOverlayLayoutで同じサイズで重ねて表示
    • 右端に追加したJButtonをクリックするとタブ選択状態を変更可能なJPopupMenuが開く
    • JTabbedPaneのタブレイアウトポリシーがSCROLL_TAB_LAYOUTの場合、UIManager.put("TabbedPane.tabAreaInsets", new Insets(...))などで設定した余白はスクロールボタンなどで無視される仕様?ため、このサンプルのポップアップボタンと重なってしまう
      • SCROLL_TAB_LAYOUTで使用する場合はこの仕様を回避するよりBoxを不透明にして各タブを表示するJScrollPaneを追加するほうが簡単かもしれない
  • 下: JPanel + CardLayout + JScrollPane + FlowLayout + BorderLayout

参考リンク

コメント