2007-09-19

Yet More Windowing - Undoing C++ Generics

Generics, eh? Great in C#, not so great in C++. Specifically, I ran into a problem with the system I came up with earlier for detecting object types. It works well when comparing the types of two instances, but what happens if you’ve only got one instance and want to work out what type it is?

The obvious way to do this is to hard-code the class names into the comparisons, and work with strings. Really ugly solution, but it should work. You’d think you could just output the typeid().name() to the screen like this to get the name of the class:

PA_Print(1, (char*)typeid(*myWindow).name());

You’d be right; you do get something that looks like the name of the class. My class was called “Window”, and I get “GWindow” back. Close enough. However, the typeid() call above does not equal “GWindow” when I try to perform a comparison, so I’m not entirely sure what typeid().name() spits out.

In this circumstance, as far as I can tell, the way to achieve it is to use the “__typeof()” function. Unfortunately this isn’t supported by all compilers (off to a bad start already), and using it seems to be vastly more complex than it really needs to be. The easy way around it is to always create an instance of a class to compare with, and then use “typeid()”, but that’s a waste of memory and, in the case of the windowing system, counter-productive, as new windows have to be added to the screen. Gahh.

The easy solution is just to create a “gadgetType” enum within the gadget base class that contains an entry for each gadget type. The base class includes a “type” member, and each gadget class that inherits from that base sets the member to the correct enum value in its constructor. I can just check the type using a getter function defined in the base class and avoid the entire problem of run-time type determination. This I duly implemented, and it saved me a lot of effort and a lot of extra code.

Another change I’ve made is to strip out the for loop to locate the active window; it made much more sense just to store a pointer to it in the Screen class. I’ve added depth-sorting to the windows, so clicking a window now activates it and moves it to the front of the screen. Achieving this was wonderfully simple - remove the active window’s pointer from its location in the window vector and push it onto the end. As the windows are drawn in sequence from the start of the vector to the end, the last window in the vector is always the highest on the screen.

The final change this time around is an improved event model. Previously, each gadget was in charge of its own stylus event detection. This is great for simplicity, but useless in practice - each gadget was detecting events regardless of whether it was sitting entirely underneath another window or not, and because of the observer effect associated with the Stylus struct (examining its properties changes its properties, so each successive gadget gets the wrong information back after the initial check invalidates everything) it simply wasn’t working. The Screen class now recieves all stylus input and distributes it through the hierarchy of gadgets.

There’s one problem with the even model as it stands - events are fed downwards, from screen to window to gadgets. A more efficient model would be to locate the gadget that the current event was relevant for, pass it the event, and then bubble the event back up through the hierarchy. I think that would be more efficient, anyway…

One last thing - I’m thinking of changing the name to “Woopsie”, as a reference to the original name “BOOPSI”.

Here’s a downloadable demo:

WindowSystemDS Demo V0.02

And here’s another screenshot (I’m putting the AmigaGuide interface together as I go):

WindowSystemDS V0.02

Comments

ant on 2008-10-05 at 21:05 said:

Note to self - of course you can’t detect whether two char arrays are the same by trying “if (string1 == string2)”. That compares pointers. Need to use strcmp().

Duh.