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 
17 /**
18  * @file
19  * Contains the event distributor.
20  *
21  * The event distributor exists of several classes which are combined in one
22  * templated distributor class. The classes are closely tight together.
23  *
24  * All classes have direct access to each others members since they should act
25  * as one. (Since the buttons are a templated subclass it's not possible to use
26  * private subclasses.)
27  *
28  * The mouse_motion class handles the mouse motion and holds the owner of us
29  * since all classes virtually inherit us.
30  *
31  * The mouse_button classes are templated classes per mouse button, the
32  * template parameters are used to make the difference between the mouse
33  * buttons. Although it's easily possible to add more mouse buttons in the
34  * code several places only expect a left, middle and right button.
35  *
36  * distributor is the main class to be used in the user code. This class
37  * contains the handling of the keyboard as well.
38  */
39 
40 #include "gui/core/event/dispatcher.hpp"
41 #include "gui/core/event/handler.hpp"
42 #include "sdl/point.hpp"
43 #include "serialization/unicode_types.hpp"
44 #include "video.hpp"
45 
46 #include <string>
47 #include <vector>
48 
49 namespace gui2
50 {
51 
52 class widget;
53 
54 namespace event
55 {
56 
57 /***** ***** ***** ***** mouse_motion ***** ***** ***** ***** *****/
58 
59 class mouse_motion
60 {
61 public:
62 	mouse_motion(widget& owner, const dispatcher::queue_position queue_position);
63 
64 	~mouse_motion();
65 
66 	/**
67 	 * Captures the mouse input.
68 	 *
69 	 * When capturing the widget that has the mouse focus_ does the capturing.
70 	 *
71 	 * @param capture             Set or release the capturing.
72 	 */
73 	void capture_mouse( // widget* widget);
74 			const bool capture = true);
75 
76 protected:
77 	/** The widget that currently has the mouse focus_. */
78 	widget* mouse_focus_;
79 
80 	/** Did the current widget capture the focus_? */
81 	bool mouse_captured_;
82 
83 	/** The widget that owns us. */
84 	widget& owner_;
85 
86 	/** The timer for the hover event. */
87 	size_t hover_timer_;
88 
89 	/** The widget which should get the hover event. */
90 	widget* hover_widget_;
91 
92 	/** The anchor point of the hover event. */
93 	point hover_position_;
94 
95 	/**
96 	 * Has the hover been shown for the widget?
97 	 *
98 	 * A widget won't get a second hover event after the tooltip has been
99 	 * triggered. Only after (shortly) entering another widget it will be shown
100 	 * again for this widget.
101 	 */
102 	bool hover_shown_;
103 
104 	/**
105 	 * Starts the hover timer.
106 	 *
107 	 * @param widget                 The widget that wants the tooltip.
108 	 * @param coordinate             The anchor coordinate.
109 	 */
110 	void start_hover_timer(widget* widget, const point& coordinate);
111 
112 	/** Stops the current hover timer. */
113 	void stop_hover_timer();
114 
115 	/**
116 	 * Called when the mouse enters a widget.
117 	 *
118 	 * @param mouse_over          The widget that should receive the event.
119 	 */
120 	void mouse_enter(widget* mouse_over);
121 
122 	/** Called when the mouse leaves the current widget. */
123 	void mouse_leave();
124 
125 private:
126 	/**
127 	 * Called when the mouse moves over a widget.
128 	 *
129 	 * @param mouse_over          The widget that should receive the event.
130 	 * @param coordinate          The current screen coordinate of the mouse.
131 	 */
132 	void mouse_hover(widget* mouse_over, const point& coordinate);
133 
134 	/** Called when the mouse wants the widget to show its tooltip. */
135 	void show_tooltip();
136 
137 	bool signal_handler_sdl_mouse_motion_entered_;
138 	void signal_handler_sdl_mouse_motion(const event::ui_event event,
139 										 bool& handled,
140 										 const point& coordinate);
141 
142 	void signal_handler_sdl_touch_motion(const event::ui_event event,
143 										 bool& handled,
144 										 const point& coordinate,
145 										 const point& distance);
146 
147 	void signal_handler_sdl_wheel(const event::ui_event event,
148 								  bool& handled,
149 								  const point& coordinate);
150 
151 	void signal_handler_show_helptip(const event::ui_event event,
152 									 bool& handled,
153 									 const point& coordinate);
154 };
155 
156 /***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/
157 
158 /**
159  * Small helper metastruct to specialize mouse_button with and provide ui_event type
160  * aliases without needing to make mouse_button take a million template types.
161  */
162 template<
163 		ui_event sdl_button_down,
164 		ui_event sdl_button_up,
165 		ui_event button_down,
166 		ui_event button_up,
167 		ui_event button_click,
168 		ui_event button_double_click>
169 struct mouse_button_event_types_wrapper
170 {
171 	static const ui_event sdl_button_down_event     = sdl_button_down;
172 	static const ui_event sdl_button_up_event       = sdl_button_up;
173 	static const ui_event button_down_event         = button_down;
174 	static const ui_event button_up_event           = button_up;
175 	static const ui_event button_click_event        = button_click;
176 	static const ui_event button_double_click_event = button_double_click;
177 };
178 
179 template<typename T>
180 class mouse_button : public virtual mouse_motion
181 {
182 public:
183 	mouse_button(const std::string& name_,
184 				  widget& owner,
185 				  const dispatcher::queue_position queue_position);
186 
187 	/**
188 	 * Initializes the state of the button.
189 	 *
190 	 * @param is_down             The initial state of the button, if true down
191 	 *                            else initialized as up.
192 	 */
193 	void initialize_state(const bool is_down);
194 
195 protected:
196 	/** The time of the last click used for double clicking. */
197 	uint32_t last_click_stamp_;
198 
199 	/** The widget the last click was on, used for double clicking. */
200 	widget* last_clicked_widget_;
201 
202 	/**
203 	 * If the mouse isn't captured we need to verify the up is on the same
204 	 * widget as the down so we send a proper click, also needed to send the
205 	 * up to the right widget.
206 	 */
207 	widget* focus_;
208 
209 private:
210 	/** used for debug messages. */
211 	const std::string name_;
212 
213 	/** Is the button down? */
214 	bool is_down_;
215 
216 	bool signal_handler_sdl_button_down_entered_;
217 	void signal_handler_sdl_button_down(const event::ui_event event,
218 										bool& handled,
219 										const point& coordinate);
220 
221 	bool signal_handler_sdl_button_up_entered_;
222 	void signal_handler_sdl_button_up(const event::ui_event event,
223 									  bool& handled,
224 									  const point& coordinate);
225 
226 
227 	void mouse_button_click(widget* widget);
228 };
229 
230 /***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
231 
232 using mouse_button_left = mouse_button<
233 	mouse_button_event_types_wrapper<
234 		SDL_LEFT_BUTTON_DOWN,
235 		SDL_LEFT_BUTTON_UP,
236 		LEFT_BUTTON_DOWN,
237 		LEFT_BUTTON_UP,
238 		LEFT_BUTTON_CLICK,
239 		LEFT_BUTTON_DOUBLE_CLICK>
240 	>;
241 
242 using mouse_button_middle = mouse_button<
243 	mouse_button_event_types_wrapper<
244 		SDL_MIDDLE_BUTTON_DOWN,
245 		SDL_MIDDLE_BUTTON_UP,
246 		MIDDLE_BUTTON_DOWN,
247 		MIDDLE_BUTTON_UP,
248 		MIDDLE_BUTTON_CLICK,
249 		MIDDLE_BUTTON_DOUBLE_CLICK>
250 	>;
251 
252 using mouse_button_right = mouse_button<
253 	mouse_button_event_types_wrapper<
254 		SDL_RIGHT_BUTTON_DOWN,
255 		SDL_RIGHT_BUTTON_UP,
256 		RIGHT_BUTTON_DOWN,
257 		RIGHT_BUTTON_UP,
258 		RIGHT_BUTTON_CLICK,
259 		RIGHT_BUTTON_DOUBLE_CLICK>
260 	>;
261 
262 
263 /** The event handler class for the widget library. */
264 class distributor :
265 	public mouse_button_left,
266 	public mouse_button_middle,
267 	public mouse_button_right
268 {
269 public:
270 	distributor(widget& owner, const dispatcher::queue_position queue_position);
271 
272 	~distributor();
273 
274 	/**
275 	 * Initializes the state of the keyboard and mouse.
276 	 *
277 	 * Needed after initialization and reactivation.
278 	 */
279 	void initialize_state();
280 
281 	/**
282 	 * Captures the keyboard input.
283 	 *
284 	 * @param widget              The widget which should capture the keyboard.
285 	 *                            Sending nullptr releases the capturing.
286 	 */
287 	void keyboard_capture(widget* widget);
288 
289 	/**
290 	 * Adds the widget to the keyboard chain.
291 	 *
292 	 * @param widget              The widget to add to the chain. The widget
293 	 *                            should be valid widget, which hasn't been
294 	 *                            added to the chain yet.
295 	 */
296 	void keyboard_add_to_chain(widget* widget);
297 
298 	/**
299 	 * Remove the widget from the keyboard chain.
300 	 *
301 	 * @param widget              The widget to be removed from the chain.
302 	 */
303 	void keyboard_remove_from_chain(widget* widget);
304 
305 	/**
306 	 * Return the widget currently capturing keyboard input.
307 	 */
308 	widget* keyboard_focus() const;
309 
310 private:
311 	class layer : public video2::draw_layering
312 	{
313 	public:
handle_event(const SDL_Event &)314 		virtual void handle_event(const SDL_Event& ) {}
handle_window_event(const SDL_Event &)315 		virtual void handle_window_event(const SDL_Event& ) {}
layer()316 		layer() : video2::draw_layering(false) { }
317 	};
318 
319 	// make sure the appropriate things happens when we close.
320 	layer layer_;
321 
322 #if 0
323 	bool hover_pending_;			   /**< Is there a hover event pending? */
324 	unsigned hover_id_;                /**< Id of the pending hover event. */
325 	SDL_Rect hover_box_;               /**< The area the mouse can move in,
326 										*   moving outside invalidates the
327 										*   pending hover event.
328 										*/
329 
330 	bool had_hover_;                   /**< A widget only gets one hover event
331 	                                    *   per enter cycle.
332 										*/
333 
334 	/** The widget of the currently active tooltip. */
335 	widget* tooltip_;
336 
337 	/** The widget of the currently active help popup. */
338 	widget* help_popup_;
339 #endif
340 	/** The widget that holds the keyboard focus_. */
341 	widget* keyboard_focus_;
342 
343 	/**
344 	 * Fall back keyboard focus_ items.
345 	 *
346 	 * When the focused widget didn't handle the keyboard event (or no handler
347 	 * for the keyboard focus_) it is send all widgets in this vector. The order
348 	 * is from rbegin() to rend().  If the keyboard_focus_ is in the vector it
349 	 * won't get the event twice. The first item added to the vector should be
350 	 * the window, so it will be the last handler and can dispatch the hotkeys
351 	 * registered.
352 	 */
353 	std::vector<widget*> keyboard_focus_chain_;
354 
355 	/**
356 	 * Set of functions that handle certain events and sends them to the proper
357 	 * widget. These functions are called by the SDL event handling functions.
358 	 */
359 
360 	void signal_handler_sdl_key_down(const SDL_Keycode key,
361 									 const SDL_Keymod modifier,
362 									 const utf8::string& unicode);
363 
364 	void signal_handler_sdl_text_input(const utf8::string& unicode, int32_t start, int32_t len);
365 	void signal_handler_sdl_text_editing(const utf8::string& unicode, int32_t start, int32_t len);
366 
367 	template<typename Fcn, typename P1, typename P2, typename P3>
368 	void signal_handler_keyboard_internal(event::ui_event evt, P1&& p1, P2&& p2, P3&& p3);
369 
370 	void signal_handler_notify_removal(dispatcher& widget, const ui_event event);
371 };
372 
373 } // namespace event
374 
375 } // namespace gui2
376