概要

JMenuItemの選択ロールオーバーを矩形ではなくラウンド矩形で描画するよう設定します。

サンプルコード

class BasicRoundMenuItemUI extends BasicMenuItemUI {
  @Override protected void paintBackground(
      Graphics g, JMenuItem menuItem, Color bgColor) {
    ButtonModel m = menuItem.getModel();
    Color oldColor = g.getColor();
    int menuWidth = menuItem.getWidth();
    int menuHeight = menuItem.getHeight();
    if (menuItem.isOpaque()) {
      if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // g2.clearRect(0, 0, menuWidth, menuHeight);
        g2.setPaint(menuItem.getBackground());
        g2.fillRect(0, 0, menuWidth, menuHeight);
        g2.setColor(bgColor);
        g2.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
        g2.dispose();
      } else {
        g.setColor(menuItem.getBackground());
        g.fillRect(0, 0, menuWidth, menuHeight);
      }
      g.setColor(oldColor);
    } else if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
      g.setColor(bgColor);
      g.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
      g.setColor(oldColor);
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • MetalLookAndFeelMotifLookAndFeel
    • WindowsLookAndFeel以外のBasicMenuItemUIを継承するMenuItemUIJMenuItemの選択ロールオーバーをラウンド矩形で描画するようBasicMenuItemUI#paintBackground(...)をオーバーライド
    • JMenu#add(String)JPopupMenu#add(String)などをオーバーライドしてこのメソッドで生成されるJMenuItemBasicRoundMenuItemUIを設定
    • JMenuItemのフチ描画はUIManager.put("MenuItem.borderPainted", Boolean.FALSE)で描画しないよう設定
    • JCheckBoxMenuItemJRadioButtonMenuItemの選択ロールオーバー描画には影響しない
    • SynthMenuItemUIを継承するNimbusLookAndFeelなどのJMenuItemには未対応
  • WindowsLookAndFeel
    • WindowsMenuItemUI.isVistaPainting()WindowsMenuItemUI.paintBackground(accessor, g, menuItem, bgColor)などがパッケージプライベートなので選択ハイライトをラウンド矩形でソフトクリップしたBufferedImage(またはVolatileImage)を生成してWindowsMenuItemUI#paintBackground(...)内で描画
class WindowsRoundMenuItemUI extends WindowsMenuItemUI {
  private BufferedImage buffer;

  @Override protected void paintBackground(
      Graphics g, JMenuItem menuItem, Color bgColor) {
    ButtonModel model = menuItem.getModel();
    if (model.isArmed() ||
        (menuItem instanceof JMenu && model.isSelected())) {
      int width = menuItem.getWidth();
      int height = menuItem.getHeight();
      if (buffer == null ||
          buffer.getWidth() != width || buffer.getHeight() != height) {
        buffer = new BufferedImage(
          width, height, BufferedImage.TYPE_INT_ARGB);
      }
      Graphics2D g2 = buffer.createGraphics();
      g2.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
      g2.fill(new RoundRectangle2D.Float(0f, 0f, width, height, 8f, 8f));
      g2.setComposite(AlphaComposite.SrcAtop);
      super.paintBackground(g2, menuItem, bgColor);
      g2.dispose();
      g.drawImage(buffer, 0, 0, menuItem);
    } else {
      super.paintBackground(g, menuItem, bgColor);
    }
  }
}

class WindowsRoundMenuItemUI2 extends WindowsMenuItemUI {
  private VolatileImage buffer;

  @Override protected void paintBackground(
      Graphics g, JMenuItem menuItem, Color bgColor) {
    ButtonModel model = menuItem.getModel();
    if (model.isArmed() ||
        (menuItem instanceof JMenu && model.isSelected())) {
      int width = menuItem.getWidth();
      int height = menuItem.getHeight();
      GraphicsConfiguration config = ((Graphics2D) g)
          .getDeviceConfiguration();
      do {
        int status = VolatileImage.IMAGE_INCOMPATIBLE;
        if (buffer != null) {
          status = buffer.validate(config);
        }
        if (status == VolatileImage.IMAGE_INCOMPATIBLE ||
            status == VolatileImage.IMAGE_RESTORED) {
          if (buffer == null ||
              buffer.getWidth() != width ||
              buffer.getHeight() != height ||
              status == VolatileImage.IMAGE_INCOMPATIBLE) {
            if (buffer != null) {
              buffer.flush();
            }
            buffer = config.createCompatibleVolatileImage(
              width, height, Transparency.TRANSLUCENT);
          }
          Graphics2D g2 = buffer.createGraphics();
          g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
          g2.setComposite(AlphaComposite.Clear);
          g2.fillRect(0, 0, width, height);
          g2.setPaintMode(); // g2.setComposite(AlphaComposite.Src);
          g2.setPaint(Color.WHITE);
          g2.fill(new RoundRectangle2D.Float(
              0f, 0f, width, height, 8f, 8f));
          g2.setComposite(AlphaComposite.SrcAtop);
          super.paintBackground(g2, menuItem, bgColor);
          g2.dispose();
        }
      } while (buffer.contentsLost());
      g.drawImage(buffer, 0, 0, menuItem);
    } else {
      super.paintBackground(g, menuItem, bgColor);
    }
  }
}

参考リンク

コメント