Skip to content

Commit 0bc17ed

Browse files
committed
Revise the Cursor class to support scaling
Previously, cursors were initialized with a single ImageData, which caused issues on systems with varying zoom levels, for example, cursors were not scaled at all on Linux, or were blurrily auto-scaled on Windows. This commit introduces a new constructor for the Cursor class that accepts an Image object instead of ImageData. This allows the code instantiating the cursor to pass an image capable of providing appropriately scaled image data based on the current zoom level.
1 parent edc5529 commit 0bc17ed

File tree

4 files changed

+140
-1
lines changed

4 files changed

+140
-1
lines changed

bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Cursor.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ void createNSCursor(int hotspotX, int hotspotY, byte[] buffer, int width, int he
382382
*/
383383
public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
384384
super(device);
385+
setupCursorFromImageData(source, hotspotX, hotspotY);
386+
}
387+
private void setupCursorFromImageData(ImageData source, int hotspotX, int hotspotY) {
385388
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
386389
if (hotspotX >= source.width || hotspotX < 0 ||
387390
hotspotY >= source.height || hotspotY < 0) {
@@ -448,6 +451,41 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
448451
}
449452
}
450453

454+
/**
455+
* Constructs a new cursor given a device, image describing
456+
* the desired cursor appearance, and the x and y coordinates of
457+
* the <em>hotspot</em> (that is, the point within the area
458+
* covered by the cursor which is considered to be where the
459+
* on-screen pointer is "pointing").
460+
* <p>
461+
* You must dispose the cursor when it is no longer required.
462+
* </p>
463+
*
464+
* @param device the device on which to allocate the cursor
465+
* @param image the image for the cursor
466+
* @param hotspotX the x coordinate of the cursor's hotspot
467+
* @param hotspotY the y coordinate of the cursor's hotspot
468+
*
469+
* @exception IllegalArgumentException <ul>
470+
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
471+
* <li>ERROR_NULL_ARGUMENT - if the image is null</li>
472+
* <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
473+
* image</li>
474+
* </ul>
475+
* @exception SWTError <ul>
476+
* <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
477+
* </ul>
478+
*
479+
* @see #dispose()
480+
*
481+
* @since 3.131
482+
*/
483+
public Cursor(Device device, Image image, int hotspotX, int hotspotY) {
484+
super(device);
485+
if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
486+
setupCursorFromImageData(image.getImageData(), hotspotX, hotspotY);
487+
}
488+
451489
@Override
452490
void destroy() {
453491
handle.release();

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Cursor.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ public Cursor(Device device, ImageData source, ImageData mask, int hotspotX, int
272272
*/
273273
public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
274274
super(device);
275+
setupCursorFromImageData(source, hotspotX, hotspotY);
276+
277+
}
278+
279+
private void setupCursorFromImageData(ImageData source, int hotspotX, int hotspotY) {
275280
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
276281
if (hotspotX >= source.width || hotspotX < 0 ||
277282
hotspotY >= source.height || hotspotY < 0) {
@@ -355,6 +360,41 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
355360
init();
356361
}
357362

363+
/**
364+
* Constructs a new cursor given a device, image describing
365+
* the desired cursor appearance, and the x and y coordinates of
366+
* the <em>hotspot</em> (that is, the point within the area
367+
* covered by the cursor which is considered to be where the
368+
* on-screen pointer is "pointing").
369+
* <p>
370+
* You must dispose the cursor when it is no longer required.
371+
* </p>
372+
*
373+
* @param device the device on which to allocate the cursor
374+
* @param image the image for the cursor
375+
* @param hotspotX the x coordinate of the cursor's hotspot
376+
* @param hotspotY the y coordinate of the cursor's hotspot
377+
*
378+
* @exception IllegalArgumentException <ul>
379+
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
380+
* <li>ERROR_NULL_ARGUMENT - if the image is null</li>
381+
* <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
382+
* image</li>
383+
* </ul>
384+
* @exception SWTError <ul>
385+
* <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
386+
* </ul>
387+
*
388+
* @see #dispose()
389+
*
390+
* @since 3.131
391+
*/
392+
public Cursor(Device device, Image image, int hotspotX, int hotspotY) {
393+
super(device);
394+
if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
395+
setupCursorFromImageData(image.getImageData(), hotspotX, hotspotY);
396+
}
397+
358398
long createCursor(byte[] sourceData, byte[] maskData, int width, int height, int hotspotX, int hotspotY, boolean reverse) {
359399
for (int i = 0; i < sourceData.length; i++) {
360400
byte s = sourceData[i];

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public final class Cursor extends Resource {
6969
private HashMap<Integer, Long> zoomLevelToHandle = new HashMap<>();
7070

7171
boolean isIcon;
72+
private Image image = null;
7273
private final ImageData source;
7374
private final ImageData mask;
7475
private final int hotspotX;
@@ -271,6 +272,10 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
271272
this.mask = null;
272273
this.hotspotX = hotspotX;
273274
this.hotspotY = hotspotY;
275+
setupCursorFromImageData(source);
276+
}
277+
278+
private void setupCursorFromImageData(ImageData source) {
274279
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
275280
/* Check the hotspots */
276281
if (hotspotX >= source.width || hotspotX < 0 ||
@@ -345,6 +350,46 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
345350
this.device.registerResourceWithZoomSupport(this);
346351
}
347352

353+
/**
354+
* Constructs a new cursor given a device, image describing
355+
* the desired cursor appearance, and the x and y coordinates of
356+
* the <em>hotspot</em> (that is, the point within the area
357+
* covered by the cursor which is considered to be where the
358+
* on-screen pointer is "pointing").
359+
* <p>
360+
* You must dispose the cursor when it is no longer required.
361+
* </p>
362+
*
363+
* @param device the device on which to allocate the cursor
364+
* @param image the image for the cursor
365+
* @param hotspotX the x coordinate of the cursor's hotspot
366+
* @param hotspotY the y coordinate of the cursor's hotspot
367+
*
368+
* @exception IllegalArgumentException <ul>
369+
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
370+
* <li>ERROR_NULL_ARGUMENT - if the image is null</li>
371+
* <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
372+
* image</li>
373+
* </ul>
374+
* @exception SWTError <ul>
375+
* <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
376+
* </ul>
377+
*
378+
* @see #dispose()
379+
*
380+
* @since 3.131
381+
*/
382+
public Cursor(Device device, Image image, int hotspotX, int hotspotY) {
383+
super(device);
384+
if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
385+
this.image = image;
386+
this.source = image.getImageData();
387+
this.mask = null;
388+
this.hotspotX = hotspotX;
389+
this.hotspotY = hotspotY;
390+
setupCursorFromImageData(image.getImageData());
391+
}
392+
348393
/**
349394
* <b>IMPORTANT:</b> This method is not part of the public
350395
* API for Image. It is marked public only so that it
@@ -371,7 +416,13 @@ public static Long win32_getHandle (Cursor cursor, int zoom) {
371416
if (cursor.source == null) {
372417
cursor.setHandleForZoomLevel(cursor.handle, zoom);
373418
} else {
374-
ImageData source = DPIUtil.scaleImageData(cursor.device, cursor.source, zoom, DEFAULT_ZOOM);
419+
ImageData source;
420+
if (cursor.image != null) {
421+
source = cursor.image.getImageData(zoom);
422+
}
423+
else {
424+
source = DPIUtil.scaleImageData(cursor.device, cursor.source, zoom, DEFAULT_ZOOM);
425+
}
375426
if (cursor.isIcon) {
376427
Cursor newCursor = new Cursor(cursor.device, source, cursor.hotspotX, cursor.hotspotY);
377428
cursor.setHandleForZoomLevel(newCursor.handle, zoom);

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Cursor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import org.eclipse.swt.SWT;
2626
import org.eclipse.swt.graphics.Cursor;
27+
import org.eclipse.swt.graphics.Image;
2728
import org.eclipse.swt.graphics.ImageData;
2829
import org.eclipse.swt.graphics.ImageLoader;
2930
import org.eclipse.swt.widgets.Display;
@@ -153,6 +154,15 @@ public void test_ConstructorLorg_eclipse_swt_graphics_DeviceLorg_eclipse_swt_gra
153154
}
154155
}
155156

157+
@Test
158+
public void test_ConstructorLorg_eclipse_swt_graphics_DeviceLorg_eclipse_swt_graphics_ImageData() {
159+
// Test new Cursor(Device device, ImageData source, ImageData mask, int
160+
// hotspotX, int hotspotY)
161+
Image sourceImage = new Image(display, 10, 10);
162+
Cursor cursor = new Cursor(display, sourceImage, 0, 0);
163+
cursor.dispose();
164+
}
165+
156166
@Test
157167
public void test_equalsLjava_lang_Object() {
158168
/* Note: Two cursors are only considered equal if their handles are equal.

0 commit comments

Comments
 (0)