Sign up

Moving the Scale Slider to Control Ball Position...

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

Re: Moving the Scale Slider to Control Ball Position...

On 29-01-2020 15:14, Ron Tarrant wrote:

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.

Hi, you are passing the wrong position to queueDrawArea which is why the drawing area isn't updated.

The GTK documentation explains it like this:

The region here is specified in widget coordinates. Widget coordinates are a bit odd; for historical reasons, they are defined as widget->window coordinates for widgets that return TRUE for gtk_widget_get_has_window(), and are relative to widget->allocation.x , widget->allocation.y otherwise.

So the coordinates of a drawing area start at x=0 y=0.

In this case it is also easier to call queueDraw to invalidate the complete drawingArea/Widget.

Re: Moving the Scale Slider to Control Ball Position...

On Wed, 29 Jan 2020 19:38:44 GMT, Mike Wey wrote:

On 29-01-2020 15:14, Ron Tarrant wrote:

So the coordinates of a drawing area start at x=0 y=0.

In this case it is also easier to call queueDraw to invalidate the complete drawingArea/Widget.

Oh, wow. Thanks, Mike. All this time, I've been doing it wrong. And that's for the tip about queueDraw(). Takes all the worry out of it.

You've helped me solve a problem in another example I've been working on, too. I was about to abandon it, but now that this has come to light...

So again, thank you, sir. You've saved the day.