The Battle for Wesnoth  1.15.12+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 #include "utils/ranges.hpp"
21 
22 #include <SDL2/SDL_events.h>
23 
24 #include <cassert>
25 
26 namespace gui2
27 {
28 
29 namespace event
30 {
31 
33 {
34 /**
35  * Helper macro to implement the various event_signal functions.
36  *
37  * Implements two helper functions as documented in the macro.
38  *
39  * @param SET The set in which the event type needs to be.
40  * @param FUNCTION The function signature to validate the
41  * implementation function SFINAE against eg the
42  * @ref gui2::event::signal_function or another
43  * one in that header.
44  * @param QUEUE The queue in which the @p event is slotted.
45  */
46 #define IMPLEMENT_EVENT_SIGNAL(SET, FUNCTION, QUEUE) \
47  /** \
48  * Returns the signal structure for a FUNCTION. \
49  * \
50  * There are several functions that only overload the return value, in \
51  * order to do so they use SFINAE. \
52  * \
53  * @tparam F signal_function. \
54  * @param dispatcher The dispatcher whose signal queue is used. \
55  * @param event The event to get the signal for. \
56  * \
57  * @returns The signal of the type \
58  * dispatcher::signal_type<FUNCTION> \
59  */ \
60  template<typename F> \
61  static std::enable_if_t<std::is_same_v<F, FUNCTION>, dispatcher::signal_type<FUNCTION>>& \
62  event_signal(dispatcher& dispatcher, const ui_event event) \
63  { \
64  return dispatcher.QUEUE.queue[event]; \
65  } \
66 
67  IMPLEMENT_EVENT_SIGNAL(set_event, signal_function, signal_queue_)
68 
69 /**
70  * Small helper macro to wrap @ref IMPLEMENT_EVENT_SIGNAL.
71  *
72  * Since the parameters to @ref IMPLEMENT_EVENT_SIGNAL use the same parameters
73  * with a slight difference per type this macro wraps the function by its type.
74  *
75  * @param TYPE The type to wrap for @ref
76  * IMPLEMENT_EVENT_SIGNAL.
77  */
78 #define IMPLEMENT_EVENT_SIGNAL_WRAPPER(TYPE) \
79  IMPLEMENT_EVENT_SIGNAL(set_event_##TYPE, \
80  signal_##TYPE##_function, \
81  signal_##TYPE##_queue_)
82 
85  IMPLEMENT_EVENT_SIGNAL_WRAPPER(touch_motion)
86  IMPLEMENT_EVENT_SIGNAL_WRAPPER(touch_gesture)
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 
127 
128  return false;
129  }
130 
131 #undef IMPLEMENT_RUNTIME_EVENT_SIGNAL_CHECK
132 };
133 
134 namespace implementation
135 {
136 
137 /*
138  * Small sample to illustrate the effects of the various build_event_chain
139  * functions. Assume the widgets are in an window with the following widgets:
140  *
141  * -----------------------
142  * | dispatcher |
143  * | ------------------- |
144  * | | container 1 | |
145  * | | --------------- | |
146  * | | | container 2 | | |
147  * | | | ----------- | | |
148  * | | | | widget | | | |
149  * | | | ----------- | | |
150  * | | --------------- | |
151  * | ------------------- |
152  * -----------------------
153  *
154  * Note that the firing routine fires the events from:
155  * - pre child for chain.end() - > chain.begin()
156  * - child for widget
157  * - post child for chain.begin() -> chain.end()
158  */
159 
160 /**
161  * Build the event chain.
162  *
163  * The event chain is a chain of events starting from the first parent of the
164  * widget until (and including) the wanted parent. For all these widgets it
165  * will be tested whether they have either a pre or post handler for the event.
166  * This ways there will be list of widgets to try to send the events to.
167  * If there's no line from widget to parent the result is undefined.
168  * (If widget == dispatcher the result will always be empty.)
169  *
170  * @pre dispatcher != nullptr
171  * @pre widget != nullptr
172  *
173  * @param event The event to test.
174  * @param dispatcher The final widget to test, this is also the
175  * dispatcher the sends the event.
176  * @param w The widget should parent(s) to check.
177  *
178  * @returns The list of widgets with a handler.
179  * The order will be (assuming all have a
180  * handler):
181  * * container 2
182  * * container 1
183  * * dispatcher
184  */
185 template<typename T>
186 inline std::vector<std::pair<widget*, ui_event>>
188 {
189  assert(dispatcher);
190  assert(w);
191 
192  std::vector<std::pair<widget*, ui_event>> result;
193 
194  while(true) {
196  result.emplace_back(w, event);
197  }
198 
199  if(w == dispatcher) {
200  break;
201  }
202 
203  w = w->parent();
204  assert(w);
205  }
206 
207  return result;
208 }
209 
210 /**
211  * Build the event chain for signal_notification_function.
212  *
213  * The notification is only send to the receiver it returns an empty chain.
214  * Since the pre and post queues are unused, it validates whether they are
215  * empty (using asserts).
216  *
217  * @returns An empty vector.
218  */
219 template<>
220 inline std::vector<std::pair<widget*, ui_event>>
222 {
223  assert(dispatcher);
224  assert(w);
225 
226  assert(!w->has_event(event, dispatcher::event_queue_type(dispatcher::pre | dispatcher::post)));
227 
228  return std::vector<std::pair<widget*, ui_event>>();
229 }
230 
231 #ifdef _MSC_VER
232 #pragma warning(push)
233 #pragma warning(disable : 4706)
234 #endif
235 /**
236  * Build the event chain for signal_message_function.
237  *
238  * This function expects that the widget sending it is also the receiver. This
239  * assumption might change, but is valid for now. The function doesn't build an
240  * event chain from @p dispatcher to @p widget but from @p widget to its
241  * toplevel item (the first one without a parent) which we call @p window.
242  *
243  * @pre dispatcher == widget
244  *
245  * @returns The list of widgets with a handler.
246  * The order will be (assuming all have a
247  * handler):
248  * * window
249  * * container 1
250  * * container 2
251  */
252 template<>
253 inline std::vector<std::pair<widget*, ui_event>>
255 {
256  assert(dispatcher);
257  assert(w);
258  assert(w == dispatcher);
259 
260  std::vector<std::pair<widget*, ui_event>> result;
261 
262  /* We only should add the parents of the widget to the chain. */
263  while((w = w->parent())) {
264  assert(w);
265 
267  result.emplace(result.begin(), w, event);
268  }
269  }
270 
271  return result;
272 }
273 #ifdef _MSC_VER
274 #pragma warning(pop)
275 #endif
276 
277 /**
278  * Helper function for fire_event.
279  *
280  * This is called with the same parameters as fire_event except for the
281  * event_chain, which contains the widgets with the events to call for them.
282  */
283 template<typename T, typename... F>
284 inline bool fire_event(const ui_event event,
285  std::vector<std::pair<widget*, ui_event>>& event_chain,
286  widget* dispatcher,
287  widget* w,
288  F&&... params)
289 {
290  bool handled = false;
291  bool halt = false;
292 
293  /***** ***** ***** Pre ***** ***** *****/
294  for(auto& ritor_widget : utils::reversed_view(event_chain)) {
295  auto& signal = dispatcher_implementation::event_signal<T>(*ritor_widget.first, ritor_widget.second);
296 
297  for(auto& pre_func : signal.pre_child) {
298  pre_func(*dispatcher, ritor_widget.second, handled, halt, std::forward<F>(params)...);
299 
300  if(halt) {
301  assert(handled);
302  break;
303  }
304  }
305 
306  if(handled) {
307  return true;
308  }
309  }
310 
311  /***** ***** ***** Child ***** ***** *****/
312  if(w->has_event(event, dispatcher::child)) {
313  auto& signal = dispatcher_implementation::event_signal<T>(*w, event);
314 
315  for(auto& func : signal.child) {
316  func(*dispatcher, event, handled, halt, std::forward<F>(params)...);
317 
318  if(halt) {
319  assert(handled);
320  break;
321  }
322  }
323 
324  if(handled) {
325  return true;
326  }
327  }
328 
329  /***** ***** ***** Post ***** ***** *****/
330  for(auto& itor_widget : event_chain) {
331  auto& signal = dispatcher_implementation::event_signal<T>(*itor_widget.first, itor_widget.second);
332 
333  for(auto& post_func : signal.post_child) {
334  post_func(*dispatcher, itor_widget.second, handled, halt, std::forward<F>(params)...);
335 
336  if(halt) {
337  assert(handled);
338  break;
339  }
340  }
341 
342  if(handled) {
343  return true;
344  }
345  }
346 
347  /**** ***** ***** Unhandled ***** ***** *****/
348  assert(handled == false);
349  return false;
350 }
351 
352 } // namespace implementation
353 
354 /**
355  * Fires an event.
356  *
357  * A helper to allow the common event firing code to be shared between the
358  * different signal function types.
359  *
360  * @pre d != nullptr
361  * @pre w != nullptr
362  *
363  * @tparam T The signal type of the event to handle.
364  * @tparam F The parameter pack type.
365  *
366  *
367  * @param event The event to fire.
368  * @param d The dispatcher that handles the event.
369  * @param w The widget that should receive the event.
370  * @param params Zero or more additional arguments to pass
371  * to the signal function when it's executed.
372  *
373  * @returns Whether or not the event was handled.
374  */
375 template<typename T, typename... F>
376 inline bool
377 fire_event(const ui_event event, dispatcher* d, widget* w, F&&... params)
378 {
379  assert(d);
380  assert(w);
381 
382  widget* dispatcher_w = dynamic_cast<widget*>(d);
383 
384  std::vector<std::pair<widget*, ui_event>> event_chain =
385  implementation::build_event_chain<T>(event, dispatcher_w, w);
386 
387  return implementation::fire_event<T>(event, event_chain, dispatcher_w, w, std::forward<F>(params)...);
388 }
389 
390 template<ui_event click,
391  ui_event double_click,
392  bool(event_executor::*wants_double_click)() const,
393  typename T,
394  typename... F>
395 inline bool
396 fire_event_double_click(dispatcher* dsp, widget* wgt, F&&... params)
397 {
398  assert(dsp);
399  assert(wgt);
400 
401  std::vector<std::pair<widget*, ui_event>> event_chain;
402  widget* w = wgt;
403  widget* d = dynamic_cast<widget*>(dsp);
404 
405  while(w != d) {
406  w = w->parent();
407  assert(w);
408 
409  if((w->*wants_double_click)()) {
411  event_chain.emplace_back(w, double_click);
412  }
413  } else {
415  event_chain.emplace_back(w, click);
416  }
417  }
418  }
419 
420  if((wgt->*wants_double_click)()) {
421  return implementation::fire_event<T>(
422  double_click, event_chain, d, wgt, std::forward<F>(params)...);
423  } else {
424  return implementation::fire_event<T>(
425  click, event_chain, d, wgt, std::forward<F>(params)...);
426  }
427 }
428 
429 } // namespace event
430 
431 } // 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.
auto reversed_view(T &container)
Definition: ranges.hpp:27
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
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