diff --git a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java index 90aec41f3fdd..533beae44a39 100644 --- a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java +++ b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java @@ -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; - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index e0864abfaf30..c4fabfcb01bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -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)); + } + } }