Alternative to copying Guis

Started by Dincio, 05 March 2017, 17:10:15

Dincio

Hello  :)

I'm in the process of making a little game and I decided to use tgui for managing the user interface. The game manages multiple entities, each with its own hp/att/def/(you get it) and moves the entities around in a board each turn. I was thinking of giving an interface to each entity holding multiple ProgressBars (possibly wrapped nicely in a VerticalLayout) to show their life parameters to the player. My first approach was storing a Gui object inside the entity class, so that each entity could manage its gui the right way (drawing it when it is visible, requesting input only if the entity can be analyzed, allocate/deallocate it, etc...) but to my great despair, I found out that the copy constructor for the Gui class had been deleted... and so storing a Gui inside an entity would make it uncopyable (which is very important for multiple game systems, such as spawning). The only solution I can come up with is using a global Gui (maybe a static variable in entity) and using it as a sort of helper for showing widgets inside entities. In other words: every entity would generate a unique id (even just using a static spawn counter) and hold a VirtualLayout containing the ProgressBars (or even an whole new object component dedicated to the widgets used by the entity's interface), which will be added/removed to this global Gui when the entity is spawned/despawned using the unique id of the widgets. This approach would solve my problem , but it seems to like some sort of workaround... So my question is, why are guis uncopyable objects? Is there some fundamental reason that I am missing, or am I just using the system wrong? And what should I do in a situation like this one (which appears to be very likely)?

I hope I've made myself clear :) have a good day.

texus

The Gui is not copyable because it is not meant to be used like this. You are only supposed to create a single Gui instance per windows.
What you probably want is the Panel class (or Group class in 0.8-dev). You would add those panels to the gui when you need them and remove them again when they are no longer needed. The widgets (progress bars or the vertical layout) would be added to this panel instead of to the gui.

Dincio

Ok I looked up the documentation for the Panel class and it's exactly what I need! Still there remains the problem of removing the panel from the gui when the entity despawns, but I will have to think of something I guess... Just as a curiosity, why are guis not meant to be copyed? If it was possible it would make things like this a lot easier to implement (or perhaps I'm still missing something  ;D).

Thanks for the quick reply by the way.

Dincio

Sorry for the double post, but I just found out that you can remove widgets by using the pointer (probably shold pay more attention to tutorials...) which makes my job 1000 times easier (no need to generate unique keys at every spawn). I am still wandering why guis can't be copyed though  :P

texus

There is no technical reason why you can't copy it, but why would you? If you have multiple guis then you have to call handleEvent and draw on each of them which is more work. Everything can be done more simply by having different panels inside a single gui. And even if you had multiple guis, would you really copy the first one instead of just creating a new one?
The gui is the topmost object that is tied to the window and will handle all interaction (events and drawing) with the rest of the objects. So I don't see a reason on why it should be copyable.

The above reasons are actually only enough to justify why I don't remove the non-copyable property, I don't think I would have made the decision to arbitrary limit the possibilities. Maybe that piece of code is just almost as old as the library itself and originates from a time where the Gui inherited from sf::RenderWindow. I would have been forced to make it non-copyable as sf::RenderWindow is non-copyable. Nope, the code was only introduced halfway 2014 during a major rewrite of the entire gui, so there is no reasoning found in the commit message. So I'm just as clueless as you on why it is like this :)

Dincio

My reasoning is that, in my particular case, each entity should hold its own gui (as is the Object Oriented fashion, I suppose...) without having to worry about the existence of a global gui to add and remove panels from on spawning and despawning each entity. So better encapsulation and better resource management. Then again, I'm kind of new to big projects in C++, so I might be just overthinking a bit... but since I am off topic and my problem is solved, I will not bother you any longer  ;D

Thanks a lot for the help.

Dincio

As a side note: I found out (not on purpose, but while editing the code) that it is valid to draw a widget directly to a window... this is perfect for my case (since I need no event handling - I'm showing only progress bars and icons - I could store a panel in each entity and draw it without any need of a gui), but I can't help but feeling bad for doing it without a gui XD any thoughts?

texus

You are correct about that widgets should not rely on the existence of a global gui, and then don't.
The main use of the Gui is simplicity, so that you only need to do one function call for handling events and one for drawing. Technically I don't think there is any dependency on the gui and you should thus be able to use all widgets directly. But then you would have to manually pass the right events to the right widgets, keep a clock to update e.g. the flickering edit box caret and draw all of them. Having a Gui object which encapsulates that is easier.

texus

#8
If you have no events and no need for animations then I guess you could indeed use the code without the Gui class. But do you really need a gui library then?

Dincio

Very interesting... I never thought that the gui was optional. But then what about events? (I know I don't need them, but maybe in the future...) is it possible to pass an event directly to a widget? e.g. a Button?

texus

Although you could pass events to widgets directly by e.g. calling button->leftMousePressed(mousePos) you would be better of having the widgets in a Panel and using panel->handleEvent(event). Containers like Panel have the same functions as you would find in the Gui class, with the difference that Panel also has all the functions inherited from the Widget class.

Dincio

Ok that's fantastic! Thanks again for your fast and clear answers.