8294484: MetalBorder's FrameBorder & DialogBorder have border rendering issues when scaled
Co-authored-by: Alexey Ivanov <aivanov@openjdk.org> Reviewed-by: aivanov, kizune
This commit is contained in:
parent
c8cc7b67db
commit
5561c397c5
src/java.desktop/share/classes/javax/swing/plaf/metal
test/jdk/javax/swing
@ -235,33 +235,32 @@ public class MetalBorders {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class represents the border of a {@code JInternalFrame}.
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
public static class InternalFrameBorder extends AbstractBorder implements UIResource {
|
||||
@SuppressWarnings("serial")
|
||||
private abstract static sealed class AbstractMetalWindowBorder
|
||||
extends AbstractBorder
|
||||
implements UIResource
|
||||
permits FrameBorder, DialogBorder, InternalFrameBorderImpl {
|
||||
|
||||
protected Color background;
|
||||
protected Color highlight;
|
||||
protected Color shadow;
|
||||
|
||||
private static final int CORNER = 14;
|
||||
|
||||
/**
|
||||
* Constructs a {@code InternalFrameBorder}.
|
||||
*/
|
||||
public InternalFrameBorder() {}
|
||||
|
||||
public void paintBorder(Component c, Graphics g, int x, int y,
|
||||
int w, int h) {
|
||||
@Override
|
||||
public final void paintBorder(Component c, Graphics g,
|
||||
int x, int y, int w, int h) {
|
||||
SwingUtilities3.paintBorder(c, g,
|
||||
x, y, w, h,
|
||||
this::paintUnscaledBorder);
|
||||
}
|
||||
|
||||
private void paintUnscaledBorder(Component c, Graphics g,
|
||||
int width, int height,
|
||||
double scaleFactor) {
|
||||
Color background;
|
||||
Color highlight;
|
||||
Color shadow;
|
||||
protected abstract boolean isActive(Component c);
|
||||
|
||||
if (c instanceof JInternalFrame && ((JInternalFrame)c).isSelected()) {
|
||||
protected abstract boolean isResizable(Component c);
|
||||
|
||||
protected void updateColors(Component c) {
|
||||
if (isActive(c)) {
|
||||
background = MetalLookAndFeel.getPrimaryControlDarkShadow();
|
||||
highlight = MetalLookAndFeel.getPrimaryControlShadow();
|
||||
shadow = MetalLookAndFeel.getPrimaryControlInfo();
|
||||
@ -270,29 +269,41 @@ public class MetalBorders {
|
||||
highlight = MetalLookAndFeel.getControlShadow();
|
||||
shadow = MetalLookAndFeel.getControlInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// scaled border
|
||||
private void paintUnscaledBorder(Component c, Graphics g,
|
||||
int width, int height,
|
||||
double scaleFactor) {
|
||||
updateColors(c);
|
||||
|
||||
// scaled thickness
|
||||
int thickness = (int) Math.ceil(4 * scaleFactor);
|
||||
|
||||
g.setColor(background);
|
||||
// Draw the bulk of the border
|
||||
for (int i = 0; i <= thickness; i++) {
|
||||
g.drawRect(i, i, width - (i * 2), height - (i * 2));
|
||||
}
|
||||
|
||||
if (c instanceof JInternalFrame && ((JInternalFrame)c).isResizable()) {
|
||||
// midpoint at which highlight & shadow lines
|
||||
// are positioned on the border
|
||||
if (isResizable(c)) {
|
||||
//midpoint at which highlight & shadow lines
|
||||
//are positioned on the border
|
||||
int midPoint = thickness / 2;
|
||||
int stkWidth = clipRound(scaleFactor);
|
||||
int offset = (((scaleFactor - stkWidth) >= 0) && ((stkWidth % 2) != 0)) ? 1 : 0;
|
||||
int loc1 = thickness % 2 == 0 ? midPoint + stkWidth / 2 - stkWidth : midPoint;
|
||||
int loc2 = thickness % 2 == 0 ? midPoint + stkWidth / 2 : midPoint + stkWidth;
|
||||
int strokeWidth = clipRound(scaleFactor);
|
||||
int offset = (((scaleFactor - strokeWidth) >= 0)
|
||||
&& ((strokeWidth % 2) != 0)) ? 1 : 0;
|
||||
|
||||
int loc1 = (thickness % 2 == 0)
|
||||
? midPoint + strokeWidth / 2 - strokeWidth
|
||||
: midPoint;
|
||||
int loc2 = (thickness % 2 == 0)
|
||||
? midPoint + strokeWidth / 2
|
||||
: midPoint + strokeWidth;
|
||||
|
||||
// scaled corner
|
||||
int corner = (int) Math.round(CORNER * scaleFactor);
|
||||
|
||||
if (g instanceof Graphics2D) {
|
||||
((Graphics2D) g).setStroke(new BasicStroke((float) stkWidth));
|
||||
((Graphics2D) g).setStroke(new BasicStroke((float) strokeWidth));
|
||||
}
|
||||
|
||||
// Draw the Long highlight lines
|
||||
@ -315,72 +326,53 @@ public class MetalBorders {
|
||||
}
|
||||
}
|
||||
|
||||
public Insets getBorderInsets(Component c, Insets newInsets) {
|
||||
@Override
|
||||
public final Insets getBorderInsets(Component c, Insets newInsets) {
|
||||
newInsets.set(4, 4, 4, 4);
|
||||
return newInsets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Border for a Frame.
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static class FrameBorder extends AbstractBorder implements UIResource {
|
||||
private static final int corner = 14;
|
||||
|
||||
public void paintBorder(Component c, Graphics g, int x, int y,
|
||||
int w, int h) {
|
||||
|
||||
Color background;
|
||||
Color highlight;
|
||||
Color shadow;
|
||||
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
if (window != null && window.isActive()) {
|
||||
background = MetalLookAndFeel.getPrimaryControlDarkShadow();
|
||||
highlight = MetalLookAndFeel.getPrimaryControlShadow();
|
||||
shadow = MetalLookAndFeel.getPrimaryControlInfo();
|
||||
} else {
|
||||
background = MetalLookAndFeel.getControlDarkShadow();
|
||||
highlight = MetalLookAndFeel.getControlShadow();
|
||||
shadow = MetalLookAndFeel.getControlInfo();
|
||||
}
|
||||
|
||||
g.setColor(background);
|
||||
// Draw outermost lines
|
||||
g.drawLine( x+1, y+0, x+w-2, y+0);
|
||||
g.drawLine( x+0, y+1, x+0, y +h-2);
|
||||
g.drawLine( x+w-1, y+1, x+w-1, y+h-2);
|
||||
g.drawLine( x+1, y+h-1, x+w-2, y+h-1);
|
||||
|
||||
// Draw the bulk of the border
|
||||
for (int i = 1; i < 5; i++) {
|
||||
g.drawRect(x+i,y+i,w-(i*2)-1, h-(i*2)-1);
|
||||
}
|
||||
|
||||
if ((window instanceof Frame) && ((Frame) window).isResizable()) {
|
||||
g.setColor(highlight);
|
||||
// Draw the Long highlight lines
|
||||
g.drawLine( corner+1, 3, w-corner, 3);
|
||||
g.drawLine( 3, corner+1, 3, h-corner);
|
||||
g.drawLine( w-2, corner+1, w-2, h-corner);
|
||||
g.drawLine( corner+1, h-2, w-corner, h-2);
|
||||
|
||||
g.setColor(shadow);
|
||||
// Draw the Long shadow lines
|
||||
g.drawLine( corner, 2, w-corner-1, 2);
|
||||
g.drawLine( 2, corner, 2, h-corner-1);
|
||||
g.drawLine( w-3, corner, w-3, h-corner-1);
|
||||
g.drawLine( corner, h-3, w-corner-1, h-3);
|
||||
}
|
||||
@SuppressWarnings("serial")
|
||||
private static final class InternalFrameBorderImpl extends AbstractMetalWindowBorder {
|
||||
|
||||
@Override
|
||||
protected boolean isActive(Component c) {
|
||||
return (c instanceof JInternalFrame
|
||||
&& ((JInternalFrame)c).isSelected());
|
||||
}
|
||||
|
||||
public Insets getBorderInsets(Component c, Insets newInsets)
|
||||
{
|
||||
newInsets.set(5, 5, 5, 5);
|
||||
return newInsets;
|
||||
@Override
|
||||
protected boolean isResizable(Component c) {
|
||||
return ((c instanceof JInternalFrame
|
||||
&& ((JInternalFrame) c).isResizable()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class represents the border of a {@code JInternalFrame}.
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
public static class InternalFrameBorder extends AbstractBorder implements UIResource {
|
||||
|
||||
private final InternalFrameBorderImpl border;
|
||||
|
||||
/**
|
||||
* Constructs a {@code InternalFrameBorder}.
|
||||
*/
|
||||
public InternalFrameBorder() {
|
||||
border = new InternalFrameBorderImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder(Component c, Graphics g, int x, int y,
|
||||
int w, int h) {
|
||||
border.paintBorder(c, g, x, y, w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets(Component c, Insets newInsets) {
|
||||
return border.getBorderInsets(c, newInsets);
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,48 +381,59 @@ public class MetalBorders {
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static class DialogBorder extends AbstractBorder implements UIResource
|
||||
{
|
||||
private static final int corner = 14;
|
||||
static final class FrameBorder extends AbstractMetalWindowBorder implements UIResource {
|
||||
|
||||
protected Color getActiveBackground()
|
||||
{
|
||||
@Override
|
||||
protected boolean isActive(Component c) {
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
return (window != null && window.isActive());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isResizable(Component c) {
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
return ((window instanceof Frame)
|
||||
&& ((Frame) window).isResizable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Border for a Frame.
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static sealed class DialogBorder
|
||||
extends AbstractMetalWindowBorder
|
||||
implements UIResource
|
||||
permits ErrorDialogBorder, QuestionDialogBorder, WarningDialogBorder {
|
||||
|
||||
protected Color getActiveBackground() {
|
||||
return MetalLookAndFeel.getPrimaryControlDarkShadow();
|
||||
}
|
||||
|
||||
protected Color getActiveHighlight()
|
||||
{
|
||||
protected final Color getActiveHighlight() {
|
||||
return MetalLookAndFeel.getPrimaryControlShadow();
|
||||
}
|
||||
|
||||
protected Color getActiveShadow()
|
||||
{
|
||||
protected final Color getActiveShadow() {
|
||||
return MetalLookAndFeel.getPrimaryControlInfo();
|
||||
}
|
||||
|
||||
protected Color getInactiveBackground()
|
||||
{
|
||||
protected final Color getInactiveBackground() {
|
||||
return MetalLookAndFeel.getControlDarkShadow();
|
||||
}
|
||||
|
||||
protected Color getInactiveHighlight()
|
||||
{
|
||||
protected final Color getInactiveHighlight() {
|
||||
return MetalLookAndFeel.getControlShadow();
|
||||
}
|
||||
|
||||
protected Color getInactiveShadow()
|
||||
{
|
||||
protected final Color getInactiveShadow() {
|
||||
return MetalLookAndFeel.getControlInfo();
|
||||
}
|
||||
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h)
|
||||
{
|
||||
Color background;
|
||||
Color highlight;
|
||||
Color shadow;
|
||||
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
if (window != null && window.isActive()) {
|
||||
@Override
|
||||
protected final void updateColors(Component c) {
|
||||
if (isActive(c)) {
|
||||
background = getActiveBackground();
|
||||
highlight = getActiveHighlight();
|
||||
shadow = getActiveShadow();
|
||||
@ -439,42 +442,19 @@ public class MetalBorders {
|
||||
highlight = getInactiveHighlight();
|
||||
shadow = getInactiveShadow();
|
||||
}
|
||||
|
||||
g.setColor(background);
|
||||
// Draw outermost lines
|
||||
g.drawLine( x + 1, y + 0, x + w-2, y + 0);
|
||||
g.drawLine( x + 0, y + 1, x + 0, y + h - 2);
|
||||
g.drawLine( x + w - 1, y + 1, x + w - 1, y + h - 2);
|
||||
g.drawLine( x + 1, y + h - 1, x + w - 2, y + h - 1);
|
||||
|
||||
// Draw the bulk of the border
|
||||
for (int i = 1; i < 5; i++) {
|
||||
g.drawRect(x+i,y+i,w-(i*2)-1, h-(i*2)-1);
|
||||
}
|
||||
|
||||
|
||||
if ((window instanceof Dialog) && ((Dialog) window).isResizable()) {
|
||||
g.setColor(highlight);
|
||||
// Draw the Long highlight lines
|
||||
g.drawLine( corner+1, 3, w-corner, 3);
|
||||
g.drawLine( 3, corner+1, 3, h-corner);
|
||||
g.drawLine( w-2, corner+1, w-2, h-corner);
|
||||
g.drawLine( corner+1, h-2, w-corner, h-2);
|
||||
|
||||
g.setColor(shadow);
|
||||
// Draw the Long shadow lines
|
||||
g.drawLine( corner, 2, w-corner-1, 2);
|
||||
g.drawLine( 2, corner, 2, h-corner-1);
|
||||
g.drawLine( w-3, corner, w-3, h-corner-1);
|
||||
g.drawLine( corner, h-3, w-corner-1, h-3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Insets getBorderInsets(Component c, Insets newInsets)
|
||||
{
|
||||
newInsets.set(5, 5, 5, 5);
|
||||
return newInsets;
|
||||
@Override
|
||||
protected final boolean isActive(Component c) {
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
return (window != null && window.isActive());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isResizable(Component c) {
|
||||
Window window = SwingUtilities.getWindowAncestor(c);
|
||||
return ((window instanceof Dialog)
|
||||
&& ((Dialog) window).isResizable());
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +463,7 @@ public class MetalBorders {
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static class ErrorDialogBorder extends DialogBorder implements UIResource
|
||||
static final class ErrorDialogBorder extends DialogBorder implements UIResource
|
||||
{
|
||||
protected Color getActiveBackground() {
|
||||
return UIManager.getColor("OptionPane.errorDialog.border.background");
|
||||
@ -497,7 +477,7 @@ public class MetalBorders {
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static class QuestionDialogBorder extends DialogBorder implements UIResource
|
||||
static final class QuestionDialogBorder extends DialogBorder implements UIResource
|
||||
{
|
||||
protected Color getActiveBackground() {
|
||||
return UIManager.getColor("OptionPane.questionDialog.border.background");
|
||||
@ -510,7 +490,7 @@ public class MetalBorders {
|
||||
* @since 1.4
|
||||
*/
|
||||
@SuppressWarnings("serial") // Superclass is not serializable across versions
|
||||
static class WarningDialogBorder extends DialogBorder implements UIResource
|
||||
static final class WarningDialogBorder extends DialogBorder implements UIResource
|
||||
{
|
||||
protected Color getActiveBackground() {
|
||||
return UIManager.getColor("OptionPane.warningDialog.border.background");
|
||||
|
@ -1,266 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.AWTException;
|
||||
import java.awt.Color;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Robot;
|
||||
import java.awt.image.MultiResolutionImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8015739
|
||||
* @key headful
|
||||
* @summary Tests whether background color of JInternalFrame is visible
|
||||
* in the border region at different scales by checking the midpoints
|
||||
* and corners of the border.
|
||||
*
|
||||
* @requires (os.family == "windows")
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.25 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.5 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.75 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2.5 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=3 InternalFrameBorderTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8015739
|
||||
* @key headful
|
||||
* @summary Tests whether background color of JInternalFrame is visible
|
||||
* in the border region at different scales by checking the midpoints
|
||||
* and corners of the border.
|
||||
*
|
||||
* @requires (os.family == "mac" | os.family == "linux")
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1 InternalFrameBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2 InternalFrameBorderTest
|
||||
*/
|
||||
|
||||
public class InternalFrameBorderTest {
|
||||
private static final int FRAME_SIZE = 300;
|
||||
private static final int INTFRAME_SIZE = 150;
|
||||
private static final int MIDPOINT = INTFRAME_SIZE / 2;
|
||||
private static final int BORDER_THICKNESS = 4;
|
||||
|
||||
private static final StringBuffer errorLog = new StringBuffer();
|
||||
|
||||
private static JFrame jFrame;
|
||||
private static Rectangle jFrameBounds;
|
||||
private static JInternalFrame iFrame;
|
||||
private static Point iFrameLoc;
|
||||
private static int iFrameMaxX;
|
||||
private static int iFrameMaxY;
|
||||
|
||||
private static Robot robot;
|
||||
private static String uiScale;
|
||||
|
||||
public static void main(String[] args) throws AWTException,
|
||||
InterruptedException, InvocationTargetException {
|
||||
try {
|
||||
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Metal LAF class not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
robot = new Robot();
|
||||
robot.setAutoDelay(200);
|
||||
uiScale = System.getProperty("sun.java2d.uiScale");
|
||||
|
||||
SwingUtilities.invokeAndWait(InternalFrameBorderTest::createAndShowGUI);
|
||||
robot.waitForIdle();
|
||||
robot.delay(500);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
iFrameLoc = iFrame.getLocationOnScreen();
|
||||
iFrameMaxX = iFrameLoc.x + INTFRAME_SIZE;
|
||||
iFrameMaxY = iFrameLoc.y + INTFRAME_SIZE;
|
||||
jFrameBounds = jFrame.getBounds();
|
||||
});
|
||||
|
||||
// Check Borders
|
||||
checkBorderMidPoints("TOP");
|
||||
checkBorderMidPoints("RIGHT");
|
||||
checkBorderMidPoints("BOTTOM");
|
||||
checkBorderMidPoints("LEFT");
|
||||
|
||||
// Check Corner Diagonals
|
||||
checkCorners("TOP_LEFT");
|
||||
checkCorners("TOP_RIGHT");
|
||||
checkCorners("BOTTOM_RIGHT");
|
||||
checkCorners("BOTTOM_LEFT");
|
||||
|
||||
if (!errorLog.isEmpty()) {
|
||||
saveScreenCapture("JIF_uiScale_" + uiScale + ".png");
|
||||
throw new RuntimeException("Following error(s) occurred: \n"
|
||||
+ errorLog);
|
||||
}
|
||||
} finally {
|
||||
if (jFrame != null) {
|
||||
jFrame.dispose();
|
||||
}
|
||||
robot.delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkBorderMidPoints(String borderDirection) {
|
||||
int x, y;
|
||||
int start, stop;
|
||||
|
||||
switch (borderDirection) {
|
||||
case "TOP" -> {
|
||||
x = iFrameLoc.x + MIDPOINT;
|
||||
y = iFrameLoc.y + BORDER_THICKNESS;
|
||||
start = iFrameLoc.y;
|
||||
stop = iFrameLoc.y + BORDER_THICKNESS - 1;
|
||||
}
|
||||
case "RIGHT" -> {
|
||||
x = iFrameMaxX - BORDER_THICKNESS;
|
||||
y = iFrameLoc.y + MIDPOINT;
|
||||
start = iFrameMaxX - BORDER_THICKNESS + 1;
|
||||
stop = iFrameMaxX;
|
||||
}
|
||||
case "BOTTOM" -> {
|
||||
x = iFrameLoc.x + MIDPOINT;
|
||||
y = iFrameMaxY - BORDER_THICKNESS;
|
||||
start = iFrameMaxY - BORDER_THICKNESS + 1;
|
||||
stop = iFrameMaxY;
|
||||
}
|
||||
case "LEFT" -> {
|
||||
x = iFrameLoc.x;
|
||||
y = iFrameLoc.y + MIDPOINT;
|
||||
start = iFrameLoc.x;
|
||||
stop = iFrameLoc.x + BORDER_THICKNESS - 1;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: "
|
||||
+ borderDirection);
|
||||
}
|
||||
|
||||
boolean isVertical = borderDirection.equals("RIGHT")
|
||||
|| borderDirection.equals("LEFT");
|
||||
boolean isHorizontal = borderDirection.equals("TOP")
|
||||
|| borderDirection.equals("BOTTOM");
|
||||
|
||||
robot.mouseMove(x, y);
|
||||
for (int i = start; i < stop; i++) {
|
||||
int locX = isVertical ? i : (iFrameLoc.x + MIDPOINT);
|
||||
int locY = isHorizontal ? i : (iFrameLoc.y + MIDPOINT);
|
||||
if (Color.RED.equals(robot.getPixelColor(locX, locY))) {
|
||||
errorLog.append("At uiScale: " + uiScale
|
||||
+ ", Red background color detected at "
|
||||
+ borderDirection + " border.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
robot.delay(300);
|
||||
}
|
||||
|
||||
private static void checkCorners(String cornerLocation) {
|
||||
int x, y;
|
||||
|
||||
switch (cornerLocation) {
|
||||
case "TOP_LEFT" -> {
|
||||
x = iFrameLoc.x;
|
||||
y = iFrameLoc.y;
|
||||
}
|
||||
case "TOP_RIGHT" -> {
|
||||
x = iFrameMaxX;
|
||||
y = iFrameLoc.y;
|
||||
}
|
||||
case "BOTTOM_RIGHT" -> {
|
||||
x = iFrameMaxX;
|
||||
y = iFrameMaxY;
|
||||
}
|
||||
case "BOTTOM_LEFT" -> {
|
||||
x = iFrameLoc.x;
|
||||
y = iFrameMaxY;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: "
|
||||
+ cornerLocation);
|
||||
}
|
||||
|
||||
boolean isTop = cornerLocation.equals("TOP_LEFT")
|
||||
|| cornerLocation.equals("TOP_RIGHT");
|
||||
boolean isLeft = cornerLocation.equals("TOP_LEFT")
|
||||
|| cornerLocation.equals("BOTTOM_LEFT");
|
||||
|
||||
robot.mouseMove(x, y);
|
||||
for (int i = 0; i < BORDER_THICKNESS - 1; i++) {
|
||||
int locX = isLeft ? (x + i) : (x - i);
|
||||
int locY = isTop ? (y + i) : (y - i);
|
||||
if (Color.RED.equals(robot.getPixelColor(locX, locY))) {
|
||||
errorLog.append("At uiScale: " + uiScale + ", Red background color"
|
||||
+ " detected at " + cornerLocation + " corner.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
robot.delay(300);
|
||||
}
|
||||
|
||||
private static void createAndShowGUI() {
|
||||
jFrame = new JFrame();
|
||||
jFrame.setSize(FRAME_SIZE, FRAME_SIZE);
|
||||
jFrame.setLayout(null);
|
||||
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
|
||||
JLabel scale = new JLabel("UI Scale: " + uiScale);
|
||||
iFrame = new JInternalFrame("iframe", true);
|
||||
iFrame.setLayout(new GridBagLayout());
|
||||
iFrame.setBackground(Color.RED);
|
||||
iFrame.add(scale);
|
||||
iFrame.setLocation(30, 30);
|
||||
jFrame.getContentPane().add(iFrame);
|
||||
iFrame.setSize(INTFRAME_SIZE, INTFRAME_SIZE);
|
||||
iFrame.setVisible(true);
|
||||
jFrame.setLocation(150, 150);
|
||||
jFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void saveScreenCapture(String filename) {
|
||||
MultiResolutionImage mrImage = robot.createMultiResolutionScreenCapture(jFrameBounds);
|
||||
List<Image> variants = mrImage.getResolutionVariants();
|
||||
RenderedImage image = (RenderedImage) variants.get(variants.size() - 1);
|
||||
try {
|
||||
ImageIO.write(image, "png", new File(filename));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2023, 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.AWTException;
|
||||
import java.awt.Color;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Robot;
|
||||
import java.awt.image.MultiResolutionImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8015739 8294484
|
||||
* @key headful
|
||||
* @summary Tests whether Metal borders for JFrame, JDialog and JInternalFrame
|
||||
* scales correctly without any distortions by checking the midpoints and
|
||||
* corners of the border.
|
||||
*
|
||||
* @requires (os.family == "windows")
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.25 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.5 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1.75 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2.5 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=3 ScaledMetalBorderTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8015739 8294484
|
||||
* @key headful
|
||||
* @summary Tests whether Metal borders for JFrame, JDialog and JInternalFrame
|
||||
* scales correctly without any distortions by checking the midpoints and
|
||||
* corners of the border.
|
||||
*
|
||||
* @requires (os.family == "mac" | os.family == "linux")
|
||||
* @run main/othervm -Dsun.java2d.uiScale=1 ScaledMetalBorderTest
|
||||
* @run main/othervm -Dsun.java2d.uiScale=2 ScaledMetalBorderTest
|
||||
*/
|
||||
|
||||
public class ScaledMetalBorderTest {
|
||||
private static final int SIZE = 250;
|
||||
private static final int INTFRAME_SIZE = 180;
|
||||
private static int MIDPOINT = SIZE / 2;
|
||||
private static final int BORDER_THICKNESS = 4;
|
||||
|
||||
private static final StringBuffer errorLog = new StringBuffer();
|
||||
|
||||
private static JFrame jFrame;
|
||||
private static JDialog jDialog;
|
||||
private static JInternalFrame iFrame;
|
||||
private static Rectangle windowBounds;
|
||||
private static Point windowLoc;
|
||||
private static int windowMaxX;
|
||||
private static int windowMaxY;
|
||||
|
||||
private static Robot robot;
|
||||
private static String uiScale;
|
||||
private static JLabel scale;
|
||||
|
||||
public static void main(String[] args) throws AWTException,
|
||||
InterruptedException, InvocationTargetException {
|
||||
try {
|
||||
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
|
||||
JFrame.setDefaultLookAndFeelDecorated(true);
|
||||
JDialog.setDefaultLookAndFeelDecorated(true);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Metal LAF class not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
robot = new Robot();
|
||||
robot.setAutoDelay(100);
|
||||
uiScale = System.getProperty("sun.java2d.uiScale");
|
||||
scale = new JLabel("UI Scale: " + uiScale);
|
||||
|
||||
//Case 1: JFrame
|
||||
SwingUtilities.invokeAndWait(ScaledMetalBorderTest::createFrame);
|
||||
robot.waitForIdle();
|
||||
robot.delay(100);
|
||||
runTests("JFrame");
|
||||
|
||||
if (!errorLog.isEmpty()) {
|
||||
saveScreenCapture("Frame_uiScale_" + uiScale + ".png");
|
||||
System.err.println("JFrame at uiScale: " + uiScale);
|
||||
throw new RuntimeException("Following error(s) occurred: \n"
|
||||
+ errorLog);
|
||||
}
|
||||
errorLog.setLength(0); // to clear the StringBuffer before next test.
|
||||
|
||||
//Case 2: JDialog
|
||||
SwingUtilities.invokeAndWait(ScaledMetalBorderTest::createDialog);
|
||||
robot.waitForIdle();
|
||||
robot.delay(100);
|
||||
runTests("JDialog");
|
||||
|
||||
if (!errorLog.isEmpty()) {
|
||||
saveScreenCapture("Dialog_uiScale_" + uiScale + ".png");
|
||||
System.err.println("JDialog at uiScale: " + uiScale);
|
||||
throw new RuntimeException("Following error(s) occurred: \n"
|
||||
+ errorLog);
|
||||
}
|
||||
errorLog.setLength(0); // to clear the StringBuffer before next test.
|
||||
|
||||
//Case 3: JInternalFrame
|
||||
SwingUtilities.invokeAndWait(ScaledMetalBorderTest::createJInternalFrame);
|
||||
robot.waitForIdle();
|
||||
robot.delay(100);
|
||||
runTests("JIF");
|
||||
|
||||
if (!errorLog.isEmpty()) {
|
||||
saveScreenCapture("JIF_uiScale_" + uiScale + ".png");
|
||||
System.err.println("JInternalFrame at uiScale: " + uiScale);
|
||||
throw new RuntimeException("Following error(s) occurred: \n"
|
||||
+ errorLog);
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(() ->{
|
||||
if (jFrame != null) {
|
||||
jFrame.dispose();
|
||||
}
|
||||
if (jDialog != null) {
|
||||
jDialog.dispose();
|
||||
}
|
||||
});
|
||||
robot.delay(200);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTests(String windowType) throws InterruptedException,
|
||||
InvocationTargetException {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
switch (windowType) {
|
||||
case "JFrame" -> {
|
||||
windowLoc = jFrame.getLocationOnScreen();
|
||||
windowBounds = jFrame.getBounds();
|
||||
windowMaxX = windowLoc.x + SIZE;
|
||||
windowMaxY = windowLoc.y + SIZE;
|
||||
}
|
||||
case "JDialog" -> {
|
||||
windowLoc = jDialog.getLocationOnScreen();
|
||||
windowBounds = jDialog.getBounds();
|
||||
windowMaxX = windowLoc.x + SIZE;
|
||||
windowMaxY = windowLoc.y + SIZE;
|
||||
}
|
||||
case "JIF" -> {
|
||||
MIDPOINT = INTFRAME_SIZE / 2;
|
||||
windowLoc = iFrame.getLocationOnScreen();
|
||||
windowBounds = jFrame.getBounds();
|
||||
windowMaxX = windowLoc.x + INTFRAME_SIZE;
|
||||
windowMaxY = windowLoc.y + INTFRAME_SIZE;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check Borders
|
||||
checkBorderMidPoints("TOP");
|
||||
checkBorderMidPoints("RIGHT");
|
||||
checkBorderMidPoints("BOTTOM");
|
||||
checkBorderMidPoints("LEFT");
|
||||
|
||||
// Check Corner Diagonals
|
||||
checkCorners("TOP_LEFT");
|
||||
checkCorners("TOP_RIGHT");
|
||||
checkCorners("BOTTOM_RIGHT");
|
||||
checkCorners("BOTTOM_LEFT");
|
||||
}
|
||||
|
||||
private static void checkBorderMidPoints(String borderDirection) {
|
||||
int x, y;
|
||||
int start, stop;
|
||||
|
||||
switch (borderDirection) {
|
||||
case "TOP" -> {
|
||||
x = windowLoc.x + MIDPOINT;
|
||||
y = windowLoc.y + BORDER_THICKNESS;
|
||||
start = windowLoc.y;
|
||||
stop = windowLoc.y + BORDER_THICKNESS - 1;
|
||||
}
|
||||
case "RIGHT" -> {
|
||||
x = windowMaxX - BORDER_THICKNESS;
|
||||
y = windowLoc.y + MIDPOINT;
|
||||
start = windowMaxX - BORDER_THICKNESS + 1;
|
||||
stop = windowMaxX;
|
||||
}
|
||||
case "BOTTOM" -> {
|
||||
x = windowLoc.x + MIDPOINT;
|
||||
y = windowMaxY - BORDER_THICKNESS;
|
||||
start = windowMaxY - BORDER_THICKNESS + 1;
|
||||
stop = windowMaxY;
|
||||
}
|
||||
case "LEFT" -> {
|
||||
x = windowLoc.x;
|
||||
y = windowLoc.y + MIDPOINT;
|
||||
start = windowLoc.x;
|
||||
stop = windowLoc.x + BORDER_THICKNESS - 1;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: "
|
||||
+ borderDirection);
|
||||
}
|
||||
|
||||
boolean isVertical = borderDirection.equals("RIGHT")
|
||||
|| borderDirection.equals("LEFT");
|
||||
boolean isHorizontal = borderDirection.equals("TOP")
|
||||
|| borderDirection.equals("BOTTOM");
|
||||
|
||||
robot.mouseMove(x, y);
|
||||
for (int i = start; i < stop; i++) {
|
||||
int locX = isVertical ? i : (windowLoc.x + MIDPOINT);
|
||||
int locY = isHorizontal ? i : (windowLoc.y + MIDPOINT);
|
||||
if (Color.RED.equals(robot.getPixelColor(locX, locY))) {
|
||||
errorLog.append("At uiScale: " + uiScale
|
||||
+ ", Red background color detected at "
|
||||
+ borderDirection + " border.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
robot.delay(100);
|
||||
}
|
||||
|
||||
private static void checkCorners(String cornerLocation) {
|
||||
int x, y;
|
||||
|
||||
switch (cornerLocation) {
|
||||
case "TOP_LEFT" -> {
|
||||
x = windowLoc.x;
|
||||
y = windowLoc.y;
|
||||
}
|
||||
case "TOP_RIGHT" -> {
|
||||
x = windowMaxX;
|
||||
y = windowLoc.y;
|
||||
}
|
||||
case "BOTTOM_RIGHT" -> {
|
||||
x = windowMaxX;
|
||||
y = windowMaxY;
|
||||
}
|
||||
case "BOTTOM_LEFT" -> {
|
||||
x = windowLoc.x;
|
||||
y = windowMaxY;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: "
|
||||
+ cornerLocation);
|
||||
}
|
||||
|
||||
boolean isTop = cornerLocation.equals("TOP_LEFT")
|
||||
|| cornerLocation.equals("TOP_RIGHT");
|
||||
boolean isLeft = cornerLocation.equals("TOP_LEFT")
|
||||
|| cornerLocation.equals("BOTTOM_LEFT");
|
||||
|
||||
robot.mouseMove(x, y);
|
||||
for (int i = 0; i < BORDER_THICKNESS - 1; i++) {
|
||||
int locX = isLeft ? (x + i) : (x - i);
|
||||
int locY = isTop ? (y + i) : (y - i);
|
||||
if (Color.RED.equals(robot.getPixelColor(locX, locY))) {
|
||||
errorLog.append("At uiScale: " + uiScale + ", Red background color"
|
||||
+ " detected at " + cornerLocation + " corner.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
robot.delay(100);
|
||||
}
|
||||
|
||||
private static void createFrame() {
|
||||
jFrame = new JFrame("Frame with Metal Border");
|
||||
jFrame.setSize(SIZE, SIZE);
|
||||
jFrame.setBackground(Color.RED);
|
||||
jFrame.getContentPane().setBackground(Color.RED);
|
||||
jFrame.setLayout(new GridBagLayout());
|
||||
jFrame.getContentPane().add(scale);
|
||||
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
jFrame.setLocation(150, 150);
|
||||
jFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void createDialog() {
|
||||
jDialog = new JDialog((Frame) null , "Dialog with Metal Border");
|
||||
jDialog.setSize(SIZE, SIZE);
|
||||
jDialog.setBackground(Color.RED);
|
||||
jDialog.getContentPane().setBackground(Color.RED);
|
||||
jDialog.setLayout(new GridBagLayout());
|
||||
jDialog.getContentPane().add(scale);
|
||||
jDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
jDialog.setLocation(150, 150);
|
||||
jDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void createJInternalFrame() {
|
||||
jFrame = new JFrame("JIF with Metal Border");
|
||||
jFrame.setSize(SIZE, SIZE);
|
||||
jFrame.setLayout(null);
|
||||
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
|
||||
iFrame = new JInternalFrame("iframe", true);
|
||||
iFrame.setLayout(new GridBagLayout());
|
||||
iFrame.setBackground(Color.RED);
|
||||
iFrame.add(scale);
|
||||
iFrame.setLocation(30, 30);
|
||||
jFrame.getContentPane().add(iFrame);
|
||||
iFrame.setSize(INTFRAME_SIZE, INTFRAME_SIZE);
|
||||
iFrame.setVisible(true);
|
||||
jFrame.setLocation(150, 150);
|
||||
jFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void saveScreenCapture(String filename) {
|
||||
MultiResolutionImage mrImage = robot.createMultiResolutionScreenCapture(windowBounds);
|
||||
List<Image> variants = mrImage.getResolutionVariants();
|
||||
RenderedImage image = (RenderedImage) variants.get(variants.size() - 1);
|
||||
try {
|
||||
ImageIO.write(image, "png", new File(filename));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user