I'm not having much luck figuring out Gestures. I did find a few C-lang examples, but I'm not sure how they translate into D.

The problem seems to be how to get access to Gesture events. Signals trigger, but in the Gesture-based callbacks, the Events are null.

I'm not even sure if I've associated the Gesture with the DrawingArea properly. I'm also starting to wonder if the whole thing isn't just redundant and unnecessary, especially considering that there isn't a phone-based version of D (not that I'm aware of, anyway).

Here's my code (the relevant code is the last two classes at the bottom):

// Cairo: Use the Mouse to Draw a Curve

import std.stdio;
import std.conv;

import gtk.MainWindow;
import gtk.Main;
import gtk.Box;
import gtk.Widget;
import gdk.Event;
import gtk.Gesture;
import gtk.GestureSingle;
import cairo.Context;
import gtk.DrawingArea;
import glib.Timeout;

void main(string[] args)
{
	TestRigWindow testRigWindow;
	
	Main.init(args);
    
	testRigWindow = new TestRigWindow();
	 
	Main.run();
	
} // main()


class TestRigWindow : MainWindow
{
	string title = "Cairo: Draw Cubic Bezier with Mouse";
	AppBox appBox;
	
	this()
	{
		super(title);
		setSizeRequest(640, 360);
		
		addOnDestroy(&quitApp);
		
		appBox = new AppBox();
		add(appBox);
		
		showAll();

	} // this() CONSTRUCTOR
	
		
	void quitApp(Widget widget)
	{
		writeln("Bye.");
		Main.quit();
		
	} // quitApp()

} // class TestRigWindow


class AppBox : Box
{
	MyDrawingArea myDrawingArea;
	
	this()
	{
		super(Orientation.VERTICAL, 10);
		
		myDrawingArea = new MyDrawingArea();
		
		packStart(myDrawingArea, true, true, 0); // LEFT justify
		
	} // this()

} // class AppBox


class NoodleGesture : GestureSingle
{
	MyDrawingArea _myDrawingArea;
	
	this(MyDrawingArea myDrawingArea)
	{
		super(myDrawingArea);
		
		_myDrawingArea = myDrawingArea;
writeln("NoodleGesture built");
		addOnUpdate(&onUpdate);
		addOnBegin(&onBegin);
		addOnEnd(&onEnd);
		
	} // this()

	
	public void onUpdate(Event event, Gesture gesture)
	{
writeln("onUpdate()");
		_myDrawingArea.updateCoordinates(event);
		
	} // onUpdate()

	
	public void onBegin(Event event, Gesture gesture)
	{
writeln("onBegin(), event = ", event);
		_myDrawingArea.beginDraw(event);
				
	} // onBegin()


	public void onEnd(Event event, Gesture gesture)
	{
writeln("onEnd()");
		_myDrawingArea.endDraw(event);
		
	} // onEnd()

} // class NoodleGesture


class MyDrawingArea : DrawingArea
{
	Timeout _timeout;
	int fps = 1000 / 24; // 24 frames per second
	bool dragAndDraw = false;
	double xStart = 25, yStart = 128;
	double controlPointX1 = 153, controlPointY1 = 230,
		 	 controlPointX2 = 25, controlPointY2 = 25,
			 xEnd, yEnd;
	NoodleGesture noodleGesture;
	
	this()
	{
		addOnDraw(&onDraw);

		noodleGesture = new NoodleGesture(this);

	} // this()


	bool onDraw(Scoped!Context context, Widget w)
	{
writeln("onDraw()");

		if(_timeout is null)
		{
			_timeout = new Timeout(fps, &onFrameElapsed, false);
		}

		if(dragAndDraw == true)
		{
			// set up and draw a cubic Bezier
			context.setLineWidth(3);
			context.setSourceRgb(0.3, 0.2, 0.1);
			context.moveTo(xStart, yStart);
			context.curveTo(controlPointX1, controlPointY1, controlPointX2, controlPointY2, xEnd, yEnd);
			context.stroke();
		}

		return(true);
		
	} // onDraw()


	bool onFrameElapsed()
	{
		GtkAllocation size;
		getAllocation(size);
		
		if(dragAndDraw == true)
		{
			queueDrawArea(size.x, size.y, size.width, size.height);
		}
		
		return(true);
		
	} // onFrameElapsed()
	
	
	void beginDraw(Event event)
	{
writeln("beginDraw() event = ", event);

		// Turn on context canvas redraw. Click-n-hold a mouse button to start
		// drawing a cubic Bezier and as long as the mouse button is depressed,
		// the end point of the curve will follow the mouse pointer. Let go, and
		// the Bezier will freeze in place.
		dragAndDraw = true;

		GdkEventButton* mouseEvent = event.button;
		xStart = event.button.x;
		yStart = event.button.y;

	} // beginDraw()
	
	
	void updateCoordinates(Event event)
	{
writeln("updateCoordinates()");

		// get the curve's end point
		xEnd = event.motion.x;
		yEnd = event.motion.y;
		
		// Recalculate the control points so we always have
		// a nice-looking double curve.
		controlPointX1 = xEnd;
		controlPointY1 = yStart;
		controlPointX2 = xStart;
		controlPointY2 = yEnd;
	
	} // updateCoordinates()


	void endDraw(Event event)
	{
writeln("endDraw()");

		GdkEventButton* buttonEvent = event.button;
		xEnd = event.button.x;
		yEnd = event.button.y;

		// When the mouse button is released, stop redrawing the context canvas.
		dragAndDraw = false;
	
		
	} // endDraw()
	
} // class MyDrawingArea