]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java
Small but effective HiDPI fixes for platform G2D
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / SWTDPIUtil.java
diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java
new file mode 100644 (file)
index 0000000..9622764
--- /dev/null
@@ -0,0 +1,232 @@
+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.
+ * 
+ * <p>
+ * 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).
+ * 
+ * <p>
+ * 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;
+       }
+
+       // SWT API Coordinates <-> pixels
+
+       public static double downscaleSwt(double x) {
+               initialize();
+               return hasSwtScale ? x * fromSwtInternalScalingFactorD : x;
+       }
+
+       public static int downscaleSwt(int x) {
+               if (!hasSwtScale)
+                       return x;
+               return (int) Math.round(downscaleSwt((double) 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) {
+               return new Point(downscaleSwt(x), downscaleSwt(y));
+       }
+
+       public static Point2D downscaleSwt(Point2D p) {
+               return downscaleSwt(p.getX(), p.getY());
+       }
+
+       public static Point downscaleSwtToInteger(Point2D p) {
+               return new Point(
+                               (int) Math.round(downscaleSwt(p.getX())),
+                               (int) Math.round(downscaleSwt(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) {
+               return new Rectangle( 
+                               (int) Math.round(downscaleSwt(r.getMinX())),
+                               (int) Math.round(downscaleSwt(r.getMinY())),
+                               (int) Math.round(downscaleSwt(r.getWidth())),
+                               (int) Math.round(downscaleSwt(r.getHeight())));
+       }
+
+       public static double upscaleSwt(double x) {
+               initialize();
+               return hasSwtScale ? x * toSwtInternalScalingFactorD : x;
+       }
+
+       public static int upscaleSwt(int x) {
+               initialize();
+               if (!hasSwtScale)
+                       return x;
+               return (int) Math.round((double) x * toSwtInternalScalingFactorD);
+       }
+
+       public static Point2D upscaleSwt(double x, double y) {
+               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) {
+               return new Point(upscaleSwt(x), upscaleSwt(y));
+       }
+
+       public static Point2D upscaleSwt(Point2D p) {
+               return upscaleSwt(p.getX(), p.getY());
+       }
+
+       public static Point upscaleSwtToInteger(Point2D p) {
+               return new Point(
+                               (int) Math.round(upscaleSwt(p.getX())),
+                               (int) Math.round(upscaleSwt(p.getY())));
+       }
+
+       public static Point upscaleSwt(Point p) {
+               return upscaleSwt(p.x, p.y);
+       }
+
+       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( 
+                               (int) Math.round(upscaleSwt(r.getMinX())),
+                               (int) Math.round(upscaleSwt(r.getMinY())),
+                               (int) Math.round(upscaleSwt(r.getWidth())),
+                               (int) Math.round(upscaleSwt(r.getHeight())));
+       }
+
+}