Hi Mike,
I'm working on a special request demo for the GtkDcoding blog wherein a slider is used to control the position of a ball on a DrawingArea.

I've run into what seems like a bug in the DrawingArea's redraw system, but I wanted to run it past you to be sure.

Set up: The ball is initialized to x = 30. Using the slider, the ball should move by 15 pixels per incremental step of the slider until it reaches 180.

Issue: When moving the slider, the first incremental step moves the ball to 45, but then the rest of the steps are ignored until the last one where the ball jumps from 45 to 180. The opposite happens when moving right to left. The first step (180 to 165) moves the ball, but all the rest are ignored until the last where the ball jumps from 165 to 30.

I've dropped in a couple of writeln() statements to track the value of x and it's being updated and assigned properly throughout as far as I can tell. The entire DrawingArea is being queued up for redraw (line 145) so that shouldn't be the problem.

I also tried this with a scale of 30-190 and had the ball's x position correspond one to one with the scale. The only change was that the ball's position moved three times from left to right instead of two, two small steps, then a big one.

If you could please look this over and tell me if this is me or GTK (or the GtkD wrapper) I'd very much appreciate it.

Here's the code:

// This source code is in the public domain.

// Scale Controls Ball Position in DrawingArea

import std.stdio;

import gtk.MainWindow;
import gtk.Main;
import gtk.Box;
import gtk.Widget;
import gtk.Scale;
import gtk.Range;
import gtk.Adjustment;
import cairo.Context;
import gtk.DrawingArea;

void main(string[] args)
{
	TestRigWindow testRigWindow;
	
	Main.init(args);

	testRigWindow = new TestRigWindow();
	
	Main.run();
	
} // main()


class TestRigWindow : MainWindow
{
	string title = "Scale Moves Ball";
	int borderWidth = 10;
	int width = 250;
	int height = 175;
	AppBox appBox;
	string exitMessage = "Bye.";
	
	this()
	{
		super(title);
		addOnDestroy(&quitApp);
		setBorderWidth(borderWidth);
		setSizeRequest(width, height);
		
		appBox = new AppBox();
		add(appBox);
		
		showAll();

	} // this()
	
		
	void quitApp(Widget widget)
	{
		writeln(exitMessage);
		Main.quit();
		
	} // quitApp()

} // class TestRigWindow


class AppBox : Box
{
	MyScale myScale;
	int localPadding = 0, globalPadding = 10;
	bool expand = false, fill = false;
	MyDrawingArea myDrawingArea;
	
	this()
	{
		super(Orientation.VERTICAL, globalPadding);
		
		myDrawingArea = new MyDrawingArea();
		myScale = new MyScale(myDrawingArea);

		packStart(myScale, expand, fill, localPadding);
		packStart(myDrawingArea, true, true, 0); // LEFT justify
		
	} // this()

} // class AppBox


class MyScale : Scale
{
	double minimum = 0;
	double maximum = 10;
	double step = 1;
	MyDrawingArea ballDisplay;
	
	this(MyDrawingArea myDrawingArea)
	{
		super(Orientation.HORIZONTAL, minimum, maximum, step);
		
		ballDisplay = myDrawingArea;
		addOnValueChanged(&valueChanged);
		
	} // this()
	
	
	void valueChanged(Range range)
	{
		double scaleValue = getValue();
		ballDisplay.setBallPosition(cast(int)scaleValue);
		
	} // valueChanged()


} // class MyScaleButton


class MyDrawingArea : DrawingArea
{
	int ballX, prevBallX;
	GtkAllocation area;
	
	this()
	{
		ballX = 30;
		
		addOnDraw(&onDraw);
		
	} // this()
	
	bool onDraw(Scoped!Context context, Widget w)
	{
		context.setLineWidth(3); // prepare the context
		context.arc(ballX, 50, 10, 0, 2 * 3.1415); // define the circle as an arc
		context.stroke(); // and draw
writeln("redrawing... ballX: ", ballX);
		return(true);
		
	} // onDraw()
	
	
	void setBallPosition(int x)
	{
		x *= 15; // move the ball a noticeable distance
		getAllocation(area);

		ballX = x + 30;
writeln("ballX: ", ballX, ", area: ", area);
		queueDrawArea(area.x, area.y, area.width, area.height);
		
	} // setBallPosition()
	
} // class MyDrawingArea