From e8e87f1926f84d8d999333e304c332e75f141d23 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Sun, 28 Jul 2024 18:14:10 +0300 Subject: [PATCH] Separate check/radio icon from menu item icon --- .../flatlaf/ui/FlatMenuItemRenderer.java | 70 +++++++++++++++++-- .../com/formdev/flatlaf/FlatLaf.properties | 1 + 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java index 463ebe428..3484d10c7 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java @@ -35,11 +35,14 @@ import java.text.AttributedCharacterIterator; import java.util.Map; import javax.swing.Icon; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; import javax.swing.KeyStroke; +import javax.swing.MenuElement; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicHTML; @@ -73,6 +76,7 @@ * @uiDefault MenuItem.underlineSelectionCheckBackground Color * @uiDefault MenuItem.underlineSelectionColor Color * @uiDefault MenuItem.underlineSelectionHeight int + * @uiDefault MenuItem.separateIconFromCheck boolean * * @author Karl Tauber */ @@ -103,6 +107,7 @@ public class FlatMenuItemRenderer @Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" ); @Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" ); @Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" ); + @Styleable protected boolean separateIconFromCheck = UIManager.getBoolean( "MenuItem.separateIconFromCheck" ); private boolean iconsShared = true; private final Font menuFont = UIManager.getFont( "Menu.font" ); @@ -195,6 +200,7 @@ protected Dimension getPreferredMenuItemSize() { int width = 0; int height = 0; boolean isTopLevelMenu = isTopLevelMenu( menuItem ); + boolean isIconSeparated = isIconSeparated( menuItem ); Rectangle viewRect = new Rectangle( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE ); Rectangle iconRect = new Rectangle(); @@ -237,6 +243,11 @@ protected Dimension getPreferredMenuItemSize() { height = Math.max( arrowIcon.getIconHeight(), height ); } + if( isIconSeparated ) { + // gap between check and icon + width += iconRect.width + scale( menuItem.getIconTextGap() ); + } + // add insets Insets insets = menuItem.getInsets(); width += insets.left + insets.right; @@ -251,10 +262,11 @@ protected Dimension getPreferredMenuItemSize() { return new Dimension( width, height ); } - private void layout( Rectangle viewRect, Rectangle iconRect, Rectangle textRect, - Rectangle accelRect, Rectangle arrowRect, Rectangle labelRect ) + private void layout( Rectangle viewRect, Rectangle checkRect, Rectangle iconRect, + Rectangle textRect, Rectangle accelRect, Rectangle arrowRect, Rectangle labelRect ) { boolean isTopLevelMenu = isTopLevelMenu( menuItem ); + boolean isIconSeparated = isIconSeparated( menuItem ); // layout arrow boolean showArrowIcon = (arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem ))); @@ -311,6 +323,16 @@ private void layout( Rectangle viewRect, Rectangle iconRect, Rectangle textRect, menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(), menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(), labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) ); + + checkRect.setBounds( iconRect ); + + if( isIconSeparated ) { + int width = checkRect.width + scale( menuItem.getIconTextGap() ); + iconRect.x += width; + textRect.x += width; + labelRect.x += width; + textRect.width -= width; + } } private static int centerOffset( int wh1, int wh2 ) { @@ -329,15 +351,17 @@ protected void paintMenuItem( Graphics g, Color selectionBackground, Color selec viewRect.width -= (insets.left + insets.right); viewRect.height -= (insets.top + insets.bottom); + Rectangle checkRect = new Rectangle(); Rectangle iconRect = new Rectangle(); Rectangle textRect = new Rectangle(); Rectangle accelRect = new Rectangle(); Rectangle arrowRect = new Rectangle(); Rectangle labelRect = new Rectangle(); - layout( viewRect, iconRect, textRect, accelRect, arrowRect, labelRect ); + layout( viewRect, checkRect, iconRect, textRect, accelRect, arrowRect, labelRect ); /*debug + g.setColor( Color.pink ); g.drawRect( checkRect.x, checkRect.y, checkRect.width - 1, checkRect.height - 1 ); g.setColor( Color.green ); g.drawRect( viewRect.x, viewRect.y, viewRect.width - 1, viewRect.height - 1 ); g.setColor( Color.red ); g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 ); g.setColor( Color.blue ); g.drawRect( iconRect.x, iconRect.y, iconRect.width - 1, iconRect.height - 1 ); @@ -356,7 +380,19 @@ protected void paintMenuItem( Graphics g, Color selectionBackground, Color selec else paintSelection( g, selectionBackground, selectionInsets, selectionArc ); } - paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground ); + + if( isIconSeparated( menuItem ) ) { + if( menuItem instanceof JCheckBoxMenuItem ) { + paintIcon( g, checkRect, UIManager.getIcon( "CheckBox.icon" ) ); + } else if( menuItem instanceof JRadioButtonMenuItem ) { + paintIcon( g, checkRect, UIManager.getIcon( "RadioButton.icon" ) ); + } + + paintIcon( g, iconRect, getIconForPainting(false) ); + } else { + paintIcon( g, iconRect, getIconForPainting(true), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground ); + } + paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground ); paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground ); if( arrowIcon != null && (!isTopLevelMenu( menuItem ) || isInVerticalMenuBar( menuItem )) ) @@ -429,6 +465,10 @@ protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color check paintIcon( g, menuItem, icon, iconRect ); } + protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) { + paintIcon( g, menuItem, icon, iconRect ); + } + protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) { View htmlView = (View) menuItem.getClientProperty( BasicHTML.propertyKey ); if( htmlView != null ) { @@ -537,6 +577,24 @@ protected boolean isUnderlineSelection() { return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) ); } + protected boolean isIconSeparated( JMenuItem item ) { + if( !separateIconFromCheck) + return false; + + Container parent = item.getParent(); + if( !(parent instanceof MenuElement) ) + return false; + + for( MenuElement element : ((MenuElement) parent).getSubElements() ) { + Component c = element.getComponent(); + if( c instanceof JCheckBoxMenuItem && ((JCheckBoxMenuItem) c).getIcon() != null ) + return true; + if( c instanceof JRadioButtonMenuItem && ((JRadioButtonMenuItem) c).getIcon() != null ) + return true; + } + return false; + } + private Font getTopLevelFont() { Font font = menuItem.getFont(); // menu item parent may be null if JMenu.isTopLevelMenu() is overridden @@ -546,10 +604,10 @@ private Font getTopLevelFont() { : menuItem.getParent().getFont(); } - private Icon getIconForPainting() { + private Icon getIconForPainting(boolean canUseCheckIcon) { Icon icon = menuItem.getIcon(); - if( icon == null && checkIcon != null && !isTopLevelMenu( menuItem ) ) + if( icon == null && canUseCheckIcon && checkIcon != null && !isTopLevelMenu( menuItem ) ) return checkIcon; if( icon == null ) diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 1f23a13ce..9ae7e6b04 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -457,6 +457,7 @@ MenuItem.acceleratorDelimiter = "+" [mac]MenuItem.acceleratorDelimiter = "" MenuItem.selectionInsets = 0,0,0,0 MenuItem.selectionArc = 0 +MenuItem.separateIconFromCheck = false # for MenuItem.selectionType = underline MenuItem.underlineSelectionBackground = @menuHoverBackground