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.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
* @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));
- 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);
});
}
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.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
* @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));
- 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);
});
}
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>
/**
* 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");
import org.simantics.utils.threads.ThreadUtils;
import org.simantics.utils.threads.logger.ITask;
import org.simantics.utils.threads.logger.ThreadLogger;
+import org.simantics.utils.ui.SWTDPIUtil;
/**
* @author Toni Kalajainen <toni.kalajainen@vtt.fi>
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);
+ // 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
{
- 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);
+ 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);
}
};
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() {
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();
});
}
});
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));
}
};
//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);
- 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();
+ });
}
/**
// 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();
// 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$
}
});
// 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()) {
// 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);
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;
}
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);
});
}
}
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);
});
}
}
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.simantics.utils.ui.SWTDPIUtil;
public final class FontHelper {
}
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);
--- /dev/null
+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())));
+ }
+
+}