]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Small but effective HiDPI fixes for platform G2D 95/2195/1
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Sun, 16 Sep 2018 12:25:59 +0000 (15:25 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Sun, 16 Sep 2018 12:44:13 +0000 (15:44 +0300)
* GalleryViewer now scales accoring to swt.autoScale setting
* SWTPopupMenuParticipant now properly downscales pixel coordinates to
  SWT API coordinates
* Default symbol title font size is now 12 which is more readable by
  default now that the font size is scaled more correctly than before

gitlab #119

Change-Id: I9ced77879e367efd905138798b18c3fb908e5b21

bundles/org.simantics.diagram/src/org/simantics/diagram/ui/SWTPopupMenuParticipant.java
bundles/org.simantics.diagram/src/org/simantics/diagram/ui/e4/SWTPopupMenuParticipant.java
bundles/org.simantics.g2d/plugin.xml
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java
bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java
bundles/org.simantics.g2d/src/org/simantics/g2d/utils/FontHelper.java
bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java [new file with mode: 0644]

index 588692b917fc3b5a3fa5cdd33ca175311b373c1b..c489373b6a5b0391ea2e64b7f6ec8b22fd78731c 100644 (file)
@@ -27,7 +27,6 @@ import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.Widget;
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.IWorkbenchPartSite;
 import org.simantics.g2d.canvas.ICanvasContext;
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.IWorkbenchPartSite;
 import org.simantics.g2d.canvas.ICanvasContext;
@@ -42,6 +41,7 @@ import org.simantics.utils.datastructures.hints.HintListenerAdapter;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintListener;
 import org.simantics.utils.datastructures.hints.IHintObservable;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintListener;
 import org.simantics.utils.datastructures.hints.IHintObservable;
+import org.simantics.utils.ui.SWTDPIUtil;
 
 /**
  * A participant that initializes an SWT pop-up menu and registers it with the
 
 /**
  * A participant that initializes an SWT pop-up menu and registers it with the
@@ -221,20 +221,15 @@ public class SWTPopupMenuParticipant extends AbstractCanvasParticipant {
      * @param newValue
      * @thread canvas-thread (AWT)
      */
      * @param newValue
      * @thread canvas-thread (AWT)
      */
-    protected void showPopup(Point2D newValue) {
-       final Point2D cp = (Point2D) newValue;
+    protected void showPopup(Point2D cp) {
         setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp);
         setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null));
         setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp);
         setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null));
-        display.asyncExec(new Runnable() {
-            @Override
-            public void run() {
-               if (control == null || control.isDisposed())
-                    return;
-                
-                Point p = control.toDisplay((int) cp.getX(), (int) cp.getY());
-                menuManager.getMenu().setLocation(p);
-                menuManager.getMenu().setVisible(true);
-            }
+        display.asyncExec(() -> {
+            if (control == null || control.isDisposed())
+                return;
+            Point p = control.toDisplay( SWTDPIUtil.downscaleSwtToInteger(cp) );
+            menuManager.getMenu().setLocation(p);
+            menuManager.getMenu().setVisible(true);
         });
     }
 
         });
     }
 
index eb7fd99281b39255a22d2ca98f3d7cd4e371c86f..e6e4d33ee1e98639ee64e063fd557db368209b5e 100644 (file)
@@ -14,9 +14,6 @@ package org.simantics.diagram.ui.e4;
 import java.awt.geom.Point2D;
 
 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
 import java.awt.geom.Point2D;
 
 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
-import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
-import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu;
 import org.eclipse.jface.action.GroupMarker;
 import org.eclipse.jface.action.IMenuListener2;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.GroupMarker;
 import org.eclipse.jface.action.IMenuListener2;
 import org.eclipse.jface.action.IMenuManager;
@@ -46,6 +43,7 @@ import org.simantics.utils.datastructures.hints.HintListenerAdapter;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintListener;
 import org.simantics.utils.datastructures.hints.IHintObservable;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintListener;
 import org.simantics.utils.datastructures.hints.IHintObservable;
+import org.simantics.utils.ui.SWTDPIUtil;
 
 /**
  * A participant that initializes an SWT pop-up menu and registers it with the
 
 /**
  * A participant that initializes an SWT pop-up menu and registers it with the
@@ -215,19 +213,15 @@ public class SWTPopupMenuParticipant extends AbstractCanvasParticipant {
      * @param newValue
      * @thread canvas-thread (AWT)
      */
      * @param newValue
      * @thread canvas-thread (AWT)
      */
-    protected void showPopup(Point2D newValue) {
-        final Point2D cp = (Point2D) newValue;
+    protected void showPopup(Point2D cp) {
         setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp);
         setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null));
         setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp);
         setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null));
-        display.asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                if (control == null || control.isDisposed())
-                    return;
-                Point p = control.toDisplay((int) cp.getX(), (int) cp.getY());
-                menuManager.getMenu().setLocation(p);
-                menuManager.getMenu().setVisible(true);
-            }
+        display.asyncExec(() -> {
+            if (control == null || control.isDisposed())
+                return;
+            Point p = control.toDisplay( SWTDPIUtil.downscaleSwtToInteger(cp) );
+            menuManager.getMenu().setLocation(p);
+            menuManager.getMenu().setVisible(true);
         });
     }
 
         });
     }
 
index 10cb3b73f512c4a0b637ef70c8aefc1128596658..85dfc192cbdd2ef30073cb26cf5f71d5f9115b9c 100644 (file)
@@ -34,7 +34,7 @@
             categoryId="org.simantics.g2d"
             id="org.simantics.gallery.itemfont"
             label="Symbol Font"
             categoryId="org.simantics.g2d"
             id="org.simantics.gallery.itemfont"
             label="Symbol Font"
-            value="Tahoma-regular-8">
+            value="Tahoma-regular-12">
          <description>
             The symbol font is used for labels of symbols in the symbol library view.
          </description>
          <description>
             The symbol font is used for labels of symbols in the symbol library view.
          </description>
index aded9d170a2ccc9238b9e72f96e62c79f89fd3ef..0c11d348ab62fcc3860437b1e8a72f779f5349d5 100644 (file)
@@ -155,7 +155,7 @@ public class DiagramHints {
 
     /**
      * A Hint for other participants to use for showing the context menu at the
 
     /**
      * A Hint for other participants to use for showing the context menu at the
-     * contol position specified by the Point2D argument.
+     * control position specified by the Point2D argument.
      */
     public static final Key SHOW_POPUP_MENU                = new KeyOf(Point2D.class, "SHOW_POPUP_MENU_CMD");
 
      */
     public static final Key SHOW_POPUP_MENU                = new KeyOf(Point2D.class, "SHOW_POPUP_MENU_CMD");
 
index 2e8ad5b66f434345ef03e435a5440bb409403fb2..ce5687e8486e05b38f4ca2b28efd72fa8af7b9c7 100644 (file)
@@ -105,6 +105,7 @@ import org.simantics.utils.threads.logger.ITask;
 import org.simantics.utils.threads.logger.ThreadLogger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.simantics.utils.threads.logger.ThreadLogger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.simantics.utils.ui.SWTDPIUtil;
 
 /**
  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
 
 /**
  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
@@ -165,22 +166,24 @@ public class GalleryViewer extends ContentViewer {
         chassis = new SWTChassis(composite, style) {
             @Override
             public Point computeSize(int wHint, int hHint, boolean changed) {
         chassis = new SWTChassis(composite, style) {
             @Override
             public Point computeSize(int wHint, int hHint, boolean changed) {
-//              System.out.println("chassis compute size: " + wHint + ", " + hHint + ", " + changed);
-
                 if (diagram == null)
                     return super.computeSize(wHint, hHint, changed);
 
                 if (diagram == null)
                     return super.computeSize(wHint, hHint, changed);
 
+                // Note: This code must take into account that FlowLayout expects to 
+                // receive pixel coordinates, not SWT API coordinates.
+
                 Rectangle2D rect;
 //                if (!changed) {
 //                    rect = ElementUtils.getSurroundingElementBoundsOnDiagram(diagram.getSnapshot());
 //                }
 //                else
                 {
                 Rectangle2D rect;
 //                if (!changed) {
 //                    rect = ElementUtils.getSurroundingElementBoundsOnDiagram(diagram.getSnapshot());
 //                }
 //                else
                 {
-                    Double wH = wHint==SWT.DEFAULT ? null : (double) wHint-vMargin-vMargin;
-                    Double hH = hHint==SWT.DEFAULT ? null : (double) hHint-hMargin-hMargin;
+                    Double wH = wHint==SWT.DEFAULT ? null : (double) SWTDPIUtil.upscaleSwt(wHint)-hMargin*2;
+                    Double hH = hHint==SWT.DEFAULT ? null : (double) SWTDPIUtil.upscaleSwt(hHint)-vMargin*2;
                     rect = fl.computeSize(diagram, wH, hH);
                     rect = fl.computeSize(diagram, wH, hH);
+                    SWTDPIUtil.downscaleSwt(rect, rect);
                 }
                 }
-                return new Point((int)rect.getMaxX()+hMargin*2, (int)rect.getMaxY()+vMargin*2);
+                return new Point((int)rect.getWidth()+hMargin*2, (int)rect.getHeight()+vMargin*2);
             }
         };
 
             }
         };
 
@@ -210,12 +213,7 @@ public class GalleryViewer extends ContentViewer {
             hintCtx.setHint(DiagramHints.KEY_DIAGRAM, diagram);
 
             // Force layout
             hintCtx.setHint(DiagramHints.KEY_DIAGRAM, diagram);
 
             // Force layout
-            ThreadUtils.asyncExec(swtThread, new Runnable() {
-                @Override
-                public void run() {
-                    resized(false);
-                }
-            });
+            ThreadUtils.asyncExec(swtThread, () -> resized(false));
         });
 
         chassis.addControlListener(new ControlListener() {
         });
 
         chassis.addControlListener(new ControlListener() {
@@ -255,12 +253,9 @@ public class GalleryViewer extends ContentViewer {
                     fontRegistry.removeListener(fontRegistryListener);
 
                 // Prevent memory leaks.
                     fontRegistry.removeListener(fontRegistryListener);
 
                 // Prevent memory leaks.
-                ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
-                    @Override
-                    public void run() {
-                        chassis.getAWTComponent().setCanvasContext(null);
-                        ctx.dispose();
-                    }
+                ThreadUtils.asyncExec(ctx.getThreadAccess(), () -> {
+                    chassis.getAWTComponent().setCanvasContext(null);
+                    ctx.dispose();
                 });
             }
         });
                 });
             }
         });
@@ -274,12 +269,7 @@ public class GalleryViewer extends ContentViewer {
             currentItemFont = FontHelper.toAwt(fdn);
             itemClass.getSingleItem(GalleryItemSGNode.class).setFont(currentItemFont);
             // FIXME: a bug exists in this case. The group size will not be refreshed even though the sizes of the gallery items are recalculated and changed.
             currentItemFont = FontHelper.toAwt(fdn);
             itemClass.getSingleItem(GalleryItemSGNode.class).setFont(currentItemFont);
             // FIXME: a bug exists in this case. The group size will not be refreshed even though the sizes of the gallery items are recalculated and changed.
-            ThreadUtils.asyncExec(swtThread, new Runnable() {
-                @Override
-                public void run() {
-                    resized(true);
-                }
-            });
+            ThreadUtils.asyncExec(swtThread, () -> resized(true));
         }
     };
 
         }
     };
 
@@ -324,26 +314,25 @@ public class GalleryViewer extends ContentViewer {
         //System.out.println(this + ".resized(" + refreshElementSizes + ")");
         if (chassis.isDisposed())
             return;
         //System.out.println(this + ".resized(" + refreshElementSizes + ")");
         if (chassis.isDisposed())
             return;
-        org.eclipse.swt.graphics.Rectangle b = chassis.getBounds();
+        org.eclipse.swt.graphics.Rectangle b = SWTDPIUtil.upscaleSwt(chassis.getBounds());
+        //System.out.println("chassis bounds: " + b);
         final Rectangle2D bounds = new Rectangle2D.Double(hMargin, vMargin, b.width-hMargin*2, b.height-vMargin*2);
         final Rectangle2D bounds = new Rectangle2D.Double(hMargin, vMargin, b.width-hMargin*2, b.height-vMargin*2);
-        ctx.getThreadAccess().asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                if (ctx.isDisposed())
-                    return;
-                if (diagram == null)
-                    return;
-                //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update");
-                if (refreshElementSizes)
-                    refreshElementSizes();
-                fl.layout(diagram, bounds);
-
-                // Makes sure RTreeNode is marked dirty and everything is
-                // properly repainted.
-                if (itemPainter != null)
-                    itemPainter.updateAll();
-                ctx.getContentContext().setDirty();
-            }});
+        ctx.getThreadAccess().asyncExec(() -> {
+            if (ctx.isDisposed())
+                return;
+            if (diagram == null)
+                return;
+            //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update");
+            if (refreshElementSizes)
+                refreshElementSizes();
+            fl.layout(diagram, bounds);
+
+            // Makes sure RTreeNode is marked dirty and everything is
+            // properly repainted.
+            if (itemPainter != null)
+                itemPainter.updateAll();
+            ctx.getContentContext().setDirty();
+        });
     }
 
     /**
     }
 
     /**
@@ -392,12 +381,9 @@ public class GalleryViewer extends ContentViewer {
         // wrong thread (SWT) for AWTChassis.
         chassis.getAWTComponent().setCanvasContext(canvasContext);
 
         // wrong thread (SWT) for AWTChassis.
         chassis.getAWTComponent().setCanvasContext(canvasContext);
 
-        swtThread.asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                if (!chassis.isDisposed())
-                    chassis.setCanvasContext(canvasContext);
-            }
+        swtThread.asyncExec(() -> {
+            if (!chassis.isDisposed())
+                chassis.setCanvasContext(canvasContext);
         });
 
         canvasContext.assertParticipantDependencies();
         });
 
         canvasContext.assertParticipantDependencies();
@@ -573,12 +559,7 @@ public class GalleryViewer extends ContentViewer {
                 // 3. Calculate maximum vertical space needed by current diagram element texts
                 refreshElementSizes();
 
                 // 3. Calculate maximum vertical space needed by current diagram element texts
                 refreshElementSizes();
 
-                ThreadUtils.asyncExec(swtThread, new Runnable() {
-                    @Override
-                    public void run() {
-                        resized(false);
-                    }
-                });
+                ThreadUtils.asyncExec(swtThread, () -> resized(false));
                 // $AWT-Thread-End$
             }
         });
                 // $AWT-Thread-End$
             }
         });
@@ -609,9 +590,9 @@ public class GalleryViewer extends ContentViewer {
         // Calculate maximum vertical space needed by current diagram element texts
         FontMetrics metrics = awtComponent.getFontMetrics(currentItemFont);
         int fontHeight = metrics.getHeight();
         // Calculate maximum vertical space needed by current diagram element texts
         FontMetrics metrics = awtComponent.getFontMetrics(currentItemFont);
         int fontHeight = metrics.getHeight();
-        int maxWidth = (int) itemSize.getWidth();
-        Rectangle2D size = itemSize;
-        java.awt.Point targetSize = new java.awt.Point((int) itemSize.getWidth(), (int) itemSize.getHeight());
+        Rectangle2D size = SWTDPIUtil.upscaleSwt(itemSize);
+        int maxWidth = (int) size.getWidth();
+        java.awt.Point targetSize = new java.awt.Point((int) size.getWidth(), (int) size.getHeight());
         diagram.setHint(DiagramHints.KEY_ELEMENT_RASTER_TARGET_SIZE, targetSize);
         int maxLinesNeeded = 0;
         for (IElement el : diagram.getElements()) {
         diagram.setHint(DiagramHints.KEY_ELEMENT_RASTER_TARGET_SIZE, targetSize);
         int maxLinesNeeded = 0;
         for (IElement el : diagram.getElements()) {
@@ -619,7 +600,7 @@ public class GalleryViewer extends ContentViewer {
             // for caching rendered images in the correct size only.
             // NOTE: currently this is not used in GalleryItemPainter since the
             // target size is now propagated through the element class loading
             // for caching rendered images in the correct size only.
             // NOTE: currently this is not used in GalleryItemPainter since the
             // target size is now propagated through the element class loading
-            // process through the diagram hint KEY_ELEMENT_RASTER_REFERENCE_SIZE.
+            // process through the diagram hint KEY_ELEMENT_RASTER_TARGET_SIZE.
             el.setHint(GalleryItemSGNode.KEY_TARGET_IMAGE_SIZE, targetSize);
 
             String text = ElementUtils.getText(el);
             el.setHint(GalleryItemSGNode.KEY_TARGET_IMAGE_SIZE, targetSize);
 
             String text = ElementUtils.getText(el);
@@ -647,14 +628,11 @@ public class GalleryViewer extends ContentViewer {
                 if (image != i)
                     continue;
 
                 if (image != i)
                     continue;
 
-                ctx.getThreadAccess().asyncExec(new Runnable() {
-                    @Override
-                    public void run() {
-                        //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")");
-                        // Update scene graph and repaint.
-                        el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
-                        ctx.getContentContext().setDirty();
-                    }
+                ctx.getThreadAccess().asyncExec(() -> {
+                    //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")");
+                    // Update scene graph and repaint.
+                    el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
+                    ctx.getContentContext().setDirty();
                 });
                 break;
             }
                 });
                 break;
             }
@@ -665,12 +643,9 @@ public class GalleryViewer extends ContentViewer {
         if (ctx.getThreadAccess().currentThreadAccess()) {
             ctx.add(p);
         } else {
         if (ctx.getThreadAccess().currentThreadAccess()) {
             ctx.add(p);
         } else {
-            ctx.getThreadAccess().asyncExec(new Runnable() {
-                @Override
-                public void run() {
-                    if (!ctx.isDisposed())
-                        ctx.add(p);
-                }
+            ctx.getThreadAccess().asyncExec(() -> {
+                if (!ctx.isDisposed())
+                    ctx.add(p);
             });
         }
     }
             });
         }
     }
@@ -679,12 +654,9 @@ public class GalleryViewer extends ContentViewer {
         if (ctx.getThreadAccess().currentThreadAccess()) {
             ctx.add(p);
         } else {
         if (ctx.getThreadAccess().currentThreadAccess()) {
             ctx.add(p);
         } else {
-            ctx.getThreadAccess().asyncExec(new Runnable() {
-                @Override
-                public void run() {
-                    if (!ctx.isDisposed())
-                        ctx.add(p);
-                }
+            ctx.getThreadAccess().asyncExec(() -> {
+                if (!ctx.isDisposed())
+                    ctx.add(p);
             });
         }
     }
             });
         }
     }
index 06bd80aa8e027c45a971de4b179bf1899c3e38c6..98d5c78f664759653e2a5ffad20cbf7600da24af 100644 (file)
 package org.simantics.g2d.utils;
 
 import java.awt.Font;
 package org.simantics.g2d.utils;
 
 import java.awt.Font;
-import java.awt.Toolkit;
 
 import org.eclipse.jface.resource.FontRegistry;
 import org.eclipse.swt.graphics.FontData;
 import org.eclipse.ui.PlatformUI;
 
 import org.eclipse.jface.resource.FontRegistry;
 import org.eclipse.swt.graphics.FontData;
 import org.eclipse.ui.PlatformUI;
+import org.simantics.utils.ui.SWTDPIUtil;
 
 public final class FontHelper {
 
 
 public final class FontHelper {
 
@@ -35,8 +35,7 @@ public final class FontHelper {
     }
 
     public static java.awt.Font toAwt(FontData fd) {
     }
 
     public static java.awt.Font toAwt(FontData fd) {
-        int resolution = Toolkit.getDefaultToolkit().getScreenResolution();
-        int awtFontSize = (int) Math.round((double) fd.getHeight() * resolution / 72.0);
+        int awtFontSize = SWTDPIUtil.upscaleSwt(fd.getHeight());
         // The style constants for SWT and AWT map exactly, and since they are int constants, they should
         // never change. So, the SWT style is passed through as the AWT style.
         Font font = new java.awt.Font(fd.getName(), fd.getStyle(), awtFontSize);
         // The style constants for SWT and AWT map exactly, and since they are int constants, they should
         // never change. So, the SWT style is passed through as the AWT style.
         Font font = new java.awt.Font(fd.getName(), fd.getStyle(), awtFontSize);
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())));
+       }
+
+}