The Battle for Wesnoth  1.15.6+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 <SDL2/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 
86  IMPLEMENT_EVENT_SIGNAL_WRAPPER(touch_motion)
87  IMPLEMENT_EVENT_SIGNAL_WRAPPER(touch_gesture)
88  IMPLEMENT_EVENT_SIGNAL_WRAPPER(notification)
92 
93 #undef IMPLEMENT_EVENT_SIGNAL_WRAPPER
94 #undef IMPLEMENT_EVENT_SIGNAL
95 
96 #define IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK(TYPE) \
97  else if(is_##TYPE##_event(event)) { \
98  return queue_check(dispatcher.signal_##TYPE##_queue_); \
99  }
100 
101  /**
102  * A helper to test whether dispatcher has an handler for a certain event.
103  *
104  * @param dispatcher The dispatcher whose signal queue is used.
105  * @param queue_type The type of event to look for.
106  * @param event The event to get the signal for.
107  *
108  * @returns Whether or not the handler is found.
109  */
111  {
112  const auto queue_check = [&](auto& queue_set) {
113  return !queue_set.queue[event].empty(queue_type);
114  };
115 
116  if(is_general_event(event)) {
117  return queue_check(dispatcher.signal_queue_);
118  }
119 
128 
129  return false;
130  }
131 
132 #undef IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK
133 };
134 
135 namespace implementation
136 {
137 
138 /*
139  * Small sample to illustrate the effects of the various build_event_chain
140  * functions. Assume the widgets are in an window with the following widgets:
141  *
142  * -----------------------
143  * | dispatcher |
144  * | ------------------- |
145  * | | container 1 | |
146  * | | --------------- | |
147  * | | | container 2 | | |
148  * | | | ----------- | | |
149  * | | | | widget | | | |
150  * | | | ----------- | | |
151  * | | --------------- | |
152  * | ------------------- |
153  * -----------------------
154  *
155  * Note that the firing routine fires the events from:
156  * - pre child for chain.end() - > chain.begin()
157  * - child for widget
158  * - post child for chain.begin() -> chain.end()
159  */
160 
161 /**
162  * Build the event chain.
163  *
164  * The event chain is a chain of events starting from the first parent of the
165  * widget until (and including) the wanted parent. For all these widgets it
166  * will be tested whether they have either a pre or post handler for the event.
167  * This ways there will be list of widgets to try to send the events to.
168  * If there's no line from widget to parent the result is undefined.
169  * (If widget == dispatcher the result will always be empty.)
170  *
171  * @pre dispatcher != nullptr
172  * @pre widget != nullptr
173  *
174  * @param event The event to test.
175  * @param dispatcher The final widget to test, this is also the
176  * dispatcher the sends the event.
177  * @param w The widget should parent(s) to check.
178  *
179  * @returns The list of widgets with a handler.
180  * The order will be (assuming all have a
181  * handler):
182  * * container 2
183  * * container 1
184  * * dispatcher
185  */
186 template<typename T>
187 inline std::vector<std::pair<widget*, ui_event>>
189 {
190  assert(dispatcher);
191  assert(w);
192 
193  std::vector<std::pair<widget*, ui_event>> result;
194 
195  while(true) {
197  result.emplace_back(w, event);
198  }
199 
200  if(w == dispatcher) {
201  break;
202  }
203 
204  w = w->parent();
205  assert(w);
206  }
207 
208  return result;
209 }
210 
211 /**
212  * Build the event chain for signal_notification_function.
213  *
214  * The notification is only send to the receiver it returns an empty chain.
215  * Since the pre and post queues are unused, it validates whether they are
216  * empty (using asserts).
217  *
218  * @returns An empty vector.
219  */
220 template<>
221 inline std::vector<std::pair<widget*, ui_event>>
223 {
224  assert(dispatcher);
225  assert(w);
226 
227  assert(!w->has_event(event, dispatcher::event_queue_type(dispatcher::pre | dispatcher::post)));
228 
229  return std::vector<std::pair<widget*, ui_event>>();
230 }
231 
232 #ifdef _MSC_VER
233 #pragma warning(push)
234 #pragma warning(disable : 4706)
235 #endif
236 /**
237  * Build the event chain for signal_message_function.
238  *
239  * This function expects that the widget sending it is also the receiver. This
240  * assumption might change, but is valid for now. The function doesn't build an
241  * event chain from @p dispatcher to @p widget but from @p widget to its
242  * toplevel item (the first one without a parent) which we call @p window.
243  *
244  * @pre dispatcher == widget
245  *
246  * @returns The list of widgets with a handler.
247  * The order will be (assuming all have a
248  * handler):
249  * * window
250  * * container 1
251  * * container 2
252  */
253 template<>
254 inline std::vector<std::pair<widget*, ui_event>>
256 {
257  assert(dispatcher);
258  assert(w);
259  assert(w == dispatcher);
260 
261  std::vector<std::pair<widget*, ui_event>> result;
262 
263  /* We only should add the parents of the widget to the chain. */
264  while((w = w->parent())) {
265  assert(w);
266 
268  result.emplace(result.begin(), w, event);
269  }
270  }
271 
272  return result;
273 }
274 #ifdef _MSC_VER
275 #pragma warning(pop)
276 #endif
277 
278 /**
279  * Helper function for fire_event.
280  *
281  * This is called with the same parameters as fire_event except for the
282  * event_chain, which contains the widgets with the events to call for them.
283  */
284 template<typename T, typename... F>
285 inline bool fire_event(const ui_event event,
286  std::vector<std::pair<widget*, ui_event>>& event_chain,
287  widget* dispatcher,
288  widget* w,
289  F&&... params)
290 {
291  bool handled = false;
292  bool halt = false;
293 
294  /***** ***** ***** Pre ***** ***** *****/
295  for(auto& ritor_widget : boost::adaptors::reverse(event_chain)) {
296  auto& signal = dispatcher_implementation::event_signal<T>(*ritor_widget.first, ritor_widget.second);
297 
298  for(auto& pre_func : signal.pre_child) {
299  pre_func(*dispatcher, ritor_widget.second, handled, halt, std::forward<F>(params)...);
300 
301  if(halt) {
302  assert(handled);
303  break;
304  }
305  }
306 
307  if(handled) {
308  return true;
309  }
310  }
311 
312  /***** ***** ***** Child ***** ***** *****/
313  if(w->has_event(event, dispatcher::child)) {
314  auto& signal = dispatcher_implementation::event_signal<T>(*w, event);
315 
316  for(auto& func : signal.child) {
317  func(*dispatcher, event, handled, halt, std::forward<F>(params)...);
318 
319  if(halt) {
320  assert(handled);
321  break;
322  }
323  }
324 
325  if(handled) {
326  return true;
327  }
328  }
329 
330  /***** ***** ***** Post ***** ***** *****/
331  for(auto& itor_widget : event_chain) {
332  auto& signal = dispatcher_implementation::event_signal<T>(*itor_widget.first, itor_widget.second);
333 
334  for(auto& post_func : signal.post_child) {
335  post_func(*dispatcher, itor_widget.second, handled, halt, std::forward<F>(params)...);
336 
337  if(halt) {
338  assert(handled);
339  break;
340  }
341  }
342 
343  if(handled) {
344  return true;
345  }
346  }
347 
348  /**** ***** ***** Unhandled ***** ***** *****/
349  assert(handled == false);
350  return false;
351 }
352 
353 } // namespace implementation
354 
355 /**
356  * Fires an event.
357  *
358  * A helper to allow the common event firing code to be shared between the
359  * different signal function types.
360  *
361  * @pre d != nullptr
362  * @pre w != nullptr
363  *
364  * @tparam T The signal type of the event to handle.
365  * @tparam F The parameter pack type.
366  *
367  *
368  * @param event The event to fire.
369  * @param d The dispatcher that handles the event.
370  * @param w The widget that should receive the event.
371  * @param params Zero or more additional arguments to pass
372  * to the signal function when it's executed.
373  *
374  * @returns Whether or not the event was handled.
375  */
376 template<typename T, typename... F>
377 inline bool
378 fire_event(const ui_event event, dispatcher* d, widget* w, F&&... params)
379 {
380  assert(d);
381  assert(w);
382 
383  widget* dispatcher_w = dynamic_cast<widget*>(d);
384 
385  std::vector<std::pair<widget*, ui_event>> event_chain =
386  implementation::build_event_chain<T>(event, dispatcher_w, w);
387 
388  return implementation::fire_event<T>(event, event_chain, dispatcher_w, w, std::forward<F>(params)...);
389 }
390 
391 template<ui_event click,
392  ui_event double_click,
393  bool(event_executor::*wants_double_click)() const,
394  typename T,
395  typename... F>
396 inline bool
397 fire_event_double_click(dispatcher* dsp, widget* wgt, F&&... params)
398 {
399  assert(dsp);
400  assert(wgt);
401 
402  std::vector<std::pair<widget*, ui_event>> event_chain;
403  widget* w = wgt;
404  widget* d = dynamic_cast<widget*>(dsp);
405 
406  while(w != d) {
407  w = w->parent();
408  assert(w);
409 
410  if((w->*wants_double_click)()) {
412  event_chain.emplace_back(w, double_click);
413  }
414  } else {
416  event_chain.emplace_back(w, click);
417  }
418  }
419  }
420 
421  if((wgt->*wants_double_click)()) {
422  return implementation::fire_event<T>(
423  double_click, event_chain, d, wgt, std::forward<F>(params)...);
424  } else {
425  return implementation::fire_event<T>(
426  click, event_chain, d, wgt, std::forward<F>(params)...);
427  }
428 }
429 
430 } // namespace event
431 
432 } // namespace gui2
#define IMPLEMENT_EVENT_SIGNAL_WRAPPER(TYPE)
Small helper macro to wrap IMPLEMENT_EVENT_SIGNAL.
Base class for event handling.
Definition: dispatcher.hpp:306
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:49
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:197
bool has_event(const ui_event event, const event_queue_type event_type)
Definition: dispatcher.cpp:57
Generic file dialog.
Definition: field-fwd.hpp:22
The message callbacks hold a reference to a message.
Definition: message.hpp:45
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:159
signal_queue< signal_function > signal_queue_
Signal queue for callbacks in set_event.
Definition: dispatcher.hpp:999
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:211
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