GUI
GUI code is both very nice and annoying at the same time. Very nice because I get to define and use lots of OO interfaces and have lots of good static checking. GUI and games are excellent applications for OO.
Annoying because it's practically impossible to unittest. The logic one or two levels beneath is more meaningfully tested independently from the GUI. But the GUI should still look nice and the buttons should all call the right logic. You chance 1-2 lines of code, recompile, run the game by hand, plow through the menus, look at it, change 1-2 more lines of code, ...
Wisdom
Some wisdom from hand-rolling Lix's UI years ago that has stood the test of 7-8 years.
GUI elements are nested tree-like. A window has buttons as its children, and a button has a text label or an image as child.
Define geometry relative to the parent. I want this button to be drawn 20 units from the left edge of me (the parent), and its width shall be 1/4 of my width.
Children calculate first, then the parent calculates. Reason: The parent is interested in any state change of the children, therefore the children should already bring their own new state to the party. The children, on the other hand, don't even need to know their parent, even though they must know the parent's geometry.
If the parent has information to pass to a child, that can still happen during calculation of the parent (after the child has finished its normal calculation), and trigger more code to run in the child separately from the child's regular calculation. All of this must still happen before anybody paints to the screen, thus separate calculation and painting completely.
Callbacks are handy for buttons. Give the parent a choice whether to hook a callback to the button (in OO code, everything happens elsewhere), or whether to query the button's state (soviet style: the central government knows best). Sometimes callbacks are nicer, sometimes soviet style is nicer. As the button, offer both.
Make stuff idempotent and avoid unnecessaary work, e.g., extra painting if it's already painted as you want. Push this logic down into the childmost GUI element. As a parent, don't { ask your button if it's already on, and if not, set it on }. The parent should write the simplest possible code: { set the button on }. The parent should trust that the button doesn't do unnecessary work, i.e., the parent should trust that the button is implemented roughly like this:
void Button::switchOn()
{
if already on {
return;
}
_on = true;
require to be repainted (scheduled for the next round of painting);
call your callbacks (that react to you getting switched on);
}
Outside of GUI, you rarely want to write setters, you'd usually prefer immutable objects that you discard and replace. But in GUI, setter-like methods are common and feel correct. As above, they tend to have more logic than setting one field.
Parent paints firsts, then the childrens paint. This is the inverse order in which they're calculated. Reason: You want the text label in front of the button, not behind the button (where the button's flat surface would obscure it entirely). You want the button in front of the window.
When a parent must repaint itself, all children must recursively repaint.
-- Simon