Button: fixed painting icon and text at wrong location when using HTML text, left/right vertical alignment and running in Java 19+ (issue #746)

This commit is contained in:
Karl Tauber
2023-10-14 19:16:23 +02:00
parent 67b0faa9ae
commit 460b6492cb
4 changed files with 162 additions and 1 deletions

View File

@@ -5,6 +5,8 @@ FlatLaf Change Log
#### Fixed bugs
- Button: Fixed painting icon and text at wrong location when using HTML text,
left/right vertical alignment and running in Java 19+. (issue #746)
- CheckBox and RadioButton: Fixed cut off right side when border is removed and
horizontal alignment is set to `right`. (issue #734)

View File

@@ -53,6 +53,8 @@ import javax.swing.plaf.ToolBarUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
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.FlatHelpButtonIcon;
@@ -551,9 +553,45 @@ public class FlatButtonUI
}
}
/**
* Similar to BasicButtonUI.paint(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
@Override
public void paint( Graphics g, JComponent c ) {
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
g = FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c );
AbstractButton b = (AbstractButton) c;
// layout
String clippedText = layout( b, b.getFontMetrics( b.getFont() ), b.getWidth(), b.getHeight() );
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
clearTextShiftOffset();
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
ButtonModel model = b.getModel();
if( model.isArmed() && model.isPressed() )
paintButtonPressed( g, b );
// paint icon
if( b.getIcon() != null )
paintIcon( g, b, iconR );
// paint text
if( clippedText != null && !clippedText.isEmpty() ) {
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null )
view.paint( g, textR ); // HTML text
else
paintText( g, b, textR, clippedText );
}
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
if( b.isFocusPainted() && b.hasFocus() )
paintFocus( g, b, viewR, textR, iconR );
}
@Override
@@ -786,6 +824,67 @@ public class FlatButtonUI
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
}
@Override
public int getBaseline( JComponent c, int width, int height ) {
return getBaselineImpl( c, width, height );
}
/**
* Similar to BasicButtonUI.getBaseline(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
static int getBaselineImpl( JComponent c, int width, int height ) {
if( width < 0 || height < 0 )
throw new IllegalArgumentException();
AbstractButton b = (AbstractButton) c;
String text = b.getText();
if( text == null || text.isEmpty() )
return -1;
FontMetrics fm = b.getFontMetrics( b.getFont() );
layout( b, fm, width, height );
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null ) {
// HTML text
int baseline = BasicHTML.getHTMLBaseline( view, textR.width, textR.height );
return (baseline >= 0) ? textR.y + baseline : baseline;
} else
return textR.y + fm.getAscent();
}
/**
* Similar to BasicButtonUI.layout(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
private static String layout( AbstractButton b, FontMetrics fm, int width, int height ) {
// compute view rectangle
Insets insets = b.getInsets();
viewR.setBounds( insets.left, insets.top,
width - insets.left - insets.right,
height - insets.top - insets.bottom );
// reset rectangles
textR.setBounds( 0, 0, 0, 0 );
iconR.setBounds( 0, 0, 0, 0 );
String text = b.getText();
return SwingUtilities.layoutCompoundLabel( b, fm, text, b.getIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewR, iconR, textR,
(text != null) ? b.getIconTextGap() : 0 );
}
private static Rectangle viewR = new Rectangle();
private static Rectangle textR = new Rectangle();
private static Rectangle iconR = new Rectangle();
//---- class FlatButtonListener -------------------------------------------
protected class FlatButtonListener

View File

@@ -336,6 +336,11 @@ public class FlatRadioButtonUI
: 0;
}
@Override
public int getBaseline( JComponent c, int width, int height ) {
return FlatButtonUI.getBaselineImpl( c, width, height );
}
//---- class FlatRadioButtonListener --------------------------------------
/** @since 2 */

View File

@@ -0,0 +1,55 @@
package com.formdev.flatlaf.testing.jdk;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.FlatLightLaf;
/**
* https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
public class HtmlButtonTest
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatLightLaf.setup();
JFrame frame = new JFrame( "HTML Button Test" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel panel = new JPanel( new GridBagLayout() );
panel.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
createButtons( panel, "center", SwingConstants.CENTER, SwingConstants.CENTER, null );
createButtons( panel, "left", SwingConstants.LEFT, SwingConstants.CENTER, null );
createButtons( panel, "right", SwingConstants.RIGHT, SwingConstants.CENTER, null );
createButtons( panel, "center with margin 30,4,4,4", SwingConstants.CENTER, SwingConstants.CENTER, new Insets( 30, 4, 4, 4 ) );
createButtons( panel, "left with margin 30,4,4,4", SwingConstants.LEFT, SwingConstants.CENTER, new Insets( 30, 4, 4, 4 ) );
createButtons( panel, "left/top with margin 30,4,4,4", SwingConstants.LEFT, SwingConstants.TOP, new Insets( 30, 4, 4, 4 ) );
frame.add( new JLabel( "Java version " + System.getProperty( "java.version" ) ), BorderLayout.NORTH );
frame.add( panel );
frame.pack();
frame.setVisible( true );
} );
}
private static void createButtons( JPanel panel, String text, int horizontalAlignment, int verticalAlignment, Insets margin ) {
JButton button = new JButton( text );
button.setHorizontalAlignment( horizontalAlignment );
button.setVerticalAlignment( verticalAlignment );
if( margin != null )
button.setMargin( margin );
panel.add( button, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 4, 4, 4, 4 ), 0, 0 ) );
JButton htmlButton = new JButton( "<html>HTML " + text + "</html>" );
htmlButton.setHorizontalAlignment( horizontalAlignment );
htmlButton.setVerticalAlignment( verticalAlignment );
if( margin != null )
htmlButton.setMargin( margin );
panel.add( htmlButton, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 4, 4, 24, 4 ), 0, 0 ) );
}
}