From 25968f028327dcf6e489d43f06286c872dc5184f Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Tue, 16 Jul 2024 06:32:16 +0200 Subject: [PATCH] Fix errors in drag cursors when using high-DPI screens The cursors used in the flyout palette only exist for the default 100%. If a system is using a higher zoom level, the creation of those cursors fails with an IllegalArgumentException. Instead, we should instead scale the cursor image to match the current level. This change moves the code for detecting the device zoom from the SharedCursors class to the InternalGEFPlugin, so that it becomes available to the DragCursors class. From there, we call Image.getImageData(...) instead of ImageDescriptor.getImageData(), in order to get a scaled ImageData object via DPIUtil magic. See https://github.com/eclipse/gef-classic/issues/464 --- .../src/org/eclipse/gef/SharedCursors.java | 29 ++----------- .../gef/internal/InternalGEFPlugin.java | 22 ++++++++++ .../ui/palette/FlyoutPaletteComposite.java | 42 ++++++++++--------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/org.eclipse.gef/src/org/eclipse/gef/SharedCursors.java b/org.eclipse.gef/src/org/eclipse/gef/SharedCursors.java index 4bceed9d1..73e264378 100644 --- a/org.eclipse.gef/src/org/eclipse/gef/SharedCursors.java +++ b/org.eclipse.gef/src/org/eclipse/gef/SharedCursors.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -15,10 +15,11 @@ import org.eclipse.swt.graphics.Cursor; import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.core.runtime.Platform; + import org.eclipse.draw2d.Cursors; import org.eclipse.gef.internal.Internal; +import org.eclipse.gef.internal.InternalGEFPlugin; /** * A shared collection of Cursors. @@ -44,8 +45,6 @@ public class SharedCursors extends Cursors { */ public static final Cursor CURSOR_TREE_MOVE; - private static int deviceZoom = -1; - static { CURSOR_PLUG = createCursor("icons/plug-cursor.png"); //$NON-NLS-1$ CURSOR_PLUG_NOT = createCursor("icons/plugnot-cursor.png"); //$NON-NLS-1$ @@ -55,26 +54,6 @@ public class SharedCursors extends Cursors { private static Cursor createCursor(String sourceName) { ImageDescriptor src = ImageDescriptor.createFromFile(Internal.class, sourceName); - return new Cursor(null, src.getImageData(getDeviceZoom()), 0, 0); + return new Cursor(null, src.getImageData(InternalGEFPlugin.getDeviceZoom()), 0, 0); } - - private static int getDeviceZoom() { - if (deviceZoom == -1) { - deviceZoom = 100; // default value - String deviceZoomProperty = System.getProperty("org.eclipse.swt.internal.deviceZoom"); //$NON-NLS-1$ - if (deviceZoomProperty != null) { - try { - deviceZoom = Integer.parseInt(deviceZoomProperty); - } catch (NumberFormatException ex) { - // if the property can not be parsed we keep the default 100% zoom level - } - } - } - // On Mac and Linux X11 ImageData for cursors should always be created with 100% device zoom - return Platform.getOS().equals(Platform.OS_MACOSX) || - (Platform.getOS().equals(Platform.OS_LINUX) && "x11".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE"))) //$NON-NLS-1$ //$NON-NLS-2$ - ? 100 - : deviceZoom; - } - } diff --git a/org.eclipse.gef/src/org/eclipse/gef/internal/InternalGEFPlugin.java b/org.eclipse.gef/src/org/eclipse/gef/internal/InternalGEFPlugin.java index 6a6ee9494..7dbb96e5e 100644 --- a/org.eclipse.gef/src/org/eclipse/gef/internal/InternalGEFPlugin.java +++ b/org.eclipse.gef/src/org/eclipse/gef/internal/InternalGEFPlugin.java @@ -13,6 +13,7 @@ package org.eclipse.gef.internal; +import org.eclipse.core.runtime.Platform; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; @@ -28,6 +29,7 @@ public class InternalGEFPlugin extends AbstractUIPlugin { private static BundleContext context; private static AbstractUIPlugin singleton; + private static int deviceZoom = -1; public InternalGEFPlugin() { singleton = this; @@ -58,6 +60,26 @@ public static AbstractUIPlugin getDefault() { return singleton; } + public static int getDeviceZoom() { + if (deviceZoom == -1) { + deviceZoom = 100; // default value + String deviceZoomProperty = System.getProperty("org.eclipse.swt.internal.deviceZoom"); //$NON-NLS-1$ + if (deviceZoomProperty != null) { + try { + deviceZoom = Integer.parseInt(deviceZoomProperty); + } catch (NumberFormatException ex) { + // if the property can not be parsed we keep the default 100% zoom level + } + } + } + // On Mac and Linux X11 ImageData for cursors should always be created with 100% + // device zoom + return Platform.getOS().equals(Platform.OS_MACOSX) || (Platform.getOS().equals(Platform.OS_LINUX) + && "x11".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE"))) //$NON-NLS-1$ //$NON-NLS-2$ + ? 100 + : deviceZoom; + } + /** * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ diff --git a/org.eclipse.gef/src/org/eclipse/gef/ui/palette/FlyoutPaletteComposite.java b/org.eclipse.gef/src/org/eclipse/gef/ui/palette/FlyoutPaletteComposite.java index cdf23e875..a3fa4b0ed 100644 --- a/org.eclipse.gef/src/org/eclipse/gef/ui/palette/FlyoutPaletteComposite.java +++ b/org.eclipse.gef/src/org/eclipse/gef/ui/palette/FlyoutPaletteComposite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 IBM Corporation and others. + * Copyright (c) 2004, 2024 IBM Corporation and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -32,6 +32,8 @@ import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; @@ -51,7 +53,6 @@ import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.TransferDropTargetListener; @@ -84,6 +85,7 @@ import org.eclipse.gef.GraphicalViewer; import org.eclipse.gef.dnd.TemplateTransfer; import org.eclipse.gef.internal.GEFMessages; +import org.eclipse.gef.internal.InternalGEFPlugin; import org.eclipse.gef.internal.InternalImages; import org.eclipse.gef.internal.ui.palette.PaletteColorUtil; import org.eclipse.gef.ui.views.palette.PaletteView; @@ -1557,37 +1559,37 @@ private static class DragCursors { * @return the cursor */ public static Cursor getCursor(int code) { - Display display = Display.getCurrent(); if (cursors[code] == null) { - ImageDescriptor source = null; - ImageDescriptor mask = null; switch (code) { case LEFT: - source = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_LEFT_SOURCE); - mask = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_LEFT_MASK); - cursors[LEFT] = new Cursor(display, source.getImageData(), mask.getImageData(), 16, 16); + cursors[LEFT] = createCursor(ISharedImages.IMG_OBJS_DND_LEFT_SOURCE, + ISharedImages.IMG_OBJS_DND_LEFT_MASK); break; case RIGHT: - source = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_RIGHT_SOURCE); - mask = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_RIGHT_MASK); - cursors[RIGHT] = new Cursor(display, source.getImageData(), mask.getImageData(), 16, 16); + cursors[RIGHT] = createCursor(ISharedImages.IMG_OBJS_DND_RIGHT_SOURCE, + ISharedImages.IMG_OBJS_DND_RIGHT_MASK); break; default: case INVALID: - source = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_INVALID_SOURCE); - mask = PlatformUI.getWorkbench().getSharedImages() - .getImageDescriptor(ISharedImages.IMG_OBJS_DND_INVALID_MASK); - cursors[INVALID] = new Cursor(display, source.getImageData(), mask.getImageData(), 16, 16); + cursors[INVALID] = createCursor(ISharedImages.IMG_OBJS_DND_INVALID_SOURCE, + ISharedImages.IMG_OBJS_DND_INVALID_MASK); break; } } return cursors[code]; } + private static Cursor createCursor(String sourceName, String maskName) { + Image source = PlatformUI.getWorkbench().getSharedImages().getImage(sourceName); + Image mask = PlatformUI.getWorkbench().getSharedImages().getImage(maskName); + // Scales the image if the display is using neither 100% nor 200% zoom + ImageData sourceData = source.getImageData(InternalGEFPlugin.getDeviceZoom()); + ImageData maskData = mask.getImageData(InternalGEFPlugin.getDeviceZoom()); + // Hotspot should be the center of the image. e.g. (16, 16) on 100% zoom + int hotspotX = sourceData.width / 2; + int hotspotY = sourceData.height / 2; + return new Cursor(null, sourceData, maskData, hotspotX, hotspotY); + } + } } \ No newline at end of file