Swing/RoundedSelectionMenuItem の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/RoundedSelectionMenuItem へ行く。
- Swing/RoundedSelectionMenuItem の差分を削除
--- category: swing folder: RoundedSelectionMenuItem title: JMenuItemの選択ロールオーバーをラウンド矩形で描画する tags: [JMenuItem, JPopupMenu, JMenu] author: aterai pubdate: 2024-02-05T00:45:47+09:00 description: JMenuItemの選択ロールオーバーを矩形ではなくラウンド矩形で描画するよう設定します。 image: https://drive.google.com/uc?id=1FdiqbmBLtgx7TvBg4XokxUJq6BN24u6C hreflang: href: https://java-swing-tips.blogspot.com/2024/02/paint-jmenuitem-selection-rollover-with.html lang: en --- * 概要 [#summary] `JMenuItem`の選択ロールオーバーを矩形ではなくラウンド矩形で描画するよう設定します。 #download(https://drive.google.com/uc?id=1FdiqbmBLtgx7TvBg4XokxUJq6BN24u6C) * サンプルコード [#sourcecode] #code(link){{ 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); } } } }} * 解説 [#explanation] - `MetalLookAndFeel`、`MotifLookAndFeel` -- `WindowsLookAndFeel`以外の`BasicMenuItemUI`を継承する`MenuItemUI`で`JMenuItem`の選択ロールオーバーをラウンド矩形で描画するよう`BasicMenuItemUI#paintBackground(...)`をオーバーライド -- `JMenu#add(String)`、`JPopupMenu#add(String)`などをオーバーライドしてこのメソッドで生成される`JMenuItem`に`BasicRoundMenuItemUI`を設定 -- `JMenuItem`のフチ描画は`UIManager.put("MenuItem.borderPainted", Boolean.FALSE)`で描画しないよう設定 -- `JCheckBoxMenuItem`や`JRadioButtonMenuItem`の選択ロールオーバー描画には影響しない -- `SynthMenuItemUI`を継承する`NimbusLookAndFeel`などの`JMenuItem`には未対応 - `WindowsLookAndFeel` -- `WindowsMenuItemUI.isVistaPainting()`や`WindowsMenuItemUI.paintBackground(accessor, g, menuItem, bgColor)`などがパッケージプライベートなので選択ハイライトをラウンド矩形でソフトクリップした`BufferedImage`(または`VolatileImage`)を生成して`WindowsMenuItemUI#paintBackground(...)`内で描画 --- [[Windowの縁をソフトクリッピングでなめらかにする>Swing/SoftClippedWindow]] #code{{ 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); } } } }} * 参考リンク [#reference] - [[JPopupMenuの角を丸める>Swing/RoundedCornerPopupMenu]] -- `JPopupMenu`の角丸はリンク先と同一の`RoundedPopupMenuUI.java`を使用している - [[Windowの縁をソフトクリッピングでなめらかにする>Swing/SoftClippedWindow]] * コメント [#comment] #comment #comment