I want to create a class where I store connections from widgets to callbacks. So in Gtk++ you would get a widget and connect it to a function:
Gtk::Button* button;
builder->get_widget("fooButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &ThisClassName::onButtonPressed)); // ThisClassName being the name of *this object's class
I want to have a class where I store a list of these connections. The goal is to be able to defer the establishment of the connections, to be able to disconnect or block all connections and reconnect/unblock them. So I created the following WidgetConnector:
#ifndef WIDGETCONNECTOR_H
#define WIDGETCONNECTOR_H
#include <functional>
#include <map>
#include <algorithm>
class WidgetConnector {
public:
using ConnectionProxy = std::function<sigc::connection()>;
WidgetConnector(const Glib::RefPtr<Gtk::Builder>& builder)
: mBuilder(builder) { assert(mBuilder); }
virtual ~WidgetConnector() {}
template<class T_Widget, auto S, typename... T>
T_Widget& getConnectedWidget(std::string label, const typename sigc::slot< void(T...)>& slot) {
T_Widget* w = nullptr;
mBuilder->get_widget(label, w);
assert(w);
using signal_type = decltype(std::mem_fun(S));
const signal_type& widgetSignal = std::mem_fun(S);
// delegate calls to connectWidgetWithSignal() which shall be called after the interface is build
// to avoid any callbacks during build. This is done by storing the functors in a map.
// Note: all captures must be copied so that they are valid during delegated call
mConnectRefList[label] = std::make_unique<ConnectionProxy>([w, slot, widgetSignal]() {
return widgetSignal(w).connect(slot);
});
return *w; // Return the object and get a reference to it (no nullptr testing anymore)
}
void connectWidgetsWithSignals() {
for (auto const& [label, functor] : mConnectRefList)
{
connectWidgetWithSignal(label);
}
}
void connectWidgetWithSignal(std::string label, bool doOverwrite = false) {
auto connectionRef = (*mConnectRefList.at(label))();
addDisconnectRef(label, connectionRef, doOverwrite);
}
template<typename CONN>
void addDisconnectRef(std::string label, CONN connectRef, bool doOverwrite = false) {
try {
if (mDisconnectRefList[label].connected() && doOverwrite)
mDisconnectRefList[label].disconnect();
if (! mDisconnectRefList[label].connected()) {
mDisconnectRefList[label] = connectRef;
mIsConnected = true;
}
}
catch (...) {
std::cerr << "No connection set up for " << label << std::endl;
}
}
void disconnectWidgetsWithSignals() {
for (auto const& [label, functor] : mDisconnectRefList)
{
disconnectWidgetWithSignal(label);
}
mIsConnected = false;
}
void disconnectWidgetWithSignal(std::string label) {
//if (mConnectRefList.find(label) != mConnectRefList.end()) return; // not connected
try {
if (mDisconnectRefList.at(label).connected())
mDisconnectRefList.at(label).disconnect();
}
catch (...) {
std::cerr << "No connection set up for " << label << std::endl;
}
}
private:
const Glib::RefPtr<Gtk::Builder> mBuilder;
std::map<std::string, std::unique_ptr<ConnectionProxy>> mConnectRefList; //!< list of SignalProxies for connect calls
std::map<std::string, sigc::connection> mDisconnectRefList; //!< list of SignalProxies for disconnect calls
};
#endif // WIDGETCONNECTOR_H
When inheriting from this class I can easily retrieve widgets, connect them and later disconnect and reconnect them:
auto& w = getConnectedWidget<Gtk::Button, Gtk::Button::signal_clicked>("fooButton",
static_cast<const typename sigc::slot<void()>>(sigc::mem_fun(this,
&ThisClassName::onButtonPressed)));
// do some more stuff with w, e.g. w.set_sensitive()
// till now the connections are merely stored
connectWidgetsWithSignals();
// now the connections are actually connected and executing on fired signals
Despite the looks of *getConnectedWidget *arguments: const typename sigc::slot< void(T...)> I cannot use this template function to store a connection with a function that has arguments, or a function that returns e.g. bool instead of void. So with
void onButtonPressed() {...}
everything works fine but
void onButtonPressed(int x) {...} or
bool onButtonPressed() { ... return true; }
does not compile.
How do I need to correct the class/template function to make this work?