-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
The mouse cursor is a humble little pointer. It never needs to change for correct program operation, but the subtle feedback provided by doing so can translate to a great improvement to the user experience.
To start the conversation I propose a simple API along the lines;
// Cursor may identify a pre-defined system cursor or custom (user-created) cursor
type Cursor int
const (
UnspecifiedCursor Cursor = iota
NormalCursor
CrosshairCursor
IBeamCursor
// you get the idea -- the full set of portable cursors is discussed below
}
interface CursorCreator {
// CreateCursor gracefully degrades if eg. a coloured image is provided but the
// driver only supports greyscale/monochrome cursors
CreateCursor(img image.Image, hotspot imagePoint) Cursor
}
interface CursorSetter {
SetCursor(c Cursor)
}
The underlying platforms do things a bit differently. In windows, changing the cursor affects the entire desktop, but users are advised to be courteous and "set the cursor shape only when the cursor is in its client area or when the window is capturing mouse input"[1]. In OSX, changing the cursor affects only the calling application (and all windows it has open). In X11, changing the cursor is a window-specific operation, and only affects the cursor while it is over the window in question. X11's API suggests SetCursor must be implemented by shiny.Window.
[1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms648393(v=vs.85).aspx
It might be useful for SetCursor to return the previously active Cursor. But since the cursor is application-local in X11/OSX and best practice in windows is to try and avoid affecting other applications, in most circumstances the caller already knows the cursor's previous state (because it was responsible for the previous change, if any).
Portable cursors:
| Name | WIN32 | OSX | X11 | Notes |
|---|---|---|---|---|
| Normal | 👍 | 👍 | 👍 | |
| Crosshair | 👍 | 👍 | 👍 | |
| IBeam | 👍 | 👍 | 👍 | |
| Hand | 👍 | 👍 | 👍 | OSX has both open and closed variants |
| NotAllowed | 👍 | 👍 | (but X11 does provide a space shuttle!) | |
| Busy | 👍 | [2] | 👍 | |
| ResizeN | [3] | 👍 | 👍 | |
| ResizeS | [3] | 👍 | 👍 | |
| ResizeE | [3] | 👍 | 👍 | |
| ResizeW | [3] | 👍 | 👍 | |
| ResizeNS | 👍 | 👍 | 👍 | |
| ResizeEW | 👍 | 👍 | 👍 | |
| ResizeNE | [3] | [4] | 👍 | |
| ResizeSE | [3] | [4] | 👍 | |
| ResizeSW | [3] | [4] | 👍 | |
| ResizeNW | [3] | [4] | 👍 | |
| ResizeNESW | 👍 | [4] | ||
| ResizeNWSE | 👍 | [4] |
[2] OSX's "busy" spinning wheel cursor is automatically displayed by the system if the application's main loop stops responding. There is no API allowing the application to set this cursor on request. Modern versions of windows have similar automatic behaviour which hints at consensus that setting the cursor to a busy icon has been decided to be a poor ux -- it may not be worth providing the busy cursor.
[3] Windows just has the four double-sided resize arrows - NS, EW, NESW, and NWSE. These can be reused to cover the N, S, E, W, NE, NW, SE, SW cases.
[4] OSX has diagonal resize cursors but they are not easily available to applications. possible workaround: http://stackoverflow.com/questions/10733228/native-osx-lion-resize-cursor-for-custom-nswindow-or-nsview . Alternately it may be worth automatically generating custom cursors to cover both these and X11's lack of NotAllowed cursor.
I gathered this information while implementing a cursor API for go.wde, primarily through API docs and explorative code. I do not have a wide experience with graphics APIs in general.