IDEA-165949 [hidpi-jdk] IDEA main frame doesn't restore its bounds on the second monitor when reopened

This commit is contained in:
Anton Tarasov
2016-12-28 16:07:56 +03:00
parent 4d4cf5459a
commit 650ea52539
2 changed files with 85 additions and 73 deletions
@@ -527,75 +527,4 @@ public class ScreenUtil {
return false;
}
/**
* Transforms the bounds provided in the device space to the user space of the screen device they fit to.
*
* @param bounds the bounds to transform
* @return the transformed bounds
*/
public static Rectangle boundsFromDeviceSpace(@NotNull Rectangle bounds) {
Rectangle b = bounds.getBounds();
if (SystemInfo.isLinux/* && !UIUtil.isJDKManagedHiDPI()*/) return b; // [tav] todo: leave default on Linux so far
try {
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
Rectangle screenBounds = screenBoundsToDeviceSpace(gd);
float screenScale = screenScale(gd);
Rectangle2D.Float screen2d = new Rectangle2D.Float(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
Point2D.Float center2d = new Point2D.Float(b.x + b.width / 2, b.y + b.height / 2);
if (screen2d.contains(center2d)) {
float toScale = UIUtil.isJDKManagedHiDPI() ? 1 : JBUI.sysScale();
scaleRect(b, toScale / screenScale);
}
}
}
catch (HeadlessException ignore) {
}
return b;
}
/**
* Transforms the bounds provided in the user space of the graphics device to the device space.
*
* @param g the graphics representing the device
* @param bounds the bounds to transform
* @return the transformed bounds
*/
public static Rectangle boundsToDeviceSpace(Graphics2D g, @NotNull Rectangle bounds) {
Rectangle b = bounds.getBounds();
if (SystemInfo.isLinux/* && !UIUtil.isJDKManagedHiDPI()*/) return b; // [tav] todo: leave default on Linux so far
try {
if (g != null) {
float screenScale = screenScale(g.getDeviceConfiguration().getDevice());
float fromScale = UIUtil.isJDKManagedHiDPI() ? 1 : JBUI.sysScale();
scaleRect(b, screenScale / fromScale);
}
}
catch (HeadlessException ignore) {
}
return b;
}
private static Rectangle scaleRect(Rectangle r, float scale) {
r.setBounds((int)Math.floor(r.x * scale), (int)Math.floor(r.y * scale),
(int)Math.ceil(r.width * scale), (int)Math.ceil(r.height * scale));
return r;
}
private static float screenScale(GraphicsDevice gd) {
if (UIUtil.isJDKManagedHiDPI()) {
return JBUI.sysScale(gd);
}
if (gd == GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()) {
// for the default device the scale is known
return JBUI.sysScale();
}
// [tav] todo: for other devices we don't know the _real_ scale...
return 1;
}
private static Rectangle screenBoundsToDeviceSpace(GraphicsDevice gd) {
Rectangle screenBounds = gd.getDefaultConfiguration().getBounds();
return UIUtil.isJDKManagedHiDPI() ? scaleRect(screenBounds, screenScale(gd)) : screenBounds;
}
}
@@ -41,6 +41,7 @@ import com.intellij.util.EventDispatcher;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.ui.GraphicsUtil;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.sun.jna.platform.WindowUtils;
import org.jdom.Element;
@@ -51,6 +52,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.peer.ComponentPeer;
import java.awt.peer.FramePeer;
import java.util.Collection;
@@ -699,7 +702,7 @@ public final class WindowManagerImpl extends WindowManagerEx implements NamedCom
catch (NumberFormatException ignored) {
return null;
}
return ScreenUtil.boundsFromDeviceSpace(bounds);
return FrameBoundsConverter.convertFromDeviceSpace(bounds);
}
@Nullable
@@ -736,7 +739,7 @@ public final class WindowManagerImpl extends WindowManagerEx implements NamedCom
int extendedState = updateFrameBounds(frame);
Rectangle rectangle = ScreenUtil.boundsToDeviceSpace((Graphics2D)GraphicsUtil.safelyGetGraphics(frame), myFrameBounds);
Rectangle rectangle = FrameBoundsConverter.convertToDeviceSpace((Graphics2D)GraphicsUtil.safelyGetGraphics(frame), myFrameBounds);
final Element frameElement = new Element(FRAME_ELEMENT);
frameElement.setAttribute(X_ATTR, Integer.toString(rectangle.x));
@@ -798,4 +801,84 @@ public final class WindowManagerImpl extends WindowManagerEx implements NamedCom
public static boolean isFloatingMenuBarSupported() {
return !SystemInfo.isMac && getInstance().isFullScreenSupportedInCurrentOS();
}
/**
* Converts the frame bounds b/w the user space (JDK-managed HiDPI mode) and the device space (IDE-managed HiDPI mode).
* See {@link UIUtil#isJDKManagedHiDPI()}
*/
private static class FrameBoundsConverter {
/**
* @param bounds the bounds in the device space
* @return the bounds in the user space
*/
public static Rectangle convertFromDeviceSpace(@NotNull Rectangle bounds) {
Rectangle b = bounds.getBounds();
if (!shouldConvert()) return b;
try {
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
Rectangle devBounds = gd.getDefaultConfiguration().getBounds(); // in user space
scaleUp(devBounds, gd); // to device space
Rectangle2D.Float devBounds2D = new Rectangle2D.Float(devBounds.x, devBounds.y, devBounds.width, devBounds.height);
Point2D.Float center2d = new Point2D.Float(b.x + b.width / 2, b.y + b.height / 2);
if (devBounds2D.contains(center2d)) {
scaleDown(b, gd);
break;
}
}
}
catch (HeadlessException ignore) {
}
return b;
}
/**
* @param g the graphics of the device
* @param bounds the bounds in the user space
* @return the bounds in the device space
*/
public static Rectangle convertToDeviceSpace(Graphics2D g, @NotNull Rectangle bounds) {
Rectangle b = bounds.getBounds();
if (!shouldConvert()) return b;
try {
scaleUp(b, g.getDeviceConfiguration().getDevice());
}
catch (HeadlessException ignore) {
}
return b;
}
private static boolean shouldConvert() {
if (SystemInfo.isLinux || // JDK-managed HiDPI mode is not yet implemented (pending)
SystemInfo.isMac) // JDK-managed HiDPI mode is permanent
{
return false;
}
if (!UIUtil.isJDKManagedHiDPI()) return false; // device space equals user space
return true;
}
private static void scaleUp(@NotNull Rectangle bounds, @NotNull GraphicsDevice gd) {
scale(bounds, gd.getDefaultConfiguration().getBounds(), JBUI.sysScale(gd));
}
private static void scaleDown(@NotNull Rectangle bounds, @NotNull GraphicsDevice gd) {
float scale = JBUI.sysScale(gd);
assert scale != 0;
scale(bounds, gd.getDefaultConfiguration().getBounds(), 1 / scale);
}
private static void scale(@NotNull Rectangle bounds, @NotNull Rectangle deviceBounds, float scale) {
// On Windows, JB SDK transforms the screen bounds to the user space as follows:
// [x, y, width, height] -> [x, y, width / scale, height / scale]
// xy are not transformed in order to avoid overlapping of the screen bounds in multi-dpi env.
// scale the delta b/w xy and deviceBounds.xy
int x = (int)Math.floor(deviceBounds.x + (bounds.x - deviceBounds.x) * scale);
int y = (int)Math.floor(deviceBounds.y + (bounds.y - deviceBounds.y) * scale);
bounds.setBounds(x, y, (int)Math.ceil(bounds.width * scale), (int)Math.ceil(bounds.height * scale));
}
}
}