概要

JDK 6で導入されたタブにコンポーネントを追加する機能を使って、長いタイトルのタブは文字列をクリップして表示します。

サンプルコード

class ClippedTitleTabbedPane extends JTabbedPane {
  public ClippedTitleTabbedPane() {
    super();
  }
  public ClippedTitleTabbedPane(int tabPlacement) {
    super(tabPlacement);
  }
  private Insets getTabInsets() {
    Insets i = UIManager.getInsets("TabbedPane.tabInsets");
    if (i != null) {
      return i;
    } else {
      SynthStyle style = SynthLookAndFeel.getStyle(this, Region.TABBED_PANE_TAB);
      SynthContext context = new SynthContext(
        this, Region.TABBED_PANE_TAB, style, SynthConstants.ENABLED);
      return style.getInsets(context, null);
    }
  }
  private Insets getTabAreaInsets() {
    Insets i = UIManager.getInsets("TabbedPane.tabAreaInsets");
    if (i != null) {
      return i;
    } else {
      SynthStyle style = SynthLookAndFeel.getStyle(this, Region.TABBED_PANE_TAB_AREA);
      SynthContext context = new SynthContext(
        this, Region.TABBED_PANE_TAB_AREA, style, SynthConstants.ENABLED);
      return style.getInsets(context, null);
    }
  }
  @Override public void doLayout() {
    int tabCount  = getTabCount();
    if (tabCount == 0) return;
    Insets tabInsets   = getTabInsets();
    Insets tabAreaInsets = getTabAreaInsets();
    Insets insets = getInsets();
    int areaWidth = getWidth() - tabAreaInsets.left - tabAreaInsets.right
                    - insets.left        - insets.right;
    int tabWidth  = 0; // = tabInsets.left + tabInsets.right + 3;
    int gap     = 0;

    switch (getTabPlacement()) {
    case LEFT:
    case RIGHT:
      tabWidth = areaWidth / 4;
      gap = 0;
      break;
    case BOTTOM:
    case TOP:
    default:
      tabWidth = areaWidth / tabCount;
      gap = areaWidth - (tabWidth * tabCount);
    }
    // "3" is magic number @see BasicTabbedPaneUI#calculateTabWidth
    tabWidth = tabWidth - tabInsets.left - tabInsets.right - 3;
    for (int i = 0; i < tabCount; i++) {
      JComponent l = (JComponent) getTabComponentAt(i);
      if (l == null) break;
      l.setPreferredSize(new Dimension(tabWidth + (i < gap ? 1 : 0), l.getPreferredSize().height));
    }
    super.doLayout();
  }
  @Override public void insertTab(
      String title, Icon icon, Component component, String tip, int index) {
    super.insertTab(title, icon, component, tip == null ? title : tip, index);
    JLabel label = new JLabel(title, SwingConstants.CENTER);
    Dimension dim = label.getPreferredSize();
    Insets tabInsets = getTabInsets();
    label.setPreferredSize(new Dimension(0, dim.height + tabInsets.top + tabInsets.bottom));
    setTabComponentAt(index, label);
  }
}
view all

解説

下のJTabbedPaneでは、タブにJTabbedPane#setTabComponentAtメソッドを使ってJLabelを追加し、そのクリップ機能を利用して長いタイトル文字列の後半を省略表示しています。

JTabbedPaneのタブを等幅にしてタイトルをクリップと、ほぼ同等(文字列の長さがばらばらでも、左右にタブをおいた場合は全体の1/4の幅に、上下にタブをおいた場合はすべてのタブ幅が均等になる)ですが、TabbedPaneUI#paintTextで文字列の描画をオーバーライドする必要も無く、ソースも短くて実装が簡単です。

参考リンク

コメント

  • tabAreaInsetsを考慮するように修正し、TOP-LEFTの切り替え機能を追加しました。 -- aterai
  • SynthLookAndFeelに仮?対応。 -- aterai
  • JTabbedPane#doLayout()をオーバーライドするように変更。 -- aterai
  • SynthLookAndFeelsetForegroundAtが反映されないため困っていたため参考にさせていただきました。setForegroundAtOverrideしてgetTabComponentAt(index).setForeground(foreground)するといい感じです。 -- sawshun
  • メモ: 省略記号、Truncating3 periods(dots) ellipsisCSS3text-overflowプロパティにclip|ellipsis|string、省略記号を表示しているのでタイトルはellipsisにした方がよかったかも。 -- aterai