JRE-210 JEditorPane may return wrong preferred size as it moves b/w monitors of different scale

(cherry picked from commit 6c3087e6bda32ae9b095e069d8bea614502f5c03)
(cherry picked from commit adb3a4be16)
This commit is contained in:
Anton Tarasov
2017-01-31 20:26:52 +03:00
committed by alexey.ushakov@jetbrains.com
parent 51212a481c
commit b257f0f49e
9 changed files with 279 additions and 0 deletions

View File

@@ -25,6 +25,7 @@
package java.awt;
import java.awt.geom.AffineTransform;
import java.applet.Applet;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
@@ -1194,7 +1195,15 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (graphicsConfig == gc) {
return false;
}
AffineTransform tx = graphicsConfig != null ? graphicsConfig.getDefaultTransform() : new AffineTransform();
AffineTransform newTx = gc != null ? gc.getDefaultTransform() : new AffineTransform();
graphicsConfig = gc;
if (tx.getScaleX() != newTx.getScaleX() ||
tx.getScaleY() != newTx.getScaleY())
{
firePropertyChange("graphicsContextScaleTransform", tx, newTx);
}
ComponentPeer peer = this.peer;
if (peer != null) {

View File

@@ -1920,9 +1920,24 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
updateCursor();
modelChanged();
}
if (propertyName.equals("graphicsContextScaleTransform")) {
// force re-layout of the document view
forwardPreferenceChangeToView(rootView);
}
BasicTextUI.this.propertyChange(evt);
}
private void forwardPreferenceChangeToView(View view) {
if (view.getViewCount() == 0) {
// propagate the change up the hierarchy of this leaf view
view.preferenceChanged(null, true, true);
return;
}
for (int i=0; i<view.getViewCount(); i++) {
forwardPreferenceChangeToView(view.getView(i));
}
}
private void dropIndexChanged() {
if (editor.getDropMode() == DropMode.USE_SELECTION) {
return;

View File

@@ -292,6 +292,15 @@ public abstract class FlowView extends BoxView {
strategy.changedUpdate(this, changes, getInsideAllocation(a));
}
/** {@inheritDoc} */
@Override
public void preferenceChanged(View child, boolean width, boolean height) {
super.preferenceChanged(child, width, height);
if (strategy instanceof TextLayoutStrategy) {
((TextLayoutStrategy) strategy).syncFRC(this);
}
}
/** {@inheritDoc} */
public void setParent(View parent) {
super.setParent(parent);

View File

@@ -24,7 +24,10 @@
*/
package javax.swing.text;
import sun.swing.SwingUtilities2;
import java.awt.*;
import java.awt.geom.AffineTransform;
/**
* A class to perform rendering of the glyphs.
@@ -221,6 +224,15 @@ class GlyphPainter1 extends GlyphView.GlyphPainter {
@SuppressWarnings("deprecation")
void sync(GlyphView v) {
if (metrics != null) {
AffineTransform frcTx = metrics.getFontRenderContext().getTransform();
AffineTransform newFrcTx = SwingUtilities2.getFontRenderContext(v.getContainer()).getTransform();
if (frcTx.getScaleX() != newFrcTx.getScaleX() ||
frcTx.getScaleY() != newFrcTx.getScaleY())
{
metrics = null;
}
}
Font f = v.getFont();
FontMetrics fm = null;
Container c = v.getContainer();

View File

@@ -31,6 +31,7 @@ import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.text.AttributedCharacterIterator;
import java.text.BreakIterator;
import java.util.HashSet;
@@ -42,6 +43,7 @@ import javax.swing.JComponent;
import javax.swing.event.DocumentEvent;
import sun.font.BidiUtils;
import sun.swing.SwingUtilities2;
/**
* A flow strategy that uses java.awt.font.LineBreakMeasurer to
@@ -289,6 +291,18 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
return pos;
}
/**
* Synchronize the strategy if the container's FRC scale changes.
*/
void syncFRC(FlowView fv) {
AffineTransform newFrcTx = SwingUtilities2.getFontRenderContext(fv.getContainer()).getTransform();
if (frcTx.getScaleX() != newFrcTx.getScaleX() ||
frcTx.getScaleY() != newFrcTx.getScaleY())
{
sync(fv);
}
}
/**
* Synchronize the strategy with its FlowView. Allows the strategy
* to update its state to account for changes in that portion of the
@@ -302,6 +316,8 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
Container container = fv.getContainer();
FontRenderContext frc = sun.swing.SwingUtilities2.
getFontRenderContext(container);
frcTx = frc.getTransform();
BreakIterator iter;
Container c = fv.getContainer();
if (c != null) {
@@ -344,6 +360,7 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
private LineBreakMeasurer measurer;
private AttributedSegment text;
private AffineTransform frcTx = new AffineTransform();
/**
* Implementation of AttributedCharacterIterator that supports

View File

@@ -1251,6 +1251,13 @@ public class SwingUtilities2 {
GraphicsConfiguration gc = c.getGraphicsConfiguration();
AffineTransform tx = (gc == null) ? null : gc.getDefaultTransform();
if (tx == null && !GraphicsEnvironment.isHeadless()) {
tx = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.getDefaultTransform();
}
Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING);
return getFRCFromCache(tx, aaHint);
}

View File

@@ -0,0 +1,10 @@
<html>
<head>
<style type="text/css">
body { font-family: Segoe UI; font-size: 12pt; }
</style>
</head>
<body>
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
</body>
</html>

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import sun.awt.AWTAccessor;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/*
* @test
* bug JRE-210
* @summary JEditorPane's font metrics to honour switching to different GC scale
* @author anton.tarasov
* @requires (os.family == "windows")
* @run main JEditorPaneGCSwitchTest
*/
public class JEditorPaneGCSwitchTest {
static JEditorPane editorPane;
static JFrame frame;
volatile static CountDownLatch latch;
final static Map<Float, Dimension> scale2size = new HashMap<>(2);
static void initGUI() {
editorPane = new JEditorPane() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
};
try {
// This HTML text has different bounds when put into GC's with different scales
editorPane.setPage(JEditorPaneGCSwitchTest.class.getResource("JEditorPaneGCSwitchTest.html"));
} catch (IOException e) {
throw new RuntimeException(e);
}
editorPane.addPropertyChangeListener("page", (e) -> {
if (frame != null) frame.dispose();
frame = new JFrame("frame");
frame.add(editorPane);
frame.pack();
frame.setVisible(true);
latch.countDown();
});
}
static void testSize(final float scale) {
// Emulate showing on a device with the provided scale
AWTAccessor.getComponentAccessor().setGraphicsConfiguration(editorPane, new MyGraphicsConfiguration(scale));
EventQueue.invokeLater(() -> {
Dimension d = editorPane.getPreferredSize();
System.out.println(scale + " : " + d);
scale2size.put(scale, d);
latch.countDown();
});
}
static void runSync(Runnable r) {
try {
latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> r.run());
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
/*
* 1. Recreate the editor b/w device switch
*/
runSync(() -> initGUI());
runSync(() -> testSize(1f));
runSync(() -> initGUI());
runSync(() -> testSize(2f));
if (scale2size.get(1f).equals(scale2size.get(2f))) {
throw new RuntimeException("Test FAILED: [1] expected different editor size per scale!");
}
/*
* 2. Keep the editor shown b/w device switch
*/
scale2size.clear();
runSync(() -> initGUI());
runSync(() -> testSize(1f));
runSync(() -> testSize(2f));
frame.dispose();
if (scale2size.get(1f).equals(scale2size.get(2f))) {
throw new RuntimeException("Test FAILED: [2] expected different editor size per scale!");
}
System.out.println("Test PASSED");
}
static class MyGraphicsConfiguration extends GraphicsConfiguration {
GraphicsConfiguration delegate;
float scale;
public MyGraphicsConfiguration(float scale) {
this.delegate = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
this.scale = scale;
}
@Override
public GraphicsDevice getDevice() {
return delegate.getDevice();
}
@Override
public ColorModel getColorModel() {
return delegate.getColorModel();
}
@Override
public ColorModel getColorModel(int transparency) {
return delegate.getColorModel(transparency);
}
@Override
public AffineTransform getDefaultTransform() {
return AffineTransform.getScaleInstance(scale, scale);
}
@Override
public AffineTransform getNormalizingTransform() {
return delegate.getNormalizingTransform();
}
@Override
public Rectangle getBounds() {
return delegate.getBounds();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import sun.awt.AWTAccessor;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/*
* @test
* bug JRE-210
* @summary JEditorPane's font metrics to honour switching to different GC scale
* @author anton.tarasov
* @requires (os.family == "windows")
* @run main/othervm -Dsun.font.layoutengine=icu -Di18n=true JEditorPaneGCSwitchTest_i18n
*/
// -Dsun.font.layoutengine=icu is used while Harfbuzz crashes with i18n
public class JEditorPaneGCSwitchTest_i18n extends JPanel {
public static void main(String[] args) throws InterruptedException {
JEditorPaneGCSwitchTest.main(null);
}
}