New signal system

Started by texus, 03 March 2019, 13:22:49

How would you prefer the signal system looks like?

child->connect(tgui::ChildWindow::Closed, []{});
child->onClose.connect([]{});
child->Closed.connect([]{});
child->connect.Closed([]{});
Other

texus

I'm currently thinking about changing the signal system.
Depending on how the new system would look like, it will only be added in TGUI 0.9 or it will already be added in 0.8 (but if it is added in 0.8 then the current system will be kept as well for backwards compatibility).

Currently I have been pushing a signal system that looks like this:
Code (cpp) Select
child->connect("closed", []{});

This is very flexible as the string could be read from a text file. The downside is that typos aren't caught at compile time.
Another issue is that the supported parameters of the function are only known at runtime (as they depend on the signal name), so the function signature can't be checked at compile-time either.

An improvement could be made by defining contant strings so that you would be able to write the following:
Code (cpp) Select
child->connect(tgui::ChildWindow::Closed, []{});

Because tgui::ChildWindow::Closed is a string, it is backwards compatible with the old system and using a string read from a text file is still possible. It also prevents typos.
The function signature is however still can't be checked at compile time and it adds extra code: the line is longer and the type of the widget has to be repeated on every connect call.

Currently a different method already exists, but it has been marked deprecated because I was pushing the signal systems with strings. Maybe I should stop deprecating it and start recommending something like this:
Code (cpp) Select
child->onClose.connect([]{});

It is shorter and has less repetition than having to use tgui::ChildWindow::Closed. It also allows the function signature to be checked on compile time.

It no longer allows passing a string read from a text file, but something could be done for that. If a system like this would be chosen then I will still keep some connect function that takes a string as parameter. The difference with the current implementation would be that the function signature would be limited: you would be able to pass a function without argument or taking a pointer to the widget and/or the name of the signal. The use unbound parameters (e.g. getting the text of a button as parameter) would only be possible via button->onPressed.connect(...), but not via button->connect("pressed", ...). This should however not be an issue, when loading these strings from a text file you are likely to bind a general callback function that doesn't take different parameters for different widgets (because if you know the widget type and signal upfront then you could just use the onXyz object).

TGUI currently has no public objects in its interface (other than these signals) so there is no naming convention for these signals yet. Maybe it would look slightly better if onClose would become Closed:
Code (cpp) Select
child->Closed.connect([]{});

Someone also suggested the following, although that might be more difficult to implement with inheritance:
Code (cpp) Select
child->connect.Closed([]{});

Which of these options would you like the use mostly in your code?

If you have comments or other ideas then feel free to post them in this topic.

billarhos

http://en.fairygui.com/

Fairygui is a Cross Platform UI Editor & UI framework. I 've downloaded last year but never had the chance to use it. Looking at the signal source code, they tend to use const
integers. I don't know how this can be delivered in handling signal functions from txt files but this is an alternative to constant strings. Is more like enums. However in user source code they use direct naming functions. Like this:

_view->getChild("n2")->addClickListener([this, bagBtn](EventContext*){ });

texus

Using enums is practically the same as "child->connect(tgui::ChildWindow::Closed, []{})", but with tgui::ChildWindow::Closed being an integer instead of string.
The function signature would still have to be checked at runtime. It would be slightly faster than using a string (but the difference isn't enough for me to care about it).

The addClickListener code gives another possibility. Instead of child->onClose.connect([]{}), we could have something like child->addCloseListener([]{}). It however has the big disadvantage that for every add function there would also have to be a remove function (and possible even some variants other than add and remove).

billarhos

QuoteThis is very flexible as the string could be read from a text file. The downside is that typos aren't caught at compile time.

Can static assertion caught typos?

https://en.cppreference.com/w/cpp/language/static_assert

texus

No, because the valid string depends on the widget type while the connect function is part of the base class. A virtual function call is used to find the signal that matches the name, so it can't be done at compile time.