On Fri, 5 Jun 2026 03:21:46 GMT, Prasanta Sadhukhan <[email protected]> 
wrote:

>> [JDK-8348760](https://bugs.openjdk.org/browse/JDK-8348760) fixed an issue in 
>> Windows L&F JMenuItem layout whereby radio bullet/checkmark was rendered in 
>> different columnspace than menuitem imageicon so radiobullet/checkmark is 
>> rendered in first column and imageicon is rendered in 2nd column but this 
>> rendering of imageicon in 2nd columnspace was done invariably for all 
>> JMenuItem irrespective of if it is JRadioButtonMenuItem or JCheckBoxMenuItem 
>> or JMenuItem, which is wrong.
>> 
>> Normal JMenuItem (which are not JRadioButtonMenuItem or JCheckBoxMenuItem) 
>> imageicon rendering should be done in first columnspace as was done before 
>> JDK-8348760 fix because there is no radiobullet/checkmark to render for 
>> those menuitems so no need to reserve columnspace for those bullet/checkmark 
>> icon
>> 
>> Before fix
>> 
>> <img width="205" height="127" alt="image" 
>> src="https://github.com/user-attachments/assets/13a1e352-5e8d-4251-b7a7-032935eab74e";
>>  />
>> 
>> 
>> After fix
>> 
>> <img width="195" height="131" alt="image" 
>> src="https://github.com/user-attachments/assets/84ec3ee6-2823-4bf7-840d-b53f2e9d44c3";
>>  />
>> 
>> ---------
>> - [x] I confirm that I make this contribution in accordance with the 
>> [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai).
>
> Prasanta Sadhukhan has updated the pull request incrementally with one 
> additional commit since the last revision:
> 
>   Test applicable for all L&F

Do we support placing `JCheckBoxMenuItem` and `JRadioButtonMenuItem` directly 
to menu bar? Windows doesn't allow doing it, but Swing allows, so we should 
ensure the layout works correctly when such menu items are present in the menu 
bar.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 182:

> 180:                 if (afterCheckIconGapObject instanceof Integer) {
> 181:                     afterCheckIconGap = (Integer) 
> afterCheckIconGapObject;
> 182:                 }

Suggestion:

                int afterCheckIconGap = UIManager.getInt(getPropertyPrefix() + 
".afterCheckIconGap");

[`UIManager.getInt`](https://docs.oracle.com/en/java/javase/26/docs/api/java.desktop/javax/swing/UIManager.html#getInt(java.lang.Object))
 performs exactly this logic.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 194:

> 192:         boolean checkBulletPresent;
> 193:         boolean iconPresent;
> 194:         JMenu menu;

`menu` is never used.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 249:

> 247:             }
> 248:         }
> 249:         return null;

Why top-level menu? This property should apply to a popup menu only. Each popup 
should display its own children based on whether there is at least one 
`JCheckBoxMenuItem` or `JRadioButtonMenuItem` that also have an icon.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 252:

> 250:     }
> 251:     static final String CHECK_BULLET_AND_ICON_PRESENT =
> 252:             "WindowsMenuItemUI.checkBulletAndIconPresent";

Suggestion:

    }

    static final String CHECK_BULLET_AND_ICON_PRESENT =
            "WindowsMenuItemUI.checkBulletAndIconPresent";

A blank line between methods and fields makes it easier to quickly parse the 
source code.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 290:

> 288:         if (c instanceof JMenu menu && menu.isTopLevelMenu()) {
> 289:             MenuScanResult scan = new MenuScanResult();
> 290:             scan.scanMenuForCheckBulletAndIcon(menu);

So, you can pass `menu` to the `MenuScanResult` constructor which, in its turn, 
should call `scanMenuForCheckBulletAndIcon(menu)` to initialise the two fields 
of the `MenuScanResult` object.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 296:

> 294:                     Boolean.valueOf(scan.checkBulletAndIconPresent()));
> 295:         }
> 296:         if (mi instanceof JRadioButtonMenuItem || mi instanceof 
> JCheckBoxMenuItem || mi instanceof JMenuItem) {

This condition is always `true`: the local variable `mi` is of type `JMenuItem`.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
 line 300:

> 298:                 lh.allocateIconTextGap(textGap);
> 299:             }
> 300:         }

What if the menu is modified and a menu item that triggers two-column check + 
icon rendering is added or removed?

I still think the logic for scanning menu items in a popup belongs in 
`MenuItemLayoutHelper`… but it could be unfeasible to pull it there.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
 line 33:

> 31: import java.awt.Insets;
> 32: import java.awt.Window;
> 33: import javax.swing.*;

Can we avoid using wildcard imports? There are quite a few single class imports 
from the `javax.swing` package below.

Expanding the wildcard adds just four menu-related imports.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
 line 216:

> 214: 
> 215:         if (size != null) {
> 216:             size = new Dimension(size);

Is creating new copy necessary?

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
 line 223:

> 221:                 if (afterCheckIconGapObject instanceof Integer) {
> 222:                     afterCheckIconGap = (Integer) 
> afterCheckIconGapObject;
> 223:                 }

Use `UIManager.getInt`.

src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
 line 233:

> 231: 
> 232:     private static boolean hasCheckBulletAndIconPresent(JPopupMenu 
> popupMenu) {
> 233:         for (Component child : popupMenu.getComponents()) {

This method looks very similar to what you have in 
`WindowsMenuItemUI.scanMenuComponent`, is it possible to re-use the logic?

test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java line 31:

> 29:  *          JRadioButtonMenuItem and JCheckboxMenuItem
> 30:  *          is rendered with ImageIcon in WindowsLookAndFeel
> 31:  * @requires (os.family == "windows")

Why are we removing the restriction to Windows?

test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java line 104:

> 102:         If bullet and checkmark is shown in "Menu"
> 103:         and "MenuItem2" image icon location and
> 104:         and "Menu2" rendering is as decribed, test passes else fails.""";

Suggestion:

        and "Menu2" rendering is as described, test passes else fails.""";

test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java line 107:

> 105: 
> 106:     public static void main(String[] args) throws Exception {
> 107:         if ((UIManager.getLookAndFeel().getID()).equals("Aqua")) {

Is there a reason why `getLookAndFeel().getID()` is used here instead of 
`getLookAndFeel().getName()` that's used below?

test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java line 112:

> 110: 
> 111:         String INSTRUCTIONS = null;
> 112:         int colWidth = 0;

The initialisers are redundant—the `if` statement always initialises both 
`INSTRUCTIONS` and `colWidth`.

I'd prefer lower-case `instructions` here—it's a regular local variable.

test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java line 117:

> 115:             INSTRUCTIONS = INSTRUCTIONS2 + INSTRUCTIONS3 + INSTRUCTIONS4;
> 116:             colWidth = 75;
> 117:         } else {

Since this test was specifically targeted to Windows Look-and-Feel, we should 
provide an easy way to run it in Windows L&F instead of the default (Metal) L&F.

-------------

PR Review: https://git.openjdk.org/jdk/pull/29730#pullrequestreview-4438475583
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364461573
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364558891
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364502013
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364507360
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364611997
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364599172
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364526548
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364652264
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364532740
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364534203
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364544868
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364666316
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364703491
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364670687
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364636919
PR Review Comment: https://git.openjdk.org/jdk/pull/29730#discussion_r3364676444

Reply via email to