Skip to content

Conversation

@ptziegler
Copy link
Contributor

@ptziegler ptziegler commented Jul 10, 2025

[90] Fix cursor initialization when using fractional scaling

Recent Eclipse version support fractional zoom level as opposed to 100% or 200% zoom. Because no icons exist for those levels, an IllegalArgumentException is thrown when trying to create the corresponding cursors.

In order to solve this, one has to consider the following cases:

  1. Newer SWT versions provide a Cursor constructor that accepts an ImageDataProvider. This constructor always creates image data matching the device zoom.

  2. Win32: Older SWT versions automatically upscale the image data to match the device zoom. Therefore the original image data must be at 100% zoom.

  3. On other operating systems, the image data should be pre-scaled, as no further scaling is done by SWT.

@ptziegler
Copy link
Contributor Author

Using the 2025-09 at 150% zoom:

image

Using the 2025-06 at 150% zoom:
image

The first case uses the ImageDataProvider constructor and produces a cursor the same size as the native Windows cursor. In the second case, the cursor is scaled by 225%, rather than 150%. However, I don't think older SWT versions support any other way.

@ptziegler
Copy link
Contributor Author

I tried out more configurations and I think this is the most promising one.

With UI scaling:
image

Without UI scaling:
image

I tested it with both the 2025-06 and 2025-09 and both yield good results. The only thing I'm not sure about is the auto-scaling introduced with b932e79 or whether Linux/MacOS should also always create a cursor using the 100% zoom image data.

@ptziegler
Copy link
Contributor Author

Right, there was an email about repo.eclipse.org temporarily being unavailable. I'll redo the ECA check later.

@pcdavid
Copy link
Contributor

pcdavid commented Jul 17, 2025

I tested it with both the 2025-06 and 2025-09 and both yield good results. The only thing I'm not sure about is the auto-scaling introduced with b932e79 or whether Linux/MacOS should also always create a cursor using the 100% zoom image data.

I only have access to Linux (Fedora 42, Gnome, Wayland), but I tested this with all combinations of:

  • Eclipse 2024-06, 2025-06, 2025-09
  • System zoom: 100%, 150%
  • getDeviceZoom(): use System.getProperty("org.eclipse.swt.internal.deviceZoom") or hard-code it to 100.

The only cases which gives invalid result are Eclipse < 2025-09 with System.getProperty("org.eclipse.swt.internal.deviceZoom"). For example, 2025-06, at 150%, with our own scaling based on System.getProperty("org.eclipse.swt.internal.deviceZoom"): the cursor is too big (probably upscaled twice, once in the getScaledImageData() and once in SWT and/or GTK/Wayland).

2025-06_150_prop_KO

I don't have access to macOS, but I think at least for Windows and Linux, it looks like we could simply have:

	// Taken from org.eclipse.gef.internal.InternalGEFPlugin.java
	private static Cursor createCursor(ImageDescriptor source, int hotspotX, int hotspotY) {
		ImageDataProvider imageDataProvider = zoom -> getScaledImageData(source, zoom);
		try {
			// SWT version >= 3.131.0
			Constructor<Cursor> ctor = Cursor.class.getConstructor(Device.class, ImageDataProvider.class, int.class,
					int.class);
			return ctor.newInstance(null, imageDataProvider, hotspotX, hotspotY);
		} catch (NoSuchMethodException e) {
			return new Cursor(null, imageDataProvider.getImageData(100), hotspotX, hotspotY);
		} catch (ReflectiveOperationException e) {
			throw new RuntimeException("Failed to instantiate Cursor", e); //$NON-NLS-1$
		}
	}

	private static ImageData getScaledImageData(ImageDescriptor descriptor, int zoom) {
		Image image = descriptor.createImage();
		try {
			return image.getImageData(zoom);
		} finally {
			image.dispose();
		}
	}

@ptziegler
Copy link
Contributor Author

ptziegler commented Jul 21, 2025

I don't have access to macOS, but I think at least for Windows and Linux, it looks like we could simply have:

	// Taken from org.eclipse.gef.internal.InternalGEFPlugin.java
	private static Cursor createCursor(ImageDescriptor source, int hotspotX, int hotspotY) {
		ImageDataProvider imageDataProvider = zoom -> getScaledImageData(source, zoom);
		try {
			// SWT version >= 3.131.0
			Constructor<Cursor> ctor = Cursor.class.getConstructor(Device.class, ImageDataProvider.class, int.class,
					int.class);
			return ctor.newInstance(null, imageDataProvider, hotspotX, hotspotY);
		} catch (NoSuchMethodException e) {
			return new Cursor(null, imageDataProvider.getImageData(100), hotspotX, hotspotY);
		} catch (ReflectiveOperationException e) {
			throw new RuntimeException("Failed to instantiate Cursor", e); //$NON-NLS-1$
		}
	}

	private static ImageData getScaledImageData(ImageDescriptor descriptor, int zoom) {
		Image image = descriptor.createImage();
		try {
			return image.getImageData(zoom);
		} finally {
			image.dispose();
		}
	}

That was my impression as well and also what we ended up doing in GEF. For MacOS, we had a similar bug report regarding the cursors and there the solution also was to use a zoom level of 100. So I think this would be a general solution:
eclipse-gef/gef-classic#354

@pcdavid
Copy link
Contributor

pcdavid commented Aug 12, 2025

Sorry for the long silence, I was on vacation.
I've tested the latest version of the code in GEF and it works for me. I'll merge that and ask colleagues to test it under Windows and macOS.

Recent Eclipse version support fractional zoom level as opposed to 100%
or 200% zoom. Because no icons exist for those levels, an
IllegalArgumentException is thrown when trying to create the
corresponding cursors.

In order to solve this, one has to consider the following cases:

1) Newer SWT versions provide a Cursor constructor that accepts an
ImageDataProvider. This constructor always creates image data matching
the device zoom.

2) Win32: Older SWT versions automatically upscale the image data to
match the device zoom. Therefore the original image data must be at 100%
zoom.

3) On other operating systems, the image data should be pre-scaled, as
no further scaling is done by SWT.

Bug: eclipse-gmf-runtime#90
@pcdavid pcdavid merged commit a81ef9a into eclipse-gmf-runtime:master Aug 12, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IllegalArgumentException for GMF cursors if display zoom is not 100%/200%

3 participants