ebiten: add more cursor shapes

This change adds these new cursor shapes:

* CursorShapeNESWResize
* CursorShapeNWSEResize
* CursorShapeMove
* CursorShapeNotAllowed

Closes #2476
This commit is contained in:
Hajime Hoshi 2023-01-16 02:26:41 +09:00
parent 269b557e38
commit 91e1c0ea29
21 changed files with 346 additions and 118 deletions

View File

@ -33,10 +33,14 @@ type CursorShapeType = ui.CursorShape
// CursorShapeTypes // CursorShapeTypes
const ( const (
CursorShapeDefault CursorShapeType = ui.CursorShapeDefault CursorShapeDefault CursorShapeType = CursorShapeType(ui.CursorShapeDefault)
CursorShapeText CursorShapeType = ui.CursorShapeText CursorShapeText CursorShapeType = CursorShapeType(ui.CursorShapeText)
CursorShapeCrosshair CursorShapeType = ui.CursorShapeCrosshair CursorShapeCrosshair CursorShapeType = CursorShapeType(ui.CursorShapeCrosshair)
CursorShapePointer CursorShapeType = ui.CursorShapePointer CursorShapePointer CursorShapeType = CursorShapeType(ui.CursorShapePointer)
CursorShapeEWResize CursorShapeType = ui.CursorShapeEWResize CursorShapeEWResize CursorShapeType = CursorShapeType(ui.CursorShapeEWResize)
CursorShapeNSResize CursorShapeType = ui.CursorShapeNSResize CursorShapeNSResize CursorShapeType = CursorShapeType(ui.CursorShapeNSResize)
CursorShapeNESWResize CursorShapeType = CursorShapeType(ui.CursorShapeNESWResize)
CursorShapeNWSEResize CursorShapeType = CursorShapeType(ui.CursorShapeNWSEResize)
CursorShapeMove CursorShapeType = CursorShapeType(ui.CursorShapeMove)
CursorShapeNotAllowed CursorShapeType = CursorShapeType(ui.CursorShapeNotAllowed)
) )

View File

@ -33,6 +33,7 @@ const (
type Game struct { type Game struct {
grids map[image.Rectangle]ebiten.CursorShapeType grids map[image.Rectangle]ebiten.CursorShapeType
gridColors map[image.Rectangle]color.Color gridColors map[image.Rectangle]color.Color
cursor ebiten.CursorShapeType
} }
func (g *Game) Update() error { func (g *Game) Update() error {
@ -44,7 +45,12 @@ func (g *Game) Update() error {
break break
} }
} }
// Call SetCursorShape only when this is changed to test Ebitengine remembers the current cursor correctly even when it is hidden.
if g.cursor != cursor {
ebiten.SetCursorShape(cursor) ebiten.SetCursorShape(cursor)
g.cursor = cursor
}
if inpututil.IsKeyJustPressed(ebiten.KeyC) { if inpututil.IsKeyJustPressed(ebiten.KeyC) {
switch ebiten.CursorMode() { switch ebiten.CursorMode() {
@ -76,6 +82,14 @@ func (g *Game) Draw(screen *ebiten.Image) {
ebitenutil.DebugPrint(screen, "CursorShape: EW Resize") ebitenutil.DebugPrint(screen, "CursorShape: EW Resize")
case ebiten.CursorShapeNSResize: case ebiten.CursorShapeNSResize:
ebitenutil.DebugPrint(screen, "CursorShape: NS Resize") ebitenutil.DebugPrint(screen, "CursorShape: NS Resize")
case ebiten.CursorShapeNESWResize:
ebitenutil.DebugPrint(screen, "CursorShape: NESW Resize")
case ebiten.CursorShapeNWSEResize:
ebitenutil.DebugPrint(screen, "CursorShape: NWSE Resize")
case ebiten.CursorShapeMove:
ebitenutil.DebugPrint(screen, "CursorShape: Move")
case ebiten.CursorShapeNotAllowed:
ebitenutil.DebugPrint(screen, "CursorShape: Not Allowed")
} }
} }
@ -89,18 +103,25 @@ func main() {
image.Rect(100, 100, 200, 200): ebiten.CursorShapeDefault, image.Rect(100, 100, 200, 200): ebiten.CursorShapeDefault,
image.Rect(200, 100, 300, 200): ebiten.CursorShapeText, image.Rect(200, 100, 300, 200): ebiten.CursorShapeText,
image.Rect(300, 100, 400, 200): ebiten.CursorShapeCrosshair, image.Rect(300, 100, 400, 200): ebiten.CursorShapeCrosshair,
image.Rect(100, 200, 200, 300): ebiten.CursorShapePointer, image.Rect(400, 100, 500, 200): ebiten.CursorShapePointer,
image.Rect(200, 200, 300, 300): ebiten.CursorShapeEWResize, image.Rect(100, 200, 200, 300): ebiten.CursorShapeEWResize,
image.Rect(300, 200, 400, 300): ebiten.CursorShapeNSResize, image.Rect(200, 200, 300, 300): ebiten.CursorShapeNSResize,
image.Rect(300, 200, 400, 300): ebiten.CursorShapeNESWResize,
image.Rect(400, 200, 500, 300): ebiten.CursorShapeNWSEResize,
image.Rect(100, 300, 200, 400): ebiten.CursorShapeMove,
image.Rect(200, 300, 300, 400): ebiten.CursorShapeNotAllowed,
}, },
gridColors: map[image.Rectangle]color.Color{}, gridColors: map[image.Rectangle]color.Color{},
} }
for rect, c := range g.grids { for rect, c := range g.grids {
clr := color.RGBA{0x40, 0x40, 0x40, 0xff} clr := color.RGBA{0x40, 0x40, 0x40, 0xff}
if c%2 == 0 { switch c % 3 {
case 0:
clr.R = 0x80 clr.R = 0x80
} else { case 1:
clr.G = 0x80 clr.G = 0x80
case 2:
clr.B = 0x80
} }
g.gridColors[rect] = clr g.gridColors[rect] = clr
} }

View File

@ -1728,18 +1728,64 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
@autoreleasepool { @autoreleasepool {
if (shape == GLFW_ARROW_CURSOR) SEL cursorSelector = NULL;
switch (shape)
{
case GLFW_HRESIZE_CURSOR:
cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor");
break;
case GLFW_VRESIZE_CURSOR:
cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor");
break;
case GLFW_RESIZE_NWSE_CURSOR:
cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor");
break;
case GLFW_RESIZE_NESW_CURSOR:
cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor");
break;
}
if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
{
id object = [NSCursor performSelector:cursorSelector];
if ([object isKindOfClass:[NSCursor class]])
cursor->ns.object = object;
}
if (!cursor->ns.object)
{
switch (shape)
{
case GLFW_ARROW_CURSOR:
cursor->ns.object = [NSCursor arrowCursor]; cursor->ns.object = [NSCursor arrowCursor];
else if (shape == GLFW_IBEAM_CURSOR) break;
case GLFW_IBEAM_CURSOR:
cursor->ns.object = [NSCursor IBeamCursor]; cursor->ns.object = [NSCursor IBeamCursor];
else if (shape == GLFW_CROSSHAIR_CURSOR) break;
case GLFW_CROSSHAIR_CURSOR:
cursor->ns.object = [NSCursor crosshairCursor]; cursor->ns.object = [NSCursor crosshairCursor];
else if (shape == GLFW_HAND_CURSOR) break;
case GLFW_HAND_CURSOR:
cursor->ns.object = [NSCursor pointingHandCursor]; cursor->ns.object = [NSCursor pointingHandCursor];
else if (shape == GLFW_HRESIZE_CURSOR) break;
cursor->ns.object = [NSCursor resizeLeftRightCursor]; case GLFW_RESIZE_ALL_CURSOR:
else if (shape == GLFW_VRESIZE_CURSOR) {
cursor->ns.object = [NSCursor resizeUpDownCursor]; // Use the OS's resource: https://stackoverflow.com/a/21786835/5435443
NSString *cursorName = @"move";
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];
NSImage *image = [[NSImage alloc] initByReferencingFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
cursor->ns.object = [[NSCursor alloc] initWithImage:image
hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue],
[[info valueForKey:@"hoty"] doubleValue])];
break;
}
case GLFW_NOT_ALLOWED_CURSOR:
cursor->ns.object = [NSCursor operationNotAllowedCursor];
break;
}
}
if (!cursor->ns.object) if (!cursor->ns.object)
{ {

View File

@ -964,6 +964,12 @@ extern "C" {
#define GLFW_VRESIZE_CURSOR 0x00036006 #define GLFW_VRESIZE_CURSOR 0x00036006
/*! @} */ /*! @} */
// Added in GLFW v3.4.
#define GLFW_RESIZE_NWSE_CURSOR 0x00036007
#define GLFW_RESIZE_NESW_CURSOR 0x00036008
#define GLFW_RESIZE_ALL_CURSOR 0x00036009
#define GLFW_NOT_ALLOWED_CURSOR 0x0003600A
#define GLFW_CONNECTED 0x00040001 #define GLFW_CONNECTED 0x00040001
#define GLFW_DISCONNECTED 0x00040002 #define GLFW_DISCONNECTED 0x00040002

View File

@ -470,7 +470,11 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
shape != GLFW_CROSSHAIR_CURSOR && shape != GLFW_CROSSHAIR_CURSOR &&
shape != GLFW_HAND_CURSOR && shape != GLFW_HAND_CURSOR &&
shape != GLFW_HRESIZE_CURSOR && shape != GLFW_HRESIZE_CURSOR &&
shape != GLFW_VRESIZE_CURSOR) shape != GLFW_VRESIZE_CURSOR &&
shape != GLFW_RESIZE_NWSE_CURSOR &&
shape != GLFW_RESIZE_NESW_CURSOR &&
shape != GLFW_RESIZE_ALL_CURSOR &&
shape != GLFW_NOT_ALLOWED_CURSOR)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
return NULL; return NULL;

View File

@ -1823,28 +1823,6 @@ const struct wl_data_device_listener dataDeviceListener =
dataDeviceHandleSelection, dataDeviceHandleSelection,
}; };
// Translates a GLFW standard cursor to a theme cursor name
//
static char *translateCursorShape(int shape)
{
switch (shape)
{
case GLFW_ARROW_CURSOR:
return "left_ptr";
case GLFW_IBEAM_CURSOR:
return "xterm";
case GLFW_CROSSHAIR_CURSOR:
return "crosshair";
case GLFW_HAND_CURSOR:
return "hand2";
case GLFW_HRESIZE_CURSOR:
return "sb_h_double_arrow";
case GLFW_VRESIZE_CURSOR:
return "sb_v_double_arrow";
}
return NULL;
}
void _glfwAddSeatListenerWayland(struct wl_seat* seat) void _glfwAddSeatListenerWayland(struct wl_seat* seat)
{ {
wl_seat_add_listener(seat, &seatListener, NULL); wl_seat_add_listener(seat, &seatListener, NULL);
@ -2392,26 +2370,101 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
struct wl_cursor* standardCursor; // See GLFW 3.4 implementation.
const char* name = NULL;
standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, // Try the XDG names first
translateCursorShape(shape)); switch (shape)
if (!standardCursor)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, case GLFW_ARROW_CURSOR:
"Wayland: Standard cursor \"%s\" not found", name = "default";
translateCursorShape(shape)); break;
return GLFW_FALSE; case GLFW_IBEAM_CURSOR:
name = "text";
break;
case GLFW_CROSSHAIR_CURSOR:
name = "crosshair";
break;
case GLFW_HAND_CURSOR:
name = "pointer";
break;
case GLFW_HRESIZE_CURSOR:
name = "ew-resize";
break;
case GLFW_VRESIZE_CURSOR:
name = "ns-resize";
break;
case GLFW_RESIZE_NWSE_CURSOR:
name = "nwse-resize";
break;
case GLFW_RESIZE_NESW_CURSOR:
name = "nesw-resize";
break;
case GLFW_RESIZE_ALL_CURSOR:
name = "all-scroll";
break;
case GLFW_NOT_ALLOWED_CURSOR:
name = "not-allowed";
break;
} }
cursor->wl.cursor = standardCursor; cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
cursor->wl.currentImage = 0;
if (_glfw.wl.cursorThemeHiDPI) if (_glfw.wl.cursorThemeHiDPI)
{ {
standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, cursor->wl.cursorHiDPI =
translateCursorShape(shape)); wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
cursor->wl.cursorHiDPI = standardCursor; }
if (!cursor->wl.cursor)
{
// Fall back to the core X11 names
switch (shape)
{
case GLFW_ARROW_CURSOR:
name = "left_ptr";
break;
case GLFW_IBEAM_CURSOR:
name = "xterm";
break;
case GLFW_CROSSHAIR_CURSOR:
name = "crosshair";
break;
case GLFW_HAND_CURSOR:
name = "hand2";
break;
case GLFW_HRESIZE_CURSOR:
name = "sb_h_double_arrow";
break;
case GLFW_VRESIZE_CURSOR:
name = "sb_v_double_arrow";
break;
case GLFW_RESIZE_ALL_CURSOR:
name = "fleur";
break;
default:
//_glfwInputError(GLFW_CURSOR_UNAVAILABLE,
// "Wayland: Standard cursor shape unavailable");
return GLFW_FALSE;
}
cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
if (!cursor->wl.cursor)
{
//_glfwInputError(GLFW_CURSOR_UNAVAILABLE,
// "Wayland: Failed to create standard cursor \"%s\"",
// name);
return GLFW_FALSE;
}
if (_glfw.wl.cursorThemeHiDPI)
{
if (!cursor->wl.cursorHiDPI)
{
cursor->wl.cursorHiDPI =
wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
}
}
} }
return GLFW_TRUE; return GLFW_TRUE;

View File

@ -741,6 +741,12 @@ static GLFWbool initExtensions(void)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
_glfw.x11.xcursor.GetTheme = (PFN_XcursorGetTheme)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetTheme");
_glfw.x11.xcursor.GetDefaultSize = (PFN_XcursorGetDefaultSize)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize");
_glfw.x11.xcursor.LibraryLoadImage = (PFN_XcursorLibraryLoadImage)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage");
} }
#if defined(__CYGWIN__) #if defined(__CYGWIN__)

View File

@ -87,9 +87,15 @@ typedef int (* PFN_XRRUpdateConfiguration)(XEvent*);
typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);
typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); typedef void (* PFN_XcursorImageDestroy)(XcursorImage*);
typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*);
typedef char* (* PFN_XcursorGetTheme)(Display*);
typedef int (* PFN_XcursorGetDefaultSize)(Display*);
typedef XcursorImage* (* PFN_XcursorLibraryLoadImage)(const char*,const char*,int);
#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate #define XcursorImageCreate _glfw.x11.xcursor.ImageCreate
#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy #define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy
#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor #define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor
#define XcursorGetTheme _glfw.x11.xcursor.GetTheme
#define XcursorGetDefaultSize _glfw.x11.xcursor.GetDefaultSize
#define XcursorLibraryLoadImage _glfw.x11.xcursor.LibraryLoadImage
typedef Bool (* PFN_XineramaIsActive)(Display*); typedef Bool (* PFN_XineramaIsActive)(Display*);
typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);
@ -327,6 +333,9 @@ typedef struct _GLFWlibraryX11
PFN_XcursorImageCreate ImageCreate; PFN_XcursorImageCreate ImageCreate;
PFN_XcursorImageDestroy ImageDestroy; PFN_XcursorImageDestroy ImageDestroy;
PFN_XcursorImageLoadCursor ImageLoadCursor; PFN_XcursorImageLoadCursor ImageLoadCursor;
PFN_XcursorGetTheme GetTheme;
PFN_XcursorGetDefaultSize GetDefaultSize;
PFN_XcursorLibraryLoadImage LibraryLoadImage;
} xcursor; } xcursor;
struct { struct {

View File

@ -2908,22 +2908,90 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
int native = 0; // See GLFW 3.4 implementation.
if (_glfw.x11.xcursor.handle)
{
char* theme = XcursorGetTheme(_glfw.x11.display);
if (theme)
{
const int size = XcursorGetDefaultSize(_glfw.x11.display);
const char* name = NULL;
if (shape == GLFW_ARROW_CURSOR) switch (shape)
{
case GLFW_ARROW_CURSOR:
name = "default";
break;
case GLFW_IBEAM_CURSOR:
name = "text";
break;
case GLFW_CROSSHAIR_CURSOR:
name = "crosshair";
break;
case GLFW_HAND_CURSOR:
name = "pointer";
break;
case GLFW_HRESIZE_CURSOR:
name = "ew-resize";
break;
case GLFW_VRESIZE_CURSOR:
name = "ns-resize";
break;
case GLFW_RESIZE_NWSE_CURSOR:
name = "nwse-resize";
break;
case GLFW_RESIZE_NESW_CURSOR:
name = "nesw-resize";
break;
case GLFW_RESIZE_ALL_CURSOR:
name = "all-scroll";
break;
case GLFW_NOT_ALLOWED_CURSOR:
name = "not-allowed";
break;
}
XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
if (image)
{
cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
XcursorImageDestroy(image);
}
}
}
if (!cursor->x11.handle)
{
unsigned int native = 0;
switch (shape)
{
case GLFW_ARROW_CURSOR:
native = XC_left_ptr; native = XC_left_ptr;
else if (shape == GLFW_IBEAM_CURSOR) break;
case GLFW_IBEAM_CURSOR:
native = XC_xterm; native = XC_xterm;
else if (shape == GLFW_CROSSHAIR_CURSOR) break;
case GLFW_CROSSHAIR_CURSOR:
native = XC_crosshair; native = XC_crosshair;
else if (shape == GLFW_HAND_CURSOR) break;
case GLFW_HAND_CURSOR:
native = XC_hand2; native = XC_hand2;
else if (shape == GLFW_HRESIZE_CURSOR) break;
case GLFW_HRESIZE_CURSOR:
native = XC_sb_h_double_arrow; native = XC_sb_h_double_arrow;
else if (shape == GLFW_VRESIZE_CURSOR) break;
case GLFW_VRESIZE_CURSOR:
native = XC_sb_v_double_arrow; native = XC_sb_v_double_arrow;
else break;
case GLFW_RESIZE_ALL_CURSOR:
native = XC_fleur;
break;
default:
//_glfwInputError(GLFW_CURSOR_UNAVAILABLE,
// "X11: Standard cursor shape unavailable");
return GLFW_FALSE; return GLFW_FALSE;
}
cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
if (!cursor->x11.handle) if (!cursor->x11.handle)
@ -2932,6 +3000,7 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
"X11: Failed to create standard cursor"); "X11: Failed to create standard cursor");
return GLFW_FALSE; return GLFW_FALSE;
} }
}
return GLFW_TRUE; return GLFW_TRUE;
} }

View File

@ -141,4 +141,10 @@ const (
HandCursor = StandardCursor(0x00036004) HandCursor = StandardCursor(0x00036004)
HResizeCursor = StandardCursor(0x00036005) HResizeCursor = StandardCursor(0x00036005)
VResizeCursor = StandardCursor(0x00036006) VResizeCursor = StandardCursor(0x00036006)
// v3.4
ResizeNWSECursor = StandardCursor(0x00036007)
ResizeNESWCursor = StandardCursor(0x00036008)
ResizeAllCursor = StandardCursor(0x00036009)
NotAllowedCursor = StandardCursor(0x0003600A)
) )

View File

@ -104,8 +104,12 @@ const (
_OCR_CROSS = 32515 _OCR_CROSS = 32515
_OCR_HAND = 32649 _OCR_HAND = 32649
_OCR_IBEAM = 32513 _OCR_IBEAM = 32513
_OCR_NO = 32648
_OCR_NORMAL = 32512 _OCR_NORMAL = 32512
_OCR_SIZEALL = 32646
_OCR_SIZENESW = 32643
_OCR_SIZENS = 32645 _OCR_SIZENS = 32645
_OCR_SIZENWSE = 32642
_OCR_SIZEWE = 32644 _OCR_SIZEWE = 32644
_PM_NOREMOVE = 0x0000 _PM_NOREMOVE = 0x0000
_PM_REMOVE = 0x0001 _PM_REMOVE = 0x0001

View File

@ -289,6 +289,12 @@ const (
HandCursor StandardCursor = 0x00036004 HandCursor StandardCursor = 0x00036004
HResizeCursor StandardCursor = 0x00036005 HResizeCursor StandardCursor = 0x00036005
VResizeCursor StandardCursor = 0x00036006 VResizeCursor StandardCursor = 0x00036006
// v3.4
ResizeNWSECursor StandardCursor = 0x00036007
ResizeNESWCursor StandardCursor = 0x00036008
ResizeAllCursor StandardCursor = 0x00036009
NotAllowedCursor StandardCursor = 0x0003600A
) )
type Error int type Error int

View File

@ -351,7 +351,11 @@ func CreateStandardCursor(shape StandardCursor) (*Cursor, error) {
shape != CrosshairCursor && shape != CrosshairCursor &&
shape != HandCursor && shape != HandCursor &&
shape != HResizeCursor && shape != HResizeCursor &&
shape != VResizeCursor { shape != VResizeCursor &&
shape != ResizeNWSECursor &&
shape != ResizeNESWCursor &&
shape != ResizeAllCursor &&
shape != NotAllowedCursor {
return nil, fmt.Errorf("goglfw: invalid standard cursor 0x%08X: %w", shape, InvalidEnum) return nil, fmt.Errorf("goglfw: invalid standard cursor 0x%08X: %w", shape, InvalidEnum)
} }

View File

@ -2242,6 +2242,14 @@ func (c *Cursor) platformCreateStandardCursor(shape StandardCursor) error {
id = _OCR_SIZEWE id = _OCR_SIZEWE
case VResizeCursor: case VResizeCursor:
id = _OCR_SIZENS id = _OCR_SIZENS
case ResizeNWSECursor: // v3.4
id = _OCR_SIZENWSE
case ResizeNESWCursor: // v3.4
id = _OCR_SIZENESW
case ResizeAllCursor: // v3.4
id = _OCR_SIZEALL
case NotAllowedCursor: // v3.4
id = _OCR_NO
default: default:
return fmt.Errorf("goglfw: invalid shape: %d", shape) return fmt.Errorf("goglfw: invalid shape: %d", shape)
} }

View File

@ -52,6 +52,10 @@ const (
CursorShapePointer CursorShapePointer
CursorShapeEWResize CursorShapeEWResize
CursorShapeNSResize CursorShapeNSResize
CursorShapeNESWResize
CursorShapeNWSEResize
CursorShapeMove
CursorShapeNotAllowed
) )
type WindowResizingMode int type WindowResizingMode int

View File

@ -201,6 +201,10 @@ func initialize() error {
glfwSystemCursors[CursorShapePointer] = glfw.CreateStandardCursor(glfw.HandCursor) glfwSystemCursors[CursorShapePointer] = glfw.CreateStandardCursor(glfw.HandCursor)
glfwSystemCursors[CursorShapeEWResize] = glfw.CreateStandardCursor(glfw.HResizeCursor) glfwSystemCursors[CursorShapeEWResize] = glfw.CreateStandardCursor(glfw.HResizeCursor)
glfwSystemCursors[CursorShapeNSResize] = glfw.CreateStandardCursor(glfw.VResizeCursor) glfwSystemCursors[CursorShapeNSResize] = glfw.CreateStandardCursor(glfw.VResizeCursor)
glfwSystemCursors[CursorShapeNESWResize] = glfw.CreateStandardCursor(glfw.ResizeNESWCursor)
glfwSystemCursors[CursorShapeNWSEResize] = glfw.CreateStandardCursor(glfw.ResizeNWSECursor)
glfwSystemCursors[CursorShapeMove] = glfw.CreateStandardCursor(glfw.ResizeAllCursor)
glfwSystemCursors[CursorShapeNotAllowed] = glfw.CreateStandardCursor(glfw.NotAllowedCursor)
return nil return nil
} }
@ -614,7 +618,7 @@ func (u *userInterfaceImpl) SetCursorMode(mode CursorMode) {
u.mainThread.Call(func() { u.mainThread.Call(func() {
u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(mode)) u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(mode))
if mode == CursorModeVisible { if mode == CursorModeVisible {
u.setNativeCursor(u.getCursorShape()) u.window.SetCursor(glfwSystemCursors[u.getCursorShape()])
} }
}) })
} }
@ -632,7 +636,7 @@ func (u *userInterfaceImpl) SetCursorShape(shape CursorShape) {
return return
} }
u.mainThread.Call(func() { u.mainThread.Call(func() {
u.setNativeCursor(shape) u.window.SetCursor(glfwSystemCursors[shape])
}) })
} }

View File

@ -218,17 +218,13 @@ var (
var ( var (
sel_alloc = objc.RegisterName("alloc") sel_alloc = objc.RegisterName("alloc")
sel_arrowCursor = objc.RegisterName("arrowCursor")
sel_class = objc.RegisterName("class") sel_class = objc.RegisterName("class")
sel_collectionBehavior = objc.RegisterName("collectionBehavior") sel_collectionBehavior = objc.RegisterName("collectionBehavior")
sel_crosshairCursor = objc.RegisterName("crosshairCursor")
sel_delegate = objc.RegisterName("delegate") sel_delegate = objc.RegisterName("delegate")
sel_IBeamCursor = objc.RegisterName("IBeamCursor")
sel_init = objc.RegisterName("init") sel_init = objc.RegisterName("init")
sel_initWithOrigDelegate = objc.RegisterName("initWithOrigDelegate:") sel_initWithOrigDelegate = objc.RegisterName("initWithOrigDelegate:")
sel_mouseLocation = objc.RegisterName("mouseLocation") sel_mouseLocation = objc.RegisterName("mouseLocation")
sel_performSelector = objc.RegisterName("performSelector:") sel_performSelector = objc.RegisterName("performSelector:")
sel_pointingHandCursor = objc.RegisterName("pointingHandCursor")
sel_set = objc.RegisterName("set") sel_set = objc.RegisterName("set")
sel_setCollectionBehavior = objc.RegisterName("setCollectionBehavior:") sel_setCollectionBehavior = objc.RegisterName("setCollectionBehavior:")
sel_setDelegate = objc.RegisterName("setDelegate:") sel_setDelegate = objc.RegisterName("setDelegate:")
@ -242,8 +238,6 @@ var (
sel_windowDidResignKey = objc.RegisterName("windowDidResignKey:") sel_windowDidResignKey = objc.RegisterName("windowDidResignKey:")
sel_windowDidResize = objc.RegisterName("windowDidResize:") sel_windowDidResize = objc.RegisterName("windowDidResize:")
sel_windowDidChangeOcclusionState = objc.RegisterName("windowDidChangeOcclusionState:") sel_windowDidChangeOcclusionState = objc.RegisterName("windowDidChangeOcclusionState:")
sel_windowResizeEastWestCursor = objc.RegisterName("_windowResizeEastWestCursor")
sel_windowResizeNorthSouthCursor = objc.RegisterName("_windowResizeNorthSouthCursor")
sel_windowShouldClose = objc.RegisterName("windowShouldClose:") sel_windowShouldClose = objc.RegisterName("windowShouldClose:")
sel_windowWillEnterFullScreen = objc.RegisterName("windowWillEnterFullScreen:") sel_windowWillEnterFullScreen = objc.RegisterName("windowWillEnterFullScreen:")
sel_windowWillExitFullScreen = objc.RegisterName("windowWillExitFullScreen:") sel_windowWillExitFullScreen = objc.RegisterName("windowWillExitFullScreen:")
@ -303,26 +297,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool {
return cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0 return cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0
} }
func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) {
NSCursor := objc.ID(class_NSCursor).Send(sel_class)
cursor := NSCursor.Send(sel_performSelector, sel_arrowCursor)
switch shape {
case 0:
cursor = NSCursor.Send(sel_performSelector, sel_arrowCursor)
case 1:
cursor = NSCursor.Send(sel_performSelector, sel_IBeamCursor)
case 2:
cursor = NSCursor.Send(sel_performSelector, sel_crosshairCursor)
case 3:
cursor = NSCursor.Send(sel_performSelector, sel_pointingHandCursor)
case 4:
cursor = NSCursor.Send(sel_performSelector, sel_windowResizeEastWestCursor)
case 5:
cursor = NSCursor.Send(sel_performSelector, sel_windowResizeNorthSouthCursor)
}
cursor.Send(sel_set)
}
func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool {
// TODO: If the window is transparent, we should use GLFW's windowed fullscreen (#1822, #1857). // TODO: If the window is transparent, we should use GLFW's windowed fullscreen (#1822, #1857).
// However, if the user clicks the green button, should this window be in native fullscreen mode? // However, if the user clicks the green button, should this window be in native fullscreen mode?

View File

@ -200,11 +200,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool {
return false return false
} }
func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) {
// TODO: Use native API in the future (#1571)
u.window.SetCursor(glfwSystemCursors[shape])
}
func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool {
return false return false
} }

View File

@ -189,11 +189,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool {
return false return false
} }
func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) {
// TODO: Use native API in the future (#1571)
u.window.SetCursor(glfwSystemCursors[shape])
}
func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool {
return false return false
} }

View File

@ -67,6 +67,14 @@ func driverCursorShapeToCSSCursor(cursor CursorShape) string {
return "ew-resize" return "ew-resize"
case CursorShapeNSResize: case CursorShapeNSResize:
return "ns-resize" return "ns-resize"
case CursorShapeNESWResize:
return "nesw-resize"
case CursorShapeNWSEResize:
return "nwse-resize"
case CursorShapeMove:
return "move"
case CursorShapeNotAllowed:
return "not-allowed"
} }
return "auto" return "auto"
} }

2
run.go
View File

@ -378,6 +378,8 @@ func CursorShape() CursorShapeType {
// SetCursorShape sets the cursor shape. // SetCursorShape sets the cursor shape.
// //
// If the platform doesn't implement the given shape, the default cursor shape is used.
//
// SetCursorShape is concurrent-safe. // SetCursorShape is concurrent-safe.
func SetCursorShape(shape CursorShapeType) { func SetCursorShape(shape CursorShapeType) {
ui.Get().SetCursorShape(shape) ui.Get().SetCursorShape(shape)