8142966: Wrong cursor position in text components on HiDPI display
authoralexsch
Tue, 05 Jul 2016 09:36:41 +0300
changeset 39554 12687019f2b9
parent 39553 965a62655c4c
child 39555 5cf973a23925
8142966: Wrong cursor position in text components on HiDPI display Reviewed-by: prr, serb
jdk/src/java.desktop/share/classes/javax/swing/text/PlainView.java
jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java
jdk/test/javax/swing/text/Utilities/8142966/SwingFontMetricsTest.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();
--- 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<Object, FontRenderContext> cache = (Map<Object, FontRenderContext>)
                 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
--- /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);
+    }
+}