# HG changeset patch # User alexsch # Date 1467700601 -10800 # Node ID 12687019f2b96c81ca5ea71860db0af6f216f583 # Parent 965a62655c4c18bb3fb62165bdf067381319e99c 8142966: Wrong cursor position in text components on HiDPI display Reviewed-by: prr, serb diff -r 965a62655c4c -r 12687019f2b9 jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java --- a/jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java Tue Jul 05 09:26:34 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java Tue Jul 05 09:36:41 2016 +0300 @@ -27,6 +27,7 @@ import java.util.Vector; import java.util.Properties; import java.awt.*; +import java.util.Objects; import javax.swing.event.*; /** @@ -281,7 +282,8 @@ protected void updateMetrics() { Component host = getContainer(); Font f = host.getFont(); - if (font != f) { + FontMetrics fm = (font == null) ? null : host.getFontMetrics(font); + if (font != f || !Objects.equals(metrics, fm)) { // The font changed, we need to recalculate the // longest line. calculateLongestLine(); diff -r 965a62655c4c -r 12687019f2b9 jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java --- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Tue Jul 05 09:26:34 2016 +0300 +++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Tue Jul 05 09:36:41 2016 +0300 @@ -30,6 +30,7 @@ import static java.awt.RenderingHints.*; import java.awt.event.*; import java.awt.font.*; +import java.awt.geom.AffineTransform; import java.awt.print.PrinterGraphics; import java.text.BreakIterator; import java.text.CharacterIterator; @@ -1060,17 +1061,23 @@ */ private static FontRenderContext getFRCProperty(JComponent c) { if (c != null) { + + GraphicsConfiguration gc = c.getGraphicsConfiguration(); + AffineTransform tx = (gc == null) ? null : gc.getDefaultTransform(); Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING); - if (aaHint != null) { - return getFRCFromCache(aaHint); - } + return getFRCFromCache(tx, aaHint); } return null; } private static final Object APP_CONTEXT_FRC_CACHE_KEY = new Object(); - private static FontRenderContext getFRCFromCache(Object aaHint) { + private static FontRenderContext getFRCFromCache(AffineTransform tx, + Object aaHint) { + if (tx == null && aaHint == null) { + return null; + } + @SuppressWarnings("unchecked") Map cache = (Map) AppContext.getAppContext().get(APP_CONTEXT_FRC_CACHE_KEY); @@ -1080,15 +1087,45 @@ AppContext.getAppContext().put(APP_CONTEXT_FRC_CACHE_KEY, cache); } - FontRenderContext frc = cache.get(aaHint); + Object key = (tx == null) + ? aaHint + : (aaHint == null ? tx : new KeyPair(tx, aaHint)); + + FontRenderContext frc = cache.get(key); if (frc == null) { - frc = new FontRenderContext(null, aaHint, - VALUE_FRACTIONALMETRICS_DEFAULT); - cache.put(aaHint, frc); + aaHint = (aaHint == null) ? VALUE_ANTIALIAS_OFF : aaHint; + frc = new FontRenderContext(tx, aaHint, + VALUE_FRACTIONALMETRICS_DEFAULT); + cache.put(key, frc); } return frc; } + private static class KeyPair { + + private final Object key1; + private final Object key2; + + public KeyPair(Object key1, Object key2) { + this.key1 = key1; + this.key2 = key2; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof KeyPair)) { + return false; + } + KeyPair that = (KeyPair) obj; + return this.key1.equals(that.key1) && this.key2.equals(that.key2); + } + + @Override + public int hashCode() { + return key1.hashCode() + 37 * key2.hashCode(); + } + } + /* * returns true if the Graphics is print Graphics * false otherwise diff -r 965a62655c4c -r 12687019f2b9 jdk/test/javax/swing/text/Utilities/8142966/SwingFontMetricsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/text/Utilities/8142966/SwingFontMetricsTest.java Tue Jul 05 09:36:41 2016 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.awt.Font; +import java.awt.Graphics; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; + +/** + * @test + * @bug 8142966 + * @summary Wrong cursor position in text components on HiDPI display + * @run main/othervm -Dsun.java2d.uiScale=2 SwingFontMetricsTest + */ +public class SwingFontMetricsTest { + + private static final String LOWER_CASE_TEXT = "the quick brown fox jumps over the lazy dog"; + private static final String UPPER_CASE_TEXT = LOWER_CASE_TEXT.toUpperCase(); + private static final String TEXT = LOWER_CASE_TEXT + UPPER_CASE_TEXT; + private static boolean passed = false; + private static CountDownLatch latch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(SwingFontMetricsTest::createAndShowGUI); + latch.await(5, TimeUnit.SECONDS); + + if (!passed) { + throw new RuntimeException("Test Failed!"); + } + } + + private static void createAndShowGUI() { + final JFrame frame = new JFrame(); + frame.setSize(300, 300); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel label = new JLabel(TEXT) { + @Override + public void paint(Graphics g) { + super.paint(g); + Font font = getFont(); + int width1 = getFontMetrics(font).stringWidth(TEXT); + int width2 = g.getFontMetrics(font).stringWidth(TEXT); + passed = (width1 == width2); + latch.countDown(); + frame.dispose(); + } + }; + frame.add(label); + frame.setVisible(true); + } +}