2007-10-27

Text and Gadgets

Few more changes. As the TextWriter now accepts a bitmap pointer in its output routines, I’ve added a drawText() function to the SuperBitmap class. It allows text to be written onto the SuperBitmap’s canvas in a developer-specified font.

By default, the Font class assumes that it is dealing with multicolour fonts. However, this behaviour can now be overridden by setting its output colour; it will then assume that any non-background colour should be the output colour instead. The TextWriter now requests each pixel from the Font (instead of dealing with the font bitmap directly), so it will automatically work in monochrome if the font’s output colour has been set.

The Pong demo now has palette-cycled text written on it.

One more change - windows now have depth gadgets. Looking through the list of new features that need to be added on the SourceForge feature tracker, I think I’m getting close to the point where all of the basic functionality is in place. The only item on the list that really affects the guts of Woopsi is the gadget enabled/disabled toggle (EDIT: which I’ve just added). After that, it’s just a question of adding in new gadgets and requesters. As long as there are no architectural mistakes or omissions in the rest of the system, each gadget should (ahem) just slot into place as it is written.

Here’s a screenshot; I’ll upload demo 0.24 to SourceForge at some point.

Woopsi Window Depth Buttons

Comments

Jeff on 2007-10-27 at 23:32 said:

Do you anticipate having to change anything for buttons that have a state? ie, checkboxes? If you look at OSX’s Cocoa, it seems like the default control class has ‘intValue’, ‘stringValue’, etc accessors so you can ask virtually any class what its ‘value’ is and it morphs it back to a corresponding data type. However, as I recall, for state-ful controls, it also has getState/setState style accessors.

Another architectural thing to think of, though I suspect you already have it covered because gadgets can contain other gadgets, is radio buttons? In Windows, the dialog manager is smart enough (but only just) to recognise a set of radio buttons (by proximity in the control id orderering) and can automatically turn one off when you turn another on. Typically, this is good enough because you put them all inside a static control that provides the ‘labelled group box’ so there aren’t any other ‘nearby’ controls to confuse it. But if you don’t want the group box, its a bit annoying to work with.

The natural fit would then be that you’d have a ‘RadioButtonGroup’ gadget which contains two or more RadioButtons, each of which would merely notify their parent gadget when their state changed - the parent would then keep the ‘only one selected limit’ in sync, and would probably notify its parent about the change as well.

This ability to notify the enclosing gadget of ‘a significant event that affects my value, but is not necessarily a user-initiated click’ might be useful to consider.

ant on 2007-10-28 at 12:13 said:

Radio buttons should be fine. Clicking a gadget already bubbles the focus change up to the parent.

Works like this. Screen taps are detected by Woopsi, which works out which child was clicked, then sends the click to that child. That child does the same, recursively, until eventually we get down the tree to the gadget that was clicked. If that gadget does not currently have focus, it needs to notify its parents that it is now the active gadget. It bubbles the focus event back up the hierarchy (so that clicking a button also activates the surrounding window) until we hit an ancestor gadget that is already active.

This essentially gives us the system for maintaining radio button groups for free - clicking a radio button gives it focus, and that event is bubbled up to the parent. If the parent is a container gadget (the RadioButtonGroup that you suggest), it can update its internal “value” pointer to the child gadget that sent the event. The child it pointed to previously can be reset back to an “unselected” state.

A value-change event would be handy. Text boxes, list boxes, checkboxes and drop-down lists would all benefit from it.

Jeff on 2007-10-29 at 02:37 said:

With respect to radio buttons and their enclosing radio button group, if I call something like group->selectButton(3) I would expect that it would be telling the currently selected button (call it 1) to update its state to unselected and button 3 to update to selected.

I wonder whether your current post, which talks about raiseClick() as opposed to click() addresses this. Does group::setState() call button->setState() or does it call button->raiseClick() (or button->click())

Its pretty obvious that group::setState() shouldn’t change the focus so it can’t be calling the click/raiseClick - the natural fit, I think, is for a click on a radio button to propagate to the parent, but I’m not sure what the name of that propagate event would be - its not the focus() shift, that happens when you tab into a control (thinking in Windows/Cocoa terms for a minute), its when you activate the control that you want the enclosing group to say “one of my subcontrols was clicked, it is me who implements the policy that dictates what the state of that control now is, so I will tell the various components under me to change their appearance appropriately”

I hope I made that clear - essentially its the difference between “the radio button group changed state because of a user-interface click” vs “the radio button group changed state because the application read its prefs from disk” or “… read some value from libwifi” or “… computed an interim result it wants to display as a radio button for some reason”

When the state changes in the underlying radio buttons, you don’t necessarily want that being reflected back into the owning group, or you can end up in nasty infinite loops. In our current app, we have to catch this case by having the container ignore notifications from the button that tell the container something it already knows (that the state of the child is changing).

ant on 2007-10-29 at 08:12 said:

This was a problem with the handleClick structure. As the system was designed to interact only with physical events, the only logical place for state changes to go was the handleClick function, but this then propagated the focus changes up the tree.

The new system will allow me to have “setChecked(bool)” functions. When called on a radio button, that would call the group’s “setCheckedGadget(Gadget* gadget)” function, which stores a pointer to the checked gadget and unchecks the previous gadget. It will also call the gadges “setChecked” function again, which will ignore any requests if the new state matches the current state. This way, setting the checked radio button can be done via the parent, child, or via a click (which will call the same functions).

Jeff on 2007-10-29 at 09:22 said:

Looks good. One thing to be wary of when you do the checkbox/radio buttons ;-) is that modern applications use tri-statie buttons - best not to lock it down to a ‘bool’ data type if you can help it.

ie, a checkbox can be ‘on’, ‘off’ and ‘gray’ meaning “sort-of on”

ant on 2007-10-29 at 09:55 said:

Sort-of on? How’s that work, then? Would “sort-of on” be the state a radiobutton is in once it is clicked but before it is released? The states in that case would be “off”, “held” and “on”?

I’m primarily using HTML and .NET’s web forms as my guide to the API, mainly because I haven’t done any extensive desktop GUI development since the Amiga version Blitz BASIC. As HTML only has bi-state radio buttons, I’ve never seen the tri-state idea.

If “held” is the third state, that’s easy to implement - I’d just stick a “RadioButtonState” enum in the RadioButton class. Clicking it would update its state from “RADIO_BUTTON_STATE_OFF” to “RADIO_BUTTON_STATE_HELD”, and releasing it would switch to “RADIO_BUTTON_STATE_ON”. The “setClicked” function would accept a RadioButtonState instead of a bool as its parameter.

ant on 2007-10-29 at 10:22 said:

Ah, some research suggests that it means both “on” and “off”, or “nothing”, or “something else”, and is represented by a smaller dot (possibly grey instead of black). I’ll call it “mu” just to keep Robert M. Pirsig happy. Or I might call it “Phaedrus”, but then that’d be annoying to type.

EDIT:

It looks like the tri-state behaviour should be optional, with a “setTriState(bool)” function - “true” for enabled, “false” for disabled. In normal mode, clicking the radiobutton sets it to “on”. In tri-state mode, clicking the radiobutton also sets it to “on”, but clicking it again sets it to “mu”, or the tri-state value. Clicking it for a third time sets it back to “mu”, and subsequent clicks cycle between those two values.

Jeff on 2007-10-29 at 20:44 said:

In fact, there are tri-stating checkboxes as well. They tend to be “empty”, “filled with a cross/tick” or “filled gray”. Typically they get used at the top of a tree control where gray means “some, but not all of my subvalues are selected”.

I’m inclined to suggest that you don’t need to automatically support the tri-state toggling, just the tri-state appearance. Its up to the subclasser as to whether toggling a ‘mu’ radio button goes to fully selected or fully off.

ie. typically, you enter the ‘mu’ state programmatically, not by user clicking. Which is not to say it should be precluded, but it doesn’t need to be explicitly included by default.

In the tree example above, clicking the ‘mu’ checkbox usually sets it to ‘selected’ and also forces all of its children to ‘selected’. If you unselect one or more of the children, then the parent goes back to ‘mu’. If you unselect all of the children, the parent goes back to unselected.