2008-04-22

Progress Bars

Something not in the original design spec (what there was of a spec, heh) was progress bars. I was pondering them today, then realised that most of the work was already done - they re-use the same algorithm that the sliders use to calculate the size of their grips. A few minutes later and I’ve got a ProgressBar class done. It’s little more than 30 lines of code.

In extracting the bits of the slider that I needed, I spotted the error that’s been throwing out the grip positioning and resizing routines - they should be rounding up before bitshifting away the fractional part of the calculation. I’ve now fixed that, and the grip moves correctly.

Double-clicking is now optional. It is enabled in a gadget by setting the new “doubleClickable” flag to true or, if you’re subclassing, you can send “GADGET_DOUBLE_CLICKABLE” in the flag bitmask. If this is not set, all clicks are processed as single clicks.

Something that’s been bugging me for a while (since Jeff picked it up) is the “Woopsi::play()” function. This is now called “Woopsi::run()“. I’ve also made some of the methods within the Woopsi class protected that were previously public, since they shouldn’t really be called outside of the Woopsi class.

There are a few other changes. Gadget::unregisterChildrenFromVBL() is now recursive, and unregisters all children and their children (etc) from the VBL system. Previously, only the top level of child gadgets was getting unregistered.

The Gadget destructor is now more intelligent, and will clean up gadgets that are deleted without going through the deletion queue. This is not intended to allow gadgets to be placed on the stack or deleted manually; instead, it is to ensure that the children of gadgets that are being deleted are also deleted correctly. Related to this, the context menu gets shelved if the gadget that opened it is itself closed.

Comments

ant on 2008-04-22 at 20:05 said:

There’s a Gadget::getPreferredDimensions() function, which basically does the same thing.

ant on 2008-04-22 at 20:11 said:

Think you’re right about the textbox alignment. Try the latest SVN version and see if that fixes it.

ant on 2008-04-22 at 21:01 said:

The current ProgressBar class is just a left-to-right fill-up-the-box thing, Amiga-style. The OSX-style bar would be a different class.

Two obvious ways I can think of to do that. First is to have the animation driven by whatever is taking the time, so every time the WiFi connection tries to do something it prods the progress bar, which in turn updates the rotating picture. Second way to do it is to have the animation smooth, and just register it for VBLs. The animation class would help out there. Not sure which approach OSX takes.

Jeff on 2008-04-22 at 21:04 said:

I have this nagging feeling that text centering in a box also has a similiar ‘bit shift too early’ problem.

It divides text height and box height by 2 before computing the difference between them. I think this results in the text being one pixel too high, which is an aesthetic thing, if the box is an odd number of pixels, and the font an even number.

In fact, I was trying to remember - was there a Gadget::calculateIdealSize() somewhere? My app has a bunch of buttons that just use Font::getHeight()+2 and that still looked a bit gnarly. I know that the fonts I used had trailing blank rows - the next version of bmp2font will trim that automatically, but I think the centering of the text still looks one pixel to high (because you tend to ignore space below letters more than above). Anyway, I went looking for that ideal calculation and couldn’t find it…

Jeff on 2008-04-22 at 21:10 said:

Gotcha, thanks

On the progress bars front, and synchronicity being what it is, its almost exactly what I was going to be coding next, except that I need an ‘indeterminate’ one - ie, I want to show something in progress but don’t know how long its going to take - think wifi connection. From what I’ve read, your progress bars sounds like it need to know min and max - does it do the ‘rotating barber-pole’ like OSX as well, or do you see that being a different class (I have this feeling it would be better as a completely different class…)

Jeff on 2008-04-22 at 22:58 said:

I’m torn as to whether the indeterminate-progress bar needs to be tweaked by the process, or just rely on VBL arriving - which is what I believe both OSX and Windows do. Quite dishonest really because you don’t actually know if the process hasn’t locked up or not.

There’s also the issue of putting a cancel button onto the progress WINDOW, which definitely needs to interact with the process, but thats sort of independent of the process. But, sadly, it all ties into this “modal” stuff.

I have my app doing the rudiments of wifi startup now and it gets all the way through to connection to access point, though it takes a few iterations waiting. Putting up a modal dialog which has a custom event loop will do the trick - on every iteration, it can pump the progress bar, check if the Wifi has connected or failed, check if the cancel button was pushed. I think it all hangs together, subject to being able to run that modal runloop.

On the other hand, I could have the progress bar track my TIMEOUT value - which lets me use your progress bar, though it will count DOWN rather than UP.

Nice to have lots of choices that I don’t have to code from the ground up…

Jeff on 2008-04-25 at 09:30 said:

I finally tried Gadget::getPreferredDimensions() and whilst it works, its a little odd that it updates a Rect where Gadget::changeDimensions() takes seperate x,y,w,h variables.

ant on 2008-04-25 at 12:41 said:

Not sure. In the rest of the system, I usually have a rect when I’m dealing with x, y, width and height all in one go. However, I’m usually getting information out of a gadget in those situations, not setting it. Setters all (I think) send information individually instead of bundled into rects. When getting, it’s nice to have all of the data in one easily-accessible struct. When setting, I usually have separate values and can rarely be bothered to create and populate a new rect.

API consistency is becoming a concern as I knock things off the to-do list, though. When there are no more new features to add (when Woopsi is in alpha), I’m going to go through and try to rationalise the API.

Jeff on 2008-04-25 at 22:41 said:

The (x,y) part of the preferred dimension will always be where the user put it originally anyway, so the only info that actually comes back is the (w,h) part, which could be done by passing two ints by def.

Rect r; b = new Button(x,y,64,10,“Press Me”); b->getPreferredDimensions®; b->resize(r.w,r.h);

which isn’t so tragic - being realistic, you won’t do much better.

Its complicated in the getPreferredDimensions() case because it requires that you’ve created the Gadget prior to finding out how big you want it to be.

And I also appreciate the irony that in other threads, I’ve argued that this stuff should all be worked out at COMPILE time.

ant.simianzombie.com » Key Repeats and Memory Leaks on 2010-01-22 at 10:28 said:

[…] changed the way that the gadget destructor worked back in April 2008. It was more intelligent and would recursively delete the children of a gadget that was itself […]