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