TGUI  0.8-dev
Signal.hpp
1 //
3 // TGUI - Texus' 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/Callback.hpp>
31 #include <TGUI/Exception.hpp>
32 #include <TGUI/Global.hpp>
33 #include <map>
34 #include <deque>
35 #include <memory>
36 #include <vector>
37 #include <cassert>
38 #include <functional>
39 
41 
42 namespace tgui
43 {
44  class Widget;
45  class ChildWindow;
46 
48 
49  template <typename... Types>
50  struct TypeSet;
51 
53 
54  namespace priv
55  {
56  extern TGUI_API std::deque<const void*> data;
57 
59 
60  template <typename T>
61  std::string convertTypeToString();
62 
63  template <> inline std::string convertTypeToString<int>() { return "int"; }
64  template <> inline std::string convertTypeToString<sf::Vector2f>() { return "sf::Vector2f"; }
65  template <> inline std::string convertTypeToString<sf::String>() { return "sf::String"; }
66  template <> inline std::string convertTypeToString<std::vector<sf::String>>() { return "std::vector<sf::String>"; }
67  template <> inline std::string convertTypeToString<std::shared_ptr<ChildWindow>>() { return "std::shared_ptr<ChildWindow>"; }
68 
70 
71  template <typename... Types>
72  struct extractTypes
73  {
74  static std::vector<std::vector<std::string>> get() { return {}; }
75  static std::vector<std::string> getRow() { return {}; }
76  };
77 
78  template <typename Type>
79  struct extractTypes<Type>
80  {
81  static std::vector<std::vector<std::string>> get() { return {{convertTypeToString<Type>()}}; }
82  static std::vector<std::string> getRow() { return {convertTypeToString<Type>()}; }
83  };
84 
85  template <typename Type, typename... OtherTypes>
86  struct extractTypes<Type, OtherTypes...>
87  {
88  static std::vector<std::vector<std::string>> get()
89  {
90  auto types = extractTypes<OtherTypes...>::get();
91  types.insert(types.begin(), {convertTypeToString<Type>()});
92  return types;
93  }
94 
95  static std::vector<std::string> getRow()
96  {
97  auto types = extractTypes<OtherTypes...>::getRow();
98  types.insert(types.begin(), convertTypeToString<Type>());
99  return types;
100  }
101  };
102 
103  template <typename... T>
104  struct extractTypes<TypeSet<T...>>
105  {
106  static std::vector<std::vector<std::string>> get() { return {extractTypes<T...>::getRow()}; }
107  };
108 
109  template <typename... T, typename... U>
110  struct extractTypes<TypeSet<T...>, U...>
111  {
112  static std::vector<std::vector<std::string>> get()
113  {
114  auto types = {extractTypes<T...>::getRow()};
115  types.insert(types.end(), extractTypes<U...>::get());
116  return types;
117  }
118  };
119 
121 
122  template <typename Func, typename TypeA, typename TypeB, typename... Args>
123  struct isConvertible;
124 
125  template <typename Func, typename... TypesA, typename... TypesB, typename... Args>
126  struct isConvertible<Func, TypeSet<TypesA...>, TypeSet<TypesB...>, Args...>
127  {
128  using type = typename std::conditional<std::is_convertible<Func, std::function<void(Args..., TypesA...)>>::value, TypeSet<TypesA...>, TypeSet<TypesB...>>::type;
129  };
130 
131  template <typename Func, typename... Type, typename... Args>
132  struct isConvertible<Func, TypeSet<>, TypeSet<Type...>, Args...>
133  {
134  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;
135  };
136 
138 
139  template <typename Type>
140  const Type& dereference(const void* obj)
141  {
142  return *static_cast<const Type*>(obj);
143  }
144 
146 
147  template <typename... T>
148  struct connector;
149 
150  template <typename Func, typename... Args>
151  struct connector<TypeSet<>, Func, Args...>
152  {
153  static std::function<void()> connect(Func func, std::size_t, Args... args)
154  {
155  return std::bind(func, args...);
156  }
157  };
158 
159  template <typename Func, typename... Args, typename Type>
160  struct connector<TypeSet<Type>, Func, Args...>
161  {
162  static std::function<void()> connect(Func func, std::size_t argPos, Args... args)
163  {
164  return std::bind(func, args..., std::bind(dereference<Type>, std::cref(data[argPos])));
165  }
166  };
167 
168  template <typename Func, typename... Args, typename TypeA, typename TypeB>
169  struct connector<TypeSet<TypeA, TypeB>, Func, Args...>
170  {
171  static std::function<void()> connect(Func func, std::size_t argPos, Args... args)
172  {
173  return std::bind(func, args...,
174  std::bind(dereference<TypeA>, std::cref(data[argPos])),
175  std::bind(dereference<TypeB>, std::cref(data[argPos+1])));
176  }
177  };
178 
180 
181  // If the argument is not a bind expression then we already know the type
182  template <typename Arg, typename = void>
183  struct bindRemover
184  {
185  static Arg remove(Arg);
186  };
187 
188  // If the argument is a bind expression then extract the return type from the bound function
189  template <typename Arg>
190  struct bindRemover<Arg, typename std::enable_if<std::is_bind_expression<Arg>::value>::type>
191  {
192  static auto remove(Arg) -> decltype(std::declval<Arg>()());
193  };
194 
196 
197  template <typename Func, typename... Args>
198  struct isFunctionConvertible
199  {
200  using type = typename isConvertible<Func, TypeSet<>,
201  typename isConvertible<Func, TypeSet<int>,
202  typename isConvertible<Func, TypeSet<sf::Vector2f>,
203  typename isConvertible<Func, TypeSet<sf::String>,
204  typename isConvertible<Func, TypeSet<std::vector<sf::String>>,
205  typename isConvertible<Func, TypeSet<std::shared_ptr<ChildWindow>>,
206  typename isConvertible<Func, TypeSet<sf::String, sf::String>,
207  TypeSet<void>,
208  Args...>::type,
209  Args...>::type,
210  Args...>::type,
211  Args...>::type,
212  Args...>::type,
213  Args...>::type,
214  Args...>::type;
215  };
216 
218  }
219 
220 
223  // Stores the actual callback functions and checks if their parameters are valid
225  class TGUI_API Signal
226  {
227  public:
228 
229  Signal() {};
230 
231  Signal(std::vector<std::vector<std::string>>&& types);
232 
233  template <typename Func, typename... Args>
234  void connect(unsigned int id, Func func, Args... args)
235  {
236  using type = typename priv::isFunctionConvertible<Func, decltype(priv::bindRemover<Args>::remove(args))...>::type;
237  static_assert(!std::is_same<type, TypeSet<void>>::value, "Parameters passed to the connect function are wrong!");
238 
239  auto argPos = checkCompatibleParameterType<type>();
240  m_functions[id] = priv::connector<type, Func, Args...>::connect(func, argPos, args...);
241  }
242 
243  template <typename Func, typename... Args>
244  void connectEx(unsigned int id, Func func, Args... args)
245  {
246  m_functionsEx[id] = std::bind(func, args..., std::placeholders::_1);
247  }
248 
249  bool disconnect(unsigned int id);
250 
251  void disconnectAll();
252 
253  bool isEmpty() const;
254 
255  void operator()(unsigned int count);
256 
257  template <typename T, typename... Args>
258  void operator()(unsigned int count, const T& value, Args... args)
259  {
260  priv::data[count] = static_cast<const void*>(&value);
261  (*this)(count+1, args...);
262  }
263 
264  protected:
265 
266  template <typename Type>
267  std::size_t checkCompatibleParameterType()
268  {
269  if (std::is_same<Type, TypeSet<>>::value)
270  return 0;
271 
272  auto acceptedType = priv::extractTypes<Type>::get();
273  assert(acceptedType.size() == 1);
274 
275  std::size_t count = 0;
276  for (std::size_t i = 0; i < m_allowedTypes.size(); ++i)
277  {
278  if (acceptedType[0] == m_allowedTypes[i])
279  return count;
280 
281  count += m_allowedTypes[i].size();
282  }
283 
284  throw Exception{"Failed to bind parameter to callback function. Parameter is of wrong type."};
285  }
286 
287  private:
288 
289  std::map<unsigned int, std::function<void()>> m_functions;
290  std::map<unsigned int, std::function<void(const Callback&)>> m_functionsEx;
291 
292  std::vector<std::vector<std::string>> m_allowedTypes;
293 
294  friend class SignalWidgetBase; // Only needed for m_functionsEx
295  };
296 
297 
301  class TGUI_API SignalWidgetBase
302  {
303  public:
304 
308  virtual ~SignalWidgetBase() {};
309 
310 
321  template <typename Func, typename... Args>
322  unsigned int connect(const std::string& signalNames, Func func, Args... args)
323  {
324  auto signalNameList = extractSignalNames(signalNames);
325  if (signalNameList.empty())
326  throw Exception{"connect function called with empty string"};
327 
328  for (const auto& signalName : signalNameList)
329  {
330  if (m_signals.find(toLower(signalName)) != m_signals.end())
331  {
332  try {
333  m_signals[toLower(signalName)].connect(m_lastId, func, args...);
334  m_lastId++;
335  }
336  catch (const Exception& e) {
337  throw Exception{e.what() + (" The parameters are not valid for the '" + signalName + "' signal.")};
338  }
339  }
340  else
341  {
342  if (toLower(signalName) != "all")
343  throw Exception{"Cannot connect to unknown signal '" + signalName + "'."};
344  else
345  {
346  assert(!m_signals.empty());
347 
348  for (auto& signal : m_signals)
349  {
350  try {
351  signal.second.connect(m_lastId, func, args...);
352  m_lastId++;
353  }
354  catch (const Exception& e) {
355  throw Exception{e.what() + (" The parameters are not valid for the '" + signalName + "' signal.")};
356  }
357  }
358  }
359  }
360  }
361 
362  return m_lastId - 1;
363  }
364 
365 
378  template <typename Func, typename... Args>
379  unsigned int connectEx(const std::string& signalName, Func func, Args... args)
380  {
381  auto signalNameList = extractSignalNames(signalName);
382  if (signalNameList.empty())
383  throw Exception{"connect function called with empty string"};
384 
385  for (const auto& name : signalNameList)
386  {
387  if (m_signals.find(toLower(name)) != m_signals.end())
388  {
389  try {
390  m_signals[toLower(name)].connectEx(m_lastId, func, args...);
391  m_lastId++;
392  }
393  catch (const Exception& e) {
394  throw Exception{e.what() + (" since it is not valid for the '" + name + "' signal.")};
395  }
396  }
397  else // Signal name does not exist
398  {
399  if (toLower(name) != "all")
400  throw Exception{"Cannot connect to unknown signal '" + name + "'."};
401  else
402  {
403  assert(!m_signals.empty());
404 
405  for (auto& signal : m_signals)
406  {
407  try {
408  signal.second.connectEx(m_lastId, func, args...);
409  m_lastId++;
410  }
411  catch (const Exception& e) {
412  throw Exception{e.what() + (" since it is not valid for the '" + name + "' signal.")};
413  }
414  }
415  }
416  }
417  }
418 
419  return m_lastId - 1;
420  }
421 
422 
429  void disconnect(unsigned int id);
430 
431 
439  void disconnectAll(const std::string& signalName);
440 
441 
446  void disconnectAll();
447 
448 
450  protected:
451 
453  // @brief Adds a new signal that people can bind
455  template <typename... T>
456  void addSignal(std::string&& name)
457  {
458  m_signals[toLower(name)] = {priv::extractTypes<T...>::get()};
459  }
460 
461 
463  // @brief Checks if some signal handler has been bound to the signal
465  bool isSignalBound(std::string&& name);
466 
467 
469  // @brief Sends a signal to all signal handlers that are connected with this signal
471  template <typename... Args>
472  void sendSignal(std::string&& name, Args... args)
473  {
474  assert(m_signals.find(toLower(name)) != m_signals.end());
475  auto& signal = m_signals[toLower(name)];
476 
477  if (signal.m_functionsEx.empty())
478  {
479  if (!signal.isEmpty())
480  signal(0, args...);
481  }
482  else // Legacy functions are used
483  {
484  // Copy signal to avoid problems with lifetime when signal handler destroys this object
485  Signal signalCopy = signal;
486 
487  m_callback.trigger = name;
488  for (const auto& function : signalCopy.m_functionsEx)
489  function.second(m_callback);
490 
491  if (!signalCopy.isEmpty())
492  signalCopy(0, args...);
493  }
494  }
495 
496 
498  protected:
499 
500  std::vector<std::string> extractSignalNames(std::string input);
501 
503  protected:
504 
505  std::map<std::string, Signal> m_signals;
506 
507  static unsigned int m_lastId;
508 
509  Callback m_callback;
510 
512  };
513 
514 
516 }
517 
519 
520 #endif // TGUI_SIGNAL_HPP
Namespace that contains all TGUI functions and classes.
Definition: Animation.hpp:33
Definition: Callback.hpp:43
Definition: Exception.hpp:45
Base class for widgets to enable signal handling.
Definition: Signal.hpp:301
unsigned int connect(const std::string &signalNames, Func func, Args... args)
Connects a signal handler function to one or more signals.
Definition: Signal.hpp:322
Definition: Signal.hpp:225
unsigned int connectEx(const std::string &signalName, Func func, Args... args)
Connects a signal handler function to one or more signals.
Definition: Signal.hpp:379
virtual ~SignalWidgetBase()
Virtual destructor.
Definition: Signal.hpp:308