The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
dispatcher_private.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
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
21#include "utils/ranges.hpp"
22
23#include <SDL3/SDL_events.h>
24
25#include <cassert>
26
27namespace gui2::event
28{
30{
31 /**
32 * Returns the appropriate signal queue for an event by category.
33 *
34 * @tparam C For example, general.
35 * @param dispatcher The dispatcher whose signal queue is used.
36 * @param event The event to get the signal for.
37 *
38 * @returns The signal of the type dispatcher::signal_type<T>
39 */
40 template<event_category C>
41 static auto& event_signal(dispatcher& dispatcher, const ui_event event)
42 {
43 return dispatcher.get_signal_queue<C>().queue[event];
44 }
45
46 /**
47 * A helper to test whether dispatcher has an handler for a certain event.
48 *
49 * @param dispatcher The dispatcher whose signal queue is used.
50 * @param queue_type The type of event to look for.
51 * @param event The event to get the signal for.
52 *
53 * @returns Whether or not the handler is found.
54 */
56 {
57 const auto queue_check = [&](auto& queue_set) {
58 return !queue_set.queue[event].empty(queue_type);
59 };
60
61 // We can't just use get_signal_queue since there's no way to know the event at compile time.
62 switch(get_event_category(event)) {
64 return queue_check(dispatcher.signal_queue_);
66 return queue_check(dispatcher.signal_mouse_queue_);
68 return queue_check(dispatcher.signal_keyboard_queue_);
70 return queue_check(dispatcher.signal_touch_motion_queue_);
72 return queue_check(dispatcher.signal_touch_gesture_queue_);
74 return queue_check(dispatcher.signal_notification_queue_);;
76 return queue_check(dispatcher.signal_message_queue_);
78 return queue_check(dispatcher.signal_raw_event_queue_);
80 return queue_check(dispatcher.signal_text_input_queue_);
81 default:
82 throw std::invalid_argument("Event is not categorized");
83 }
84 }
85};
86
88{
89
90/*
91 * Small sample to illustrate the effects of the various build_event_chain
92 * functions. Assume the widgets are in an window with the following widgets:
93 *
94 * -----------------------
95 * | dispatcher |
96 * | ------------------- |
97 * | | container 1 | |
98 * | | --------------- | |
99 * | | | container 2 | | |
100 * | | | ----------- | | |
101 * | | | | widget | | | |
102 * | | | ----------- | | |
103 * | | --------------- | |
104 * | ------------------- |
105 * -----------------------
106 *
107 * Note that the firing routine fires the events from:
108 * - pre child for chain.end() - > chain.begin()
109 * - child for widget
110 * - post child for chain.begin() -> chain.end()
111 */
112
113/**
114 * Build the event chain.
115 *
116 * The event chain is a chain of events starting from the first parent of the
117 * widget until (and including) the wanted parent. For all these widgets it
118 * will be tested whether they have either a pre or post handler for the event.
119 * This ways there will be list of widgets to try to send the events to.
120 * If there's no line from widget to parent the result is undefined.
121 * (If widget == dispatcher the result will always be empty.)
122 *
123 * @pre dispatcher != nullptr
124 * @pre widget != nullptr
125 *
126 * @param event The event to test.
127 * @param dispatcher The final widget to test, this is also the
128 * dispatcher the sends the event.
129 * @param w The widget should parent(s) to check.
130 *
131 * @returns The list of widgets with a handler.
132 * The order will be (assuming all have a
133 * handler):
134 * * container 2
135 * * container 1
136 * * dispatcher
137 */
138template<event_category C>
139std::vector<std::pair<widget*, ui_event>>
141{
142 assert(dispatcher);
143 assert(w);
144
145 std::vector<std::pair<widget*, ui_event>> result;
146
147 while(true) {
149 result.emplace_back(w, event);
150 }
151
152 if(w == dispatcher) {
153 break;
154 }
155
156 w = w->parent();
157 assert(w);
158 }
159
160 return result;
161}
162
163/**
164 * Build the event chain for signal_notification.
165 *
166 * The notification is only send to the receiver it returns an empty chain.
167 * Since the pre and post queues are unused, it validates whether they are
168 * empty (using asserts).
169 *
170 * @returns An empty vector.
171 */
172template<>
173std::vector<std::pair<widget*, ui_event>>
175{
176 assert(dispatcher);
177 assert(w);
178
179 assert(!w->has_event(event, dispatcher::event_queue_type(dispatcher::pre | dispatcher::post)));
180
181 return {};
182}
183
184/**
185 * Build the event chain for signal_message.
186 *
187 * This function expects that the widget sending it is also the receiver. This
188 * assumption might change, but is valid for now. The function doesn't build an
189 * event chain from @p dispatcher to @p widget but from @p widget to its
190 * toplevel item (the first one without a parent) which we call @p window.
191 *
192 * @pre dispatcher == widget
193 *
194 * @returns The list of widgets with a handler.
195 * The order will be (assuming all have a
196 * handler):
197 * * window
198 * * container 1
199 * * container 2
200 */
201template<>
202std::vector<std::pair<widget*, ui_event>>
203build_event_chain<event_category::message>(const ui_event event, widget* dispatcher, widget* w)
204{
205 assert(dispatcher);
206 assert(w);
207 assert(w == dispatcher);
208
209 std::vector<std::pair<widget*, ui_event>> result;
210
211 /* We only should add the parents of the widget to the chain. */
212 while((w = w->parent())) {
213 assert(w);
214
216 result.emplace(result.begin(), w, event);
217 }
218 }
219
220 return result;
221}
222
223/**
224 * Helper function for fire_event.
225 *
226 * This is called with the same parameters as fire_event except for the
227 * event_chain, which contains the widgets with the events to call for them.
228 */
229template<event_category C, typename... F>
230bool fire_event(const ui_event event,
231 const std::vector<std::pair<widget*, ui_event>>& event_chain,
233 widget* w,
234 F&&... params)
235{
236 bool handled = false;
237 bool halt = false;
238
239 /***** ***** ***** Pre ***** ***** *****/
240 for(const auto& [chain_target, chain_event] : event_chain | utils::views::reverse) {
241 const auto& signal = dispatcher_implementation::event_signal<C>(*chain_target, chain_event);
242
243 for(const auto& pre_func : signal.pre_child) {
244 pre_func(*dispatcher, chain_event, handled, halt, std::forward<F>(params)...);
245
246 if(halt) {
247 assert(handled);
248 break;
249 }
250 }
251
252 if(handled) {
253 return true;
254 }
255 }
256
257 /***** ***** ***** Child ***** ***** *****/
258 if(w->has_event(event, dispatcher::child)) {
259 const auto& signal = dispatcher_implementation::event_signal<C>(*w, event);
260
261 for(const auto& func : signal.child) {
262 func(*dispatcher, event, handled, halt, std::forward<F>(params)...);
263
264 if(halt) {
265 assert(handled);
266 break;
267 }
268 }
269
270 if(handled) {
271 return true;
272 }
273 }
274
275 /***** ***** ***** Post ***** ***** *****/
276 for(const auto& [chain_target, chain_event] : event_chain) {
277 const auto& signal = dispatcher_implementation::event_signal<C>(*chain_target, chain_event);
278
279 for(const auto& post_func : signal.post_child) {
280 post_func(*dispatcher, chain_event, handled, halt, std::forward<F>(params)...);
281
282 if(halt) {
283 assert(handled);
284 break;
285 }
286 }
287
288 if(handled) {
289 return true;
290 }
291 }
292
293 /**** ***** ***** Unhandled ***** ***** *****/
294 assert(handled == false);
295 return false;
296}
297
298} // namespace implementation
299
300/**
301 * Fires an event.
302 *
303 * A helper to allow the common event firing code to be shared between the
304 * different signal function types.
305 *
306 * @pre d != nullptr
307 * @pre w != nullptr
308 *
309 * @tparam C The category of the event to handle.
310 * @tparam F The parameter pack type.
311 *
312 *
313 * @param event The event to fire.
314 * @param d The dispatcher that handles the event.
315 * @param w The widget that should receive the event.
316 * @param params Zero or more additional arguments to pass
317 * to the signal function when it's executed.
318 *
319 * @returns Whether or not the event was handled.
320 */
321template<event_category C, typename... F>
322bool fire_event(const ui_event event, dispatcher* d, widget* w, F&&... params)
323{
324 assert(d);
325 assert(w);
326
327 widget* dispatcher_w = dynamic_cast<widget*>(d);
328
329 std::vector<std::pair<widget*, ui_event>> event_chain =
330 implementation::build_event_chain<C>(event, dispatcher_w, w);
331
332 return implementation::fire_event<C>(event, event_chain, dispatcher_w, w, std::forward<F>(params)...);
333}
334
335template<ui_event click,
336 ui_event double_click,
337 bool (event_executor::*wants_double_click)() const,
338 typename... F>
339bool fire_event_double_click(dispatcher* dsp, widget* wgt, F&&... params)
340{
341 assert(dsp);
342 assert(wgt);
343
344 std::vector<std::pair<widget*, ui_event>> event_chain;
345 widget* w = wgt;
346 widget* d = dynamic_cast<widget*>(dsp);
347
348 while(w != d) {
349 w = w->parent();
350 assert(w);
351
352 if(std::invoke(wants_double_click, w)) {
353 if(w->has_event(double_click, dispatcher::event_queue_type(dispatcher::pre | dispatcher::post))) {
354 event_chain.emplace_back(w, double_click);
355 }
356 } else {
358 event_chain.emplace_back(w, click);
359 }
360 }
361 }
362
363 if(std::invoke(wants_double_click, wgt)) {
364 constexpr auto C = get_event_category(double_click);
365 return implementation::fire_event<C>(double_click, event_chain, d, wgt, std::forward<F>(params)...);
366 } else {
367 constexpr auto C = get_event_category(click);
368 return implementation::fire_event<C>(click, event_chain, d, wgt, std::forward<F>(params)...);
369 }
370}
371
372} // namespace gui2::event
Base class for event handling.
signal_queue< signal_touch_gesture > signal_touch_gesture_queue_
Signal queue for callbacks in event_category::touch_gesture.
signal_queue< signal_message > signal_message_queue_
Signal queue for callbacks in event_category::message.
signal_queue< signal_text_input > signal_text_input_queue_
Signal queue for callbacks in event_category::text_input.
signal_queue< signal_raw_event > signal_raw_event_queue_
Signal queue for callbacks in event_category::raw_event.
signal_queue< signal_notification > signal_notification_queue_
Signal queue for callbacks in event_category::notification.
signal_queue< signal > signal_queue_
Signal queue for callbacks in event_category::general.
signal_queue< signal_touch_motion > signal_touch_motion_queue_
Signal queue for callbacks in event_category::touch_motion.
signal_queue< signal_keyboard > signal_keyboard_queue_
Signal queue for callbacks in event_category::keyboard.
signal_queue< signal_mouse > signal_mouse_queue_
Signal queue for callbacks in event_category::mouse.
Event execution calls.
Base class for all widgets.
Definition widget.hpp:55
widget * parent()
Definition widget.cpp:170
bool fire_event(const ui_event event, const std::vector< std::pair< widget *, ui_event > > &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
std::vector< std::pair< widget *, ui_event > > build_event_chain< event_category::message >(const ui_event event, widget *dispatcher, widget *w)
Build the event chain for signal_message.
std::vector< std::pair< widget *, ui_event > > build_event_chain(const ui_event event, widget *dispatcher, widget *w)
Build the event chain.
std::vector< std::pair< widget *, ui_event > > build_event_chain< event_category::notification >(const ui_event event, widget *dispatcher, widget *w)
Build the event chain for signal_notification.
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
bool fire_event_double_click(dispatcher *dsp, widget *wgt, F &&... params)
constexpr event_category get_event_category(const ui_event event)
Returns the category of a given event.
Definition handler.hpp:211
bool fire_event(const ui_event event, dispatcher *d, widget *w, F &&... params)
Fires an event.
dispatcher_callback<> signal
Used for events in event_category::general.
event_category
Event category masks.
Definition handler.hpp:55
@ notification
Callbacks with a sender aka notification messages.
@ keyboard
Callbacks with the keyboard values (these haven't been determined yet).
@ mouse
Callbacks with a coordinate as extra parameter.
@ message
Callbacks with a sender aka notification messages.
@ general
Callbacks without extra parameters.
Contains the implementation details for lexical_cast and shouldn't be used directly.
constexpr auto reverse
Definition ranges.hpp:44
int w
Definition pathfind.cpp:188
static auto & event_signal(dispatcher &dispatcher, const ui_event event)
Returns the appropriate signal queue for an event by category.
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.
The base template for associating string values with enum values.
Definition enum_base.hpp:33
#define d