changeset 23:d55b5b998412

Implement built-in mouse cursors with X.
author Jordan Miner <jminer7@gmail.com>
date Sat, 25 Jul 2009 14:58:43 -0500
parents 9a50d6d96815
children 43a88caead16
files dynamin/c/xlib.d dynamin/gui/control.d dynamin/gui/cursor.d dynamin/gui/window.d dynamin/gui/windows_cursor.d dynamin/gui/windows_window.d dynamin/gui/x_cursor.d dynamin/gui/x_window.d
diffstat 8 files changed, 211 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/dynamin/c/xlib.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/c/xlib.d	Sat Jul 25 14:58:43 2009 -0500
@@ -245,6 +245,13 @@
 	uint bg,
 	uint depth);
 
+Pixmap XCreateBitmapFromData(
+	Display* display,
+	Drawable d,
+	/*const*/ char* data,
+	uint width,
+	uint height);
+
 int XFreePixmap(Display* display, Pixmap pixmap);
 //}}}
 
@@ -258,12 +265,95 @@
 	uint x,
 	uint y);
 
+Cursor XCreateFontCursor(Display* display, uint shape);
+
 int XDefineCursor(Display* display, Window w, Cursor cursor);
 
 int XUndefineCursor(Display* display, Window w);
 
 int XFreeCursor(Display* display, Cursor cursor);
 
+// cursorfont.h
+enum {
+	XC_num_glyphs        = 154,
+	XC_X_cursor          = 0,
+	XC_arrow             = 2,
+	XC_based_arrow_down  = 4,
+	XC_based_arrow_up    = 6,
+	XC_boat              = 8,
+	XC_bogosity          = 10,
+	XC_bottom_left_corner  = 12,
+	XC_bottom_right_corner = 14,
+	XC_bottom_side       = 16,
+	XC_bottom_tee        = 18,
+	XC_box_spiral        = 20,
+	XC_center_ptr        = 22,
+	XC_circle            = 24,
+	XC_clock             = 26,
+	XC_coffee_mug        = 28,
+	XC_cross             = 30,
+	XC_cross_reverse     = 32,
+	XC_crosshair         = 34,
+	XC_diamond_cross     = 36,
+	XC_dot               = 38,
+	XC_dotbox            = 40,
+	XC_double_arrow      = 42,
+	XC_draft_large       = 44,
+	XC_draft_small       = 46,
+	XC_draped_box        = 48,
+	XC_exchange          = 50,
+	XC_fleur             = 52,
+	XC_gobbler           = 54,
+	XC_gumby             = 56,
+	XC_hand1             = 58,
+	XC_hand2             = 60,
+	XC_heart             = 62,
+	XC_icon              = 64,
+	XC_iron_cross        = 66,
+	XC_left_ptr          = 68,
+	XC_left_side         = 70,
+	XC_left_tee          = 72,
+	XC_leftbutton        = 74,
+	XC_ll_angle          = 76,
+	XC_lr_angle          = 78,
+	XC_man               = 80,
+	XC_middlebutton      = 82,
+	XC_mouse             = 84,
+	XC_pencil            = 86,
+	XC_pirate            = 88,
+	XC_plus              = 90,
+	XC_question_arrow    = 92,
+	XC_right_ptr         = 94,
+	XC_right_side        = 96,
+	XC_right_tee         = 98,
+	XC_rightbutton       = 100,
+	XC_rtl_logo          = 102,
+	XC_sailboat          = 104,
+	XC_sb_down_arrow     = 106,
+	XC_sb_h_double_arrow = 108,
+	XC_sb_left_arrow     = 110,
+	XC_sb_right_arrow    = 112,
+	XC_sb_up_arrow       = 114,
+	XC_sb_v_double_arrow = 116,
+	XC_shuttle           = 118,
+	XC_sizing            = 120,
+	XC_spider            = 122,
+	XC_spraycan          = 124,
+	XC_star              = 126,
+	XC_target            = 128,
+	XC_tcross            = 130,
+	XC_top_left_arrow    = 132,
+	XC_top_left_corner   = 134,
+	XC_top_right_corner  = 136,
+	XC_top_side          = 138,
+	XC_top_tee           = 140,
+	XC_trek              = 142,
+	XC_ul_angle          = 144,
+	XC_umbrella          = 146,
+	XC_ur_angle          = 148,
+	XC_watch             = 150,
+	XC_xterm             = 152,
+}
 //}}}
 
 //{{{ keyboard functions
--- a/dynamin/gui/control.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/control.d	Sat Jul 25 14:58:43 2009 -0500
@@ -74,7 +74,7 @@
 	bool _elasticX, _elasticY;
 public:
 	protected void dispatchMouseEntered(EventArgs e) {
-		Cursor.setCurrent(this, _cursor);
+		setCurrentCursor(_cursor);
 		mouseEntered.callHandlers(e);
 		mouseEntered.callMainHandler(e);
 	}
@@ -503,15 +503,24 @@
 		repaint();
 	}
 
+	void setCurrentCursor(Cursor cur) {
+		if(parent)
+			parent.setCurrentCursor(cur);
+	}
+	/**
+	 * Gets or sets the mouse cursor that will be shown when the mouse
+	 * is over this control.
+	 */
 	Cursor cursor() {
 		return _cursor;
 	}
+	/// ditto
 	void cursor(Cursor cur) {
 		if(_cursor is cur)
 			return;
 		_cursor = cur;
 		if(getHotControl() is this)
-			Cursor.setCurrent(this, _cursor);
+			setCurrentCursor(_cursor);
 	}
 
 	/**
--- a/dynamin/gui/cursor.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/cursor.d	Sat Jul 25 14:58:43 2009 -0500
@@ -29,20 +29,38 @@
 import dynamin.gui_backend;
 
 ///
-class Cursor {
+final class Cursor {
 private:
 	mixin CursorBackend;
+static:
+	Cursor _none = null;
+	Cursor _arrow = null;
+	Cursor _waitArrow = null;
+	Cursor _wait = null;
+	Cursor _text = null;
+	Cursor _hand = null;
+	Cursor _move = null;
+	Cursor _resizeHoriz = null;
+	Cursor _resizeVert = null;
+	Cursor _resizeBackslash = null;
+	Cursor _resizeSlash = null;
+	Cursor _drag = null;
+	Cursor _invalidDrag = null;
+	Cursor _reversedArrow = null;
+	Cursor _crosshair = null;
+	// handles caching for backends
+	Cursor maybeLoad(Cursor function() loadCursor, ref Cursor cache) {
+		if(cache is null)
+			cache = loadCursor();
+		return cache;
+	}
 public:
-static:
-	void setCurrent(Control c, Cursor cur) {
-		backend_SetCurrent(c, cur);
-	}
 	/**
 	 * Gets a blank cursor used for an activity, such as typing, with which
 	 * the cursor may interfere.
 	 */
 	Cursor None() {
-		return backend_None;
+		return maybeLoad(&backend_None, _none);
 	}
 	/**
 	 * Gets the cursor used normally.
@@ -52,7 +70,7 @@
 	 * $(IMAGE ../web/example_arrow_cursor.png)
 	 */
 	Cursor Arrow() {
-		return backend_Arrow;
+		return maybeLoad(&backend_Arrow, _arrow);
 	}
 	/**
 	 * Gets the cursor used when the computer is accomplishing some
@@ -63,7 +81,7 @@
 	 * $(IMAGE ../web/example_wait_arrow_cursor.png)
 	 */
 	Cursor WaitArrow() {
-		return backend_WaitArrow;
+		return maybeLoad(&backend_WaitArrow, _waitArrow);
 	}
 	/**
 	 * Gets the cursor used when the computer is accomplishing some
@@ -74,7 +92,7 @@
 	 * $(IMAGE ../web/example_wait_cursor.png)
 	 */
 	Cursor Wait() {
-		return backend_Wait;
+		return maybeLoad(&backend_Wait, _wait);
 	}
 	/**
 	 * Gets the cursor used when the mouse is over selectable text.
@@ -87,7 +105,7 @@
 	 * $(IMAGE ../web/example_text_cursor.png)
 	 */
 	Cursor Text() {
-		return backend_Text;
+		return maybeLoad(&backend_Text, _text);
 	}
 	/**
 	 * Gets the cursor used when the mouse is over a link.
@@ -97,7 +115,7 @@
 	 * $(IMAGE ../web/example_hand_cursor.png)
 	 */
 	Cursor Hand() {
-		return backend_Hand;
+		return maybeLoad(&backend_Hand, _hand);
 	}
 	/**
 	 * Gets the cursor used when moving something.
@@ -107,7 +125,7 @@
 	 * $(IMAGE ../web/example_move_cursor.png)
 	 */
 	Cursor Move() {
-		return backend_Move;
+		return maybeLoad(&backend_Move, _move);
 	}
 	/**
 	 * Gets the cursor used when resizing the left or right
@@ -118,7 +136,7 @@
 	 * $(IMAGE ../web/example_resize_horiz_cursor.png)
 	 */
 	Cursor ResizeHoriz() {
-		return backend_ResizeHoriz;
+		return maybeLoad(&backend_ResizeHoriz, _resizeHoriz);
 	}
 	/**
 	 * Gets the cursor used when resizing the top or bottom
@@ -129,7 +147,7 @@
 	 * $(IMAGE ../web/example_resize_vert_cursor.png)
 	 */
 	Cursor ResizeVert() {
-		return backend_ResizeVert;
+		return maybeLoad(&backend_ResizeVert, _resizeVert);
 	}
 	/**
 	 * Gets the cursor used when resizing the top-left or bottom-right
@@ -140,7 +158,7 @@
 	 * $(IMAGE ../web/example_resize_backslash_cursor.png)
 	 */
 	Cursor ResizeBackslash() {
-		return backend_ResizeBackslash;
+		return maybeLoad(&backend_ResizeBackslash, _resizeBackslash);
 	}
 	/**
 	 * Gets the cursor used when resizing the bottom-left or top-right
@@ -151,7 +169,7 @@
 	 * $(IMAGE ../web/example_resize_slash_cursor.png)
 	 */
 	Cursor ResizeSlash() {
-		return backend_ResizeSlash;
+		return maybeLoad(&backend_ResizeSlash, _resizeSlash);
 	}
 	/**
 	 * Gets the cursor used when the mouse is over something that
@@ -162,7 +180,7 @@
 	 * $(IMAGE ../web/example_drag_cursor.png)
 	 */
 	Cursor Drag() {
-		return backend_Drag;
+		return maybeLoad(&backend_Drag, _drag);
 	}
 	/**
 	 * Gets the cursor used when the mouse is over something that
@@ -173,7 +191,7 @@
 	 * $(IMAGE ../web/example_invalid_drag_cursor.png)
 	 */
 	Cursor InvalidDrag() {
-		return backend_InvalidDrag;
+		return maybeLoad(&backend_InvalidDrag, _invalidDrag);
 	}
 	/**
 	 * Gets the cursor used when the mouse is over a gutter that
@@ -184,7 +202,7 @@
 	 * $(IMAGE ../web/example_reversed_arrow_cursor.png)
 	 */
 	Cursor ReversedArrow() {
-		return backend_ReversedArrow;
+		return maybeLoad(&backend_ReversedArrow, _reversedArrow);
 	}
 	/**
 	 * Gets the cursor that is sometimes used for selecting.
@@ -194,7 +212,7 @@
 	 * $(IMAGE ../web/example_crosshair_cursor.png)
 	 */
 	Cursor Crosshair() {
-		return backend_Crosshair;
+		return maybeLoad(&backend_Crosshair, _crosshair);
 	}
 }
 
--- a/dynamin/gui/window.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/window.d	Sat Jul 25 14:58:43 2009 -0500
@@ -30,6 +30,7 @@
 import dynamin.all_painting;
 import dynamin.all_gui;
 import dynamin.gui.control;
+import dynamin.gui.cursor;
 import dynamin.gui_backend;
 import dynamin.gui.container;
 import dynamin.gui.events;
@@ -296,6 +297,12 @@
 		backend_borderStyle = border;
 	}
 
+	override void setCurrentCursor(Cursor cur) {
+		if(!handleCreated)
+			return;
+		backend_setCurrentCursor(cur);
+	}
+
 	alias Control.repaint repaint;
 	void repaint(Rect rect) {
 		if(!handleCreated)
--- a/dynamin/gui/windows_cursor.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/windows_cursor.d	Sat Jul 25 14:58:43 2009 -0500
@@ -35,106 +35,75 @@
 
 template CursorBackend() {
 	HCURSOR _handle;
-	bool isBuiltin = false;
 	this(HCURSOR h) {
 		_handle = h;
-		isBuiltin = true;
-	}
-static:
-	void backend_SetCurrent(Control c, Cursor cur) {
-		assert(cur.isBuiltin); // TODO: allow custom cursors
-		SetCursor(cur._handle);
 	}
-	Cursor none = null;
-	Cursor arrow = null;
-	Cursor waitArrow = null;
-	Cursor wait = null;
-	Cursor text = null;
-	Cursor hand = null;
-	Cursor move = null;
-	Cursor resizeHoriz = null;
-	Cursor resizeVert = null;
-	Cursor resizeBackslash = null;
-	Cursor resizeSlash = null;
-	Cursor drag = null; // from resource
-	Cursor invalidDrag = null;
-	Cursor reversedArrow = null; // from resource
-	Cursor crosshair = null;
-
-	Cursor backend_maybeLoad(Cursor* cache, int curRes) {
-		if(*cache is null) {
-			HCURSOR hcur = LoadImage(null, MAKEINTRESOURCE(curRes),
-					IMAGE_CURSOR,  0, 0, LR_SHARED | LR_DEFAULTSIZE);
-			if(hcur is null)
-				Stdout.format("LoadImage() failed loading cursor {}", curRes).newline;
-			else
-				*cache = new Cursor(hcur);
-		}
-		return *cache;
+	public HCURSOR handle() { return _handle; }
+static:
+	Cursor backend_getCursor(int curRes) {
+		HCURSOR hcur = LoadImage(null, MAKEINTRESOURCE(curRes),
+				IMAGE_CURSOR,  0, 0, LR_SHARED | LR_DEFAULTSIZE);
+		if(hcur is null)
+			Stdout.format("LoadImage() failed loading cursor {}", curRes).newline;
+		return new Cursor(hcur);
 	}
-	Cursor backend_maybeLoad(Cursor* cache, wchar[] name) {
-		if(*cache is null) {
-			HCURSOR hcur = LoadImage(GetModuleHandle(null), name.ptr,
-				IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
-			if(hcur is null)
-				Stdout.format("LoadImage() failed loading cursor {}", name).newline;
-			else
-				*cache = new Cursor(hcur);
-		}
-		return *cache;
+	Cursor backend_getCursor(wchar[] name) {
+		HCURSOR hcur = LoadImage(GetModuleHandle(null), name.ptr,
+			IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
+		if(hcur is null)
+			Stdout.format("LoadImage() failed loading cursor {}", name).newline;
+		return new Cursor(hcur);
 	}
 	Cursor backend_None() {
-		if(none is null)
-			none = new Cursor(cast(HCURSOR)null);
-		return none;
+		return new Cursor(cast(HCURSOR)null);
 	}
 	Cursor backend_Arrow() {
-		return backend_maybeLoad(&arrow, OCR_NORMAL);
+		return backend_getCursor(OCR_NORMAL);
 	}
 	Cursor backend_WaitArrow() {
-		return backend_maybeLoad(&waitArrow, OCR_APPSTARTING);
+		return backend_getCursor(OCR_APPSTARTING);
 	}
 	Cursor backend_Wait() {
-		return backend_maybeLoad(&wait, OCR_WAIT);
+		return backend_getCursor(OCR_WAIT);
 	}
 	Cursor backend_Text() {
-		return backend_maybeLoad(&text, OCR_IBEAM);
+		return backend_getCursor(OCR_IBEAM);
 	}
 	Cursor backend_Hand() {
-		return backend_maybeLoad(&hand, OCR_HAND); // Windows 98 & newer
+		return backend_getCursor(OCR_HAND); // Windows 98 & newer
 	}
 	Cursor backend_Move() {
-		return backend_maybeLoad(&move, OCR_SIZEALL);
+		return backend_getCursor(OCR_SIZEALL);
 	}
 	Cursor backend_ResizeHoriz() {
-		return backend_maybeLoad(&resizeHoriz, OCR_SIZEWE);
+		return backend_getCursor(OCR_SIZEWE);
 	}
 	Cursor backend_ResizeVert() {
-		return backend_maybeLoad(&resizeVert, OCR_SIZENS);
+		return backend_getCursor(OCR_SIZENS);
 	}
 	Cursor backend_ResizeBackslash() {
-		return backend_maybeLoad(&resizeBackslash, OCR_SIZENWSE);
+		return backend_getCursor(OCR_SIZENWSE);
 	}
 	Cursor backend_ResizeSlash() {
-		return backend_maybeLoad(&resizeSlash, OCR_SIZENESW);
+		return backend_getCursor(OCR_SIZENESW);
 	}
 	Cursor backend_Drag() {
 		if(checkWindowsVersion(WindowsVersion.WindowsVista))
-			return backend_maybeLoad(&drag, "AeroDragCur");
+			return backend_getCursor("AeroDragCur");
 		else
-			return backend_maybeLoad(&drag, "DragCur");
+			return backend_getCursor("DragCur");
 	}
 	Cursor backend_InvalidDrag() {
-		return backend_maybeLoad(&invalidDrag, OCR_NO);
+		return backend_getCursor(OCR_NO);
 	}
 	Cursor backend_ReversedArrow() {
 		if(checkWindowsVersion(WindowsVersion.WindowsVista))
-			return backend_maybeLoad(&drag, "AeroReversedArrowCur");
+			return backend_getCursor("AeroReversedArrowCur");
 		else
-			return backend_maybeLoad(&drag, "ReversedArrowCur");
+			return backend_getCursor("ReversedArrowCur");
 	}
 	Cursor backend_Crosshair() {
-		return backend_maybeLoad(&crosshair, OCR_CROSS);
+		return backend_getCursor(OCR_CROSS);
 	}
 }
 
--- a/dynamin/gui/windows_window.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/windows_window.d	Sat Jul 25 14:58:43 2009 -0500
@@ -197,6 +197,9 @@
 	void backend_borderStyle(WindowBorderStyle border) {
 		backend_updateWindowStyles();
 	}
+	void backend_setCurrentCursor(Cursor cur) {
+		SetCursor(cur.handle);
+	}
 	void backend_repaint(Rect rect) {
 		RECT wrect;
 		wrect.left = cast(int)(rect.x-_borderSize.left);
--- a/dynamin/gui/x_cursor.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/x_cursor.d	Sat Jul 25 14:58:43 2009 -0500
@@ -7,69 +7,63 @@
 public import dynamin.gui.control;
 
 template CursorBackend() {
-static:
-	void backend_SetCurrent(Control c, Cursor cur) {
+	XCursor _handle;
+	this(XCursor h) {
+		_handle = h;
 	}
-	Cursor none = null;
-	Cursor arrow = null;
-	Cursor waitArrow = null;
-	Cursor wait = null;
-	Cursor text = null;
-	Cursor hand = null;
-	Cursor move = null;
-	Cursor resizeHoriz = null;
-	Cursor resizeVert = null;
-	Cursor resizeBackslash = null;
-	Cursor resizeSlash = null;
-	Cursor drag = null; // from resource
-	Cursor invalidDrag = null;
-	Cursor reversedArrow = null; // from resource
-	Cursor crosshair = null;
-
+	public XCursor handle() { return _handle; }
+static:
+	Cursor getCursor(uint shape) {
+		return new Cursor(XCreateFontCursor(display, shape));
+	}
 	Cursor backend_None() {
-		return null;
+		auto p = XCreateBitmapFromData(display, msgWin,
+			"\0\0\0\0\0\0\0\0", 1, 1);
+		XColor color;
+		return new Cursor(XCreatePixmapCursor(display, p, p, &color, &color,
+			1, 1));
 	}
 	Cursor backend_Arrow() {
-		return null;
+		return getCursor(XC_left_ptr);
 	}
 	Cursor backend_WaitArrow() {
-		return null;
+		return getCursor(XC_watch);
 	}
 	Cursor backend_Wait() {
-		return null;
+		return getCursor(XC_watch);
 	}
 	Cursor backend_Text() {
-		return null;
+		return getCursor(XC_xterm);
 	}
 	Cursor backend_Hand() {
-		return null;
+		return getCursor(XC_hand2);
 	}
 	Cursor backend_Move() {
-		return null;
+		return getCursor(XC_fleur);
 	}
 	Cursor backend_ResizeHoriz() {
-		return null;
+		return getCursor(XC_left_side);
 	}
 	Cursor backend_ResizeVert() {
-		return null;
+		return getCursor(XC_top_side);
 	}
 	Cursor backend_ResizeBackslash() {
-		return null;
+		return getCursor(XC_top_left_corner);
 	}
 	Cursor backend_ResizeSlash() {
-		return null;
+		return getCursor(XC_top_right_corner);
 	}
 	Cursor backend_Drag() {
-		return null;
+		return getCursor(XC_fleur); // not the best
 	}
 	Cursor backend_InvalidDrag() {
-		return null;
+		return getCursor(XC_fleur); // bad, but what Linux does
 	}
 	Cursor backend_ReversedArrow() {
-		return null;
+		return getCursor(XC_right_ptr);
 	}
 	Cursor backend_Crosshair() {
-		return null;
+		return getCursor(XC_crosshair);
 	}
 }
 
--- a/dynamin/gui/x_window.d	Sat Jul 25 14:47:45 2009 -0500
+++ b/dynamin/gui/x_window.d	Sat Jul 25 14:58:43 2009 -0500
@@ -538,6 +538,10 @@
 		backend_update_MOTIF_WM_HINTS();
 		backend_nativeToBorderSize();
 	}
+	void backend_setCurrentCursor(Cursor cur) {
+		XDefineCursor(display, _handle, cur.handle);
+	}
+
 	static _InvalidRect[] invalidRects;
 	void backend_repaint(Rect rect) {
 		invalidRects.length = invalidRects.length+1;