Keyboards Working

I’ve implemented the suggestion in the previous post (thanks to Jeff for the vote of confidence). The keyboard now appears if a textbox is clicked. The bottom screen jumps up to the top display and the keyboard screen takes its place. When the keyboard is closed, the original screen drops back down into place in the bottom display.

The keyboard can be closed by either clicking on the return key (which also fires an action event) or by clicking a new “OK” button at the bottom of the keyboard. I’ve changed the keyboard so that it no longer inherits from the AmigaWindow class, giving it much more flexibility. The automatically-appearing keyboard is an instance of a new “WoopsiKeyboardScreen” class hosted by the Woopsi gadget.

Getting the textbox to listen to d-pad presses to move the cursor was very simple. The WoopsiKeyboardScreen and keyboard no longer attempt to steal focus, which means that the textbox that opened the keyboard will keep focus even though the keyboard is open and being interacted with. This allowed me to change the cursor so that it is only drawn when the textbox has focus, solving the dilemma of indicating which textbox is being edited with the keyboard.

I noticed that pulling down the top screen did not correctly erase that screen from the top display. It transpires that this is an issue with the GraphicsPort class - for various reasons it isn’t possible to have a gadget that exists on both displays, which the Woopsi gadget attempts to do. To fix this I’ve added two decoration screens to the Woopsi gadget that sit behind all other screens. They ensure that exposed areas of the Woopsi gadget are cleared correctly.

Here’s a demo of the new textbox/keyboard functionality:

Double-click the textbox (move the debug screen out of the way to see it) to bring up the keyboard. You will be able to type into it, delete characters from it, and close the keyboard by tapping return or OK.


Active TextBox and the Keyboard

One issue that still remains in Woopsi is the question of how the active textbox should be represented visually. One possibility that I tried was to make the cursor visible only when the textbox had focus. However, when a textbox is being used for input, the active textbox does not have focus; the keyboard does instead.

I’ve got another possible solution for this. When a textbox is clicked:

  • Flip the bottom screen to the top display.
  • Automatically open a new keyboard screen on the bottom display.
  • Automatically wire up the keyboard to the textbox so that the textbox receives input from the keyboard.
  • Automatically wire up d-pad events so that the textbox receives cursor left/right/up/down key presses from the keyboard.
  • Add a “close” button the keyboard so that it can be closed.

When the keyboard’s close button is clicked:

  • Close the keyboard screen.
  • Flip the top screen back down to the bottom display.

Thoughts? One objection is that clicking the textbox accidentally will cause the entire display to shuffle itself around. In that case, perhaps the textbox should have an “edit” icon somewhere? Or need to be double-clicked before it could be edited? Or should I not worry about this at all, and leave it up to the developer to implement their own solution?


Keyboard Event Refactoring

As part of the Great Event Refactor, I have changed the way that keyboard events work. Previously, keyboard events worked like this:

  • User clicks a key;
  • WoopsiKeyboard object raises an action event;
  • Handler receives event;
  • Handler checks event type and source;
  • Handler gets the last key pointer from the source (ie. the WoopsiKeyboard instance);
  • Handler does whatever it needs to do with the key data.

If a key was held down, the keyboard would eventually refire the action event to indicate that the key was repeating. However, there was no way to distinguish between initial presses and repeats, and no way to detect key releases.

Keyboard events now work like this:

  • User clicks a key;
  • WoopsiKeyboard object raises a keyboard pressed event;
  • Handler receives “KeyboardEventArgs” object containing a pointer to the source (the keyboard) and the key that was clicked;
  • Handler does whatever it needs to do with the key data.

Much tidier. There are separate events for keys being pressed, released and keys repeating. It’s now simple to tell exactly what happened and respond accordingly.


Keyboard Bug

Why is it that you only find really obvious bugs once you’ve put out a new release?

There’s a bug in the keyboard. More specifically, there’s a problem with the Gadget class that exhibits itself in the keyboard. The loops that work with the visible/invisible rectangular regions of each gadget use bytes as their iterators. Unfortunately, the keyboard layout is so complex that it can have more than 256 rectangles in its rect cache, which means the redraw code gets into an infinite loop because the iterator overflows and wraps back around to 0.

I’ve fixed this in the SVN version - the iterators are now signed ints. I’ve also changed the child gadget iterators to use signed ints. Expect version 0.36 fairly soon. I’ve also updated the SVN version of the XCode Woopsi project to match the 0.35 VC++ code.

I do still have the BSP tree code I mentioned a while back. I’d like to get that plugged in at some point.


Key Repeating, Timers and Bugs

First of all, a bug. The Text class doesn’t seem to be wrapping text properly if the line of text doesn’t have a break in it.

I hate the Text class. If anyone wants to earn themselves a substantial number of Zombie Points*, debugging and fixing the Text class will earn you a bucketload.

Secondly, key repeating now works in the keyboard. Instead of registering the keyboard for VBLs, I’ve written a WoopsiTimer class. This works in more or less exactly the same way as a Javascript timer. Give it a number of frames to count and tell it to start, and it’ll count that many frames and then fire an EVENT_ACTION event. It can be paused, stopped and reset, the timeout (frames until an event is fired) can be changed, and it can either fire once and stop or continually loop. It registers itself automatically for VBLs. In short, it’s a much better solution than the existing VBL system. It is far simpler to set up and considerably more helpful.

This leaves me with a question to resolve. Do I change the Woopsi class so that only timers can register themselves for VBLs or do I leave it as it is with two methods of achieving the same thing? The former results in a tidier API; the latter gives the developer more options (ie. it means they have one less child gadget to create). I’m leaning towards stripping the old method out, but I’ll leave it as it is for now in case I think of a good reason to leave it.

*Zombie Points are exchangeable for Zombie Tokens. 10 Zombie Points earns 1 Zombie Token. Zombie Tokens are exchangeable for Zombie Points. 10 Zombie Tokens earns 1 Zombie Point.


Functioning Keyboard

The keyboard works!

I’ve added the missing glyphs to the sysfont. Came up with a new glyph for the Ctrl key - a caret (^) with “ctl” written below it. The caret is the standard UNIX symbol for the Ctrl key but I needed some way to distinguish if from the actual caret key, hence the dinky “ctl” text.

I’ve re-worked the layout a bit - it’s now a more standardised American layout rather than the British layout I was originally working towards. It seems that the pound sign (£) isn’t in the standard 7-bit ASCII set; it’s actually 163 in the extended ASCII set. That places it well into the region of glyphs that I’m not intending to support, so there was little point in trying to replicate the British layout if it had a key missing.

The keyboard now acts on click rather than release events. Reacting to release events is of no use if we want to support key repeats whilst the keys are held down. However, the modifier keys still react to release events on other keys. We don’t want the modifiers to reset themselves as soon as a key is pressed because we might need to read the state of the modifiers outside of the keyboard class. So, clicking a key now fires the EVENT_ACTION event from the keyboard, and releasing a key causes any modifiers currently in the “down” position to revert back to the “up” position.

As it makes more sense when you see it in action, here’s the demo that produced the screenshot above:

Keyboard Demo

The code for this is in a new “keyboard” example in the “examples” folder. Wiring the keyboard up to an output window is trivial; there’s barely any code in the example.

Other new things include WoopsiKeyboard::isShiftDown() (etc) functions for reading the modifier states, WoopsiKey::getValue() for getting the display value of a key, and addText(char) methods for both the TextBox and MultiLineTextBox classes.

Things left to do are:

  • Key repeats (still)
  • Some way of removing characters from text boxes, to enable the delete key to work

Once that’s done, I’ll be all set up to enhance the various text box classes with cursor support.


This demo highlights the vertical alignment problems still in the MultLineTextBox class. Must get that fixed.