Wednesday, January 27, 2010

MonsterWidget ou MonsterWindow ?

Bon, voilà. J'ai fini de me créper le ch(amp)ignon avec "j'ajoute une fenêtre ou juste un widget". J'ai rajouté une fenêtre, et je peux commencer le "vrai" boulot, à savoir sélectionner, déplacer, dupliquer mes monstres, et tout ça. Si je me suis planté, ben au pire, vous me lirez râler d'ici peu. J'avais commencé à rédiger toute une tartine sur les options possibles, etc. mais finalement, je n'ai pas du tout suivi cette approche. Ce sera donc pour les plus English d'entre vous.

Well, finally i opted for giving monster edition its own, full-blown window "stacked" over the regular level editor. I've been struggling with OO design, once again. It's getting sort of a habbit. At last, I can start with "real" coding: positioning monsters over a level. I guess if I've been mistaken, you'll read me complaining about the odds of OOP once again :P Here follows the rant about the possible alternative that has not been retained.

The GUI of ?EDS tools mostly consists of "windows" through which you navigate. "Window" is a poorly chosed term, i admit. It should rather be "screens" or "layers" (since you can overlap them) that acts as container of widgets and contains the "application-specific logic". Again, the GUI toolkit is "in-house" development and grows together with the apps that grow together with the game projects.

When extending my applications on the DS, I recurrently stumble upon the same problem: I need some widget to be drawn or not, to receive events or not depending on the "application state". An obvious example is the "pop up buttons" I've added to the level editor recently. Another one is the "edit" button of the level editor's main window that only appear once you've got both a tileset and a map loaded in RAM.

I don't have (yet) a very clean and satisfying way to handle such situations. I mostly take advantage of the sequential testing of widgets within a window and alter Window->nbWidgets at run-time to make widgets appear or leave. I typically "snapshot" nbWidgets at various spots in the window initialisation code so that I can later "enable" a widget with something like

 void update_edit_buttons() {
 if (level->isready()) {
   nbWidgets=showedit; // show [edit] button.
   edit->render();     // force redraw
   // ... more actions if needed
 }
}


It is not alway possible to make it "that simple", though. Sometimes, the new widget will "shadow" an existing one. Edition of tile properties, for instance, needs the "regular" map to be drawn (a MapWidget), but additionally, the MetaLayer (widget that manipulate meta-information) and the MetaButtons (widget that let you pick a specific block type from a panel) become present. Plus, the MapWidget should no longer receive any event to prevent mess up of tiles graphics while editing tiles properties.

I then maintain metamap and normap, two copies of the window's nbWidgets that indicate the respective indexes of the MapWidget and MetaLayer. Window::swap(i,j) can help me moving around objects in the widget list so that we change the priority of both widgets. But this is tricky to extend cleanly to a "third state" as required for monsters positioning. Most likely, I'll have to keep track of the "current state" better than through the value of nbWidgets, and I'll need a reset_widgets() that returns from any state to the default (post-ctor) state so that I can properly alter stuff.

The alternative would be to add "hidden" and "disabled" states explicitly into the GUI engine, so that it can "skip" some objects.

Hopefully enough, the GUI engine also supports "stacked" windows where you can add a layer of widgets on top of another one, but it's a bit overkill to do that for every "dynamic" widget of a complex window. It *does* help when not only the display of a widget, but the whole controls layout (DPAD and physical) is redefined in the new state.

No comments: