MenuBar Item bindCallback

Started by Strikerklm96, 29 March 2014, 07:30:32

Strikerklm96

Can I bind a function for MenuBar? So rather than have to go the "pollCallback" route, bind a function to each specific menu option:
In the example I would do:
    tgui::MenuBar::Ptr menu(gui);
    menu->load(THEME_CONFIG_FILE);
    menu->setSize(window.getSize().x, 20);
    menu->addMenu("File");
    menu->addMenuItem("File", "Load");
    menu->addMenuItem("File", "Save");
    menu->addMenuItem("File", "Exit");
    menu->bindCallback(&sf::Window::close, ptr, tgui::MenuBar::MenuItemClicked);

That would close the window when ANY menu item is clicked, but could I make it close it only when "Exit" was clicked? I know how to do it via callback, but I find binding functions is nicer when possible.

texus

You will have to bind your own function that handles which item has been clicked.

The example becomes a little bit harder as the function needs to have access to the window, which is only possible if they are in the same class.
class MyClass
{
public:
    void function(const tgui::Callback& callback);
    sf::RenderWindow window;
}

void MyClass::function(const tgui::Callback& callback)
{
    if (callback.text == "Exit")
        window.close();
    /* else ... */
}

MyClass mc;
menu->bindCallbackEx(std::bind(&MyClass::function, &mc), tgui::MenuBar::MenuItemClicked);


You can also give the function the window as parameter, but then of course it will also have this window parameter even when you aren't handing the "Exit" callback.
void function(const tgui::Callback& callback, sf::RenderWindow& window)
{
    if (callback.text == "Exit")
        window.close();
}

menu->bindCallbackEx(std::bind(function, std::placeholders::_1, std::ref(window)), tgui::MenuBar::MenuItemClicked);


I guess the lambda function would perhaps be a little too long in this case:
menu->bindCallbackEx(std::bind([&](const tgui::Callback& callback, sf::RenderWindow& window) { if (callback.text == "Exit") window.close(); }, std::placeholders::_1, std::ref(window)), tgui::MenuBar::MenuItemClicked);

But I'm afraid that there is no bindCallback function to bind to a specific menu item being clicked. These bindCallback functions are inherited from a base class, and are not specific to the widget.

In the Callback struct, you will have the 'text' member as you can see in the above examples. You can also use the 'index'. This will be 0 for all the menu items in the "File" menu, but if you would also add an "Edit" menu then a callback from there would have index 1.

The callback system is complex, so make sure to read the tutorial and experiment a bit with it.

Strikerklm96

menu->bindCallbackEx(std::bind(&MyClass::function, &mc), tgui::MenuBar::MenuItemClicked);
Doesn't work. Gives errors in "functional" file.
I looked at the documentation of TGUI though, and realized this:
menu->bindCallbackEx(&MyClass::function, &mc, tgui::MenuBar::MenuItemClicked);
does work, I don't really understand what bind does though, and I have never learned this lambda stuff (is it something I should know?)
Either way I have a working callback so thanks!

texus

Quote from: Strikerklm96 on 29 March 2014, 15:22:36
menu->bindCallbackEx(std::bind(&MyClass::function, &mc), tgui::MenuBar::MenuItemClicked);
Doesn't work. Gives errors in "functional" file.
I changed the code a few times while writing the post, I must have overlooked that line. The function that bindCallbackEx needs has the Callback parameter, so it should have been
menu->bindCallbackEx(std::bind(&MyClass::function, std::placeholders::_1, &mc), tgui::MenuBar::MenuItemClicked);

Quote from: Strikerklm96 on 29 March 2014, 15:22:36
I looked at the documentation of TGUI though, and realized this:
menu->bindCallbackEx(&MyClass::function, &mc, tgui::MenuBar::MenuItemClicked);
does work
This function was added to just call the other one with the std::bind as parameter. Its just a shortcut so that you don't need to use std::bind and std::placeholders yourself.

Quote from: Strikerklm96 on 29 March 2014, 15:22:36I don't really understand what bind does though, and I have never learned this lambda stuff (is it something I should know?)
Bind is less important. It just binds a parameter to a function, so that where the function is stored it has no idea about the extra parameters, but they will be there when the function is called. Due to the way c++ works behind the scenes you need this for member functions as they actually have 'this' as argument.

But lambda functions are more important, they allow you to write nameless functions.
Here is a simple use case: the button will hide itself when it is clicked.
button->bindCallback([=](){ button->hide(); }, tgui::Button::LeftMouseClicked);

It replaces the following code:

void function()
{
    // Assuming that this function somehow manages to access the button,
    // which the lambda function could do without problems as it was in the correct scope
    button->hide();
}

button->bindCallback(function, tgui::Button::LeftMouseClicked);


Using lambda functions really starts to pay off when you need a simple functor (e.g. as last parameter in std::remove_if).

But in the end, you can perfectly work without them. But they may be interesting to look at someday.