fixed/improved vertical position of text when scaled on HiDPI screens on Windows when running on Java 9 or later

This commit is contained in:
Karl Tauber
2020-06-22 13:45:56 +02:00
parent ea2412d3a7
commit 15a714faed
12 changed files with 413 additions and 17 deletions

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
@@ -28,6 +29,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
@@ -119,6 +121,11 @@ public class FlatEditorPaneUI
return size;
}
@Override
protected void paintSafely( Graphics g ) {
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon;
@@ -29,7 +30,9 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -124,6 +127,12 @@ public class FlatLabelUI
BasicHTML.updateRenderer( c, text );
}
@Override
public void paint( Graphics g, JComponent c ) {
View v = (View) c.getClientProperty( BasicHTML.propertyKey );
super.paint( (v != null) ? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) : g, c );
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
@@ -33,6 +34,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPasswordFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
@@ -153,7 +155,8 @@ public class FlatPasswordFieldUI
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
paintCapsLock( g );
super.paintSafely( g );
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
protected void paintCapsLock( Graphics g ) {

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JTextArea;
@@ -27,6 +28,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextAreaUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextArea}.
@@ -89,6 +91,11 @@ public class FlatTextAreaUI
FlatEditorPaneUI.propertyChange( getComponent(), e );
}
@Override
protected void paintSafely( Graphics g ) {
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();

View File

@@ -38,6 +38,7 @@ import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
@@ -146,7 +147,8 @@ public class FlatTextFieldUI
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), isIntelliJTheme );
paintPlaceholder( g, getComponent(), placeholderForeground );
super.paintSafely( g );
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
@@ -26,6 +27,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextPaneUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextPane}.
@@ -99,6 +101,11 @@ public class FlatTextPaneUI
return FlatEditorPaneUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
}
@Override
protected void paintSafely( Graphics g ) {
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();

View File

@@ -29,6 +29,7 @@ import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicToolTipUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.StringUtils;
/**
@@ -130,7 +131,7 @@ public class FlatToolTipUI
FlatUIUtils.drawString( c, g, line, leftToRight ? x : x2 - SwingUtilities.computeStringWidth( fm, line ), y );
}
} else
super.paint( g, c );
super.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), c );
}
private boolean isMultiLine( JComponent c ) {

View File

@@ -46,7 +46,6 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -449,23 +448,24 @@ public class FlatUIUtils
}
/**
* Draws the given string at the specified location using text properties
* and anti-aliasing hints from the provided component.
*
* Use this method instead of Graphics.drawString() for correct anti-aliasing.
*
* Replacement for SwingUtilities2.drawString()
* Draws the given string at the specified location.
* The provided component is used to query text properties and anti-aliasing hints.
* <p>
* Use this method instead of {@link Graphics#drawString(String, int, int)} for correct anti-aliasing.
* <p>
* Replacement for {@code SwingUtilities2.drawString()}.
* Uses {@link HiDPIUtils#drawStringWithYCorrection(JComponent, Graphics2D, String, int, int)}.
*/
public static void drawString( JComponent c, Graphics g, String text, int x, int y ) {
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, -1, x, y );
HiDPIUtils.drawStringWithYCorrection( c, (Graphics2D) g, text, x, y );
}
/**
* Draws the given string at the specified location underlining the specified
* character. The provided component is used to query text properties and
* anti-aliasing hints.
*
* Replacement for SwingUtilities2.drawStringUnderlineCharAt()
* Draws the given string at the specified location underlining the specified character.
* The provided component is used to query text properties and anti-aliasing hints.
* <p>
* Replacement for {@code SwingUtilities2.drawStringUnderlineCharAt()}.
* Uses {@link HiDPIUtils#drawStringUnderlineCharAtWithYCorrection(JComponent, Graphics2D, String, int, int, int)}.
*/
public static void drawStringUnderlineCharAt( JComponent c, Graphics g,
String text, int underlinedIndex, int x, int y )
@@ -487,7 +487,7 @@ public class FlatUIUtils
};
}
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
HiDPIUtils.drawStringUnderlineCharAtWithYCorrection( c, (Graphics2D) g, text, underlinedIndex, x, y );
}
public static boolean hasOpaqueBeenExplicitlySet( JComponent c ) {

View File

@@ -16,9 +16,11 @@
package com.formdev.flatlaf.util;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import javax.swing.JComponent;
/**
@@ -95,4 +97,116 @@ public class HiDPIUtils
private static double normalize( double value ) {
return Math.floor( value + 0.25 ) + 0.25;
}
private static Boolean useTextYCorrection;
private static boolean useTextYCorrection() {
if( useTextYCorrection == null )
useTextYCorrection = Boolean.valueOf( System.getProperty( "flatlaf.useTextYCorrection", "true" ) );
return useTextYCorrection;
}
/**
* When painting text on HiDPI screens and the JRE scales, then the text is
* painted too far down on some operating systems.
* The higher the system scale factor is, the more.
* <p>
* This methods computes a correction value for the Y position.
*/
public static float computeTextYCorrection( Graphics2D g ) {
if( !useTextYCorrection() || !SystemInfo.IS_WINDOWS || !SystemInfo.IS_JAVA_9_OR_LATER )
return 0;
AffineTransform t = g.getTransform();
double scaleY = t.getScaleY();
if( scaleY < 1.25 )
return 0;
// Text is painted at slightly different Y positions depending on scale factor
// and Y position of component.
// The exact reason is not yet known (to me), but there are several factors:
// - fractional scale factors result in fractional component Y device coordinates
// - fractional text Y device coordinates are rounded for horizontal lines of characters
// - maybe different rounding methods for drawing primitives (e.g. rectangle) and text
// - Java adds 0.5 to X/Y positions in before drawing string in BufferedTextPipe.enqueueGlyphList()
// this is not the optimal solution, but works very good in most cases
// (tested with class FlatPaintingStringTest on Windows 10 with font "Segoe UI")
if( scaleY <= 1.25 )
return -0.875f;
if( scaleY <= 1.5 )
return -0.625f;
if( scaleY <= 1.75 )
return -0.875f;
if( scaleY <= 2.0 )
return -0.75f;
if( scaleY <= 2.25 )
return -0.875f;
if( scaleY <= 3.5 )
return -0.75f;
return -0.875f;
}
/**
* Applies Y correction and draws the given string at the specified location.
* The provided component is used to query text properties and anti-aliasing hints.
* <p>
* Use this method instead of {@link Graphics#drawString(String, int, int)} for correct anti-aliasing.
* <p>
* Replacement for {@code SwingUtilities2.drawString()}.
*/
public static void drawStringWithYCorrection( JComponent c, Graphics2D g, String text, int x, int y ) {
drawStringUnderlineCharAtWithYCorrection( c, g, text, -1, x, y );
}
/**
* Applies Y correction and draws the given string at the specified location underlining the specified character.
* The provided component is used to query text properties and anti-aliasing hints.
* <p>
* Replacement for {@code SwingUtilities2.drawStringUnderlineCharAt()}.
*/
public static void drawStringUnderlineCharAtWithYCorrection( JComponent c,
Graphics2D g, String text, int underlinedIndex, int x, int y )
{
float yCorrection = computeTextYCorrection( g );
if( yCorrection != 0 ) {
g.translate( 0, yCorrection );
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
g.translate( 0, -yCorrection );
} else
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
}
/**
* Creates a graphics object and applies Y correction to string drawing methods.
* If no Y correction is necessary, the passed in graphics object is returned.
*/
public static Graphics2D createGraphicsTextYCorrection( Graphics2D g ) {
float yCorrection = computeTextYCorrection( g );
if( yCorrection == 0 )
return g;
return new Graphics2DProxy( g ) {
@Override
public void drawString( String str, int x, int y ) {
super.drawString( str, x, y + yCorrection );
}
@Override
public void drawString( String str, float x, float y ) {
super.drawString( str, x, y + yCorrection );
}
@Override
public void drawString( AttributedCharacterIterator iterator, int x, int y ) {
super.drawString( iterator, x, y + yCorrection );
}
@Override
public void drawString( AttributedCharacterIterator iterator, float x, float y ) {
super.drawString( iterator, x, y + yCorrection );
}
};
}
}