2007-09-18

More WindowSystemDS Stuff

Did a little bit more coding in the wee small hours last night. First of all, button text is now centred horizontally and vertically within the button, which makes the buttons look a bit better. I’ve added a new option for the gadget outlines, which can now be either bevelled into the screen, out of the screen, or change from out to in when they are clicked.

Finally, I’ve made the distinction between active and inactive windows. This was a little tricky to do, as each new window added to the window stack needs to become the active window. The obvious solution is either to loop through the window stack and de-activate them all before adding the new window, or by storing a pointer to the active window somewhere and updating that. I went for the former option (though the latter may prove more useful; more on that later), but hit a problem. I don’t have an array of windows in my stack; instead, I have a generic list of gadgets. What I needed to do was loop through the stack and only update those gadgets that are instances of the Window class.

In C# this is easy to do, but I’d never needed to do this before in C++ (I abandoned this approach for the enemy collection in DefenderDS when I read about the performance hit, but performance isn’t so much of an issue here). A spot of googling (note the lower-case “g” - ho ho, I’m making their trademark a common-place term) lead to this:


// Create a new window
Window* newWindow = new Window(x, y, width, height, title, this);

for (u8 i = 0; i < gadgets.size(); i++) {

    // Is this gadget a window?  Compare type name with new window
    if (typeid(*gadgets[i]).name() == typeid(*newWindow).name()) {

        // Got a window, so make inactive
        ((Window*)gadgets[i])->setActive(false);
    }
}

Essentially the same thing as C#, we just compare the type of the array item with the type of the new window I’m adding to the array, and if they match we know we’ve got a window. Easy.

The next thing on my list is to improve the screen redrawing. At the moment, the entire screen gets redrawn when windows move, which is a bit crap. After much pondering, I’ve come up with this solution. We know which area of the screen needs redrawing whilst a window is moving as we have its co-ordinates and size. We send that to the Screen object (which contains the window stack) and tell it to order its children (ie. all gadgets in the window stack) to see if they need to redraw, based on the invalid rectangle it sends in as parameters to the function. The gadgets perform a quick collision check to see if the rectangle falls within their boundaries and, if so, they redraw themselves. To be even quicker, windows could just erase the invalid rectangle, then tell their children (ie. their gadgets) to redraw based on the invalid rectangle. They add their co-ordinates and size into the list of invalid rectangles that gets sent (probably by a pointer to a vector) to each gadget. This way, only those gadgets that have been corrupted will re-draw.

If we work from the screen upwards through the window stack, from the lowest window to the highest, any redraws at the lowest level will get overwritten by any overlying windows. This means we’ll always end up with the correct screen layout, with all windows and gadgets in the correct depth order.

The nice thing about all this is that I only have to write one function - since the screens, windows and gadgets all inherit from the main Gadget class, the whole system will manage itself. Marvellous!

One problem with this is window borders. As they’re just drawn on to the screen, we’ll have to handle them separately. However, what I could do is just make each section of border into a gadget of its own, which takes away the complexity as redrawing will then be handled in the usual way. I remembered something from my Blitz days here, and had a quick look at “gimmezerozero” windows in the Amiga ROM Kernel manual. The Amiga had two ways of handling window borders - in the first mode, borders could be overwritten, and in the second they were automatically refreshed. It turns out that “gimmezerozero” windows have their borders as separate gadgets. Looks like I’m on the right path, then!