概要

NimbusLookAndFeelにおけるJTabbedPaneのタブ、タブエリアなどのスタイルを変更します。

サンプルコード

private static void configureUI() {
  UIDefaults d = UIManager.getLookAndFeelDefaults();
  d.put("TabbedPane:TabbedPaneContent.contentMargins", new Insets(0, 5, 5, 5));
  //d.put("TabbedPane:TabbedPaneTab.contentMargins", new Insets(2, 8, 3, 8));
  //d.put("TabbedPane:TabbedPaneTabArea.contentMargins", new Insets(3, 10, 4, 10));
  d.put("TabbedPane:TabbedPaneTabArea.contentMargins", new Insets(3, 10, OVERPAINT, 10));

  Painter<JComponent> tabAreaPainter = new TabAreaPainter();
  d.put("TabbedPane:TabbedPaneTabArea[Disabled].backgroundPainter", tabAreaPainter);
  d.put("TabbedPane:TabbedPaneTabArea[Enabled].backgroundPainter", tabAreaPainter);
  d.put("TabbedPane:TabbedPaneTabArea[Enabled+MouseOver].backgroundPainter", tabAreaPainter);
  d.put("TabbedPane:TabbedPaneTabArea[Enabled+Pressed].backgroundPainter", tabAreaPainter);

  d.put("TabbedPane:TabbedPaneContent.backgroundPainter", new TabContentPainter());

  Painter<JComponent> tabPainter = new TabPainter(false);
  d.put("TabbedPane:TabbedPaneTab[Enabled+MouseOver].backgroundPainter", tabPainter);
  d.put("TabbedPane:TabbedPaneTab[Enabled+Pressed].backgroundPainter", tabPainter);
  d.put("TabbedPane:TabbedPaneTab[Enabled].backgroundPainter", tabPainter);

  Painter<JComponent> selectedTabPainter = new TabPainter(true);
  d.put("TabbedPane:TabbedPaneTab[Focused+MouseOver+Selected].backgroundPainter", selectedTabPainter);
  d.put("TabbedPane:TabbedPaneTab[Focused+Pressed+Selected].backgroundPainter", selectedTabPainter);
  d.put("TabbedPane:TabbedPaneTab[Focused+Selected].backgroundPainter", selectedTabPainter);
  d.put("TabbedPane:TabbedPaneTab[MouseOver+Selected].backgroundPainter", selectedTabPainter);
  d.put("TabbedPane:TabbedPaneTab[Selected].backgroundPainter", selectedTabPainter);
  d.put("TabbedPane:TabbedPaneTab[Pressed+Selected].backgroundPainter", selectedTabPainter);
}

public static void createAndShowGUI() {
  try {
    for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
      if ("Nimbus".equals(laf.getName())) {
        UIManager.setLookAndFeel(laf.getClassName());
        configureUI();
      }
    }
  } catch (ClassNotFoundException | InstantiationException
             | IllegalAccessException | UnsupportedLookAndFeelException ex) {
    ex.printStackTrace();
  }
  JFrame frame = new JFrame("@title@");
  frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  frame.getContentPane().add(new MainPanel());
  frame.pack();
  frame.setLocationRelativeTo(null);
  frame.setVisible(true);
}

private static class TabPainter implements Painter<JComponent> {
  private final Color color;
  private final boolean selected;
  protected TabPainter(boolean selected) {
    this.selected = selected;
    this.color = selected ? Color.WHITE : Color.ORANGE;
  }

  @Override public void paint(Graphics2D g, JComponent c, int width, int height) {
    int a = selected ? OVERPAINT : 0;
    int r = 6;
    int x = 3;
    int y = 3;
    Graphics2D g2 = (Graphics2D) g.create(0, 0, width, height + a);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    int w = width - r - 1;
    int h = height + r;
    for (int i = 0; i < x; i++) {
      g2.setColor(new Color(0, 0, 0, 20));
      g2.fill(new RoundRectangle2D.Double(x - i, y - i, w + i + i, h, r, r));
    }
    g2.setColor(color);
    g2.fill(new RoundRectangle2D.Double(x, y, w, h + OVERPAINT, r, r));
    if (selected) {
      g2.setColor(Color.GREEN);
      g2.fill(new Rectangle2D.Double(0, height + STROKE_SIZE, width, OVERPAINT));
    }
    g2.dispose();
  }
}

private static class TabAreaPainter implements Painter<JComponent> {
  @Override public void paint(Graphics2D g, JComponent c, int w, int h) {
    Graphics2D g2 = (Graphics2D) g.create(0, 0, w, h);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    RoundRectangle2D r = new RoundRectangle2D.Double(
      0, h - OVERPAINT,
      w - STROKE_SIZE,
      h - STROKE_SIZE,
      ARC, ARC);

    g2.setPaint(Color.WHITE);
    g2.fill(r);
    g2.setColor(Color.RED);
    g2.setStroke(new BasicStroke(STROKE_SIZE));
    g2.draw(r);
    g2.dispose();
  }
}

private static class TabContentPainter implements Painter<JComponent> {
  @Override public void paint(Graphics2D g, JComponent c, int w, int h) {
    Graphics2D g2 = (Graphics2D) g.create(0, 0, w, h);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.translate(0, -OVERPAINT);

    RoundRectangle2D r = new RoundRectangle2D.Double(
      0, 0,
      w - STROKE_SIZE,
      h - STROKE_SIZE + OVERPAINT,
      ARC, ARC);

    g2.setPaint(Color.WHITE);
    g2.fill(r);
    g2.setColor(Color.ORANGE);
    g2.setStroke(new BasicStroke(STROKE_SIZE));
    g2.draw(r);
    g2.dispose();
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、NimbusLookAndFeelUIDefaultsにタブ、タブ領域、コンテンツ領域などを描画する独自の縁や背景色のPainterを設定するテストを行っています。例えば選択タブのColor.GREEN、タブ領域のColor.CYANをコンテンツ領域と同じColor.WHITEに変更すると選択タブとコンテンツ領域が一体になったスタイルを作成できます。

  • TabbedPaneTabArea
    • タブを配置するエリア
    • このサンプルでは下部背景をColor.CYAN、下部縁をColor.REDで描画するPainterを設定
      • 下部の領域は、タブエリア下部の高さOVERPAINTTabbedPane:TabbedPaneTabArea.contentMarginsbottomと同じに設定
      • 縁は上辺のみラウンド表示されるようにRoundRectangle2Dをクリップ
    • 一番最初に描画される
  • TabbedPaneContent
    • コンテンツを配置するエリア
    • このサンプルでは背景をColor.WHITE、縁をColor.ORANGEで描画するPainterを設定
      • 縁は下辺のみラウンド表示されるようにRoundRectangle2Dをクリップ
    • 一番最後に描画される
  • TabbedPaneTab
    • タブを描画するPainterを設定
    • 選択されたタブを描画する場合はクリップ領域の高さをOVERPAINTだけ拡大(タブエリアの下部を上書き)
      • 縁に影を設定しタブエリアの下部にはみ出す部分はColor.GREENの矩形で塗り潰している

参考リンク

コメント