The Battle for Wesnoth  1.17.0-dev
dispatcher_private.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #pragma once
17 
19 
20 #include "gui/widgets/widget.hpp"
21 #include "utils/ranges.hpp"
22 
23 #include <SDL2/SDL_events.h>
24 
25 #include <cassert>
26 
27 namespace gui2
28 {
29 
30 namespace event
31 {
32 
34 {
35 /**
36  * Helper macro to implement the various event_signal functions.
37  *
38  * Implements two helper functions as documented in the macro.
39  *
40  * @param SET The set in which the event type needs to be.
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_v<F, FUNCTION>, 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 : utils::reversed_view(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:307
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:28
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:47
#define d
dispatcher_callback_func<> signal_function
Callback function signature.
Definition: dispatcher.hpp:198
bool has_event(const ui_event event, const event_queue_type event_type)
Definition: dispatcher.cpp:58
Generic file dialog.
Definition: field-fwd.hpp:23
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:160
signal_queue< signal_function > signal_queue_
Signal queue for callbacks in set_event.
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:212
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:48