1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 
20 #ifndef ui_graphical_widgetH
21 #define ui_graphical_widgetH
22 
23 #include <vector>
24 #include <memory>
25 #include <type_traits>
26 
27 #include <SDL.h>
28 
29 #include "input/mouse/mousebuttontype.h"
30 #include "utility/box.h"
31 #include "utility/position.h"
32 #include "utility/autosurface.h"
33 #include "maxrconfig.h"
34 #include "ui/graphical/shortcut.h"
35 #include "utility/signal/signalconnectionmanager.h"
36 
37 class cMouse;
38 class cKeyboard;
39 class cApplication;
40 class cWindow;
41 class cShortcut;
42 
43 class cWidget
44 {
45 public:
46 	static void toggleDrawDebugFrames();
47 
48 	cWidget();
49 	explicit cWidget (const cPosition& position);
50 	explicit cWidget (const cBox<cPosition>& area);
51 
52 	virtual ~cWidget();
53 
54 	/**
55 	 * Returns the parent widget of the current one.
56 	 *
57 	 * @return The parent widget or null if there is no parent.
58 	 */
59 	cWidget* getParent() const;
60 
61 	/**
62 	 * If a widget is disabled it will not receive any input events
63 	 * for the mouse or keyboard anymore.
64 	 *
65 	 * @return True if the widget is enabled. False if it is disabled.
66 	 */
67 	bool isEnabled() const;
68 	/**
69 	 * Disables the widget. See @ref isEnabled() for the effects.
70 	 */
71 	void disable();
72 	/**
73 	 * Enables the widget. See @ref isEnabled() for the effects.
74 	 */
75 	void enable();
76 
77 	/**
78 	 * A hidden widget will not be drawn to the screen anymore.
79 	 *
80 	 * @return True if the widget is hidden. False if not.
81 	 */
82 	bool isHidden() const;
83 	/**
84 	 * Hides the widget. See @ref isHidden for the effects.
85 	 * You may want to @ref disable the widget as well when
86 	 * it is hidden.
87 	 */
88 	void hide();
89 	/**
90 	 * Sets the widget as not hidden. See @ref isHidden for the effects.
91 	 */
92 	void show();
93 
94 	/**
95 	 * Returns the position (upper left corner) of the widget
96 	 * in absolute screen coordinates.
97 	 *
98 	 * This is the first pixel that is overdrawn with the widgets image.
99 	 */
100 	const cPosition& getPosition() const;
101 	/**
102 	 * Returns the end position (lower right corner) of the widget
103 	 * in absolute screen coordinates.
104 	 *
105 	 * This is the last pixel that is overdrawn with the widgets image.
106 	 */
107 	const cPosition& getEndPosition() const;
108 
109 	/**
110 	 * Moves the widget and all its children to a new position.
111 	 *
112 	 * @param newPosition The new position (upper left corner) to move the widget to.
113 	 */
114 	void moveTo(const cPosition& newPosition);
115 	/**
116 	 * Moves the widget and all its children by a given offset.
117 	 *
118 	 * @param offset The offset by which to move the widget. Can have negative values.
119 	 */
120 	void move(const cPosition& offset);
121 
122 	/**
123 	 * Returns the size of the widget.
124 	 */
125 	cPosition getSize() const;
126 	/**
127 	 * Resizes the widget.
128 	 */
129 	void resize(const cPosition& newSize);
130 
131 	/**
132 	 * Adjusts the size and position of the widget to fit exactly all its children.
133 	 * If the widget does not have any children its new size will be (0,0)
134 	 */
135 	void fitToChildren();
136 
137 	/**
138 	 * Returns the area of the widget in absolute screen coordinates.
139 	 */
140 	const cBox<cPosition>& getArea() const;
141 	/*
142 	 * Sets a new area for the widget.
143 	 *
144 	 * This implies changes in the position and the size of the widget.
145 	 *
146 	 * If setting the new area requires to move the widget, all children will be moved with it.
147 	 *
148 	 * @param area The new area to set.
149 	 */
150 	void setArea(const cBox<cPosition>& area);
151 
152 	/**
153 	 * Adds a shortcut to the widget.
154 	 *
155 	 * Shortcuts added to widgets will be executed when the the shortcut is hit will the window that is containing the widget is active.
156 	 *
157 	 * @param shortcut The shortcut to add.
158 	 * @return A non-owning pointer to the shortcut that has been added.
159 	 */
160 	cShortcut* addShortcut(std::unique_ptr<cShortcut> shortcut);
161 
162 	/**
163 	 * Returns all shortcuts that have been added to this widget.
164 	 */
165 	const std::vector<std::unique_ptr<cShortcut>>& getShortcuts() const;
166 
167 	/**
168 	 * Returns the most deep child (meaning possible a child of a child and so on) at the given
169 	 * position or null if there is no child at the given position.
170 	 *
171 	 * If there are multiple children on the same hierarchy the one that has last been added will
172 	 * taken into account.
173 	 *
174 	 * Children that are disabled or hidden will be ignored and never returned.
175 	 *
176 	 * @param position The position in absolute screen coordinates to test for.
177 	 */
178 	virtual cWidget* getChildAt(const cPosition& position) const;
179 
180 	/**
181 	 * Returns whether the widget is at the given position.
182 	 *
183 	 * This is true if the point is within the area of the widget.
184 	 *
185 	 * If the widget is hidden this will always return false.
186 	 *
187 	 * @param position The position in absolute screen coordinates to test for.
188 	 */
189 	virtual bool isAt(const cPosition& position) const;
190 
191 	/**
192 	 * Draws the widget and all its children to the destination surface.
193 	 *
194 	 * Children will be drawn in the order they have been added.
195 	 * This means children added last will be drawn on top of the ones added first.
196 	 *
197 	 * @param destination The surface to draw the widget onto
198 	 * @param clipRect An area with which all area to be drawn will be clipped before the drawing is performed.
199 	 *                 This means all drawing takes place only within this area.
200 	 */
201 	virtual void draw(SDL_Surface& destination, const cBox<cPosition>& clipRect);
202 
203 	/**
204 	 * Will be called when ever the mouse has been moved above the widget.
205 	 *
206 	 * If the widget has mouse focus this method will be called for the widget even
207 	 * if the current mouse position is not above the widget.
208 	 *
209 	 * @param application The application associated with the mouse that has triggered the event.
210 	 * @param mouse The mouse that has been moved.
211 	 * @param offset The offset by which the mouse has been moved.
212 	 * @return True if the widget wants to consume the mouse moved event.
213 	 *         This means this method will not be called for any other widget after this one.
214 	 */
215 	virtual bool handleMouseMoved(cApplication& application, cMouse& mouse, const cPosition& offset);
216 	/**
217 	 * Will be called when ever a mouse button has been pressed above the widget.
218 	 *
219 	 * If the widget has mouse focus this method will be called for the widget even
220 	 * if the current mouse position is not above the widget.
221 	 *
222 	 * @param application The application associated with the mouse that has triggered the event.
223 	 * @param mouse The mouse whose button has been pressed.
224 	 * @param button The button that has been pressed.
225 	 * @return True if the widget wants to consume the mouse pressed event.
226 	 *         This means this method will not be called for any other widget after this one.
227 	 */
228 	virtual bool handleMousePressed(cApplication& application, cMouse& mouse, eMouseButtonType button);
229 	/**
230 	 * Will be called when ever a mouse button has been released above the widget.
231 	 *
232 	 * If the widget has mouse focus this method will be called for the widget even
233 	 * if the current mouse position is not above the widget.
234 	 *
235 	 * @param application The application associated with the mouse that has triggered the event.
236 	 * @param mouse The mouse whose button has been released.
237 	 * @param button The button that has been released.
238 	 * @return True if the widget wants to consume the mouse release event.
239 	 *         This means this method will not be called for any other widget after this one.
240 	 */
241 	virtual bool handleMouseReleased(cApplication& application, cMouse& mouse, eMouseButtonType button);
242 	/**
243 	 * Will be called when ever the mouse wheel has been moved above the widget.
244 	 *
245 	 * If the widget has mouse focus this method will be called for the widget even
246 	 * if the current mouse position is not above the widget.
247 	 *
248 	 * @param application The application associated with the mouse that has triggered the event.
249 	 * @param mouse The mouse whose wheel has been moved.
250 	 * @param amount The amount by which the mouse wheel has been moved.
251 	 * @return True if the widget wants to consume the mouse wheel moved event.
252 	 *         This means this method will not be called for any other widget after this one.
253 	 */
254 	virtual bool handleMouseWheelMoved(cApplication& application, cMouse& mouse, const cPosition& amount);
255 
256 	/**
257 	 * Will be called when a keyboard key has been pressed.
258 	 *
259 	 * Derived classes should always call this method after processing the
260 	 * event itself and return true if the base class call has returned true.
261 	 *
262 	 * @param application The application associated with the keyboard that has triggered the event.
263 	 * @param keyboard The keyboard whose key has been pressed.
264 	 * @param key The key that has been pressed.
265 	 * @return True if the widget wants to consume the key pressed event.
266 	 *         This means this method will not be called for any other widget after this one.
267 	 *         Additionally returning true here prohibits any shortcuts to be executed for this key press.
268 	 */
269 	virtual bool handleKeyPressed(cApplication& application, cKeyboard& keyboard, SDL_Keycode key);
270 	/**
271 	 * Will be called when a keyboard key has been released.
272 	 *
273 	 * Derived classes should always call this method after processing the
274 	 * event itself and return true if the base class call has returned true.
275 	 *
276 	 * @param application The application associated with the keyboard that has triggered the event.
277 	 * @param keyboard The keyboard whose key has been released.
278 	 * @param key The key that has been released.
279 	 * @return True if the widget wants to consume the key release event.
280 	 *         This means this method will not be called for any other widget after this one.
281 	 */
282 	virtual bool handleKeyReleased(cApplication& application, cKeyboard& keyboard, SDL_Keycode key);
283 	/**
284 	 * Will be called when this widget has key focus and text has been entered via the keyboard.
285 	 *
286 	 * @param application The application associated with the keyboard that has triggered the event.
287 	 * @param keyboard The keyboard on that the text has been entered.
288 	 * @param text The text that has been entered (UTF-8 encoded).
289 	 */
290 	virtual void handleTextEntered(cApplication& application, cKeyboard& keyboard, const char* text);
291 
292 	//virtual void handleHoveredOn (cApplication& application, cMouse& mouse);
293 	//virtual void handleHoveredAway (cApplication& application, cMouse& mouse);
294 
295 	/**
296 	 * Will be called when ever the widget is going to get the key focus.
297 	 *
298 	 * At the time of the call the widget does not yet have the key focus.
299 	 *
300 	 * @param application The application associated with the keyboard that has triggered the event.
301 	 * @return return True if the widget accepts the key focus. In this case the key focus will be assigned to the widget.
302 	 *                False if the widget does not accept the key focus. In this case the key focus will be assigned
303 	 *                to the parent widget if there is any and if the parent accepts the focus.
304 	 */
305 	virtual bool handleGetKeyFocus(cApplication& application);
306 
307 	/**
308 	 * Will be called when ever the widget is losing the key focus.
309 	 *
310 	 * At the time of the call the widget does not yet have the key focus.
311 	 *
312 	 * @param application The application associated with the keyboard that has triggered the event.
313 	 */
314 	virtual void handleLooseKeyFocus(cApplication& application);
315 
316 	/**
317 	 * Will be called when ever the widget is getting the mouse focus.
318 	 *
319 	 * At the time of the call the widget does not yet have the mouse focus.
320 	 *
321 	 * @param application The application associated with the mouse that has triggered the event.
322 	 */
323 	virtual void handleGetMouseFocus(cApplication& application);
324 	/**
325 	 * Will be called when ever the widget is loosing the mouse focus.
326 	 *
327 	 * At the time of the call the widget still has the mouse focus.
328 	 *
329 	 * @param application The application associated with the mouse that has triggered the event.
330 	 */
331 	virtual void handleLooseMouseFocus(cApplication& application);
332 
333 	/**
334 	 * Will be called when ever the widget has been moved.
335 	 *
336 	 * Derived classes should always call this method.
337 	 *
338 	 * @param offset The offset by which the widget has been moved.
339 	 */
340 	virtual void handleMoved(const cPosition& offset);
341 
342 	/**
343 	 * Will be called when ever the widget has been resized.
344 	 *
345 	 * Derived classes should always call this method.
346 	 *
347 	 * @param oldSize The size of the widget before the resize took place
348 	 */
349 	virtual void handleResized(const cPosition& oldSize);
350 
351 	/**
352 	 * Triggers all active shortcuts that match the end of the given key sequence.
353 	 *
354 	 * Will be called recursively for all children of the widget.
355 	 *
356 	 * @return True if any shortcut (including the ones of the children) has been triggered.
357 	 */
358 	virtual bool hitShortcuts (const cKeySequence& keySequence);
359 
360 protected:
361 	/**
362 	 * Adds a new child
363 	 *
364 	 * This method is potentially very dangerous!
365 	 * Modifying the children while they are accessed will result in undefined behavior.
366 	 * Hence make sure this method is only called when it is absolutely sure
367 	 * that the children are not currently worked on.
368 	 * This especially means during the drawing of the children (or any of their children)
369 	 * or during the event handling (e.g. clicked event) of the children or any of their children).
370 	 *
371 	 * @tparam WidgetType The type of the widget to add. Has to inherit from @ref cWidget.
372 	 * @param child The child to add.
373 	 * @return A non owning pointer to the added child.
374 	 */
375 	template<typename WidgetType>
376 	WidgetType* addChild (std::unique_ptr<WidgetType> child);
377 
378 	void setParent (cWidget* parent);
379 
380 	/**
381 	 * Removes all children.
382 	 *
383 	 * See @ref addChild for restrictions on when this method is allowed to be used!
384 	 */
385 	void removeChildren();
386 
387 	/**
388 	 * Returns true if there are any children for this widget.
389 	 */
390 	bool hasChildren() const;
391 
392 	/**
393 	 * Returns the currently active mouse for this widget.
394 	 *
395 	 * This may be null if the widget has not been added to a window yet
396 	 * or if the window does not have an active application.
397 	 */
398 	virtual cMouse* getActiveMouse() const;
399 	/**
400 	 * Returns the currently active keyboard for this widget.
401 	 *
402 	 * This may be null if the widget has not been added to a window yet
403 	 * or if the window does not have an active application.
404 	 */
405 	virtual cKeyboard* getActiveKeyboard() const;
406 
407 	/**
408 	 * Returns the currently active application for this widget.
409 	 *
410 	 * This may be null if the widget has not been added to a window yet
411 	 * or if the window does not have an active application.
412 	 */
413 	virtual cApplication* getActiveApplication() const;
414 private:
415 	static bool drawDebugFrames;
416 	static cSignal<void ()> drawDebugFramesChanged;
417 
418 	cWidget (const cWidget& other) MAXR_DELETE_FUNCTION;
419 	cWidget& operator= (const cWidget& other) MAXR_DELETE_FUNCTION;
420 
421 	cSignalConnectionManager signalConnectionManager;
422 
423 	cWidget* parent;
424 
425 	bool enabled;
426 	bool hidden;
427 
428 	cBox<cPosition> area;
429 
430 	AutoSurface frameSurface;
431 
432 	std::vector<std::unique_ptr<cWidget>> children;
433 
434 	std::vector<std::unique_ptr<cShortcut>> shortcuts;
435 
436 	void createFrameSurface();
437 
438 	void releaseFocusRecursive (cApplication& application);
439 };
440 
441 //------------------------------------------------------------------------------
442 template<typename WidgetType>
addChild(std::unique_ptr<WidgetType> child)443 WidgetType* cWidget::addChild (std::unique_ptr<WidgetType> child)
444 {
445 	static_assert (std::is_base_of<cWidget, WidgetType>::value, "Only widgets can be added as children of widgets");
446 
447 	if (child == nullptr) return nullptr;
448 
449 	child->setParent (this);
450 	children.push_back (std::move (child));
451 
452 	return static_cast<WidgetType*> (children.back().get());
453 }
454 
455 #endif // ui_graphical_widgetH
456