TGUI  0.7.8
Signal.hpp
1
2//
3// TGUI - Texus's Graphical User Interface
4// Copyright (C) 2012-2017 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_HPP
27#define TGUI_SIGNAL_HPP
28
29
30#include <TGUI/Global.hpp>
31#include <TGUI/Callback.hpp>
32
33#include <map>
34#include <deque>
35#include <memory>
36#include <cassert>
37#include <functional>
38
40
41namespace tgui
42{
43 class Widget;
44 class ChildWindow;
45
47
48 template <typename... Types>
49 struct TypeSet;
50
52
53 namespace priv
54 {
55 extern TGUI_API std::deque<const void*> data;
56
58
59 template <typename T>
60 std::string convertTypeToString();
61
62 template <> inline std::string convertTypeToString<int>() { return "int"; }
63 template <> inline std::string convertTypeToString<sf::Vector2f>() { return "sf::Vector2f"; }
64 template <> inline std::string convertTypeToString<sf::String>() { return "sf::String"; }
65 template <> inline std::string convertTypeToString<std::vector<sf::String>>() { return "std::vector<sf::String>"; }
66 template <> inline std::string convertTypeToString<std::shared_ptr<ChildWindow>>() { return "std::shared_ptr<ChildWindow>"; }
67
69
70 template <typename... Types>
71 struct extractTypes
72 {
73 static std::vector<std::vector<std::string>> get() { return {}; }
74 static std::vector<std::string> getRow() { return {}; }
75 };
76
77 template <typename Type>
78 struct extractTypes<Type>
79 {
80 static std::vector<std::vector<std::string>> get() { return {{convertTypeToString<Type>()}}; }
81 static std::vector<std::string> getRow() { return {convertTypeToString<Type>()}; }
82 };
83
84 template <typename Type, typename... OtherTypes>
85 struct extractTypes<Type, OtherTypes...>
86 {
87 static std::vector<std::vector<std::string>> get()
88 {
89 auto types = extractTypes<OtherTypes...>::get();
90 types.insert(types.begin(), {convertTypeToString<Type>()});
91 return types;
92 }
93
94 static std::vector<std::string> getRow()
95 {
96 auto types = extractTypes<OtherTypes...>::getRow();
97 types.insert(types.begin(), convertTypeToString<Type>());
98 return types;
99 }
100 };
101
102 template <typename... T>
103 struct extractTypes<TypeSet<T...>>
104 {
105 static std::vector<std::vector<std::string>> get() { return {extractTypes<T...>::getRow()}; }
106 };
107
108 template <typename... T, typename... U>
109 struct extractTypes<TypeSet<T...>, U...>
110 {
111 static std::vector<std::vector<std::string>> get()
112 {
113 auto types = {extractTypes<T...>::getRow()};
114 types.insert(types.end(), extractTypes<U...>::get());
115 return types;
116 }
117 };
118
120
121 template <typename Func, typename TypeA, typename TypeB, typename... Args>
122 struct isConvertible;
123
124 template <typename Func, typename... TypesA, typename... TypesB, typename... Args>
125 struct isConvertible<Func, TypeSet<TypesA...>, TypeSet<TypesB...>, Args...>
126 {
127 using type = typename std::conditional<std::is_convertible<Func, std::function<void(Args..., TypesA...)>>::value, TypeSet<TypesA...>, TypeSet<TypesB...>>::type;
128 };
129
130 template <typename Func, typename... Type, typename... Args>
131 struct isConvertible<Func, TypeSet<>, TypeSet<Type...>, Args...>
132 {
133 using type = typename std::conditional<std::is_convertible<Func, std::function<void(Args...)>>::value || std::is_convertible<Func, std::function<void(Args...)>>::value, TypeSet<>, TypeSet<Type...>>::type;
134 };
135
137
138 template <typename Type>
139 const Type& dereference(const void* obj)
140 {
141 return *static_cast<const Type*>(obj);
142 }
143
145
146// Ignore warning "C4800: 'const int': forcing value to bool 'true' or 'false' (performance warning)" in Visual Studio
147#if defined SFML_SYSTEM_WINDOWS && defined _MSC_VER
148 #pragma warning(push)
149 #pragma warning(disable : 4800)
150#endif
151
152 template <typename... T>
153 struct connector;
154
155 template <typename Func, typename... Args>
156 struct connector<TypeSet<>, Func, Args...>
157 {
158 static std::function<void()> connect(Func func, std::size_t, Args... args)
159 {
160 return std::bind(func, args...);
161 }
162 };
163
164 template <typename Func, typename... Args, typename Type>
165 struct connector<TypeSet<Type>, Func, Args...>
166 {
167 static std::function<void()> connect(Func func, std::size_t argPos, Args... args)
168 {
169 return std::bind(func, args..., std::bind(dereference<Type>, std::cref(data[argPos])));
170 }
171 };
172
173 template <typename Func, typename... Args, typename TypeA, typename TypeB>
174 struct connector<TypeSet<TypeA, TypeB>, Func, Args...>
175 {
176 static std::function<void()> connect(Func func, std::size_t argPos, Args... args)
177 {
178 return std::bind(func, args...,
179 std::bind(dereference<TypeA>, std::cref(data[argPos])),
180 std::bind(dereference<TypeB>, std::cref(data[argPos+1])));
181 }
182 };
183
184#if defined SFML_SYSTEM_WINDOWS && defined _MSC_VER
185 #pragma warning(pop)
186#endif
187
189
190 template <typename Arg, typename = void>
191 struct bindRemover
192 {
193 static Arg remove(Arg arg)
194 {
195 return arg;
196 }
197 };
198
199 template <typename Arg>
200 struct bindRemover<Arg, typename std::enable_if<std::is_bind_expression<Arg>::value>::type>
201 {
202 static auto remove(Arg arg) -> decltype(arg())
203 {
204 return arg();
205 }
206 };
207
209
210 template <typename Func, typename... Args>
211 struct isFunctionConvertible
212 {
213 using type = typename isConvertible<Func, TypeSet<>,
214 typename isConvertible<Func, TypeSet<int>,
215 typename isConvertible<Func, TypeSet<sf::Vector2f>,
216 typename isConvertible<Func, TypeSet<sf::String>,
217 typename isConvertible<Func, TypeSet<std::vector<sf::String>>,
218 typename isConvertible<Func, TypeSet<std::shared_ptr<ChildWindow>>,
219 typename isConvertible<Func, TypeSet<sf::String, sf::String>,
220 TypeSet<void>,
221 Args...>::type,
222 Args...>::type,
223 Args...>::type,
224 Args...>::type,
225 Args...>::type,
226 Args...>::type,
227 Args...>::type;
228 };
229
231 }
232
233
236 // Stores the actual callback functions and checks if their parameters are valid
238 class TGUI_API Signal
239 {
240 public:
241
242 Signal(std::vector<std::vector<std::string>>&& types);
243
244 template <typename Func, typename... Args>
245 void connect(unsigned int id, Func func, Args... args)
246 {
247 using type = typename priv::isFunctionConvertible<Func, decltype(priv::bindRemover<Args>::remove(args))...>::type;
248 static_assert(!std::is_same<type, TypeSet<void>>::value, "Parameters passed to the connect function are wrong!");
249
250 auto argPos = checkCompatibleParameterType<type>();
251 m_functions[id] = priv::connector<type, Func, Args...>::connect(func, argPos, args...);
252 }
253
254 template <typename Func, typename... Args>
255 void connectEx(unsigned int id, Func func, Args... args)
256 {
257 m_functionsEx[id] = std::bind(func, args..., std::placeholders::_1);
258 }
259
260 bool disconnect(unsigned int id);
261
262 void disconnectAll();
263
264 bool isEmpty() const;
265
266 void operator()(unsigned int count);
267
268 template <typename T, typename... Args>
269 void operator()(unsigned int count, const T& value, Args... args)
270 {
271 priv::data[count] = static_cast<const void*>(&value);
272 (*this)(count+1, args...);
273 }
274
275 protected:
276
277 template <typename Type>
278 std::size_t checkCompatibleParameterType()
279 {
280 if (std::is_same<Type, TypeSet<>>::value)
281 return 0;
282
283 auto acceptedType = priv::extractTypes<Type>::get();
284 assert(acceptedType.size() == 1);
285
286 std::size_t count = 0;
287 for (std::size_t i = 0; i < m_allowedTypes.size(); ++i)
288 {
289 if (acceptedType[0] == m_allowedTypes[i])
290 return count;
291
292 count += m_allowedTypes[i].size();
293 }
294
295 throw Exception{"Failed to bind parameter to callback function. Parameter is of wrong type."};
296 }
297
298 private:
299
300 std::map<unsigned int, std::function<void()>> m_functions;
301 std::map<unsigned int, std::function<void(const Callback&)>> m_functionsEx;
302
303 std::vector<std::vector<std::string>> m_allowedTypes;
304
305 friend class SignalWidgetBase; // Only needed for m_functionsEx
306 };
307
308
312 class TGUI_API SignalWidgetBase
313 {
314 public:
315
320 SignalWidgetBase() = default;
321
322
330
331
341
342
353 template <typename Func, typename... Args>
354 unsigned int connect(const std::string& signalNames, Func func, Args... args)
355 {
356 auto signalNameList = extractSignalNames(signalNames);
357 if (signalNameList.empty())
358 throw Exception{"connect function called with empty string"};
359
360 for (auto& signalName : signalNameList)
361 {
362 if (m_signals.find(toLower(signalName)) != m_signals.end())
363 {
364 try {
365 m_signals[toLower(signalName)]->connect(m_lastId, func, args...);
366 m_lastId++;
367 }
368 catch (const Exception& e) {
369 throw Exception{e.what() + (" The parameters are not valid for the '" + signalName + "' signal.")};
370 }
371 }
372 else
373 {
374 if (toLower(signalName) != "all")
375 throw Exception{"Cannot connect to unknown signal '" + signalName + "'."};
376 else
377 {
378 assert(!m_signals.empty());
379
380 for (auto& signal : m_signals)
381 {
382 try {
383 signal.second->connect(m_lastId, func, args...);
384 m_lastId++;
385 }
386 catch (const Exception& e) {
387 throw Exception{e.what() + (" The parameters are not valid for the '" + signalName + "' signal.")};
388 }
389 }
390 }
391 }
392 }
393
394 return m_lastId - 1;
395 }
396
397
410 template <typename Func, typename... Args>
411 unsigned int connectEx(const std::string& signalName, Func func, Args... args)
412 {
413 auto signalNameList = extractSignalNames(signalName);
414 if (signalNameList.empty())
415 throw Exception{"connect function called with empty string"};
416
417 for (auto& name : signalNameList)
418 {
419 if (m_signals.find(toLower(name)) != m_signals.end())
420 {
421 try {
422 m_signals[toLower(name)]->connectEx(m_lastId, func, args...);
423 m_lastId++;
424 }
425 catch (const Exception& e) {
426 throw Exception{e.what() + (" since it is not valid for the '" + name + "' signal.")};
427 }
428 }
429 else // Signal name does not exist
430 {
431 if (toLower(name) != "all")
432 throw Exception{"Cannot connect to unknown signal '" + name + "'."};
433 else
434 {
435 assert(!m_signals.empty());
436
437 for (auto& signal : m_signals)
438 {
439 try {
440 signal.second->connectEx(m_lastId, func, args...);
441 m_lastId++;
442 }
443 catch (const Exception& e) {
444 throw Exception{e.what() + (" since it is not valid for the '" + name + "' signal.")};
445 }
446 }
447 }
448 }
449 }
450
451 return m_lastId - 1;
452 }
453
454
461 void disconnect(unsigned int id);
462
463
471 void disconnectAll(const std::string& signalName);
472
473
479
480
482 protected:
483
485 // Add a new signal that people can bind.
487 template <typename... T>
488 void addSignal(std::string&& name)
489 {
490 assert(m_signals[toLower(name)] == nullptr);
491 m_signals[toLower(name)] = std::make_shared<Signal>(priv::extractTypes<T...>::get());
492 }
493
494
496 // Check if some signal handler has been bound to the signal.
498 bool isSignalBound(std::string&& name);
499
500
502 // Send a signal to all signal handlers that are connected with this signal.
504 template <typename... Args>
505 void sendSignal(std::string&& name, Args... args)
506 {
507 assert(m_signals[toLower(name)] != nullptr);
508 auto& signal = *m_signals[toLower(name)];
509
510 if (signal.m_functionsEx.empty())
511 {
512 if (!signal.isEmpty())
513 signal(0, args...);
514 }
515 else // Legacy functions are used
516 {
517 // Copy signal to avoid problems with lifetime when signal handler destroys this object
518 Signal signalCopy = signal;
519
520 m_callback.trigger = name;
521 for (const auto& function : signalCopy.m_functionsEx)
522 function.second(m_callback);
523
524 if (!signalCopy.isEmpty())
525 signalCopy(0, args...);
526 }
527 }
528
529
531 protected:
532
533 std::vector<std::string> extractSignalNames(std::string input);
534
536 protected:
537
538 std::map<std::string, std::shared_ptr<Signal>> m_signals;
539
540 static unsigned int m_lastId;
541
542 Callback m_callback;
543
545 };
546
547
549}
550
552
553#endif // TGUI_SIGNAL_HPP
Definition: Exception.hpp:44
Base class for widgets to enable signal handling.
Definition: Signal.hpp:313
void disconnectAll(const std::string &signalName)
Disconnect all connections from a certain signal.
SignalWidgetBase(const SignalWidgetBase &copy)
Copy constructor.
unsigned int connect(const std::string &signalNames, Func func, Args... args)
Connects a signal handler function to one or more signals.
Definition: Signal.hpp:354
unsigned int connectEx(const std::string &signalName, Func func, Args... args)
Connects a signal handler function to one or more signals.
Definition: Signal.hpp:411
SignalWidgetBase & operator=(const SignalWidgetBase &right)
Overload of assignment operator.
void disconnect(unsigned int id)
Disconnects a connection.
SignalWidgetBase()=default
Default constructor.
void disconnectAll()
Disconnect all connections from a all signals.
Definition: Signal.hpp:239
Namespace that contains all TGUI functions and classes.
Definition: Animation.hpp:34
Definition: Callback.hpp:45