Skip to content

Commit 0320bbc

Browse files
committed
Create custom cursor for 100%, 150% and 200% zoom #872
At least on Windows and Linux, the operating system doesn't seem to linearly scale the system cursor. Instead, predefined cursor images are used for: - ( ..., 150%) - Cursor at 100% monitor zoom - [150%, 200%) - Cursor at 150% monitor zoom - (200%, ...) - Cursor at 200% monitor zoom SWT is linearly scaling the cursor icon. So if at e.g. 125% zoom, the cursor inside the widget will be scaled to 125% zoom but is not scaled outside the widget. To account for this, the SWT-based cursor is replaced with three cursor SVGs, one for each of those zoom levels. Note that the images are split up to make sure the line width is exactly one for each cursor. Closes #872
1 parent e319539 commit 0320bbc

File tree

5 files changed

+86
-87
lines changed

5 files changed

+86
-87
lines changed

org.eclipse.gef/src/org/eclipse/gef/internal/InternalCursor.java

Lines changed: 10 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,10 @@
1313

1414
package org.eclipse.gef.internal;
1515

16-
import java.util.HashMap;
17-
import java.util.Map;
1816
import java.util.Objects;
1917

20-
import org.eclipse.swt.SWT;
21-
import org.eclipse.swt.graphics.GC;
22-
import org.eclipse.swt.graphics.Image;
23-
import org.eclipse.swt.graphics.ImageData;
24-
import org.eclipse.swt.graphics.PaletteData;
25-
import org.eclipse.swt.graphics.Path;
26-
import org.eclipse.swt.widgets.Display;
27-
2818
import org.eclipse.jface.resource.ImageDescriptor;
2919

30-
import org.eclipse.draw2d.ColorConstants;
31-
3220
/**
3321
* This class defines the shape of the default GEF-cursor used for the plug/tree
3422
* images. The cursor can't be part of the SVG itself, as it is a)
@@ -37,90 +25,26 @@
3725
* always has a stroke-width of 1px.
3826
*/
3927
public class InternalCursor {
40-
/**
41-
* Defines the shape of the cursor at 100% zoom.
42-
*/
43-
//@formatter:off
44-
private static final float[] CURSOR_POINTS = {
45-
0f, 0f,
46-
0f, 17f,
47-
4f, 13f,
48-
7f, 19f,
49-
9f, 18f,
50-
7f, 13f,
51-
7f, 12f,
52-
12f, 12f,
53-
0f, 0f
54-
};
55-
//@formatter:on
5628
/**
5729
* Local cache to store the cursor data for each zoom level.
5830
*/
59-
private static final Map<Integer, ImageData> CURSOR_AT_ZOOM = new HashMap<>();
31+
private static final ImageDescriptor CURSOR_AT_100_ZOOM = InternalImages.createDescriptor("icons/[email protected]"); //$NON-NLS-1$
32+
private static final ImageDescriptor CURSOR_AT_150_ZOOM = InternalImages.createDescriptor("icons/[email protected]"); //$NON-NLS-1$
33+
private static final ImageDescriptor CURSOR_AT_200_ZOOM = InternalImages.createDescriptor("icons/[email protected]"); //$NON-NLS-1$
6034
/**
6135
* The default cursor that is constructed using {link {@link #CURSOR_POINTS}.
6236
* May be replaced with a custom cursor by calling
6337
* {@link #setCursorDescriptor(ImageDescriptor)}.
6438
*/
65-
private static ImageDescriptor CURRENT_CURSOR_DESCRIPTOR = ImageDescriptor
66-
.createFromImageDataProvider(zoom -> CURSOR_AT_ZOOM.computeIfAbsent(zoom, InternalCursor::getCursorAtZoom));
67-
68-
/**
69-
* This method generates the image data for the cursor at the given zoom level.
70-
* The points defined with {@link #CURSOR_POINTS} are scaled by the given zoom
71-
* and painted onto an image.
72-
*
73-
* @param zoom The zoom level. e.g. 100, 125, 200
74-
* @return The cursor image data at the given zoom level.
75-
*/
76-
private static ImageData getCursorAtZoom(int zoom) {
77-
float maxWidth = 0f;
78-
float maxHeight = 0f;
79-
80-
for (int i = 0; i < CURSOR_POINTS.length; i += 2) {
81-
maxWidth = Math.max(maxWidth, CURSOR_POINTS[i]);
82-
maxHeight = Math.max(maxHeight, CURSOR_POINTS[i + 1]);
39+
private static ImageDescriptor CURRENT_CURSOR_DESCRIPTOR = ImageDescriptor.createFromImageDataProvider(zoom -> {
40+
if (zoom < 150) {
41+
return CURSOR_AT_100_ZOOM.getImageData(100);
8342
}
84-
85-
float zoomFactor = zoom / 100.0f;
86-
87-
int width = 1 + (int) Math.ceil(zoomFactor * maxWidth);
88-
int height = 1 + (int) Math.ceil(zoomFactor * maxHeight);
89-
90-
//
91-
Display display = Display.getDefault();
92-
// Construct path
93-
Path path = new Path(display);
94-
for (int i = 0; i < CURSOR_POINTS.length; i += 2) {
95-
float x = zoomFactor * CURSOR_POINTS[i];
96-
float y = zoomFactor * CURSOR_POINTS[i + 1];
97-
if (i == 0) {
98-
path.moveTo(x, y);
99-
} else {
100-
path.lineTo(x, y);
101-
}
43+
if (zoom < 200) {
44+
return CURSOR_AT_150_ZOOM.getImageData(100);
10245
}
103-
// Construct image
104-
ImageData imageData = new ImageData(width, height, 32, new PaletteData(0xFF0000, 0x00FF00, 0x0000FF));
105-
imageData.alphaData = new byte[width * height];
106-
Image image = new Image(display, imageData);
107-
GC gc = new GC(image);
108-
gc.setAlpha(0);
109-
gc.fillRectangle(0, 0, width, height);
110-
gc.setAlpha(255);
111-
gc.setAntialias(SWT.ON);
112-
gc.setLineWidth(1);
113-
gc.setBackground(ColorConstants.white);
114-
gc.fillPath(path);
115-
gc.setBackground(ColorConstants.black);
116-
gc.drawPath(path);
117-
gc.dispose();
118-
path.dispose();
119-
// Image is already scaled to expected zoom level
120-
imageData = image.getImageData(100);
121-
image.dispose();
122-
return imageData;
123-
}
46+
return CURSOR_AT_200_ZOOM.getImageData(100);
47+
});
12448

12549
/**
12650
* Returns the image descriptor for the GEF cursor. Never {@code null}.

org.eclipse.gef/src/org/eclipse/gef/internal/InternalGEFPlugin.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,18 @@ public static ImageData scaledImageData(ImageDescriptor descriptor, int zoom) {
127127
*/
128128
public static Cursor createCursor(ImageDescriptor source, int hotspotX, int hotspotY) {
129129
try {
130+
ImageDataProvider provider = zoom -> {
131+
if (zoom < 150) {
132+
return source.getImageData(100);
133+
}
134+
if (zoom < 200) {
135+
return source.getImageData(150);
136+
}
137+
return source.getImageData(200);
138+
};
130139
Constructor<Cursor> ctor = Cursor.class.getConstructor(Device.class, ImageDataProvider.class, int.class,
131140
int.class);
132-
return ctor.newInstance(null, (ImageDataProvider) source::getImageData, hotspotX, hotspotY);
141+
return ctor.newInstance(null, provider, hotspotX, hotspotY);
133142
} catch (NoSuchMethodException e) {
134143
// SWT version < 3.131.0 (no ImageDataProvider-based constructor)
135144
return new Cursor(null, source.getImageData(100), hotspotX, hotspotY); // older constructor
Lines changed: 22 additions & 0 deletions
Loading
Lines changed: 22 additions & 0 deletions
Loading
Lines changed: 22 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)