Skip to content

Commit 6842283

Browse files
arunjose696fedejeanne
authored andcommitted
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 ImageDataProvider object instead of ImageData. This allows the code instantiating the cursor to pass an imageDataProvider capable of providing appropriately scaled image data based on the current zoom level.
1 parent 0d3622a commit 6842283

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ 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+
388+
private void setupCursorFromImageData(ImageData source, int hotspotX, int hotspotY) {
385389
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
386390
if (hotspotX >= source.width || hotspotX < 0 ||
387391
hotspotY >= source.height || hotspotY < 0) {
@@ -448,6 +452,41 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
448452
}
449453
}
450454

455+
/**
456+
* Constructs a new cursor given a device, image describing
457+
* the desired cursor appearance, and the x and y coordinates of
458+
* the <em>hotspot</em> (that is, the point within the area
459+
* covered by the cursor which is considered to be where the
460+
* on-screen pointer is "pointing").
461+
* <p>
462+
* You must dispose the cursor when it is no longer required.
463+
* </p>
464+
*
465+
* @param device the device on which to allocate the cursor
466+
* @param imageDataProvider the ImageDataProvider for the cursor
467+
* @param hotspotX the x coordinate of the cursor's hotspot
468+
* @param hotspotY the y coordinate of the cursor's hotspot
469+
*
470+
* @exception IllegalArgumentException <ul>
471+
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
472+
* <li>ERROR_NULL_ARGUMENT - if the image is null</li>
473+
* <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
474+
* image</li>
475+
* </ul>
476+
* @exception SWTError <ul>
477+
* <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
478+
* </ul>
479+
*
480+
* @see #dispose()
481+
*
482+
* @since 3.131
483+
*/
484+
public Cursor(Device device, ImageDataProvider imageDataProvider, int hotspotX, int hotspotY) {
485+
super(device);
486+
if (imageDataProvider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
487+
setupCursorFromImageData(imageDataProvider.getImageData(100), hotspotX, hotspotY);
488+
}
489+
451490
@Override
452491
void destroy() {
453492
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 imageDataProvider the ImageDataProvider 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, ImageDataProvider imageDataProvider, int hotspotX, int hotspotY) {
393+
super(device);
394+
if (imageDataProvider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
395+
setupCursorFromImageData(imageDataProvider.getImageData(100), 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: 55 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 final ImageDataProvider imageDataProvider;
7273
private final ImageData source;
7374
private final ImageData mask;
7475
private final int hotspotX;
@@ -79,6 +80,7 @@ public final class Cursor extends Resource {
7980
Cursor(Device device) {
8081
super(device);
8182
this.source = null;
83+
this.imageDataProvider = null;
8284
this.mask = null;
8385
this.hotspotX = -1;
8486
this.hotspotY = -1;
@@ -204,6 +206,7 @@ public Cursor(Device device, ImageData source, ImageData mask, int hotspotX, int
204206
this.mask = mask;
205207
this.hotspotX = hotspotX;
206208
this.hotspotY = hotspotY;
209+
this.imageDataProvider = null;
207210
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
208211
if (mask == null) {
209212
if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
@@ -271,6 +274,11 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
271274
this.mask = null;
272275
this.hotspotX = hotspotX;
273276
this.hotspotY = hotspotY;
277+
this.imageDataProvider = null;
278+
setupCursorFromImageData(source);
279+
}
280+
281+
private void setupCursorFromImageData(ImageData source) {
274282
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
275283
/* Check the hotspots */
276284
if (hotspotX >= source.width || hotspotX < 0 ||
@@ -345,6 +353,46 @@ public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
345353
this.device.registerResourceWithZoomSupport(this);
346354
}
347355

356+
/**
357+
* Constructs a new cursor given a device, image describing
358+
* the desired cursor appearance, and the x and y coordinates of
359+
* the <em>hotspot</em> (that is, the point within the area
360+
* covered by the cursor which is considered to be where the
361+
* on-screen pointer is "pointing").
362+
* <p>
363+
* You must dispose the cursor when it is no longer required.
364+
* </p>
365+
*
366+
* @param device the device on which to allocate the cursor
367+
* @param imageDataProvider the ImageDataProvider for the cursor
368+
* @param hotspotX the x coordinate of the cursor's hotspot
369+
* @param hotspotY the y coordinate of the cursor's hotspot
370+
*
371+
* @exception IllegalArgumentException <ul>
372+
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
373+
* <li>ERROR_NULL_ARGUMENT - if the image is null</li>
374+
* <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
375+
* image</li>
376+
* </ul>
377+
* @exception SWTError <ul>
378+
* <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
379+
* </ul>
380+
*
381+
* @see #dispose()
382+
*
383+
* @since 3.131
384+
*/
385+
public Cursor(Device device, ImageDataProvider imageDataProvider, int hotspotX, int hotspotY) {
386+
super(device);
387+
if (imageDataProvider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
388+
this.imageDataProvider = imageDataProvider;
389+
this.source = imageDataProvider.getImageData(100);
390+
this.mask = null;
391+
this.hotspotX = hotspotX;
392+
this.hotspotY = hotspotY;
393+
setupCursorFromImageData(this.source);
394+
}
395+
348396
/**
349397
* <b>IMPORTANT:</b> This method is not part of the public
350398
* API for Image. It is marked public only so that it
@@ -371,7 +419,13 @@ public static Long win32_getHandle (Cursor cursor, int zoom) {
371419
if (cursor.source == null) {
372420
cursor.setHandleForZoomLevel(cursor.handle, zoom);
373421
} else {
374-
ImageData source = DPIUtil.scaleImageData(cursor.device, cursor.source, zoom, DEFAULT_ZOOM);
422+
ImageData source;
423+
if (cursor.imageDataProvider != null) {
424+
source = DPIUtil.validateAndGetImageDataAtZoom(cursor.imageDataProvider, zoom).element();
425+
}
426+
else {
427+
source = DPIUtil.scaleImageData(cursor.device, cursor.source, zoom, DEFAULT_ZOOM);
428+
}
375429
if (cursor.isIcon) {
376430
Cursor newCursor = new Cursor(cursor.device, source, cursor.hotspotX, cursor.hotspotY);
377431
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
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;
29+
import org.eclipse.swt.graphics.ImageDataProvider;
2830
import org.eclipse.swt.graphics.ImageLoader;
2931
import org.eclipse.swt.widgets.Display;
3032
import org.junit.Before;
@@ -153,6 +155,23 @@ public void test_ConstructorLorg_eclipse_swt_graphics_DeviceLorg_eclipse_swt_gra
153155
}
154156
}
155157

158+
@Test
159+
public void test_ConstructorWithImageDataProvider() {
160+
// Test new Cursor(Device device, ImageData source, ImageData mask, int
161+
// hotspotX, int hotspotY)
162+
Image sourceImage = new Image(display, 10, 10);
163+
Cursor cursor = new Cursor(display, sourceImage::getImageData, 0, 0);
164+
cursor.dispose();
165+
sourceImage.dispose();
166+
167+
try {
168+
cursor = new Cursor(display, (ImageDataProvider) null, 0, 0);
169+
cursor.dispose();
170+
fail("No exception thrown when ImageDataProvider is null");
171+
} catch (IllegalArgumentException e) {
172+
}
173+
}
174+
156175
@Test
157176
public void test_equalsLjava_lang_Object() {
158177
/* Note: Two cursors are only considered equal if their handles are equal.

0 commit comments

Comments
 (0)