package org.simantics.utils.ui; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.DPIUtil; /** * This class is needed to support {@link SWTAWTComponent} and HiDPI screens * with different display zoom settings. * *

* See {@link DPIUtil} for explanations on what downscaling and upscaling are in * this context. If user has zoom > 100% in use in the system display settings, * SWT's API coordinates will be "downscaled" from actual internal HiDPI * coordinates (pixels). * *

* This class contains methods to work around problems with e.g. opening context * menu's in the correct location when using {@link SWTAWTComponent}. AWT always * returns HiDPI pixel coordinates which need to be converted to SWT API * coordinates before giving them to any SWT APIs. * * @author Tuukka Lehtonen * @since 1.36.0 */ @SuppressWarnings("restriction") public class SWTDPIUtil { private static boolean initialized = false; private static int swtZoom; private static boolean hasSwtScale; private static float fromSwtInternalScalingFactorF; private static double fromSwtInternalScalingFactorD; private static float toSwtInternalScalingFactorF; private static double toSwtInternalScalingFactorD; private static void initialize() { if (initialized) return; swtZoom = DPIUtil.autoScaleUp(100); hasSwtScale = swtZoom != 100; fromSwtInternalScalingFactorD = 100.0 / (double) swtZoom; toSwtInternalScalingFactorD = (double) swtZoom / 100.0; fromSwtInternalScalingFactorF = (float) fromSwtInternalScalingFactorD; toSwtInternalScalingFactorF = (float) toSwtInternalScalingFactorD; // System.out.format("SWTDPIUtil:%n\tswt zoom = %d%n\tfrom swt internal scaling factor = %f%n\tto swt internal scaling factor = %f%n", // swtZoom, // fromSwtInternalScalingFactorD, // toSwtInternalScalingFactorD, // ); initialized = true; } // Internals private static Rectangle scale(float s, Rectangle r, Rectangle target) { if (s == 1.0f) { if (r == target) return r; if (target == null) { return new Rectangle(r.x, r.y, r.width, r.height); } else { target.x = r.x; target.y = r.y; target.width = r.width; target.height = r.height; return target; } } if (target == null) { return new Rectangle( Math.round(r.x*s), Math.round(r.y*s), Math.round(r.width*s), Math.round(r.height*s)); } else { target.x = Math.round(r.x*s); target.y = Math.round(r.y*s); target.width = Math.round(r.width*s); target.height = Math.round(r.height*s); return target; } } private static Rectangle2D scale(double s, Rectangle2D r, Rectangle2D target) { if (s == 1.0) { if (r == target) return r; if (target == null) return (Rectangle2D) r.clone(); target.setFrame(r); return target; } if (target == null) target = (Rectangle2D) r.clone(); target.setFrame(r.getX()*s, r.getY()*s, r.getWidth()*s, r.getHeight()*s); return target; } private static double downscaleSwt0(double x) { return hasSwtScale ? x * fromSwtInternalScalingFactorD : x; } private static int downscaleToIntegerSwt0(double x) { return (int)(hasSwtScale ? Math.round((double) x * fromSwtInternalScalingFactorD) : x); } private static int downscaleSwt0(int x) { return hasSwtScale ? (int) Math.round((double) x * fromSwtInternalScalingFactorD) : x; } private static double upscaleSwt0(double x) { return hasSwtScale ? x * toSwtInternalScalingFactorD : x; } private static int upscaleToIntegerSwt0(double x) { return (int)(hasSwtScale ? Math.round((double) x * toSwtInternalScalingFactorD) : x); } private static int upscaleSwt0(int x) { return hasSwtScale ? (int) Math.round((double) x * toSwtInternalScalingFactorD) : x; } // SWT API Coordinates <-> pixels // Downscaling public static double downscaleSwt(double x) { initialize(); return downscaleSwt0(x); } public static int downscaleSwt(int x) { initialize(); return downscaleSwt0(x); } public static Point2D downscaleSwt(double x, double y) { initialize(); if (!hasSwtScale) return new Point2D.Double(x, y); double s = fromSwtInternalScalingFactorD; return new Point2D.Double(x * s, y * s); } public static Point downscaleSwt(int x, int y) { initialize(); return new Point(downscaleSwt0(x), downscaleSwt0(y)); } public static Point2D downscaleSwt(Point2D p) { return downscaleSwt(p.getX(), p.getY()); } public static Point downscaleSwtToInteger(Point2D p) { initialize(); return new Point(downscaleToIntegerSwt0(p.getX()), downscaleToIntegerSwt0(p.getY())); } public static Rectangle2D downscaleSwt(Rectangle2D r, Rectangle2D target) { initialize(); return scale(fromSwtInternalScalingFactorD, r, target); } public static Rectangle2D downscaleSwt(Rectangle2D r) { return downscaleSwt(r, null); } public static Rectangle downscaleSwt(Rectangle r, Rectangle target) { initialize(); return scale(fromSwtInternalScalingFactorF, r, target); } public static Rectangle downscaleSwt(Rectangle r) { return downscaleSwt(r, null); } public static Rectangle downscaleSwtToInteger(Rectangle2D r) { initialize(); return new Rectangle( downscaleToIntegerSwt0(r.getMinX()), downscaleToIntegerSwt0(r.getMinY()), downscaleToIntegerSwt0(r.getWidth()), downscaleToIntegerSwt0(r.getHeight())); } // Upscaling public static double upscaleSwt(double x) { initialize(); return upscaleSwt0(x); } public static int upscaleSwt(int x) { initialize(); return upscaleSwt0(x); } public static Point2D upscaleSwt(double x, double y) { initialize(); if (!hasSwtScale) return new Point2D.Double(x, y); double s = toSwtInternalScalingFactorD; return new Point2D.Double(x * s, y * s); } public static Point upscaleSwt(int x, int y) { initialize(); return new Point(upscaleSwt0(x), upscaleSwt0(y)); } public static Point2D upscaleSwt(Point2D p) { initialize(); return (hasSwtScale && p != null) ? upscaleSwt(p.getX(), p.getY()) : p; } public static Point upscaleSwtToInteger(Point2D p) { initialize(); return new Point(upscaleToIntegerSwt0(p.getX()), upscaleToIntegerSwt0(p.getY())); } public static Point upscaleSwt(Point p) { initialize(); return (hasSwtScale && p != null) ? upscaleSwt(p.x, p.y) : p; } public static Rectangle2D upscaleSwt(Rectangle2D r, Rectangle2D target) { initialize(); return scale(toSwtInternalScalingFactorD, r, target); } public static Rectangle upscaleSwt(Rectangle r, Rectangle target) { initialize(); return scale(toSwtInternalScalingFactorF, r, target); } public static Rectangle2D upscaleSwt(Rectangle2D r) { return upscaleSwt(r, null); } public static Rectangle upscaleSwt(Rectangle r) { return upscaleSwt(r, null); } public static Rectangle upscaleSwtToInteger(Rectangle2D r) { return new Rectangle( upscaleToIntegerSwt0(r.getMinX()), upscaleToIntegerSwt0(r.getMinY()), upscaleToIntegerSwt0(r.getWidth()), upscaleToIntegerSwt0(r.getHeight())); } }