I received an email the other day from the chap who decided to write his own GUI, then decided to use Woopsi, then decided to switch back to his own GUI again. It seems he’s returning to Woopsi as it’s solved everyone’s least-favourite GUI problem - clipping.
Anyhoo, one of the questions he had was about the drawing system. At present, Woopsi’s gadget drawing system has a flaw. If you need to move a gadget, for example, and then resize the same gadget, you’d do something like this:
myGadget->moveTo(10, 10); myGadget->resize(20, 50);
Makes sense. The problem is that both of these functions call the gadget’s draw() method. Drawing is one of the most expensive parts of the GUI’s functionality, so it’s not something it should really do unnecessarily. Also, it’s not something that the programmer should ever have to worry about. I really don’t want to start issuing draw() commands every time I need a gadget to update.
I came up with the following solution, but I haven’t really thought it through yet. It may have some problems. It will definitely introduce one new limitation - all drawing to a gadget will have to be done in the draw(clipRect) function, or in a function called by draw(clipRect).
The idea goes like this:
- Add a new flag to the Gadget class called “requiresDraw” and a getter/setter pair.
- Remove the draw() call from all functions and replace it with “setRequiresDraw()”.
- Create a new gadget pointer array in the Woopsi class that stores pointers to gadgets that need redrawing.
- Make the setRequiresDraw() function add the current gadget into the new Woopsi array if the gadget’s requiresDraw flag is false, then set the flag to true.
- After all of the normal gadget processing has taken place, make the Woopsi class iterate through the new gadget pointer array calling the draw() method of each object and resetting its requiresDraw flag to false.
- Modify the draw() method so that it raises a new GADGET_EVENT_DRAW event.
This has several advantages over the current system:
- Hacky methods of avoiding superfluous drawing (like the MultiLineTextBox’s “setAutoDrawing()” function) can be removed.
- Woopsi should run faster as less drawing operations will take place.
- Developers can add their own drawing code by handling the new draw event.
I’d need to think some more about how the erase() method will work before doing this. Probably work a bit like this:
- Call erase() - check if a new “erased” flag is false.
- If erased is false, erase and set to true.
- If erased is true, end function.
The erase() function has to be called immediately, because it’s called before a gadget moves or resizes. There’s no point in calling it if the gadget has already moved, as erase() would wipe the wrong area of the display.
The other option for improving drawing is to just add a list of dirty rectangles to the Woopsi class and get it to redraw those. This would mean recursing through the gadget tree until the dirty list is empty (similar to the current rectangle splitting code), but this would probably be slower than the existing solution or the one outlined above. However, it would probably mean I could remove the rect caches from the gadgets.