Following on from yesterday’s post, I’ve removed the existing VBL timing system and replaced it with a WoopsiTimer class. So, instead of adding your gadgets to the woopsiApplication’s VBL listener vector, you now create an instance of the WoopsiTimer class and handle its EVENT_ACTION event. The change has resulted in the removal of some nasty, counterintuitive code and cruft, and made VBL timing easier to work with. There’s a new “timer” example in the examples folder.

I’ve fiddled with a number of other things, too. The TinyFont bitmap now has all of the missing glyphs in place, and I’ve moved the at symbol to the correct location. Haven’t made the .c file yet, though.

SkinnedWindows move to the front when they are clicked. Not sure when I broke that.

All references to text position in any classes involved with text have been renamed to alignment. For example, TextBox::setTextPositionVert() is now TextBox::setTextAlignmentVert(), and TEXT_POSITION_VERT_CENTRE is now TEXT_ALIGNMENT_VERT_CENTRE. “Position” didn’t quite cut it. Similarly, all methods called “addText()” that were really appending text are now called “appendText()”.

I’ve made a few other changes and fixes to the TextBox, but the most significant is the addition of the concept of a cursor. This can be positioned anywhere within the string that the TextBox contains, and the new “insertTextAtCursor()” function allows text to be inserted at that location. The cursor isn’t drawn yet, as it’s a bit
tricky. A rectangular XOR box the size and width of the character at the cursor position is probably the easiest option.

As soon as I get that done I think it’s about time for another release. I’d planned to release a new version on the 11th of this month, as that was (so I thought) the day when I first started Woopsi development. However, it appears I’ve been working on this for over a year; the first post about it on this blog was on September 9th last year. Nuts, a milestone missed.

Comments

ant on 2008-10-05 at 21:28 said:

Have a look at the new timer system example in the examples directory. I was using the existing vbl() functionality to handle scrollbar button repeats, the Pong and Pacman display refreshes and key repeats on the keyboard. I managed to get them all changed over to use WoopsiTimers in no time at all. It’s much easier to use. Previously I was having to implement my own counter variables and timer logic every time I wanted to trigger something every few vertical blanks. Now it’s all handled for me in one class.

The cursor system I’ve implemented is modelled on the Amiga/DOS way of doing things. The cursor is represented by a rectangle and text is inserted at the cursor position. The main reasons for choosing this uber-simple approach are that I wasn’t really sure how it would work or even if it would work. Turns out that it’s worked exactly as I’d hoped. It would be possible to upgrade it to a more sophisticated system, though.

Jeff on 2008-10-05 at 22:15 said:

Hmmm, removing vbl() completely, I’m not 100% happy about - I had this feeling that I relied on that somewhere, and the use of it worked out quite neatly - you may have substituted cruft in Woopsi for cruft in user code.

If you are going to implement cursors (and thats a good thing), it might be an idea to look at how other systems do that stuff. Rather than have a “cursor” per se, they have a “selection”. If the selection does not include any characters, its drawn as a single line (the cursor, or insertion point).

Instead of having insertTextAtCursor(), you have replaceSelection() which does the same thing but which also handles the case where you have something selected. This simplifies state management later on for the user, since they don’t have to check whether there is a selection and then call some different function instead.

Jeff on 2008-10-07 at 23:51 said:

Well, it wasn’t too tragic switching to timers, though it took me a while to remember that I needed to inherit from EventHandler to handle events.

It did add more cruft into my object due to the fact that you need to pull apart the event record to determine that it is indeed the timer firing. Not tragic, just a little wordier than it originally was…

Oh, and the example was a little odd. It compared the notifying gadget with nil, before then comparing it with the timer - there seemed no need for the outer compare.

ant on 2008-10-08 at 05:26 said:

The null check is only there because I’ve followed the event handling pattern from everywhere else in Woopsi’s code. If the event handler is somehow passed a malformed event that doesn’t include a pointer to a gadget it won’t crash.

Jeff on 2008-10-09 at 09:19 said:

If you’re going to be that paranoid, you should check that the event pointer itself isn’t NULL.

Seriously, though, its fine to use a convention like that, it justs looks odd; the first time I looked at it, I thought “hmmm, I wonder if he thinks he’s checking that _timer has been allocated or something clever like that?”

Personally, if I’m going to be that paranoid, I put

if (!event->theGadget) return(FALSE);

at the top, which violates some peoples ideas about only having one entry point and one exit point to a function. I’m prepared to compromise that for the argument checking paranoia, since it tends to indent the rest of the function unnaturally otherwise.

Jeff on 2008-10-09 at 09:20 said:

And of course, after posting, I go back and check the source and theEvent is passed in by reference so it can’t be NULL.

Still, I think my point is valid.

ant on 2008-10-09 at 12:37 said:

Yeah, good point. Though it’s painfully obvious, having basic checks at the top of a function and returning early if they fail didn’t occur to me until I started getting into C/C++ and saw it used. People don’t seem to do that so much in other languages. I wonder why not? Perhaps the single exit point mentality is more pervasive elsewhere. If you look through the Woopsi source, you’ll see that I’ve gradually changed styles. Whereas I was initially doing this:

void myFunction() {
if (something) {
doStuff();
}
}

I now tend to do this:

void myFunction() {
if (!something) return;
doStuff();
}

It makes things much tidier. However, if I’ve established a pattern for a particular situation, I’ll generally follow it as I think consistency makes for more readable code (hence the check you picked up on).

You can probably tell the vintage of a piece of code by the style I use.