Internal Button Events

Started by Strikerklm96, 12 March 2014, 04:09:04

Strikerklm96

I wanted to make a messaging system that is working with a bunch of other stuff, and I figured I could just have everything inherit from a class called IOBase, which would have virtual send(Message) and recieve(Message) functions. Then, each class could override the function to decide how to interpret the message. So I wanted to know whether this was a good solution or not.

class myButton :  public IOBase, public tgui::Button //<---should that be tgui::Button::Ptr????
{
public:
    void send()
    {
        otherThing->getTarget(target).recieve(message);
    };

private:
    Message message;
    string target;
    IOBase* target;
};

Then later in code
myButtonInstancePtr->bindCallback(&myButton::send, &gui, tgui::Button::LeftMouseClicked);

Is this the BEST way for my button to accomplish that? I know I can do it using callBackId() but I think that's worse.

I would also like to say that this https://github.com/texus/TGUI/blob/master/examples/FullExample/FullExample.cpp, or something like it should be linked in the tutorials. Examples are the most useful thing in the world, and the tutorial only goes over a few things.

texus

#1
QuoteIs this the BEST way for my button to accomplish that? I know I can do it using callBackId() but I think that's worse.
First of all, there is no best way. But binding callback is indeed much better than polling from the gui.

Quoteclass myButton :  public IOBase, public tgui::Button //<---should that be tgui::Button::Ptr????
I don't even think that inheriting from tgui::Button::Ptr is an option. Although it might compile, it probably won't do what you expect.
If you choose to inherit from tgui::Button, then you should also add the following line in your class:
typedef SharedWidgetPtr<myButton> Ptr;
This will allow you to later create the object with
myButton::Ptr button(gui);

But I don't think you should inherit from tgui::Button. You should only inherit from it when you want to extend its functionality, and that isn't really what you are doing. Composition is probably a better solution.

But of course I don't know the full situation so I can't really tell you which design you should use. I would just go with a design that you find good and learn from the mistakes. If you later find out that you had certain limitations due to your design then next time you will think about these cases when you write another program.

It will probably be different in your real code, but from the small code you showed you can put the contents of the send function in IOBase and you don't need to override it.

And if you haven't already, take a look at the Observer Pattern. It is used a lot for message systems.

Btw, I would recommend making your class names start with a capital letter, to avoid confusion.


I know that my post contained mainly bad critics but that doesn't mean that what you have come up with is that bad. Inheriting from a base class like your IOBase is probably the way to go, its just the way you do this that could possibly still be improved.


QuoteI would also like to say that this https://github.com/texus/TGUI/blob/master/examples/FullExample/FullExample.cpp, or something like it should be linked in the tutorials. Examples are the most useful thing in the world, and the tutorial only goes over a few things.
I'll add a link at the bottom of the tutorial to both the documentation and the example code.
But my site already contains the example code btw (https://tgui.eu/example-code/v06/).

Strikerklm96

Callback bind being better than polling was what I was hoping to hear.
And to use Composition instead of Inheritance was also the answer I needed, so Thanks :)

As for the Observer Pattern, that's essentially what I'm doing right?
I have objects which store messages, and conditions to go with them, and when something in the object changes, they check themselves for related output messages that need to be sent depending on what changed. If they have them, and the condition of the message got satisfied, they send a Message to the target, which then receives it and does something. I have been using Observer Pattern, I thought.


All my classes start with Caps, I just quickly typed myButton.
Also, any recommendations for how to name my stuff? Because I plan to do this with all the types: Buttons, Sliders, ect. And I thought I should put some sort of differentiation between my Buttons and the tgui buttons. The namespace solves it, but still.

texus

QuoteAs for the Observer Pattern, that's essentially what I'm doing right?
Yes, but it was hard to tell with seeing little code.

QuoteAlso, any recommendations for how to name my stuff? Because I plan to do this with all the types: Buttons, Sliders, ect. And I thought I should put some sort of differentiation between my Buttons and the tgui buttons. The namespace solves it, but still.
I don't really see why you would need all these classes.
What is it that you have to add to all my widgets? If it is only the send function then you can just as well bind the receive function of the target immediately.

Depending on what you are planning to do with the widgets, you can write one class for all widgets instead of one for button, one for edit box, ...
class MyWidget : public IOBase
{
    MyWidget(tgui::Widget::Ptr widget) :
        m_widget(widget)
    {
    }

    tgui::Widget::Ptr m_widget;
}

MyWidget myWidget{ tgui::Button::Ptr(gui) };


A class like above could take any widget, depending on what you pass in the constructor. But it is of course only usefull if you don't need different behavior for every different widget.

Strikerklm96

#4
I also need the widget instance, like a button, to store what message or messages it plans to send and what its target is.

My plan is later to have a text file with something like this in it, for a game.(this is a very simple version)
Entity_Human
{
targetName = "player_1"
health = 100
}

Entity_Human
{
targetName = "enemy_1"
health = 100
outputs = {onHealth, <, 50, "enemy_1", setHealth, 0; onHealth, <, 50, "enemy_2", setHealth, 0; onHealth, <, 50, "button_1", disable}
}

Entity_Human
{
targetName = "enemy_2"
health = 130
}

Button
{
targetName = "enemy_2"
outputs = {onClicked, "enemy_1", setHealth, 500; onClicked, "enemy_2", setHealth, 300}
}

Slider
{
targetName = "enemy_2"
outputs = {onChanged, "enemy_1", setHealth, getData1}
}


So if you click the button, it sets the health of the enemies to 500 and 300, And if you move the slider, it sets enemy_1 health to whatever the sliders value at the time it gets sent. Also, if your health drops below 50, the button gets disabled, and the enemies killed. I already have this system working by the way, just not tgui. If you'd like more explanation, I can give it. I'm basically doing this: https://developer.valvesoftware.com/wiki/Inputs_and_Outputs

texus

I wouldn't know any good way either, I guess you'll just have to keep differentiating the classes with 'Button' and 'tgui::Button'.

But what you are doing looks really interesting.

Strikerklm96

#6
It's not that bad, I just have to make around 10 classes that are pretty much the same.

And thanks, I think!  ;) I'm pretty far along with my game, ill post it here, and other places, and give credit!
I made some pretty advanced game levels in Source SDK(Half-Life 2 Editor basically), and Source's Input Output system was really great, but actually lacked things which I plan to have. I could also theoretically do this more efficiently with function pointers, but that would mean things would start to get weird, and this works out nicer. My levels are still up on a Garrys-Mod server, getting played possibly right now!

Another question, maybe this should be another topic, but:

What is the intended way to have multiple GUI's in existence at the same time?
Is it to use Child-Windows? But if someone closes out of a window, I want to keep the window in memory for the next time it opens. So then what?
Another solution I thought was to have multiple GUI instances. So while playing the game, the HUD-GUI would be displayed and take input. But if the player presses escape, the hud GUI no longer accepts input, and the new Menu is displayed on top of that. And then if they select "Options" from that menu, Another pops up, but on top of that. It's pretty easy to have multiple GUI instances, and to store them in 3 vectors of GUI pointers.
1 Vector holds the active GUI's that accept input.
1 Vector holds the GUIs that are displayed, but not accepting input.
1 Vector holds the GUIS that are not displayed and not accepting input.
I did this and it worked... but two fully active GUI's at the same time caused occasional strange things to happen, which is solved by just having a single active GUI.

texus

QuoteWhat is the intended way to have multiple GUI's in existence at the same time?
I dont have much time to give a detailed answer right now, but you should have a look at the Panel.
You can create multiple panels and then just hide the once that you don't want to show. Using multiple Gui objects is not a good idea because you will have to pass the events to the right one and draw the right one. With panels you just use the show and hide functions and you don't need to change anything about the handleEvent or draw functions.

Strikerklm96

Cool, and thanks a bunch  :D.
Is there anywhere else I could have known that's the purpose of a Panel class? The documentation just says its a container for widgets. If not, you should put it in the doc maybe? And how should tabs be utilized? Enabling and disabling panels?

Sorry if these are stupid questions, but for someone like me who has never really dealt with GUI before, I tend to try and solve problems that already have a designated solution, like the Panel,  and or I don't understand how the design is intended to work.

texus

#9
QuoteIs there anywhere else I could have known that's the purpose of a Panel class? The documentation just says its a container for widgets.
Well, "a container for widgets" is exactly what you need. In the end, the Gui class itself is not much more than a container. But I understand that when you are looking to implement different screens that this isn't what you think about.

Quoteyou should put it in the doc maybe?
My biggest limitation is always the lack of time. There was a tutorial for v0.5 (https://tgui.weebly.com/v05---panel.html), but just like many tutorials it hasn't been ported to v0.6 yet. But I might add a tutorial for panel to the site when I find some more time.

Edit: I managed to find some time: https://tgui.eu/tutorials/v06/panel/

QuoteAnd how should tabs be utilized? Enabling and disabling panels?
I actually have some example code of how to use tabs and panels together: https://tgui.eu/tutorials/v06/tabs/

QuoteSorry if these are stupid questions, but for someone like me who has never really dealt with GUI before, I tend to try and solve problems that already have a designated solution, like the Panel,  and or I don't understand how the design is intended to work.
There not stupid questions, most things are just underdocumented. And its always harder to use code that someone else wrote, because if you would write it yourself then you would probably do it differently in a way that makes more sense to you.