The Battle for Wesnoth  1.15.0-dev
dispatcher_private.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
18 
19 #include "gui/widgets/widget.hpp"
20 
21 #include <SDL_events.h>
22 
23 #include <boost/range/adaptor/reversed.hpp>
24 
25 namespace gui2
26 {
27 
28 namespace event
29 {
30 
32 {
33 /**
34  * Helper macro to implement the various event_signal functions.
35  *
36  * Implements two helper functions as documented in the macro.
37  *
38  * @param SET The set in which the event type needs to be
39  * eg the @ref gui2::event::set_event or a
40  * similar set defined in that header.
41  * @param FUNCTION The function signature to validate the
42  * implementation function SFINAE against eg the
43  * @ref gui2::event::signal_function or another
44  * one in that header.
45  * @param QUEUE The queue in which the @p event is slotted.
46  */
47 #define IMPLEMENT_EVENT_SIGNAL(SET, FUNCTION, QUEUE) \
48  /** \
49  * Returns the signal structure for a FUNCTION. \
50  * \
51  * There are several functions that only overload the return value, in \
52  * order to do so they use SFINAE. \
53  * \
54  * @tparam F signal_function. \
55  * @param dispatcher The dispatcher whose signal queue is used. \
56  * @param event The event to get the signal for. \
57  * \
58  * @returns The signal of the type \
59  * dispatcher::signal_type<FUNCTION> \
60  */ \
61  template<typename F> \
62  static std::enable_if_t<std::is_same<F, FUNCTION>::value, dispatcher::signal_type<FUNCTION>>& \
63  event_signal(dispatcher& dispatcher, const ui_event event) \
64  { \
65  return dispatcher.QUEUE.queue[event]; \
66  } \
67 
68  IMPLEMENT_EVENT_SIGNAL(set_event, signal_function, signal_queue_)
69 
70 /**
71  * Small helper macro to wrap @ref IMPLEMENT_EVENT_SIGNAL.
72  *
73  * Since the parameters to @ref IMPLEMENT_EVENT_SIGNAL use the same parameters
74  * with a slight difference per type this macro wraps the function by its type.
75  *
76  * @param TYPE The type to wrap for @ref
77  * IMPLEMENT_EVENT_SIGNAL.
78  */
79 #define IMPLEMENT_EVENT_SIGNAL_WRAPPER(TYPE) \
80  IMPLEMENT_EVENT_SIGNAL(set_event_##TYPE, \
81  signal_##TYPE##_function, \
82  signal_##TYPE##_queue_)
83 
87  IMPLEMENT_EVENT_SIGNAL_WRAPPER(notification)
91 
92 #undef IMPLEMENT_EVENT_SIGNAL_WRAPPER
93 #undef IMPLEMENT_EVENT_SIGNAL
94 
95 #define IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(TYPE) \
96  else if(is_##TYPE##_event(event)) { \
97  return queue_check(dispatcher.signal_##TYPE##_queue_); \
98  }
99 
100  /**
101  * A helper to test whether dispatcher has an handler for a certain event.
102  *
103  * @param dispatcher The dispatcher whose signal queue is used.
104  * @param queue_type The type of event to look for.
105  * @param event The event to get the signal for.
106  *
107  * @returns Whether or not the handler is found.
108  */
110  {
111  const auto queue_check = [&](auto& queue_set) {
112  return !queue_set.queue[event].empty(queue_type);
113  };
114 
115  if(is_general_event(event)) {
116  return queue_check(dispatcher.signal_queue_);
117  }
118 
126 
127  return false;
128  }
129 
130 #undef IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK
131 };
132 
133 namespace implementation
134 {
135 
136 /*
137  * Small sample to illustrate the effects of the various build_event_chain
138  * functions. Assume the widgets are in an window with the following widgets:
139  *
140  * -----------------------
141  * | dispatcher |
142  * | ------------------- |
143  * | | container 1 | |
144  * | | --------------- | |
145  * | | | container 2 | | |
146  * | | | ----------- | | |
147  * | | | | widget | | | |
148  * | | | ----------- | | |
149  * | | --------------- | |
150  * | ------------------- |
151  * -----------------------
152  *
153  * Note that the firing routine fires the events from:
154  * - pre child for chain.end() - > chain.begin()
155  * - child for widget
156  * - post child for chain.begin() -> chain.end()
157  */
158 
159 /**
160  * Build the event chain.
161  *
162  * The event chain is a chain of events starting from the first parent of the
163  * widget until (and including) the wanted parent. For all these widgets it
164  * will be tested whether they have either a pre or post handler for the event.
165  * This ways there will be list of widgets to try to send the events to.
166  * If there's no line from widget to parent the result is undefined.
167  * (If widget == dispatcher the result will always be empty.)
168  *
169  * @pre dispatcher != nullptr
170  * @pre widget != nullptr
171  *
172  * @param event The event to test.
173  * @param dispatcher The final widget to test, this is also the
174  * dispatcher the sends the event.
175  * @param w The widget should parent(s) to check.
176  *
177  * @returns The list of widgets with a handler.
178  * The order will be (assuming all have a
179  * handler):
180  * * container 2
181  * * container 1
182  * * dispatcher
183  */
184 template<typename T>
185 inline std::vector<std::pair<widget*, ui_event>>
187 {
188  assert(dispatcher);
189  assert(w);
190 
191  std::vector<std::pair<widget*, ui_event>> result;
192 
193  while(true) {
195  result.emplace_back(w, event);
196  }
197 
198  if(w == dispatcher) {
199  break;
200  }
201 
202  w = w->parent();
203  assert(w);
204  }
205 
206  return result;
207 }
208 
209 /**
210  * Build the event chain for signal_notification_function.
211  *
212  * The notification is only send to the receiver it returns an empty chain.
213  * Since the pre and post queues are unused, it validates whether they are
214  * empty (using asserts).
215  *
216  * @returns An empty vector.
217  */
218 template<>
219 inline std::vector<std::pair<widget*, ui_event>>
221 {
222  assert(dispatcher);
223  assert(w);
224 
225  assert(!w->has_event(event, dispatcher::event_queue_type(dispatcher::pre | dispatcher::post)));
226 
227  return std::vector<std::pair<widget*, ui_event>>();
228 }
229 
230 #ifdef _MSC_VER
231 #pragma warning(push)
232 #pragma warning(disable : 4706)
233 #endif
234 /**
235  * Build the event chain for signal_message_function.
236  *
237  * This function expects that the widget sending it is also the receiver. This
238  * assumption might change, but is valid for now. The function doesn't build an
239  * event chain from @p dispatcher to @p widget but from @p widget to its
240  * toplevel item (the first one without a parent) which we call @p window.
241  *
242  * @pre dispatcher == widget
243  *
244  * @returns The list of widgets with a handler.
245  * The order will be (assuming all have a
246  * handler):
247  * * window
248  * * container 1
249  * * container 2
250  */
251 template<>
252 inline std::vector<std::pair<widget*, ui_event>>
254 {
255  assert(dispatcher);
256  assert(w);
257  assert(w == dispatcher);
258 
259  std::vector<std::pair<widget*, ui_event>> result;
260 
261  /* We only should add the parents of the widget to the chain. */
262  while((w = w->parent())) {
263  assert(w);
264 
266  result.emplace(result.begin(), w, event);
267  }
268  }
269 
270  return result;
271 }
272 #ifdef _MSC_VER
273 #pragma warning(pop)
274 #endif
275 
276 /**
277  * Helper function for fire_event.
278  *
279  * This is called with the same parameters as fire_event except for the
280  * event_chain, which contains the widgets with the events to call for them.
281  */
282 template<typename T, typename... F>
283 inline bool fire_event(const ui_event event,
284  std::vector<std::pair<widget*, ui_event>>& event_chain,
285  widget* dispatcher,
286  widget* w,
287  F&&... params)
288 {
289  bool handled = false;
290  bool halt = false;
291 
292  /***** ***** ***** Pre ***** ***** *****/
293  for(auto& ritor_widget : boost::adaptors::reverse(event_chain)) {
294  auto& signal = dispatcher_implementation::event_signal<T>(*ritor_widget.first, ritor_widget.second);
295 
296  for(auto& pre_func : signal.pre_child) {
297  pre_func(*dispatcher, ritor_widget.second, handled, halt, std::forward<F>(params)...);
298 
299  if(halt) {
300  assert(handled);
301  break;
302  }
303  }
304 
305  if(handled) {
306  return true;
307  }
308  }
309 
310  /***** ***** ***** Child ***** ***** *****/
311  if(w->has_event(event, dispatcher::child)) {
312  auto& signal = dispatcher_implementation::event_signal<T>(*w, event);
313 
314  for(auto& func : signal.child) {
315  func(*dispatcher, event, handled, halt, std::forward<F>(params)...);
316 
317  if(halt) {
318  assert(handled);
319  break;
320  }
321  }
322 
323  if(handled) {
324  return true;
325  }
326  }
327 
328  /***** ***** ***** Post ***** ***** *****/
329  for(auto& itor_widget : event_chain) {
330  auto& signal = dispatcher_implementation::event_signal<T>(*itor_widget.first, itor_widget.second);
331 
332  for(auto& post_func : signal.post_child) {
333  post_func(*dispatcher, itor_widget.second, handled, halt, std::forward<F>(params)...);
334 
335  if(halt) {
336  assert(handled);
337  break;
338  }
339  }
340 
341  if(handled) {
342  return true;
343  }
344  }
345 
346  /**** ***** ***** Unhandled ***** ***** *****/
347  assert(handled == false);
348  return false;
349 }
350 
351 } // namespace implementation
352 
353 /**
354  * Fires an event.
355  *
356  * A helper to allow the common event firing code to be shared between the
357  * different signal function types.
358  *
359  * @pre d != nullptr
360  * @pre w != nullptr
361  *
362  * @tparam T The signal type of the event to handle.
363  * @tparam F The parameter pack type.
364  *
365  *
366  * @param event The event to fire.
367  * @param d The dispatcher that handles the event.
368  * @param w The widget that should receive the event.
369  * @param params Zero or more additional arguments to pass
370  * to the signal function when it's executed.
371  *
372  * @returns Whether or not the event was handled.
373  */
374 template<typename T, typename... F>
375 inline bool
376 fire_event(const ui_event event, dispatcher* d, widget* w, F&&... params)
377 {
378  assert(d);
379  assert(w);
380 
381  widget* dispatcher_w = dynamic_cast<widget*>(d);
382 
383  std::vector<std::pair<widget*, ui_event>> event_chain =
384  implementation::build_event_chain<T>(event, dispatcher_w, w);
385 
386  return implementation::fire_event<T>(event, event_chain, dispatcher_w, w, std::forward<F>(params)...);
387 }
388 
389 template<ui_event click,
390  ui_event double_click,
391  bool(event_executor::*wants_double_click)() const,
392  typename T,
393  typename... F>
394 inline bool
395 fire_event_double_click(dispatcher* dsp, widget* wgt, F&&... params)
396 {
397  assert(dsp);
398  assert(wgt);
399 
400  std::vector<std::pair<widget*, ui_event>> event_chain;
401  widget* w = wgt;
402  widget* d = dynamic_cast<widget*>(dsp);
403 
404  while(w != d) {
405  w = w->parent();
406  assert(w);
407 
408  if((w->*wants_double_click)()) {
410  event_chain.emplace_back(w, double_click);
411  }
412  } else {
414  event_chain.emplace_back(w, click);
415  }
416  }
417  }
418 
419  if((wgt->*wants_double_click)()) {
420  return implementation::fire_event<T>(
421  double_click, event_chain, d, wgt, std::forward<F>(params)...);
422  } else {
423  return implementation::fire_event<T>(
424  click, event_chain, d, wgt, std::forward<F>(params)...);
425  }
426 }
427 
428 } // namespace event
429 
430 } // namespace gui2
#define IMPLEMENT_EVENT_SIGNAL_WRAPPER(TYPE)
Small helper macro to wrap IMPLEMENT_EVENT_SIGNAL.
Base class for event handling.
Definition: dispatcher.hpp:284
bool fire_event_double_click(dispatcher *dsp, widget *wgt, F &&... params)
#define IMPLEMENT_EVENT_SIGNAL(SET, FUNCTION, QUEUE)
Helper macro to implement the various event_signal functions.
Base class for all widgets.
Definition: widget.hpp:48
std::vector< std::pair< widget *, ui_event > > build_event_chain(const ui_event event, widget *dispatcher, widget *w)
Build the event chain.
static bool has_handler(dispatcher &dispatcher, const dispatcher::event_queue_type queue_type, ui_event event)
A helper to test whether dispatcher has an handler for a certain event.
constexpr bool is_general_event(const ui_event event)
Helper for catching use error of dispatcher::connect_signal.
Definition: dispatcher.hpp:46
#define d
dispatcher_callback_func<> signal_function
Callback function signature.
Definition: dispatcher.hpp:187
bool has_event(const ui_event event, const event_queue_type event_type)
Definition: dispatcher.cpp:55
Generic file dialog.
Definition: field-fwd.hpp:22
The message callbacks hold a reference to a message.
Definition: message.hpp:46
bool fire_event(const ui_event event, dispatcher *d, widget *w, F &&... params)
Fires an event.
#define IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(TYPE)
widget * parent()
Definition: widget.cpp:156
signal_queue< signal_function > signal_queue_
Signal queue for callbacks in set_event.
Definition: dispatcher.hpp:954
Event execution calls.
std::vector< std::pair< widget *, ui_event > > build_event_chain< signal_notification_function >(const ui_event event, widget *dispatcher, widget *w)
Build the event chain for signal_notification_function.
bool click(int mousex, int mousey)
Definition: tooltips.cpp:214
std::vector< std::pair< widget *, ui_event > > build_event_chain< signal_message_function >(const ui_event event, widget *dispatcher, widget *w)
Build the event chain for signal_message_function.
int w
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
ui_event
The event send to the dispatcher.
Definition: handler.hpp:47