MenuItem: vertically align text if icons have different widths (issue #437)

This commit is contained in:
Karl Tauber
2021-12-08 11:45:17 +01:00
parent 27786ec00a
commit 023e356057
16 changed files with 152 additions and 3 deletions

View File

@@ -61,7 +61,9 @@ FlatLaf Change Log
- Slider: Support specifying width of thumb border (see UI value
`Slider.thumbBorderWidth`).
- TabbedPane: Optionally paint selected tab as card. (PR #343)
- MenuItem: Paint the selected icon when the item is selected. (PR #415)
- MenuItem:
- Paint the selected icon when the item is selected. (PR #415)
- Vertically align text if icons have different widths. (issue #437)
- Added more color functions to class `ColorFunctions` for easy use in
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
`tint()`, `shade()` and `luma()`.

View File

@@ -89,6 +89,7 @@ public class FlatCheckBoxMenuItemUI
protected void uninstallDefaults() {
super.uninstallDefaults();
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
renderer = null;
oldStyleValues = null;
}

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
@@ -32,6 +33,7 @@ import java.awt.event.KeyEvent;
import java.text.AttributedCharacterIterator;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
@@ -39,6 +41,7 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
@@ -52,6 +55,7 @@ import com.formdev.flatlaf.util.SystemInfo;
/**
* Renderer for menu items.
*
* @uiDefault MenuItem.verticallyAlignText boolean
* @uiDefault MenuItem.minimumWidth int
* @uiDefault MenuItem.minimumIconSize Dimension
* @uiDefault MenuItem.textAcceleratorGap int
@@ -67,12 +71,15 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatMenuItemRenderer
{
private static final String KEY_MAX_ICONS_WIDTH = "FlatLaf.internal.FlatMenuItemRenderer.maxIconWidth";
protected final JMenuItem menuItem;
protected Icon checkIcon;
protected Icon arrowIcon;
protected final Font acceleratorFont;
protected final String acceleratorDelimiter;
/** @since 2 */ @Styleable protected boolean verticallyAlignText = FlatUIUtils.getUIBoolean( "MenuItem.verticallyAlignText", true );
@Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
@Styleable protected Dimension minimumIconSize;
@Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
@@ -405,11 +412,10 @@ debug*/
return;
// center because the real icon may be smaller than dimension in iconRect
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
// paint
icon.paintIcon( menuItem, g, x, y );
icon.paintIcon( menuItem, g, iconRect.x, y );
}
protected static void paintText( Graphics g, JMenuItem menuItem,
@@ -570,6 +576,44 @@ debug*/
shiftGlyph = 0x21E7,
commandGlyph = 0x2318;
/**
* Calculates the maximum width of all menu item icons in the popup.
*/
private int getMaxIconsWidth() {
if( !verticallyAlignText )
return 0;
Container parent = menuItem.getParent();
if( !(parent instanceof JComponent) )
return 0;
int maxWidth = FlatClientProperties.clientPropertyInt( (JComponent) parent, KEY_MAX_ICONS_WIDTH, -1 );
if( maxWidth >= 0 )
return maxWidth;
maxWidth = 0;
for( Component c : parent.getComponents() ) {
if( !(c instanceof JMenuItem) )
continue;
Icon icon = ((JMenuItem)c).getIcon();
if( icon != null )
maxWidth = Math.max( maxWidth, icon.getIconWidth() );
}
((JComponent)parent).putClientProperty( KEY_MAX_ICONS_WIDTH, maxWidth );
return maxWidth;
}
static void clearClientProperties( Component c ) {
if( !(c instanceof JComponent) )
return;
JComponent jc = (JComponent) c;
jc.putClientProperty( FlatMenuItemRenderer.KEY_MAX_ICONS_WIDTH, null );
}
//---- class MinSizeIcon --------------------------------------------------
private class MinSizeIcon
@@ -584,6 +628,7 @@ debug*/
@Override
public int getIconWidth() {
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
iconWidth = Math.max( iconWidth, getMaxIconsWidth() );
return Math.max( iconWidth, scale( minimumIconSize.width ) );
}

View File

@@ -89,6 +89,7 @@ public class FlatMenuItemUI
protected void uninstallDefaults() {
super.uninstallDefaults();
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
renderer = null;
oldStyleValues = null;
}

View File

@@ -111,6 +111,7 @@ public class FlatMenuUI
protected void uninstallDefaults() {
super.uninstallDefaults();
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
renderer = null;
oldStyleValues = null;
}

View File

@@ -16,12 +16,18 @@
package com.formdev.flatlaf.ui;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicPopupMenuUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -64,6 +70,15 @@ public class FlatPopupMenuUI
borderShared = null;
}
@Override
public void installDefaults() {
super.installDefaults();
LayoutManager layout = popupMenu.getLayout();
if( layout == null || layout instanceof UIResource )
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
}
@Override
protected void installListeners() {
super.installListeners();
@@ -106,4 +121,21 @@ public class FlatPopupMenuUI
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
}
//---- class FlatMenuLayout -----------------------------------------------
protected static class FlatMenuLayout
extends DefaultMenuLayout
{
public FlatMenuLayout( Container target, int axis ) {
super( target, axis );
}
@Override
public Dimension preferredLayoutSize( Container target ) {
FlatMenuItemRenderer.clearClientProperties( target );
return super.preferredLayoutSize( target );
}
}
}

View File

@@ -89,6 +89,7 @@ public class FlatRadioButtonMenuItemUI
protected void uninstallDefaults() {
super.uninstallDefaults();
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
renderer = null;
oldStyleValues = null;
}

View File

@@ -430,6 +430,7 @@ MenuItem.checkIcon = null
MenuItem.margin = @menuItemMargin
MenuItem.opaque = false
MenuItem.borderPainted = true
MenuItem.verticallyAlignText = true
MenuItem.background = @menuBackground
MenuItem.checkBackground = @menuCheckBackground
MenuItem.checkMargins = 2,2,2,2

View File

@@ -346,6 +346,7 @@ public class TestFlatStyleableInfo
private void menuItemRenderer( Map<String, Class<?>> expected ) {
expectedMap( expected,
"verticallyAlignText", boolean.class,
"minimumWidth", int.class,
"minimumIconSize", Dimension.class,
"textAcceleratorGap", int.class,

View File

@@ -495,6 +495,7 @@ public class TestFlatStyling
}
private void menuItemRenderer( Consumer<String> applyStyle ) {
applyStyle.accept( "verticallyAlignText: false" );
applyStyle.accept( "minimumWidth: 10" );
applyStyle.accept( "minimumIconSize: 16,16" );
applyStyle.accept( "textAcceleratorGap: 28" );

View File

@@ -628,6 +628,7 @@ MenuItem.underlineSelectionBackground #484c4f HSL 206 5 30 com.formdev.fl
MenuItem.underlineSelectionCheckBackground #3c588b HSL 219 40 39 com.formdev.flatlaf.util.DerivedColor [UI] darken(10%)
MenuItem.underlineSelectionColor #4c87c8 HSL 211 53 54 javax.swing.plaf.ColorUIResource [UI]
MenuItem.underlineSelectionHeight 3
MenuItem.verticallyAlignText true
MenuItemUI com.formdev.flatlaf.ui.FlatMenuItemUI

View File

@@ -633,6 +633,7 @@ MenuItem.underlineSelectionBackground #e6e6e6 HSL 0 0 90 com.formdev.fl
MenuItem.underlineSelectionCheckBackground #bfd9f2 HSL 209 66 85 com.formdev.flatlaf.util.DerivedColor [UI] lighten(40%)
MenuItem.underlineSelectionColor #3c83c5 HSL 209 54 50 javax.swing.plaf.ColorUIResource [UI]
MenuItem.underlineSelectionHeight 3
MenuItem.verticallyAlignText true
MenuItemUI com.formdev.flatlaf.ui.FlatMenuItemUI

View File

@@ -638,6 +638,7 @@ MenuItem.underlineSelectionBackground #e6e6e6 HSL 0 0 90 javax.swing.pl
MenuItem.underlineSelectionCheckBackground #ccccff HSL 240 100 90 javax.swing.plaf.ColorUIResource [UI]
MenuItem.underlineSelectionColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
MenuItem.underlineSelectionHeight 3
MenuItem.verticallyAlignText true
MenuItemUI com.formdev.flatlaf.ui.FlatMenuItemUI

View File

@@ -165,6 +165,11 @@ public class FlatMenusTest
JMenu menu12 = new JMenu();
JMenuItem menuItem41 = new JMenuItem();
JMenuItem menuItem42 = new JMenuItem();
JMenuItem menuItem43 = new JMenuItem();
JMenuItem menuItem44 = new JMenuItem();
JMenuItem menuItem45 = new JMenuItem();
JMenuItem menuItem46 = new JMenuItem();
JMenuItem menuItem47 = new JMenuItem();
menuBar2 = new JMenuBar();
JMenu menu8 = new JMenu();
FlatMenusTest.LargerMenuItem menuItem13 = new FlatMenusTest.LargerMenuItem();
@@ -397,6 +402,32 @@ public class FlatMenusTest
menuItem42.setDisabledIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/disabled_icons_test/intellij-menu-paste.png")));
menuItem42.setEnabled(false);
menu12.add(menuItem42);
menu12.addSeparator();
//---- menuItem43 ----
menuItem43.setText("text");
menuItem43.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test16.png")));
menu12.add(menuItem43);
//---- menuItem44 ----
menuItem44.setText("text");
menuItem44.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test24.png")));
menu12.add(menuItem44);
//---- menuItem45 ----
menuItem45.setText("text");
menuItem45.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test32.png")));
menu12.add(menuItem45);
//---- menuItem46 ----
menuItem46.setText("text");
menuItem46.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test48.png")));
menu12.add(menuItem46);
//---- menuItem47 ----
menuItem47.setText("text");
menuItem47.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test64.png")));
menu12.add(menuItem47);
}
menuBar1.add(menu12);
}

View File

@@ -165,6 +165,34 @@ new FormModel {
"disabledIcon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/disabled_icons_test/intellij-menu-paste.png" )
"enabled": false
} )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
name: "separator6"
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem43"
"text": "text"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test16.png" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem44"
"text": "text"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test24.png" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem45"
"text": "text"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test32.png" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem46"
"text": "text"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test48.png" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem47"
"text": "text"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test64.png" )
} )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0 2 1,growx"

View File

@@ -495,6 +495,7 @@ MenuItem.underlineSelectionBackground
MenuItem.underlineSelectionCheckBackground
MenuItem.underlineSelectionColor
MenuItem.underlineSelectionHeight
MenuItem.verticallyAlignText
MenuItemUI
MenuUI
MonthViewUI