Skip to content

Conversation

ShahzaibIbrahim
Copy link
Contributor

On Windows, the mouse pointer size can be increased in Accessibility settings. Previously SWT always created cursors at their logical bitmap size (e.g. 16x16, 32x32), ignoring the accessibility scale.

This change reads CursorBaseSize from
HKCU\Control Panel\Cursors and uses it as a scale factor when creating SWT cursors. For example, with scale = 5, a 16px cursor bitmap is scaled to 80px before being displayed, matching the user’s configured pointer size.

This aligns SWT custom cursors with the system accessibility setting and improves usability for users with enlarged cursors.

How to Test

  • Run following snippet.
public class Snippet386 {

	private static final int IMAGE_SIZE_IN_POINTS = 16;

	public static void main(String[] args) {
		System.setProperty("swt.autoScale.updateOnRuntime", "true");
		Display display = new Display();
		Shell shell = createShell(display);

		Label zoomLabel = createZoomLabel(shell);
		Combo combo = createConstructorCombo(shell);

		addZoomChangedListener(shell, zoomLabel);
		addPaintTicks(shell);

		CursorManager cursorManager = new CursorManager(display, shell, combo);
		combo.addListener(SWT.Selection, e -> cursorManager.updateCursor());
		cursorManager.updateCursor();

		shell.setSize(300, 600);
		shell.open();

		eventLoop(display, shell);
		display.dispose();
	}

	private static Shell createShell(Display display) {
		Shell shell = new Shell(display);
		shell.setText("Cursor Constructors Example");
		shell.setLayout(new GridLayout(1, false));

		Label label = new Label(shell, SWT.NONE);
		label.setText("Choose Cursor Constructor:");
		return shell;
	}

	private static Combo createConstructorCombo(Composite parent) {
		Combo combo = new Combo(parent, SWT.READ_ONLY);
		combo.setItems("Cursor(Device, int)", "Cursor(Device, ImageData, ImageData, int, int)",
				"Cursor(Device, ImageData, int, int)", "Cursor(Device, ImageDataProvider, int, int)");
		combo.select(0);
		return combo;
	}

	private static Label createZoomLabel(Composite parent) {
		Label zoomLabel = new Label(parent, SWT.NONE);
		zoomLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		setZoomLabelText(zoomLabel);
		return zoomLabel;
	}

	private static void setZoomLabelText(Label label) {
		@SuppressWarnings("restriction")
		int zoom = DPIUtil.getDeviceZoom();
		int expectedCursorSize = Math.round(IMAGE_SIZE_IN_POINTS * (zoom / 100f));
		label.setText("Current zoom: " + zoom + "% Expected Cursor Size = " + expectedCursorSize);
	}

	private static void addZoomChangedListener(Shell shell, Label zoomLabel) {
		shell.addListener(SWT.ZoomChanged, event -> {
			setZoomLabelText(zoomLabel);
			shell.layout();
		});
	}

	private static void addPaintTicks(Shell shell) {
		shell.addPaintListener(event -> {
			drawTicks(event.gc);
		});
	}

	private static void drawTicks(GC gc) {
		@SuppressWarnings("restriction")
		int deviceZoom = DPIUtil.getDeviceZoom();
		float devScale = deviceZoom / 100f;
		int yPos = 300;
		int tickHeight = 10;

		for (int tickIndex = 0; tickIndex < 6; tickIndex++) {
			int xPos = 100 + tickIndex * IMAGE_SIZE_IN_POINTS;
			int yOffset = (tickIndex % 3 == 1) ? 20 : (tickIndex % 3 == 2) ? 40 : 0;
			int xPosUnscaled = (int) (xPos / devScale);
			int yPosUnscaled = (int) (yPos / devScale);

			gc.drawLine(xPosUnscaled, yPosUnscaled, xPosUnscaled, yPosUnscaled + tickHeight);
			gc.drawText(Integer.toString(tickIndex * IMAGE_SIZE_IN_POINTS), xPosUnscaled - 5, yPosUnscaled + 12 + yOffset);
		}
	}

	private static void eventLoop(Display display, Shell shell) {
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
	}

	// --- Utility: creates solid color square ImageData ---
	public static ImageData createSolidColorImageData(int size, RGB color) {
		PaletteData palette = new PaletteData(0xFF0000, 0x00FF00, 0x0000FF);
		ImageData imageData = new ImageData(size, size, 24, palette);

		int pixel = palette.getPixel(color);
		for (int y = 0; y < size; y++)
			for (int x = 0; x < size; x++)
				imageData.setPixel(x, y, pixel);

		return imageData;
	}

	// --- Cursor management ---
	private static class CursorManager {
		private final Display display;
		private final Shell shell;
		private final Combo combo;

		CursorManager(Display display, Shell shell, Combo combo) {
			this.display = display;
			this.shell = shell;
			this.combo = combo;
		}

		void updateCursor() {
			int selection = combo.getSelectionIndex();
			Cursor oldCursor = shell.getCursor();
			if (oldCursor != null && !oldCursor.isDisposed()) {
				oldCursor.dispose();
			}
			Cursor cursor = createCursor(selection);
			if (cursor != null) {
				shell.setCursor(cursor);
			}
		}

		private Cursor createCursor(int selection) {
			switch (selection) {
			case 0:
				return new Cursor(display, SWT.CURSOR_HAND);

			case 1: {
				ImageData source = new ImageData(IMAGE_SIZE_IN_POINTS, IMAGE_SIZE_IN_POINTS, 1,
						new PaletteData(new RGB[] { new RGB(0, 255, 0) }));
				ImageData mask = new ImageData(IMAGE_SIZE_IN_POINTS, IMAGE_SIZE_IN_POINTS, 1,
						new PaletteData(new RGB[] { new RGB(0, 255, 0) }));
				return new Cursor(display, source, mask, 0, 0);
			}

			case 2:
				return new Cursor(display, createSolidColorImageData(IMAGE_SIZE_IN_POINTS, new RGB(128, 0, 128)), 0, 0);

			case 3: {
				RGB green = new RGB(0, 255, 0);
				ImageDataProvider provider = zoom -> {
					switch (zoom) {
					case 100:
						return createSolidColorImageData(16, green);
					case 150:
						return createSolidColorImageData(24, green);
					case 200:
						return createSolidColorImageData(32, green);
					case 250:
						return createSolidColorImageData(40, green);
					case 300:
						return createSolidColorImageData(48, green);
					case 350:
						return createSolidColorImageData(56, green);
					default:
						return createSolidColorImageData(64, green);
					}
				};
				return new Cursor(display, provider, 0, 0);
			}
			default:
				return null;
			}
		}
	}
}
  • Choose the following Cursor Constructor from the combo "Cursor(Device, ImageData, int, int)"
  • Now from Windows Setting -> Accessibility -> Mouse Pointer and touch -> Increase the Size from the slider.
  • Check if the size of the custom cursor is also increased when hovering over the shell.

Copy link
Contributor

github-actions bot commented Sep 9, 2025

Test Results

   546 files  ±0     546 suites  ±0   36m 10s ⏱️ + 3m 53s
 4 431 tests ±0   4 414 ✅ ±0   17 💤 ±0  0 ❌ ±0 
16 764 runs  ±0  16 637 ✅ ±0  127 💤 ±0  0 ❌ ±0 

Results for commit 1470815. ± Comparison against base commit f858239.

♻️ This comment has been updated with latest results.

On Windows, the mouse pointer size can be increased in Accessibility
settings. Previously SWT always created cursors at their logical
bitmap size (e.g. 16x16, 32x32), ignoring the accessibility scale.

This change reads `CursorBaseSize` from
`HKCU\Control Panel\Cursors` and uses it as a scale factor when
creating SWT cursors. For example, with scale = 5, a 16px cursor
bitmap is scaled to 80px before being displayed, matching the user’s
configured pointer size.

This aligns SWT custom cursors with the system accessibility setting
and improves usability for users with enlarged cursors.
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.

GEF cursors do not scale with Windows cursor size settings
3 participants