Woopsi now supports window closing. Each window can now have a close gadget at the top-left (switched on or off by a bool in the window’s constructor) that will close the window when clicked.
Sounds easy to implement. In practice, not so much. First problem I encountered was a null pointer issue - if a window closes, and that window is the active window, everything falls apart. Woopsi will try to do more things with the active window once it has closed and been deleted.
Fixing that led me to think some more about the way events are routed throughout the gadget tree, which inevitably led me to refactor it somewhat. Each gadget can now contain an active gadget pointer. If that pointer is null, and the gadget’s parent has the current gadget as its active gadget, we know that the current gadget is active (and none of its children are). Events can now be routed through the tree simply by sending them to each active gadget pointer in turn, stopping when we hit a NULL and processing them on that gadget. Any events that should be processed along the way can be passed to every gadget we traverse through. Key press events are now sent to every active gadget along the trail, for example.
There are other improvements brought about by the changes - events are now fired more logically. Previously, clicking a gadget in an active window fired the click event for that window and the focus event. This doesn’t make any sense - the window itself hasn’t been clicked (the gadget has), and the window already has focus. We shouldn’t fire either of those events.
Conversely, events are bubbled back up the hierarchy automatically as required - if we click a button in an inactive window, the button tells all of the gadgets above it to handle the focus event. The window receives the notification, checks its focus state, and calls its focus event handler as it knows it does not currently have focus.
This still left me with null pointer problems, though. Clicking the close gadget caused the screen to delete the window before it handled the gadget’s release event. When the release event was processed, the clicked gadget pointer pointed to trashed memory, so everything fell over. This was a simple one to fix - just point the clickedGadget event to NULL.
However, there was yet another problem. Clicking the close gadget called the window’s close() function, which in turn called the screen’s closeChild() function. This function deleted the window. The problem here is that, as we exit the closeChild() function, we return to the window’s close() function. This function no longer exists. In fact, what we’re really doing is a complex version of “delete this”, then trying to carry on running the deleted code.
Since gadgets need to have a “close()” method in order to simplify removing gadgets from the system, I had to come up with a method of deleting gadgets that entirely removed the gadget to be deleted from the control process. The solution was a deletion queue. Calling the close() method on a gadget now marks it as deleted and adds a pointer to that gadget to the parent’s deletion queue (actually a vector, since that required no extra coding and deletion order isn’t a concern). Each event now calls a “processDeletionQueue()” function after executing that will delete any queued gadgets.
One thing that has rapidly become apparent is that recursion is a real swine to debug if you haven’t got a debugger or a console to print to that doesn’t wrap around uselessly. PALib’s “PA_Print()” function scrolls lines off the top of the screen, then scrolls them back on the bottom again a short time later. I really need to fix the TextViewer clipping problem so that I can write a proper console gadget.
Here’s a screengrab of the latest demo:
Three of the windows have been constructed with close buttons switched on; one has been created with close buttons switched off. Woopsi automatically resizes and repositions the window title bars for the different situations.
At present, the close buttons are slightly thinner than the screen depth gadget. It looks better this way (less squat), but I’ve found that it makes the buttons rather more difficult to click. I’d prefer to go for accessibility than squarer buttons, so I might make them fatter soon.
New demo should appear on the SourceForge page soon (demo 0.23). The SVN code is already up to date.