+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())));
+ }
+
+}