8165594: Bad rendering of Swing UI controls with Windows Classic L&F on HiDPI display
Reviewed-by: serb, ssadetsky
This commit is contained in:
parent
30661bb269
commit
7fa5f53815
@ -36,6 +36,7 @@ import static com.sun.java.swing.plaf.windows.TMSchema.*;
|
||||
import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
|
||||
|
||||
import sun.swing.MenuItemCheckIconFactory;
|
||||
import sun.swing.SwingUtilities2;
|
||||
|
||||
/**
|
||||
* Factory object that can vend Icons appropriate for the Windows {@literal L & F}.
|
||||
@ -400,15 +401,24 @@ public class WindowsIconFactory implements Serializable
|
||||
|
||||
// paint check
|
||||
if (model.isSelected()) {
|
||||
g.drawLine(x+9, y+3, x+9, y+3);
|
||||
g.drawLine(x+8, y+4, x+9, y+4);
|
||||
g.drawLine(x+7, y+5, x+9, y+5);
|
||||
g.drawLine(x+6, y+6, x+8, y+6);
|
||||
g.drawLine(x+3, y+7, x+7, y+7);
|
||||
g.drawLine(x+4, y+8, x+6, y+8);
|
||||
g.drawLine(x+5, y+9, x+5, y+9);
|
||||
g.drawLine(x+3, y+5, x+3, y+5);
|
||||
g.drawLine(x+3, y+6, x+4, y+6);
|
||||
if (SwingUtilities2.isScaledGraphics(g)) {
|
||||
int[] xPoints = {3, 5, 9, 9, 5, 3};
|
||||
int[] yPoints = {5, 7, 3, 5, 9, 7};
|
||||
g.translate(x, y);
|
||||
g.fillPolygon(xPoints, yPoints, 6);
|
||||
g.drawPolygon(xPoints, yPoints, 6);
|
||||
g.translate(-x, -y);
|
||||
} else {
|
||||
g.drawLine(x + 9, y + 3, x + 9, y + 3);
|
||||
g.drawLine(x + 8, y + 4, x + 9, y + 4);
|
||||
g.drawLine(x + 7, y + 5, x + 9, y + 5);
|
||||
g.drawLine(x + 6, y + 6, x + 8, y + 6);
|
||||
g.drawLine(x + 3, y + 7, x + 7, y + 7);
|
||||
g.drawLine(x + 4, y + 8, x + 6, y + 8);
|
||||
g.drawLine(x + 5, y + 9, x + 5, y + 9);
|
||||
g.drawLine(x + 3, y + 5, x + 3, y + 5);
|
||||
g.drawLine(x + 3, y + 6, x + 4, y + 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -475,54 +485,94 @@ public class WindowsIconFactory implements Serializable
|
||||
g.fillRect(x+2, y+2, 8, 8);
|
||||
|
||||
|
||||
boolean isScaledGraphics = SwingUtilities2.isScaledGraphics(g);
|
||||
|
||||
if (isScaledGraphics) {
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
Stroke oldStroke = g2d.getStroke();
|
||||
g2d.setStroke(new BasicStroke(1.03f, BasicStroke.CAP_ROUND,
|
||||
BasicStroke.JOIN_ROUND));
|
||||
Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// outter left arc
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
g.drawLine(x+4, y+0, x+7, y+0);
|
||||
g.drawLine(x+2, y+1, x+3, y+1);
|
||||
g.drawLine(x+8, y+1, x+9, y+1);
|
||||
g.drawLine(x+1, y+2, x+1, y+3);
|
||||
g.drawLine(x+0, y+4, x+0, y+7);
|
||||
g.drawLine(x+1, y+8, x+1, y+9);
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
g.drawArc(x, y, 11, 11, 45, 180);
|
||||
// outter right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.highlight"));
|
||||
g.drawArc(x, y, 11, 11, 45, -180);
|
||||
// inner left arc
|
||||
g.setColor(UIManager.getColor("RadioButton.darkShadow"));
|
||||
g.drawArc(x + 1, y + 1, 9, 9, 45, 180);
|
||||
// inner right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.light"));
|
||||
g.drawArc(x + 1, y + 1, 9, 9, 45, -180);
|
||||
|
||||
// outter right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.highlight"));
|
||||
g.drawLine(x+2, y+10, x+3, y+10);
|
||||
g.drawLine(x+4, y+11, x+7, y+11);
|
||||
g.drawLine(x+8, y+10, x+9, y+10);
|
||||
g.drawLine(x+10, y+9, x+10, y+8);
|
||||
g.drawLine(x+11, y+7, x+11, y+4);
|
||||
g.drawLine(x+10, y+3, x+10, y+2);
|
||||
g2d.setStroke(oldStroke);
|
||||
|
||||
|
||||
// inner left arc
|
||||
g.setColor(UIManager.getColor("RadioButton.darkShadow"));
|
||||
g.drawLine(x+4, y+1, x+7, y+1);
|
||||
g.drawLine(x+2, y+2, x+3, y+2);
|
||||
g.drawLine(x+8, y+2, x+9, y+2);
|
||||
g.drawLine(x+2, y+3, x+2, y+3);
|
||||
g.drawLine(x+1, y+4, x+1, y+7);
|
||||
g.drawLine(x+2, y+8, x+2, y+8);
|
||||
|
||||
|
||||
// inner right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.light"));
|
||||
g.drawLine(x+2, y+9, x+3, y+9);
|
||||
g.drawLine(x+4, y+10, x+7, y+10);
|
||||
g.drawLine(x+8, y+9, x+9, y+9);
|
||||
g.drawLine(x+9, y+8, x+9, y+8);
|
||||
g.drawLine(x+10, y+7, x+10, y+4);
|
||||
g.drawLine(x+9, y+3, x+9, y+3);
|
||||
|
||||
|
||||
// indicate whether selected or not
|
||||
if (model.isSelected()) {
|
||||
if (model.isEnabled()) {
|
||||
g.setColor(UIManager.getColor("RadioButton.foreground"));
|
||||
} else {
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
if (model.isSelected()) {
|
||||
if (model.isEnabled()) {
|
||||
g.setColor(UIManager.getColor("RadioButton.foreground"));
|
||||
} else {
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
}
|
||||
g.fillOval(x + 3, y + 3, 5, 5);
|
||||
}
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint);
|
||||
|
||||
} else {
|
||||
|
||||
// outter left arc
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
g.drawLine(x+4, y+0, x+7, y+0);
|
||||
g.drawLine(x+2, y+1, x+3, y+1);
|
||||
g.drawLine(x+8, y+1, x+9, y+1);
|
||||
g.drawLine(x+1, y+2, x+1, y+3);
|
||||
g.drawLine(x+0, y+4, x+0, y+7);
|
||||
g.drawLine(x+1, y+8, x+1, y+9);
|
||||
|
||||
// outter right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.highlight"));
|
||||
g.drawLine(x+2, y+10, x+3, y+10);
|
||||
g.drawLine(x+4, y+11, x+7, y+11);
|
||||
g.drawLine(x+8, y+10, x+9, y+10);
|
||||
g.drawLine(x+10, y+9, x+10, y+8);
|
||||
g.drawLine(x+11, y+7, x+11, y+4);
|
||||
g.drawLine(x+10, y+3, x+10, y+2);
|
||||
|
||||
|
||||
// inner left arc
|
||||
g.setColor(UIManager.getColor("RadioButton.darkShadow"));
|
||||
g.drawLine(x+4, y+1, x+7, y+1);
|
||||
g.drawLine(x+2, y+2, x+3, y+2);
|
||||
g.drawLine(x+8, y+2, x+9, y+2);
|
||||
g.drawLine(x+2, y+3, x+2, y+3);
|
||||
g.drawLine(x+1, y+4, x+1, y+7);
|
||||
g.drawLine(x+2, y+8, x+2, y+8);
|
||||
|
||||
|
||||
// inner right arc
|
||||
g.setColor(UIManager.getColor("RadioButton.light"));
|
||||
g.drawLine(x+2, y+9, x+3, y+9);
|
||||
g.drawLine(x+4, y+10, x+7, y+10);
|
||||
g.drawLine(x+8, y+9, x+9, y+9);
|
||||
g.drawLine(x+9, y+8, x+9, y+8);
|
||||
g.drawLine(x+10, y+7, x+10, y+4);
|
||||
g.drawLine(x+9, y+3, x+9, y+3);
|
||||
|
||||
|
||||
// indicate whether selected or not
|
||||
if (model.isSelected()) {
|
||||
if (model.isEnabled()) {
|
||||
g.setColor(UIManager.getColor("RadioButton.foreground"));
|
||||
} else {
|
||||
g.setColor(UIManager.getColor("RadioButton.shadow"));
|
||||
}
|
||||
g.fillRect(x+4, y+5, 4, 2);
|
||||
g.fillRect(x+5, y+4, 2, 4);
|
||||
}
|
||||
g.fillRect(x+4, y+5, 4, 2);
|
||||
g.fillRect(x+5, y+4, 2, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,13 @@ package javax.swing.plaf.basic;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Path2D;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import sun.swing.SwingUtilities2;
|
||||
|
||||
/**
|
||||
* JButton object that draws a scaled Arrow in one of the cardinal directions.
|
||||
@ -236,6 +240,16 @@ public class BasicArrowButton extends JButton implements SwingConstants
|
||||
*/
|
||||
public void paintTriangle(Graphics g, int x, int y, int size,
|
||||
int direction, boolean isEnabled) {
|
||||
if (SwingUtilities2.isScaledGraphics(g)) {
|
||||
paintScaledTriangle(g, x, y, size, direction, isEnabled);
|
||||
} else {
|
||||
paintUnscaledTriangle(g, x, y, size, direction, isEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void paintUnscaledTriangle(Graphics g, int x, int y, int size,
|
||||
int direction, boolean isEnabled)
|
||||
{
|
||||
Color oldColor = g.getColor();
|
||||
int mid, i, j;
|
||||
|
||||
@ -309,4 +323,32 @@ public class BasicArrowButton extends JButton implements SwingConstants
|
||||
g.setColor(oldColor);
|
||||
}
|
||||
|
||||
private void paintScaledTriangle(Graphics g, double x, double y, double size,
|
||||
int direction, boolean isEnabled) {
|
||||
size = Math.max(size, 2);
|
||||
Path2D.Double path = new Path2D.Double();
|
||||
path.moveTo(-size, size / 2);
|
||||
path.lineTo(size, size / 2);
|
||||
path.lineTo(0, -size / 2);
|
||||
path.closePath();
|
||||
AffineTransform affineTransform = new AffineTransform();
|
||||
affineTransform.rotate(Math.PI * (direction - 1) / 4);
|
||||
path.transform(affineTransform);
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
double tx = x + size / 2;
|
||||
double ty = y + size / 2;
|
||||
g2d.translate(tx, ty);
|
||||
Color oldColor = g.getColor();
|
||||
if (!isEnabled) {
|
||||
g2d.translate(1, 0);
|
||||
g2d.setColor(highlight);
|
||||
g2d.fill(path);
|
||||
g2d.translate(-1, 0);
|
||||
}
|
||||
g2d.setColor(isEnabled ? darkShadow : shadow);
|
||||
g2d.fill(path);
|
||||
g2d.translate(-tx, -ty);
|
||||
g2d.setColor(oldColor);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ import static java.awt.RenderingHints.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.font.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import static java.awt.geom.AffineTransform.TYPE_FLIP;
|
||||
import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
|
||||
import java.awt.print.PrinterGraphics;
|
||||
import java.text.BreakIterator;
|
||||
import java.text.CharacterIterator;
|
||||
@ -2036,6 +2038,14 @@ public class SwingUtilities2 {
|
||||
return path;
|
||||
}
|
||||
|
||||
public static boolean isScaledGraphics(Graphics g) {
|
||||
if (g instanceof Graphics2D) {
|
||||
AffineTransform tx = ((Graphics2D) g).getTransform();
|
||||
return (tx.getType() & ~(TYPE_TRANSLATION | TYPE_FLIP)) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to listen to "blit" repaints in RepaintManager.
|
||||
*/
|
||||
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8165594
|
||||
* @key headful
|
||||
* @requires (os.family == "windows")
|
||||
* @summary Bad rendering of Swing UI controls with Windows Classic L&F on HiDPI
|
||||
* display
|
||||
* @run main/manual/othervm -Dsun.java2d.uiScale=2
|
||||
* -Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
|
||||
* WindowsClassicHiDPIIconsTest
|
||||
*/
|
||||
|
||||
public class WindowsClassicHiDPIIconsTest {
|
||||
|
||||
private static volatile boolean testResult = false;
|
||||
private static volatile CountDownLatch countDownLatch;
|
||||
private static final String INSTRUCTIONS = "INSTRUCTIONS:\n"
|
||||
+ "Check that the icons are painted smoothly on Swing UI controls:\n"
|
||||
+ " - JRadioButton\n"
|
||||
+ " - JCheckBox\n"
|
||||
+ " - JComboBox\n"
|
||||
+ " - JScrollPane (vertical and horizontal scroll bars)\n"
|
||||
+ "\n"
|
||||
+ "If so, press PASS, else press FAIL.\n";
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
countDownLatch = new CountDownLatch(1);
|
||||
|
||||
SwingUtilities.invokeLater(WindowsClassicHiDPIIconsTest::createUI);
|
||||
countDownLatch.await(15, TimeUnit.MINUTES);
|
||||
|
||||
if (!testResult) {
|
||||
throw new RuntimeException("Test fails!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void createUI() {
|
||||
|
||||
final JFrame mainFrame = new JFrame("Windows Classic L&F icons test");
|
||||
GridBagLayout layout = new GridBagLayout();
|
||||
JPanel mainControlPanel = new JPanel(layout);
|
||||
JPanel resultButtonPanel = new JPanel(layout);
|
||||
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
|
||||
|
||||
JPanel testPanel = createJPanel();
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
mainControlPanel.add(testPanel, gbc);
|
||||
|
||||
JTextArea instructionTextArea = new JTextArea();
|
||||
instructionTextArea.setText(INSTRUCTIONS);
|
||||
instructionTextArea.setEditable(false);
|
||||
instructionTextArea.setBackground(Color.white);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
mainControlPanel.add(instructionTextArea, gbc);
|
||||
|
||||
JButton passButton = new JButton("Pass");
|
||||
passButton.setActionCommand("Pass");
|
||||
passButton.addActionListener((ActionEvent e) -> {
|
||||
testResult = true;
|
||||
mainFrame.dispose();
|
||||
countDownLatch.countDown();
|
||||
|
||||
});
|
||||
|
||||
JButton failButton = new JButton("Fail");
|
||||
failButton.setActionCommand("Fail");
|
||||
failButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
mainFrame.dispose();
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
resultButtonPanel.add(passButton, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 0;
|
||||
resultButtonPanel.add(failButton, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
mainControlPanel.add(resultButtonPanel, gbc);
|
||||
|
||||
mainFrame.add(mainControlPanel);
|
||||
mainFrame.pack();
|
||||
|
||||
mainFrame.addWindowListener(new WindowAdapter() {
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
mainFrame.dispose();
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
});
|
||||
mainFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private static JPanel createJPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel iconPanel = new JPanel(new FlowLayout());
|
||||
JRadioButton radioButton = new JRadioButton();
|
||||
radioButton.setSelected(false);
|
||||
iconPanel.add(radioButton);
|
||||
radioButton = new JRadioButton();
|
||||
radioButton.setSelected(true);
|
||||
iconPanel.add(radioButton);
|
||||
panel.add(iconPanel);
|
||||
|
||||
iconPanel = new JPanel(new FlowLayout());
|
||||
JCheckBox checkBox = new JCheckBox();
|
||||
checkBox.setSelected(false);
|
||||
iconPanel.add(checkBox);
|
||||
checkBox = new JCheckBox();
|
||||
checkBox.setSelected(true);
|
||||
iconPanel.add(checkBox);
|
||||
panel.add(iconPanel);
|
||||
|
||||
iconPanel = new JPanel(new FlowLayout());
|
||||
JComboBox<String> comboBox = new JComboBox(new String[]{"111", "222"});
|
||||
iconPanel.add(comboBox);
|
||||
panel.add(iconPanel);
|
||||
|
||||
iconPanel = new JPanel(new FlowLayout());
|
||||
JTextArea textArea = new JTextArea(3, 7);
|
||||
textArea.setText("AAA");
|
||||
JScrollPane scrollPane = new JScrollPane(textArea);
|
||||
scrollPane.setHorizontalScrollBarPolicy(
|
||||
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||
scrollPane.setVerticalScrollBarPolicy(
|
||||
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
iconPanel.add(scrollPane);
|
||||
panel.add(iconPanel);
|
||||
|
||||
return panel;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user