TGUI  0.8.9
SignalImpl.hpp
1
2//
3// TGUI - Texus' Graphical User Interface
4// Copyright (C) 2012-2020 Bruno Van de Velde (vdv_b@tgui.eu)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
25
26#ifndef TGUI_SIGNAL_IMPL_HPP
27#define TGUI_SIGNAL_IMPL_HPP
28
29#include <TGUI/Signal.hpp>
30
32
33namespace tgui
34{
35 namespace internal_signal
36 {
37#if defined(__cpp_lib_void_t) && (__cpp_lib_void_t >= 201411L)
38 using std::void_t;
39#else
40 // void_t only exists in c++17 so we use our own implementation to support c++14 compilers
41 template<typename...>
42 using void_t = void;
43#endif
44
45 // Type to pass a list of template types
46 template <typename...>
47 struct TypeSet;
48
49 // The dereference function turns the void* elements in the parameters list back into its original type right before calling the signal handler
50#if defined(__cpp_if_constexpr) && (__cpp_if_constexpr >= 201606L)
51 template <typename Type>
52 decltype(auto) dereference(const void* obj)
53 {
54 if constexpr (std::is_same_v<Type, std::string>) // Signal handlers are allowed to have std::string parameters while the signal sends sf::String
55 return static_cast<std::string>(*static_cast<const sf::String*>(obj));
56 else if constexpr (std::is_same_v<Type, sf::Vector2f>) // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
57 return static_cast<sf::Vector2f>(*static_cast<const Vector2f*>(obj));
58 else if constexpr (std::is_same_v<Type, sf::Color>) // Signal handlers are allowed to have sf::Color parameters while the signal sends tgui::Color
59 return static_cast<sf::Color>(*static_cast<const Color*>(obj));
60 else
61 return *static_cast<const std::decay_t<Type>*>(obj);
62 }
63#else
64 template <typename Type, typename std::enable_if<std::is_same<Type, std::string>::value>::type* = nullptr>
65 decltype(auto) dereference(const void* obj)
66 {
67 // Signal handlers are allowed to have std::string parameters while the signal sends sf::String
68 return static_cast<std::string>(*static_cast<const sf::String*>(obj));
69 }
70
71 template <typename Type, typename std::enable_if<std::is_same<Type, sf::Vector2f>::value>::type* = nullptr>
72 decltype(auto) dereference(const void* obj)
73 {
74 // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
75 return static_cast<sf::Vector2f>(*static_cast<const Vector2f*>(obj));
76 }
77
78 template <typename Type, typename std::enable_if<std::is_same<Type, sf::Color>::value>::type* = nullptr>
79 decltype(auto) dereference(const void* obj)
80 {
81 // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
82 return static_cast<sf::Color>(*static_cast<const Color*>(obj));
83 }
84
85 template <typename Type, typename std::enable_if<!std::is_same<Type, std::string>::value && !std::is_same<Type, sf::Vector2f>::value && !std::is_same<Type, sf::Color>::value>::type* = nullptr>
86 decltype(auto) dereference(const void* obj)
87 {
88 return *static_cast<const typename std::decay<Type>::type*>(obj);
89 }
90#endif
91
92#if !defined(__cpp_lib_invoke) || (__cpp_lib_invoke < 201411L)
93 // std::invoke only exists in c++17 so we use our own implementation to support c++14 compilers
94 // Visual Studio compiler did not like it when the function was called "invoke"
95 template <typename Func, typename... Args, typename std::enable_if<std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
96 void invokeFunc(Func&& func, Args&&... args)
97 {
98 std::mem_fn(func)(std::forward<Args>(args)...);
99 }
100
101 template <typename Func, typename... Args, typename std::enable_if<!std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
102 void invokeFunc(Func&& func, Args&&... args)
103 {
104 std::forward<Func>(func)(std::forward<Args>(args)...);
105 }
106#endif
107
108 // The binder will figure out the unbound parameters and bind them if they correspond to what the signal sends
109 template <typename... Args>
110 struct binder;
111
112 template <typename Arg, typename... AllArgs, typename BoundArg, typename... BoundArgs>
113 struct binder<TypeSet<Arg, AllArgs...>, TypeSet<BoundArg, BoundArgs...>>
114 : binder<TypeSet<AllArgs...>, TypeSet<BoundArgs...>>
115 {
116 };
117
118 template <typename... UnboundArgs>
119 struct binder<TypeSet<std::shared_ptr<Widget>, std::string, UnboundArgs...>, TypeSet<>>
120 {
121 template <typename Func, typename... BoundArgs>
122 static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
123 {
124 return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
125 }
126
127 private:
128
129 template <typename Func, typename... BoundArgs, std::size_t... Indices>
130 static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
131 {
132 const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
133#if defined(__cpp_lib_invoke) && (__cpp_lib_invoke >= 201411L)
134 return [=,o=offset](const std::shared_ptr<Widget>& widget, const std::string& signalName) {
135 std::invoke(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
136 args...,
137 widget,
138 signalName,
139 internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
140#else
141 return [=,o=offset](const std::shared_ptr<Widget>& widget, const std::string& signalName) {
142 invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
143 args...,
144 widget,
145 signalName,
146 internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
147#endif
148 };
149 }
150 };
151
152 template <typename... UnboundArgs>
153 struct binder<TypeSet<UnboundArgs...>, TypeSet<>>
154 {
155 template <typename Func, typename... BoundArgs>
156 static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
157 {
158 return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
159 }
160
161 private:
162
163 template <typename Func, typename... BoundArgs, std::size_t... Indices>
164 static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
165 {
166 const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
167#if defined(__cpp_lib_invoke) && (__cpp_lib_invoke >= 201411L)
168 return [=,o=offset]{
169 std::invoke(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
170 args...,
171 internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
172#else
173 return [=,o=offset]{
174 invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
175 args...,
176 internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
177#endif
178 };
179 }
180 };
181
182
183#if defined(__cpp_noexcept_function_type) && (__cpp_noexcept_function_type >= 201510L)
184 // Error case (function signature did not match anything)
185 template <typename Enable, typename Func, typename... BoundArgs>
186 struct func_traits;
187
188 // Free function
189 template <typename... Args, typename... BoundArgs>
190 struct func_traits<void, void(*)(Args...), BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
191 template <typename... Args, typename... BoundArgs>
192 struct func_traits<void, void(*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
193
194 // Non-const member function
195 template <typename Class, typename... Args, typename... BoundArgs>
196 struct func_traits<void, void(Class::*)(Args...), BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
197 template <typename Class, typename... Args, typename... BoundArgs>
198 struct func_traits<void, void(Class::*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
199 template <typename Class, typename... Args, typename... BoundArgs>
200 struct func_traits<void, void(Class::*)(Args...) volatile, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
201 template <typename Class, typename... Args, typename... BoundArgs>
202 struct func_traits<void, void(Class::*)(Args...) volatile noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
203 template <typename Class, typename... Args, typename... BoundArgs>
204 struct func_traits<void, void(Class::*)(Args...) &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
205 template <typename Class, typename... Args, typename... BoundArgs>
206 struct func_traits<void, void(Class::*)(Args...) & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
207 template <typename Class, typename... Args, typename... BoundArgs>
208 struct func_traits<void, void(Class::*)(Args...) volatile &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
209 template <typename Class, typename... Args, typename... BoundArgs>
210 struct func_traits<void, void(Class::*)(Args...) volatile & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
211 template <typename Class, typename... Args, typename... BoundArgs>
212 struct func_traits<void, void(Class::*)(Args...) &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
213 template <typename Class, typename... Args, typename... BoundArgs>
214 struct func_traits<void, void(Class::*)(Args...) && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
215 template <typename Class, typename... Args, typename... BoundArgs>
216 struct func_traits<void, void(Class::*)(Args...) volatile &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
217 template <typename Class, typename... Args, typename... BoundArgs>
218 struct func_traits<void, void(Class::*)(Args...) volatile && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
219
220 // Const member function
221 template <typename Class, typename... Args, typename... BoundArgs>
222 struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
223 template <typename Class, typename... Args, typename... BoundArgs>
224 struct func_traits<void, void(Class::*)(Args...) const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
225 template <typename Class, typename... Args, typename... BoundArgs>
226 struct func_traits<void, void(Class::*)(Args...) volatile const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
227 template <typename Class, typename... Args, typename... BoundArgs>
228 struct func_traits<void, void(Class::*)(Args...) volatile const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
229 template <typename Class, typename... Args, typename... BoundArgs>
230 struct func_traits<void, void(Class::*)(Args...) const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
231 template <typename Class, typename... Args, typename... BoundArgs>
232 struct func_traits<void, void(Class::*)(Args...) const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
233 template <typename Class, typename... Args, typename... BoundArgs>
234 struct func_traits<void, void(Class::*)(Args...) volatile const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
235 template <typename Class, typename... Args, typename... BoundArgs>
236 struct func_traits<void, void(Class::*)(Args...) volatile const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
237 template <typename Class, typename... Args, typename... BoundArgs>
238 struct func_traits<void, void(Class::*)(Args...) const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
239 template <typename Class, typename... Args, typename... BoundArgs>
240 struct func_traits<void, void(Class::*)(Args...) const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
241 template <typename Class, typename... Args, typename... BoundArgs>
242 struct func_traits<void, void(Class::*)(Args...) volatile const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
243 template <typename Class, typename... Args, typename... BoundArgs>
244 struct func_traits<void, void(Class::*)(Args...) volatile const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
245
246 // std::function or lambda
247 template <typename Func, typename... BoundArgs>
248 struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...> : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...> {};
249#else
250 // Error case (function signature did not match anything)
251 template <typename Enable, typename Func, typename... BoundArgs>
252 struct func_traits;
253
254 // std::function or lambda
255 template <typename Func, typename... BoundArgs>
256 struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...>
257 : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...>
258 {
259 };
260
261 // Non-const member function
262 template <typename Class, typename... Args, typename... BoundArgs>
263 struct func_traits<void, void(Class::*)(Args...), BoundArgs...>
264 : binder<TypeSet<Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
265 {
266 };
267
268 // Const member function
269 template <typename Class, typename... Args, typename... BoundArgs>
270 struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...>
271 : binder<TypeSet<const Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
272 {
273 };
274
275 // Free function
276 template <typename... Args, typename... BoundArgs>
277 struct func_traits<void, void(*)(Args...), BoundArgs...>
278 : binder<TypeSet<typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
279 {
280 };
281#endif
282 }
283
285
286#if defined(__cpp_if_constexpr) && (__cpp_if_constexpr >= 201606L)
287 template <typename Func, typename... BoundArgs>
288 unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const BoundArgs&... args)
289 {
290 unsigned int id;
291 Signal& signal = getSignal(toLower(signalName));
292
293 if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>
294 && std::is_invocable_v<decltype(&handler), BoundArgs...>
295 && !std::is_function_v<Func>)
296 {
297 // Reference to function, all parameters bound
298 id = signal.connect([=, f=std::function<void(const BoundArgs&...)>(handler)]{ std::invoke(f, args...); });
299 }
300 else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>)
301 {
302 // Function with all parameters bound
303 id = signal.connect([=]{ std::invoke(handler, args...); });
304 }
305 else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>
306 && std::is_invocable_v<decltype(&handler), BoundArgs..., const std::shared_ptr<Widget>&, const std::string&>
307 && !std::is_function_v<Func>)
308 {
309 // Reference to function with caller arguments, all parameters bound
310 id = signal.connect([=, f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>& w, const std::string& s)>(handler)](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(f, args..., w, s); });
311 }
312 else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>)
313 {
314 // Function with caller arguments, all parameters bound
315 id = signal.connect([=](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(handler, args..., w, s); });
316 }
317 else
318 {
319 // Function with unbound arguments
320 using binder = internal_signal::func_traits<void, std::decay_t<Func>, BoundArgs...>;
321 id = signal.connect(binder::bind(signal, std::forward<Func>(handler), args...));
322 }
323
324 m_connectedSignals[id] = toLower(signalName);
325 return id;
326 }
327
328#else
329 template <typename Func, typename... Args, typename std::enable_if<std::is_convertible<Func, std::function<void(const Args&...)>>::value>::type*>
330 unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const Args&... args)
331 {
332 const unsigned int id = getSignal(toLower(signalName)).connect([f=std::function<void(const Args&...)>(handler),args...](){ f(args...); });
333 m_connectedSignals[id] = toLower(signalName);
334 return id;
335 }
336
337 template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value // Ambigious otherwise when passing bind expression
338 && std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
339 unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
340 {
341 const unsigned int id = getSignal(toLower(signalName)).connect(
342 [f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>(handler), args...]
343 (const std::shared_ptr<Widget>& w, const std::string& s)
344 { f(args..., w, s); }
345 );
346
347 m_connectedSignals[id] = toLower(signalName);
348 return id;
349 }
350
351 template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value
352 && !std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
353 unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
354 {
355 Signal& signal = getSignal(toLower(signalName));
356 using binder = internal_signal::func_traits<void, typename std::decay<Func>::type, BoundArgs...>;
357 const unsigned int id = signal.connect(binder::bind(signal, std::forward<Func>(handler), std::forward<BoundArgs>(args)...));
358 m_connectedSignals[id] = toLower(signalName);
359 return id;
360 }
361#endif
362
364
365 template <typename Func, typename... BoundArgs>
366 unsigned int SignalWidgetBase::connect(std::initializer_list<std::string> signalNames, Func&& handler, BoundArgs&&... args)
367 {
368 unsigned int lastId = 0;
369 for (auto& signalName : signalNames)
370 lastId = connect(std::move(signalName), handler, args...);
371
372 return lastId;
373 }
374}
375
377
378#endif // TGUI_SIGNAL_IMPL_HPP
unsigned int connect(std::string signalName, Func &&handler, const Args &... args)
Connects a signal handler that will be called when this signal is emitted.
Definition: SignalImpl.hpp:330
virtual Signal & getSignal(std::string signalName)=0
Retrieves a signal based on its name.
Signal to which the user can subscribe to get callbacks from.
Definition: Signal.hpp:231
unsigned int connect(const Delegate &handler)
Connects a signal handler that will be called when this signal is emitted.
Namespace that contains all TGUI functions and classes.
Definition: AbsoluteOrRelativeValue.hpp:37
TGUI_API std::string toLower(const std::string &str)
Converts a string to lowercase.