TGUI  0.7.4
Signal.hpp
1 //
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 
41 namespace 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 
329  SignalWidgetBase(const SignalWidgetBase& copy);
330 
331 
340  SignalWidgetBase& operator=(const SignalWidgetBase& right);
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 
478  void disconnectAll();
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
Namespace that contains all TGUI functions and classes.
Definition: Animation.hpp:33
Definition: Callback.hpp:44
Definition: Exception.hpp:44
Base class for widgets to enable signal handling.
Definition: Signal.hpp:312
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
Definition: Signal.hpp:238
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