8178025: HiDPI with non-integer scale factor - SPANs in HTML are rendered overlapping each other
Reviewed-by: serb, ssadetsky
--- a/src/java.desktop/share/classes/java/awt/Component.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/java/awt/Component.java Thu Nov 16 12:24:02 2017 +0530
@@ -1135,9 +1135,18 @@
if (graphicsConfig == gc) {
return false;
}
-
+ GraphicsConfiguration oldConfig = graphicsConfig;
graphicsConfig = gc;
+ /*
+ * If component is moved from one screen to another sceeen
+ * graphicsConfiguration property is fired to enable the component
+ * to recalculate any rendering data, if needed
+ */
+ if (oldConfig != null && gc != null) {
+ firePropertyChange("graphicsConfiguration", oldConfig, gc);
+ }
+
ComponentPeer peer = this.peer;
if (peer != null) {
return peer.updateGraphicsData(gc);
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java Thu Nov 16 12:24:02 2017 +0530
@@ -75,7 +75,8 @@
checkOpacity((AbstractButton) e.getSource() );
}
else if(prop == AbstractButton.TEXT_CHANGED_PROPERTY ||
- "font" == prop || "foreground" == prop) {
+ "font" == prop || "foreground" == prop ||
+ "ancestor" == prop || "graphicsConfiguration" == prop) {
AbstractButton b = (AbstractButton) e.getSource();
BasicHTML.updateRenderer(b, b.getText());
}
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Thu Nov 16 12:24:02 2017 +0530
@@ -472,7 +472,8 @@
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
- if (name == "text" || "font" == name || "foreground" == name) {
+ if (name == "text" || "font" == name || "foreground" == name ||
+ "ancestor" == name || "graphicsConfiguration" == name) {
// remove the old html view client property if one
// existed, and install a new one if the text installed
// into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Thu Nov 16 12:24:02 2017 +0530
@@ -1106,7 +1106,8 @@
name == "accelerator") {
updateAcceleratorBinding();
} else if (name == "text" || "font" == name ||
- "foreground" == name) {
+ "foreground" == name ||
+ "ancestor" == name || "graphicsConfiguration" == name) {
// remove the old html view client property if one
// existed, and install a new one if the text installed
// into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolTipUI.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolTipUI.java Thu Nov 16 12:24:02 2017 +0530
@@ -261,7 +261,8 @@
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if (name.equals("tiptext") || "font".equals(name) ||
- "foreground".equals(name)) {
+ "foreground".equals(name) ||
+ "ancestor" == name || "graphicsConfiguration" == name) {
// remove the old html view client property if one
// existed, and install a new one if the text installed
// into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolTipUI.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolTipUI.java Thu Nov 16 12:24:02 2017 +0530
@@ -227,7 +227,8 @@
}
String name = e.getPropertyName();
if (name.equals("tiptext") || "font".equals(name) ||
- "foreground".equals(name)) {
+ "foreground".equals(name) ||
+ "ancestor" == name || "graphicsConfiguration" == name) {
// remove the old html view client property if one
// existed, and install a new one if the text installed
// into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java Thu Nov 16 12:24:02 2017 +0530
@@ -59,7 +59,8 @@
sync(v);
Segment text = v.getText(p0, p1);
int[] justificationData = getJustificationData(v);
- int width = Utilities.getTabbedTextWidth(v, text, metrics, (int) x, e, p0,
+
+ float width = Utilities.getTabbedTextWidth(v, text, metrics, x, e, p0,
justificationData);
SegmentCache.releaseSharedSegment(text);
return width;
@@ -222,10 +223,15 @@
@SuppressWarnings("deprecation")
void sync(GlyphView v) {
Font f = v.getFont();
- if ((metrics == null) || (! f.equals(metrics.getFont()))) {
+ FontMetrics fm = null;
+ Container c = v.getContainer();
+ if (c != null) {
+ fm = c.getFontMetrics(f);
+ }
+ if ((metrics == null) || (! f.equals(metrics.getFont()))
+ || (! metrics.equals(fm))) {
// fetch a new FontMetrics
- Container c = v.getContainer();
- metrics = (c != null) ? c.getFontMetrics(f) :
+ metrics = (c != null) ? fm :
Toolkit.getDefaultToolkit().getFontMetrics(f);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java Thu Nov 16 12:24:02 2017 +0530
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+/*
+ * @test
+ * @bug 8178025
+ * @summary Verifies if graphicsConfiguration property notification is sent
+ * when frame is moved from one screen to another in multiscreen
+ * environment.
+ * @run main TestMultiScreenGConfigNotify
+ */
+
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+public class TestMultiScreenGConfigNotify {
+
+ static JFrame f;
+ static String propName[];
+ static int propCount = 0;
+ static boolean result = false;
+ public static void main(String[] args) throws Exception {
+
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] gds = ge.getScreenDevices();
+ if (gds.length < 2) {
+ return;
+ }
+ GraphicsConfiguration gc = gds[0].getDefaultConfiguration();
+ GraphicsConfiguration gc2 = gds[1].getDefaultConfiguration();
+ propName = new String[10];
+ SwingUtilities.invokeAndWait(() -> {
+ f = new JFrame();
+ f.setSize(300, 300);
+ f.setBounds(gc.getBounds().x, gc.getBounds().y, f.getWidth(), f.getHeight());
+ f.setVisible(true);
+
+ f.addPropertyChangeListener((PropertyChangeEvent evt) -> {
+ String name = evt.getPropertyName();
+ System.out.println("propertyChange " + name);
+ propName[propCount] = name;
+ propCount++;
+ });
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException ex) {
+ }
+ f.setBounds(gc2.getBounds().x, gc2.getBounds().y,
+ f.getWidth(), f.getHeight());
+ });
+
+ Thread.sleep(1000);
+ for(int i = 0; i < propCount; i++) {
+ if (propName[i].equals("graphicsConfiguration")) {
+ result = true;
+ }
+ }
+ SwingUtilities.invokeAndWait(() -> f.dispose());
+ if(!result) {
+ throw new RuntimeException("graphicsConfiguration notification not sent");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/JLabel/GetSpanHiDpiBug.java Thu Nov 16 12:24:02 2017 +0530
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+/*
+ * @test
+ * @bug 8178025
+ * @summary Verifies if SPANs in HTML text are rendered properly in hidpi
+ * @run main/manual/othervm -Dsun.java2d.uiScale=2.25 GetSpanHiDpiBug
+ */
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.beans.PropertyChangeSupport;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+
+public class GetSpanHiDpiBug {
+ public static void main(String[] args) throws Exception {
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ SpanTest test = new SpanTest(latch);
+ Thread T1 = new Thread(test);
+ T1.start();
+
+ // wait for latch to complete
+ boolean ret = false;
+ try {
+ ret = latch.await(3000, TimeUnit.SECONDS);
+ } catch (InterruptedException ie) {
+ throw ie;
+ }
+ if (!ret) {
+ test.dispose();
+ throw new RuntimeException(" User has not executed the test");
+ }
+ if (test.testResult == false) {
+ throw new RuntimeException("Some characters overlap");
+ }
+ }
+}
+
+class SpanTest implements Runnable {
+ static JFrame f;
+ static JDialog dialog;
+ public boolean testResult = false;
+ private final CountDownLatch latch;
+
+ public SpanTest(CountDownLatch latch) throws Exception {
+ this.latch = latch;
+ }
+
+ @Override
+ public void run() {
+ try {
+ createUI();
+ spanTest();
+ } catch (Exception ex) {
+ dispose();
+ latch.countDown();
+ throw new RuntimeException("createUI Failed: " + ex.getMessage());
+ }
+ }
+
+ public void dispose() {
+ if (dialog != null) {
+ dialog.dispose();
+ }
+ if (f != null) {
+ f.dispose();
+ }
+ }
+
+ private static void spanTest() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ JLabel label =
+ new JLabel("<html><span>A few words to get started "
+ + "before the bug</span><span>overlapping text</span></html>");
+ f = new JFrame("");
+ f.getContentPane().add(label, BorderLayout.CENTER);
+ f.setSize(500,500);
+ f.setVisible(true);
+ }
+ });
+ }
+
+ private final void createUI() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ String description
+ = " INSTRUCTIONS:\n"
+ + " A string will be shown.\n "
+ + " Press Pass if there is no overlap of characters\n"
+ + " else press Fail.";
+
+ dialog = new JDialog();
+ dialog.setTitle("textselectionTest");
+ JTextArea textArea = new JTextArea(description);
+ textArea.setEditable(false);
+ final JButton passButton = new JButton("PASS");
+ passButton.addActionListener((e) -> {
+ testResult = true;
+ dispose();
+ latch.countDown();
+ });
+ final JButton failButton = new JButton("FAIL");
+ failButton.addActionListener((e) -> {
+ testResult = false;
+ dispose();
+ latch.countDown();
+ });
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.add(textArea, BorderLayout.CENTER);
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+ buttonPanel.add(passButton);
+ buttonPanel.add(failButton);
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+ dialog.add(mainPanel);
+ dialog.pack();
+ dialog.setVisible(true);
+ }
+ });
+ }
+}