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