ui: fix frame maximized bounds with dpi scaling
Due to JDK-4737788, maximizing an undecorated frame incorrectly covers the taskbar. Substance attempts to correct this by computing its own maximized bounds, but it does not account for DPI scaling. Since Substance automatically calls setMaximizedBounds when the frame is resized to do this we simply override it and replace the bounds with our own which includes the scaling of the display. This also fixes the frame maximizing to the incorrect display on 11.0.8 due to JDK-8231564, which changes the coordinates setMaximizedBounds() expects to be relative to the primary display instead of the current display. Previously, Substance was always setting the bounds x/y to 0 due to this. Co-authored-by: Runemoro <runemoro1@gmail.com>
This commit is contained in:
@@ -25,11 +25,21 @@
|
||||
package net.runelite.client.ui;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import javax.swing.JFrame;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.config.ExpandResizeType;
|
||||
import net.runelite.client.util.OSType;
|
||||
|
||||
@Slf4j
|
||||
public class ContainableFrame extends JFrame
|
||||
{
|
||||
public enum Mode
|
||||
@@ -39,6 +49,23 @@ public class ContainableFrame extends JFrame
|
||||
NEVER;
|
||||
}
|
||||
|
||||
private static boolean jdk8231564;
|
||||
|
||||
static
|
||||
{
|
||||
try
|
||||
{
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String[] s = javaVersion.split("\\.");
|
||||
int major = Integer.parseInt(s[0]), minor = Integer.parseInt(s[1]), patch = Integer.parseInt(s[2]);
|
||||
jdk8231564 = major > 11 || (major == 11 && minor > 0) || (major == 11 && minor == 0 && patch >= 8);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.error("error checking java version", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40;
|
||||
|
||||
@Setter
|
||||
@@ -186,6 +213,97 @@ public class ContainableFrame extends JFrame
|
||||
expandedClientOppositeDirection = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to Java bug JDK-4737788, maximizing an undecorated frame causes it to cover the taskbar.
|
||||
* As a workaround, Substance calls this method when the window is maximized to manually set the
|
||||
* bounds, but its calculation ignores high-DPI scaling. We're overriding it to correctly calculate
|
||||
* the maximized bounds.
|
||||
*/
|
||||
@Override
|
||||
public void setMaximizedBounds(Rectangle bounds)
|
||||
{
|
||||
if (OSType.getOSType() == OSType.MacOS)
|
||||
{
|
||||
// OSX seems to correctly handle DPI scaling already
|
||||
super.setMaximizedBounds(bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
super.setMaximizedBounds(getWindowAreaBounds());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the {@link GraphicsConfiguration} of the display the window is currently on. If it's on more than
|
||||
* one screen, returns the one it's most on (largest area of intersection)
|
||||
*/
|
||||
private GraphicsConfiguration getCurrentDisplayConfiguration()
|
||||
{
|
||||
return Arrays.stream(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
|
||||
.map(GraphicsDevice::getDefaultConfiguration)
|
||||
.max(Comparator.comparing(config ->
|
||||
{
|
||||
Rectangle intersection = config.getBounds().intersection(getBounds());
|
||||
return intersection.width * intersection.height;
|
||||
}))
|
||||
.orElseGet(this::getGraphicsConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the bounds of the window area of the screen.
|
||||
* <p>
|
||||
* The bounds returned by {@link GraphicsEnvironment#getMaximumWindowBounds} are incorrectly calculated on
|
||||
* high-DPI screens.
|
||||
*/
|
||||
private Rectangle getWindowAreaBounds()
|
||||
{
|
||||
log.trace("Current bounds: {}", getBounds());
|
||||
for (GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
|
||||
{
|
||||
log.trace("Device: {} bounds {}", device, device.getDefaultConfiguration().getBounds());
|
||||
}
|
||||
|
||||
GraphicsConfiguration config = getCurrentDisplayConfiguration();
|
||||
// get screen bounds
|
||||
Rectangle bounds = config.getBounds();
|
||||
log.trace("Chosen device: {} bounds {}", config, bounds);
|
||||
|
||||
// transform bounds to dpi-independent coordinates
|
||||
if (!jdk8231564)
|
||||
{
|
||||
// JDK-8231564 fixed setMaximizedBounds to scale the bounds, so this must only be done on <11.0.8
|
||||
bounds = config.getDefaultTransform().createTransformedShape(bounds).getBounds();
|
||||
log.trace("Transformed bounds {}", bounds);
|
||||
}
|
||||
|
||||
// subtract insets (taskbar, etc.)
|
||||
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
|
||||
if (!jdk8231564)
|
||||
{
|
||||
// Prior to JDK-8231564, WFramePeer expects the bounds to be relative to the current monitor instead of the
|
||||
// primary display.
|
||||
bounds.x = bounds.y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The insets from getScreenInsets are not scaled, we must convert them to DPI scaled pixels on 11.0.8 due
|
||||
// to JDK-8231564 which expects the bounds to be in DPI-aware pixels.
|
||||
double scaleX = config.getDefaultTransform().getScaleX();
|
||||
double scaleY = config.getDefaultTransform().getScaleY();
|
||||
insets.top /= scaleY;
|
||||
insets.bottom /= scaleY;
|
||||
insets.left /= scaleX;
|
||||
insets.right /= scaleX;
|
||||
}
|
||||
bounds.x += insets.left;
|
||||
bounds.y += insets.top;
|
||||
bounds.height -= (insets.bottom + insets.top);
|
||||
bounds.width -= (insets.right + insets.left);
|
||||
|
||||
log.trace("Final bounds: {}", bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force minimum size of frame to be it's layout manager's minimum size
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user