Software design patterns for UI behaviors?

Started by
5 comments, last by DekuTree64 7 years, 7 months ago

So, i'm building the UI for my strategy game and i quickly got trapped into a big spaghetti. I've got a design that works something like this:

[attachment=33183:test.jpg]

  • All panels trigger a dark overlay on the screen. (total black 70%)
  • The blue panels slide from left and right, respectively. They can be present at all times, can appear together, and have the lowest zindex.
  • The red one always appears on top of everyone, but does not force others to close
  • The orange one makes any other panel slide back to their "hidden" position. When the orange slides up, the blue ones slide back and the red one slides up.
  • I also can have centered "popups".

Right now, my code is a mess:

Since all panels are independent objects, they share an instance of an Overlay object. They can call the methods hide() and display(). Calling close() on the left blue panel should not close the overlay if the right blue panel is still open, so i implemented a counter inside the Overlay object: I only really hide it when the counter reaches zero. Kinda like a refcounter. I also have to wrap every open()/close() method of my panels into conditional, to check if the panel is not already open, in order to avoid refcounting the overlay incorrectly.

Clicking on the overlay should also trigger all panels to close instantly. So i have more ifs. And i have even more nasty ifs to make a difference between the red and orange panels. When i started doing the popups, which are dynamic, i lost my mind.

So, this mess is already too difficult to maintain and i could not think of a decent solution. Can anyone guide me to a good book on Software UI architecture and proper design patterns? I've been working for too long with Java Web API's and i got too dumb for realtime programming. :P

Advertisement
You can use MVC. Get data, get interface that needs the data, update it. Wait for callback if needed (animation callback, network callback, input callback, or whatever event callbacks).

As for your hide and close, not sure what you really wanna do about it, but you can use observer pattern, and register the functions (or any observer object you wish) which should be called (open/hide/close) by a specific button.

Yes, the key is "don't have GUI elements tell other parts of the UI what to do". There should be some sort of central mediating model for what is on screen, and the GUI elements adjust themselves to attempt to follow that.

So, Panels wouldn't hide or show the Overlay - in abstract terms, the model would have some sort of "IsOverlayVisible" property which it manipulates based on whether any Panels are visible, and the Overlay itself only renders when that flag is set. The flag could even be on the Overlay itself, but it's the model that decides to set it, not the other Panels.

Same with input - when you click on the overlay, that event should be passed up to the mediator/model, and that decides which UI elements now need to be hidden based on that.

I can't have a single flag. Panel A might fire his own "close" event, but the listener of that event can't simply set IsOverlayVisible to false because he needs to know which other panels are using the Overlay.

I already have events in place, but that only means that all my spaghetti is inside the Mediator (which i call Controller).

I can't have a single flag. Panel A might fire his own "close" event, but the listener of that event can't simply set IsOverlayVisible to false because he needs to know which other panels are using the Overlay. I already have events in place, but that only means that all my spaghetti is inside the Mediator (which i call Controller).
Complexity doesn't magically disappear when adding more people to the party. The primary gain is that you can shift stuff around, and keep all complicated stuff at one place rather than spreading it around over a lot of components.

As such, there would be one listener for your "close" event, namely the central controller. That controller knows which other panels exist, and whether they can be closed too (either by itself, or it could ask those components, for example). The controller then eventually decides whether to close.

In that way, each panel only worries about its own part, and it listens to the "boss" controller. The boss coordinates wishes of the panels, and makes the global decisions.

I can't have a single flag. Panel A might fire his own "close" event, but the listener of that event can't simply set IsOverlayVisible to false because he needs to know which other panels are using the Overlay.

I already have events in place, but that only means that all my spaghetti is inside the Mediator (which i call Controller).

Now, if you are using observer, when a Panel A is triggered to close, it observes the registered object to be triggered when that close event is performed. One of the registered object should be the overlay. This overlay itself should be the one that checks the counter whether it's ready to close (equals or less than 0) or not. This goes the same to other UI objects registered to Panel A that listen when close is triggered.

So you spread all your conditions to each of its own. Panel A only tells that it is about to close, the others listen to that information (regardless of who call it, in this case, it's Panel A, but these guys don't know), and then make their own decision on what to do based on that call. It's basically back to what Kylotan said.

Just an addition, you can pass the Panel A if you like so the registered objects know who make that call, in case you need to know who.

First of all with this kind of thing, you need to iron out the details of how it should behave. Is it possible for game code to request data to be displayed on one of the panels while it's already open? Should it queue and wait for a button press, or automatically slide out and slide in with the new text, or instantly redraw without sliding out and in? What if there's a blue panel request while the orange panel is open? Is the blue panel simply ignored and never displayed, or queued so it will slide in after the orange panel is dismissed? And how should the popup window behave?

I agree with the others that you need a manager object. Something that owns the overlay and all the panels. Game code sends panel requests to the manager, and it coordinates their behavior. Then you don't need a refcount on the overlay. Just a logical OR of all panels' visibility. If they're all hidden offscreen, then disable the overlay, otherwise enable it. If needed, the manager can also have a queue of panels and popups, and after a close event, display whatever is the next highest priority thing in the queue.

This topic is closed to new replies.

Advertisement