FifeGUI 0.2.0
A C++ GUI library designed for games.
gui.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5#include "fifechan/gui.hpp"
6
7#include <algorithm>
8#include <cstdint>
9#include <list>
10#include <memory>
11#include <queue>
12#include <set>
13#include <string>
14#include <utility>
15
16#include "fifechan/deathlistener.hpp"
17#include "fifechan/exception.hpp"
18#include "fifechan/focushandler.hpp"
19#include "fifechan/graphics.hpp"
20#include "fifechan/input.hpp"
21#include "fifechan/keyevent.hpp"
22#include "fifechan/keyinput.hpp"
23#include "fifechan/keylistener.hpp"
24#include "fifechan/mouseevent.hpp"
25#include "fifechan/mouseinput.hpp"
26#include "fifechan/mouselistener.hpp"
27#include "fifechan/rectangle.hpp"
28#include "fifechan/visibilityeventhandler.hpp"
29#include "fifechan/widget.hpp"
30
31namespace fcn
32{
37 class GuiDeathListener : public DeathListener
38 {
39 public:
41 explicit GuiDeathListener(Gui* gui) : mGui(gui) { }
42 ~GuiDeathListener() override = default;
43
44 void death(Event const & event) override
45 {
46 mGui->widgetDied(event.getSource());
47 }
48
49 // Delete copy constructor and copy assignment operator
50 GuiDeathListener(GuiDeathListener const &) = delete;
51 GuiDeathListener& operator=(GuiDeathListener const &) = delete;
52
53 // Delete move constructor and move assignment operator
55 GuiDeathListener& operator=(GuiDeathListener&&) = delete;
56
57 private:
58 Gui* mGui;
59 };
60
61 Gui::Gui() :
62 mTop(nullptr),
63 mGraphics(nullptr),
64 mInput(nullptr),
65 mTabbing(true),
66 mFocusHandler(new FocusHandler()),
67 mVisibilityEventHandler(new VisibilityEventHandler(this)),
68 mShiftPressed(false),
69 mMetaPressed(false),
70 mControlPressed(false),
71 mAltPressed(false),
72 mLastMousePressButton(0),
73 mLastMousePressTimeStamp(0),
74 mLastMouseX(0),
75 mLastMouseY(0),
76 mClickCount(1),
77 mLastMouseDragButton(0),
78 mDeathListener(new GuiDeathListener(this))
79 {
80 Widget::_setVisibilityEventHandler(mVisibilityEventHandler);
81
82 Widget::_setGuiDeathListener(mDeathListener);
83 }
84
85 Gui::~Gui()
86 {
88 if (mTop != nullptr) {
89 mTop->_setFocusHandler(nullptr);
90 }
91 mTop = nullptr;
92 }
95
96 delete mFocusHandler;
98 delete mDeathListener;
99 }
100
102 {
103 if (top != mOwnedTop.get()) {
104 mOwnedTop.reset();
105 }
106
107 if (mTop != nullptr) {
108 mTop->_setFocusHandler(nullptr);
109 }
110 if (top != nullptr) {
112 }
113
114 mTop = top;
115 }
116
117 void Gui::setTop(std::unique_ptr<Widget> top)
118 {
119 mOwnedTop = std::move(top);
120 setTop(mOwnedTop.get());
121 }
122
124 {
125 setTop(top);
126 }
127
128 void Gui::setRoot(std::unique_ptr<Widget> top)
129 {
130 setTop(std::move(top));
131 }
132
134 {
135 return mTop;
136 }
137
139 {
140 if (graphics != mOwnedGraphics.get()) {
141 mOwnedGraphics.reset();
142 }
143 mGraphics = graphics;
144 }
145
146 void Gui::setGraphics(std::unique_ptr<Graphics> graphics)
147 {
148 mOwnedGraphics = std::move(graphics);
150 }
151
153 {
154 return mGraphics;
155 }
156
157 void Gui::setInput(Input* input)
158 {
159 if (input != mOwnedInput.get()) {
160 mOwnedInput.reset();
161 }
162 mInput = input;
163 }
164
165 void Gui::setInput(std::unique_ptr<Input> input)
166 {
167 mOwnedInput = std::move(input);
168 setInput(mOwnedInput.get());
169 }
170
172 {
173 return mInput;
174 }
175
176 void Gui::initialize(std::unique_ptr<Graphics> graphics, std::unique_ptr<Input> input, int width, int height)
177 {
178 (void)width;
179 (void)height;
180 setGraphics(std::move(graphics));
181 setInput(std::move(input));
182 }
183
185 {
186 return mFocusHandler;
187 }
188
189 void Gui::setGlobalFont(std::string const & filename, int size)
190 {
191 if (mGraphics == nullptr) {
192 throwException("No graphics set");
193 }
194
195 auto createdFont = mGraphics->createFont(filename, size);
196 if (!createdFont) {
197 throwException("Failed to create font using active graphics backend");
198 }
199
200 mGlobalFont = std::move(createdFont);
202 }
203
205 {
206 if (mTop == nullptr) {
207 throwException("No top widget set");
208 }
209
212
213 if (mInput != nullptr) {
214 mInput->_pollInput();
215
218 }
219
220 mTop->_logic();
221
224 }
225
227 {
228 if (mTop == nullptr) {
229 throwException("No top widget set");
230 }
231
232 if (mGraphics == nullptr) {
233 throwException("No graphics set");
234 }
235
236 if (!mTop->isVisible()) {
237 return;
238 }
239
240 mGraphics->_beginDraw();
241 mTop->_draw(mGraphics);
242 mGraphics->_endDraw();
243 }
244
246 {
247 mFocusHandler->focusNone();
248 }
249
250 void Gui::setTabbingEnabled(bool tabbing)
251 {
252 mTabbing = tabbing;
253 }
254
256 {
257 return mTabbing;
258 }
259
261 {
262 mKeyListeners.push_back(keyListener);
263 }
264
266 {
267 mKeyListeners.remove(keyListener);
268 }
269
271 {
272 mHiddenWidgets.push(widget);
273 }
274
276 {
277 mShownWidgets.push(widget);
278 }
279
280 void Gui::widgetDied(Widget const * widget)
281 {
282 std::queue<Widget*> tmp;
283 while (!mShownWidgets.empty()) {
284 Widget* shownWidget = mShownWidgets.front();
285 if (shownWidget != widget) {
286 tmp.push(shownWidget);
287 }
288 mShownWidgets.pop();
289 }
290 mShownWidgets = tmp;
291
292 tmp = std::queue<Widget*>();
293 while (!mHiddenWidgets.empty()) {
294 Widget* hiddenWidget = mHiddenWidgets.front();
295 if (hiddenWidget != widget) {
296 tmp.push(hiddenWidget);
297 }
298 mHiddenWidgets.pop();
299 }
300 mHiddenWidgets = tmp;
301 }
302
304 {
305 while (!mInput->isMouseQueueEmpty()) {
306 MouseInput const mouseInput = mInput->dequeueMouseInput();
307
308 switch (mouseInput.getType()) {
309 case MouseInput::Type::Pressed:
310 handleMousePressed(mouseInput);
311 break;
312 case MouseInput::Type::Released:
313 handleMouseReleased(mouseInput);
314 break;
315 case MouseInput::Type::Moved:
316 handleMouseMoved(mouseInput);
317 break;
318 case MouseInput::Type::WheelMovedDown:
319 handleMouseWheelMovedDown(mouseInput);
320 break;
321 case MouseInput::Type::WheelMovedUp:
322 handleMouseWheelMovedUp(mouseInput);
323 break;
324 case MouseInput::Type::WheelMovedRight:
325 handleMouseWheelMovedRight(mouseInput);
326 break;
327 case MouseInput::Type::WheelMovedLeft:
328 handleMouseWheelMovedLeft(mouseInput);
329 break;
330 default:
331 throwException("Unknown mouse input type.");
332 break;
333 }
334
335 // Save the current mouse state. It's needed to send
336 // mouse exited events and mouse entered events when
337 // the mouse exits a widget and when a widget releases
338 // modal mouse input focus.
339 mLastMouseX = mouseInput.getX();
340 mLastMouseY = mouseInput.getY();
341 }
342 }
343
345 {
346 while (!mInput->isKeyQueueEmpty()) {
347 KeyInput const keyInput = mInput->dequeueKeyInput();
348
349 // Save modifiers state
350 mShiftPressed = keyInput.isShiftPressed();
351 mMetaPressed = keyInput.isMetaPressed();
353 mAltPressed = keyInput.isAltPressed();
354
355 KeyEvent keyEventToGlobalKeyListeners(
356 nullptr,
357 nullptr,
362 static_cast<KeyEvent::Type>(static_cast<std::uint8_t>(keyInput.getType())),
363 keyInput.isNumericPad(),
364 keyInput.getKey());
365
366 distributeKeyEventToGlobalKeyListeners(keyEventToGlobalKeyListeners);
367
368 // If a global key listener consumes the event it will not be
369 // sent further to the source of the event.
370 if (keyEventToGlobalKeyListeners.isConsumed()) {
371 continue;
372 }
373
374 bool keyEventConsumed = false;
375
376 // Send key inputs to the focused widgets
377 if (mFocusHandler->getFocused() != nullptr) {
378 Widget* source = getKeyEventSource();
379 KeyEvent keyEvent(
380 source,
381 source,
386 static_cast<KeyEvent::Type>(static_cast<std::uint8_t>(keyInput.getType())),
387 keyInput.isNumericPad(),
388 keyInput.getKey());
389
390 if (!mFocusHandler->getFocused()->isFocusable()) {
391 mFocusHandler->focusNone();
392 } else {
393 distributeKeyEvent(keyEvent);
394 }
395
396 keyEventConsumed = keyEvent.isConsumed();
397 }
398
399 // If the key event hasn't been consumed and
400 // tabbing is enable check for tab press and
401 // change focus.
402 if (!keyEventConsumed && mTabbing && keyInput.getKey().getValue() == Key::Tab &&
403 keyInput.getType() == KeyInput::Type::Pressed) {
404 if (keyInput.isShiftPressed()) {
405 mFocusHandler->tabPrevious();
406 } else {
407 mFocusHandler->tabNext();
408 }
409 }
410 }
411 }
412
413 void Gui::handleMouseMoved(MouseInput const & mouseInput)
414 {
415 // Get tha last widgets with the mouse using the
416 // last known mouse position.
417 std::set<Widget*> const mLastWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
418
419 // Check if the mouse has left the application window.
420 if (mouseInput.getX() < 0 || mouseInput.getY() < 0 ||
421 !mTop->getDimension().isContaining(mouseInput.getX(), mouseInput.getY())) {
422 std::set<Widget*>::const_iterator iter;
423 for (iter = mLastWidgetsWithMouse.begin(); iter != mLastWidgetsWithMouse.end(); ++iter) {
425 (*iter),
426 MouseEvent::Type::Exited,
427 static_cast<MouseEvent::Button>(mouseInput.getButton()),
428 mouseInput.getX(),
429 mouseInput.getY(),
430 true,
431 true);
432 }
433 } else {
434 // The mouse is in the application window.
435
436 // Calculate which widgets should receive a mouse exited event
437 // and which should receive a mouse entered event by using the
438 // last known mouse position and the latest mouse position.
439 std::set<Widget*> const mWidgetsWithMouse = getWidgetsAt(mouseInput.getX(), mouseInput.getY());
440 std::set<Widget*> mWidgetsWithMouseExited;
441 std::set<Widget*> mWidgetsWithMouseEntered;
442 std::ranges::set_difference(
443 mLastWidgetsWithMouse,
444
445 mWidgetsWithMouse,
446
447 std::inserter(mWidgetsWithMouseExited, mWidgetsWithMouseExited.begin()));
448 std::ranges::set_difference(
449 mWidgetsWithMouse,
450
451 mLastWidgetsWithMouse,
452
453 std::inserter(mWidgetsWithMouseEntered, mWidgetsWithMouseEntered.begin()));
454
455 std::set<Widget*>::const_iterator iter;
456 for (iter = mWidgetsWithMouseExited.begin(); iter != mWidgetsWithMouseExited.end(); ++iter) {
458 (*iter),
459 MouseEvent::Type::Exited,
460 static_cast<MouseEvent::Button>(mouseInput.getButton()),
461 mouseInput.getX(),
462 mouseInput.getY(),
463 true,
464 true);
465 // As the mouse has exited a widget we need
466 // to reset the click count and the last mouse
467 // press time stamp.
468 mClickCount = 1;
470 }
471
472 for (iter = mWidgetsWithMouseEntered.begin(); iter != mWidgetsWithMouseEntered.end(); ++iter) {
473 Widget* widget = (*iter);
474 // If a widget has modal mouse input focus we
475 // only want to send entered events to that widget
476 // and the widget's parents.
477 if ((mFocusHandler->getModalMouseInputFocused() != nullptr && widget->isModalMouseInputFocused()) ||
478 mFocusHandler->getModalMouseInputFocused() == nullptr) {
480 widget,
481 MouseEvent::Type::Entered,
482 static_cast<MouseEvent::Button>(mouseInput.getButton()),
483 mouseInput.getX(),
484 mouseInput.getY(),
485 true,
486 true);
487 }
488 }
489 }
490
491 if (mFocusHandler->getDraggedWidget() != nullptr) {
493 mFocusHandler->getDraggedWidget(),
494 MouseEvent::Type::Dragged,
496 mouseInput.getX(),
497 mouseInput.getY());
498 } else {
499 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
501 sourceWidget,
502 MouseEvent::Type::Moved,
503 static_cast<MouseEvent::Button>(mouseInput.getButton()),
504 mouseInput.getX(),
505 mouseInput.getY());
506 }
507 }
508
509 void Gui::handleMousePressed(MouseInput const & mouseInput)
510 {
511 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
512
513 if (mFocusHandler->getDraggedWidget() != nullptr) {
514 sourceWidget = mFocusHandler->getDraggedWidget();
515 }
516
517 int sourceWidgetX = 0;
518 int sourceWidgetY = 0;
519 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
520
521 if ((mFocusHandler->getModalFocused() != nullptr && sourceWidget->isModalFocused()) ||
522 mFocusHandler->getModalFocused() == nullptr) {
523 sourceWidget->requestFocus();
524 }
525
526 if (mouseInput.getTimeStamp() - mLastMousePressTimeStamp < 250 &&
527 mLastMousePressButton == static_cast<unsigned int>(mouseInput.getButton())) {
528 mClickCount++;
529 } else {
530 mClickCount = 1;
531 }
532
534 sourceWidget,
535 MouseEvent::Type::Pressed,
536 static_cast<MouseEvent::Button>(mouseInput.getButton()),
537 mouseInput.getX(),
538 mouseInput.getY());
539
540 mFocusHandler->setLastWidgetPressed(sourceWidget);
541
542 mFocusHandler->setDraggedWidget(sourceWidget);
543 mLastMouseDragButton = static_cast<unsigned int>(mouseInput.getButton());
544
545 mLastMousePressButton = static_cast<unsigned int>(mouseInput.getButton());
547 }
548
550 {
551 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
552
553 if (mFocusHandler->getDraggedWidget() != nullptr) {
554 sourceWidget = mFocusHandler->getDraggedWidget();
555 }
556
557 int sourceWidgetX = 0;
558 int sourceWidgetY = 0;
559 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
560
562 sourceWidget,
563 MouseEvent::Type::WheelMovedDown,
564 static_cast<MouseEvent::Button>(mouseInput.getButton()),
565 mouseInput.getX(),
566 mouseInput.getY());
567 }
568
570 {
571 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
572
573 if (mFocusHandler->getDraggedWidget() != nullptr) {
574 sourceWidget = mFocusHandler->getDraggedWidget();
575 }
576
577 int sourceWidgetX = 0;
578 int sourceWidgetY = 0;
579 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
580
582 sourceWidget,
583 MouseEvent::Type::WheelMovedUp,
584 static_cast<MouseEvent::Button>(mouseInput.getButton()),
585 mouseInput.getX(),
586 mouseInput.getY());
587 }
588
590 {
591 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
592
593 if (mFocusHandler->getDraggedWidget() != nullptr) {
594 sourceWidget = mFocusHandler->getDraggedWidget();
595 }
596
597 int sourceWidgetX = 0;
598 int sourceWidgetY = 0;
599 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
600
602 sourceWidget,
603 MouseEvent::Type::WheelMovedRight,
604 static_cast<MouseEvent::Button>(mouseInput.getButton()),
605 mouseInput.getX(),
606 mouseInput.getY());
607 }
608
610 {
611 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
612
613 if (mFocusHandler->getDraggedWidget() != nullptr) {
614 sourceWidget = mFocusHandler->getDraggedWidget();
615 }
616
617 int sourceWidgetX = 0;
618 int sourceWidgetY = 0;
619 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
620
622 sourceWidget,
623 MouseEvent::Type::WheelMovedLeft,
624 static_cast<MouseEvent::Button>(mouseInput.getButton()),
625 mouseInput.getX(),
626 mouseInput.getY());
627 }
628
629 void Gui::handleMouseReleased(MouseInput const & mouseInput)
630 {
631 Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
632
633 if (mFocusHandler->getDraggedWidget() != nullptr) {
634 if (sourceWidget != mFocusHandler->getLastWidgetPressed()) {
635 mFocusHandler->setLastWidgetPressed(nullptr);
636 }
637
638 sourceWidget = mFocusHandler->getDraggedWidget();
639 }
640
641 int sourceWidgetX = 0;
642 int sourceWidgetY = 0;
643 sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
644
646 sourceWidget,
647 MouseEvent::Type::Released,
648 static_cast<MouseEvent::Button>(mouseInput.getButton()),
649 mouseInput.getX(),
650 mouseInput.getY());
651
652 if (static_cast<unsigned int>(mouseInput.getButton()) == mLastMousePressButton &&
653 mFocusHandler->getLastWidgetPressed() == sourceWidget) {
655 sourceWidget,
656 MouseEvent::Type::Clicked,
657 static_cast<MouseEvent::Button>(mouseInput.getButton()),
658 mouseInput.getX(),
659 mouseInput.getY());
660
661 mFocusHandler->setLastWidgetPressed(nullptr);
662 } else {
664 mClickCount = 0;
665 }
666
667 if (mFocusHandler->getDraggedWidget() != nullptr) {
668 mFocusHandler->setDraggedWidget(nullptr);
669 }
670 }
671
672 Widget* Gui::getWidgetAt(int x, int y, Widget* exclude)
673 {
674 // If the widget's parent has no child then we have found the widget..
675 Widget* parent = mTop;
676 Widget* child = mTop;
677
678 while (child != nullptr) {
679 Widget* swap = child;
680 int parentX = 0;
681 int parentY = 0;
682 parent->getAbsolutePosition(parentX, parentY);
683 child = parent->getWidgetAt(x - parentX, y - parentY, exclude);
684 parent = swap;
685 }
686
687 return parent;
688 }
689
690 std::set<Widget*> Gui::getWidgetsAt(int x, int y)
691 {
692 std::set<Widget*> result;
693
694 Widget* widget = mTop;
695
696 while (widget != nullptr) {
697 result.insert(widget);
698 int absoluteX = 0;
699 int absoluteY = 0;
700 widget->getAbsolutePosition(absoluteX, absoluteY);
701 widget = widget->getWidgetAt(x - absoluteX, y - absoluteY);
702 }
703
704 return result;
705 }
706
708 {
709 Widget* widget = getWidgetAt(x, y);
710
711 if (mFocusHandler->getModalMouseInputFocused() != nullptr && !widget->isModalMouseInputFocused()) {
712 return mFocusHandler->getModalMouseInputFocused();
713 }
714
715 return widget;
716 }
717
719 {
720 Widget* widget = mFocusHandler->getFocused();
721
722 while (widget->_getInternalFocusHandler() != nullptr &&
723 widget->_getInternalFocusHandler()->getFocused() != nullptr) {
724 widget = widget->_getInternalFocusHandler()->getFocused();
725 }
726
727 return widget;
728 }
729
731 Widget* source, MouseEvent::Type type, MouseEvent::Button button, int x, int y, bool force, bool toSourceOnly)
732 {
733 Widget* parent = source;
734 Widget* widget = source;
735
736 if (mFocusHandler->getModalFocused() != nullptr && !widget->isModalFocused() && !force) {
737 return;
738 }
739
740 if (mFocusHandler->getModalMouseInputFocused() != nullptr && !widget->isModalMouseInputFocused() && !force) {
741 return;
742 }
743
744 MouseEvent mouseEvent(
745 source, source, mShiftPressed, mControlPressed, mAltPressed, mMetaPressed, type, button, x, y, mClickCount);
746
747 while (parent != nullptr) {
748 // If the widget has been removed due to input
749 // cancel the distribution.
750 if (!Widget::widgetExists(widget)) {
751 break;
752 }
753
754 parent = widget->getParent();
755
756 if (widget->isEnabled() || force) {
757 int widgetX = 0;
758 int widgetY = 0;
759 widget->getAbsolutePosition(widgetX, widgetY);
760
761 mouseEvent.mX = x - widgetX;
762 mouseEvent.mY = y - widgetY;
763 mouseEvent.mDistributor = widget;
764 std::list<MouseListener*> mouseListeners = widget->_getMouseListeners();
765
766 // Send the event to all mouse listeners of the widget.
767 for (auto& mouseListener : mouseListeners) {
768 switch (mouseEvent.getType()) {
769 case MouseEvent::Type::Entered:
770 mouseListener->mouseEntered(mouseEvent);
771 break;
772 case MouseEvent::Type::Exited:
773 mouseListener->mouseExited(mouseEvent);
774 break;
775 case MouseEvent::Type::Moved:
776 mouseListener->mouseMoved(mouseEvent);
777 break;
778 case MouseEvent::Type::Pressed:
779 mouseListener->mousePressed(mouseEvent);
780 break;
781 case MouseEvent::Type::Released:
782 mouseListener->mouseReleased(mouseEvent);
783 break;
784 case MouseEvent::Type::WheelMovedUp:
785 mouseListener->mouseWheelMovedUp(mouseEvent);
786 break;
787 case MouseEvent::Type::WheelMovedDown:
788 mouseListener->mouseWheelMovedDown(mouseEvent);
789 break;
790 case MouseEvent::Type::WheelMovedRight:
791 mouseListener->mouseWheelMovedRight(mouseEvent);
792 break;
793 case MouseEvent::Type::WheelMovedLeft:
794 mouseListener->mouseWheelMovedLeft(mouseEvent);
795 break;
796 case MouseEvent::Type::Dragged:
797 mouseListener->mouseDragged(mouseEvent);
798 break;
799 case MouseEvent::Type::Clicked:
800 mouseListener->mouseClicked(mouseEvent);
801 break;
802 default:
803 throwException("Unknown mouse event type.");
804 }
805 }
806
807 if (toSourceOnly) {
808 break;
809 }
810 }
811
812 Widget const * swap = widget;
813 widget = parent;
814 parent = swap->getParent();
815
816 // If a non-modal focused widget has been reached
817 // and we have modal focus cancel the distribution.
818 if (mFocusHandler->getModalFocused() != nullptr && widget != nullptr && !widget->isModalFocused()) {
819 break;
820 }
821
822 // If a non-modal mouse input focused widget has been reached
823 // and we have modal mouse input focus cancel the distribution.
824 if (mFocusHandler->getModalMouseInputFocused() != nullptr && widget != nullptr &&
825 !widget->isModalMouseInputFocused()) {
826 break;
827 }
828 }
829 }
830
832 {
833 Widget* parent = keyEvent.getSource();
834 Widget* widget = keyEvent.getSource();
835
836 if (mFocusHandler->getModalFocused() != nullptr && !widget->isModalFocused()) {
837 return;
838 }
839
840 while (parent != nullptr) {
841 // If the widget has been removed due to input
842 // cancel the distribution.
843 if (!Widget::widgetExists(widget)) {
844 break;
845 }
846
847 parent = widget->getParent();
848
849 if (widget->isEnabled()) {
850 keyEvent.mDistributor = widget;
851 std::list<KeyListener*> keyListeners = widget->_getKeyListeners();
852
853 // Send the event to all key listeners of the source widget.
854 for (auto& keyListener : keyListeners) {
855 switch (keyEvent.getType()) {
856 case KeyEvent::Type::Pressed:
857 keyListener->keyPressed(keyEvent);
858 break;
859 case KeyEvent::Type::Released:
860 keyListener->keyReleased(keyEvent);
861 break;
862 default:
863 throwException("Unknown key event type.");
864 }
865 }
866 }
867
868 Widget const * swap = widget;
869 widget = parent;
870 parent = swap->getParent();
871
872 // If a non-modal focused widget has been reached
873 // and we have modal focus cancel the distribution.
874 if (mFocusHandler->getModalFocused() != nullptr && !widget->isModalFocused()) {
875 break;
876 }
877 }
878 }
879
881 {
883
884 for (it = mKeyListeners.begin(); it != mKeyListeners.end(); ++it) {
885 switch (keyEvent.getType()) {
886 case KeyEvent::Type::Pressed:
887 (*it)->keyPressed(keyEvent);
888 break;
889 case KeyEvent::Type::Released:
890 (*it)->keyReleased(keyEvent);
891 break;
892 default:
893 throwException("Unknown key event type.");
894 }
895
896 if (keyEvent.isConsumed()) {
897 break;
898 }
899 }
900 }
901
903 {
904 // Check if modal mouse input focus has been gained by a widget.
905 if ((mFocusHandler->getLastWidgetWithModalMouseInputFocus() != mFocusHandler->getModalMouseInputFocused()) &&
906 (mFocusHandler->getLastWidgetWithModalMouseInputFocus() == nullptr)) {
908 } else if (
909 // Check if modal mouse input focus has been released.
910 (mFocusHandler->getLastWidgetWithModalMouseInputFocus() != mFocusHandler->getModalMouseInputFocused()) &&
911 (mFocusHandler->getLastWidgetWithModalMouseInputFocus() != nullptr)) {
913 }
914 }
915
917 {
918 // Check if modal focus has been gained by a widget.
919 if ((mFocusHandler->getLastWidgetWithModalFocus() != mFocusHandler->getModalFocused()) &&
920 (mFocusHandler->getLastWidgetWithModalFocus() == nullptr)) {
922 } else if (
923 // Check if modal focus has been released.
924 (mFocusHandler->getLastWidgetWithModalFocus() != mFocusHandler->getModalFocused()) &&
925 (mFocusHandler->getLastWidgetWithModalFocus() != nullptr)) {
927 }
928 }
929
931 {
932 // Get all widgets at the last known mouse position
933 // and send them a mouse exited event.
934 std::set<Widget*> const mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
935
936 std::set<Widget*>::const_iterator iter;
937 for (iter = mWidgetsWithMouse.begin(); iter != mWidgetsWithMouse.end(); ++iter) {
938 if ((*iter)->isModalFocused() || (*iter)->isModalMouseInputFocused()) {
939 continue;
940 }
942 (*iter),
943 MouseEvent::Type::Exited,
947 true,
948 true);
949 }
950 mFocusHandler->setLastWidgetWithModalFocus(mFocusHandler->getModalFocused());
951 }
952
954 {
955 // Get all widgets at the last known mouse position
956 // and send them a mouse entered event.
957 std::set<Widget*> const mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
958
959 std::set<Widget*>::const_iterator iter;
960 for (iter = mWidgetsWithMouse.begin(); iter != mWidgetsWithMouse.end(); ++iter) {
962 (*iter),
963 MouseEvent::Type::Entered,
967 false,
968 true);
969 }
970 mFocusHandler->setLastWidgetWithModalFocus(nullptr);
971 }
972
974 {
975 // Get all widgets at the last known mouse position
976 // and send them a mouse exited event.
977 std::set<Widget*> const mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
978
979 std::set<Widget*>::const_iterator iter;
980 for (iter = mWidgetsWithMouse.begin(); iter != mWidgetsWithMouse.end(); ++iter) {
981 if ((*iter)->isModalMouseInputFocused()) {
982 continue;
983 }
985 (*iter),
986 MouseEvent::Type::Exited,
990 true,
991 true);
992 }
993 mFocusHandler->setLastWidgetWithModalMouseInputFocus(mFocusHandler->getModalMouseInputFocused());
994 }
995
997 {
998 // Get all widgets at the last known mouse position
999 // and send them a mouse entered event.
1000 std::set<Widget*> const mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
1001
1002 std::set<Widget*>::const_iterator iter;
1003 for (iter = mWidgetsWithMouse.begin(); iter != mWidgetsWithMouse.end(); ++iter) {
1005 (*iter),
1006 MouseEvent::Type::Entered,
1010 false,
1011 true);
1012 }
1013 mFocusHandler->setLastWidgetWithModalMouseInputFocus(nullptr);
1014 }
1015
1017 {
1018 // process each hidden widget in queue
1019 while (!mHiddenWidgets.empty()) {
1020 // if the hidden widget had the mouse cursor inside
1021 Widget const * hiddenWidget = mHiddenWidgets.front();
1022
1023 // make sure that the widget wasn't freed after hiding
1024 if (Widget::widgetExists(hiddenWidget) && hiddenWidget->isEnabled()) {
1025 int hiddenWidgetX = 0;
1026 int hiddenWidgetY = 0;
1027 hiddenWidget->getAbsolutePosition(hiddenWidgetX, hiddenWidgetY);
1028
1029 Rectangle const r(hiddenWidgetX, hiddenWidgetY, hiddenWidget->getWidth(), hiddenWidget->getHeight());
1030
1032 // get the widget that has the cursor now and distribute that the mouse entered it
1033 Widget* underMouseCursor = getWidgetAt(mLastMouseX, mLastMouseY);
1034
1036 underMouseCursor,
1037 MouseEvent::Type::Entered,
1038 MouseEvent::Button::Empty,
1041 true,
1042 true);
1043 }
1044 }
1045
1046 mHiddenWidgets.pop();
1047 }
1048 }
1049
1051 {
1052 // process each shown widget in queue
1053 while (!mShownWidgets.empty()) {
1054 Widget* shownWidget = mShownWidgets.front();
1055
1056 // if the shown widget has the mouse cursor inside it
1057 int shownWidgetX = 0;
1058 int shownWidgetY = 0;
1059 shownWidget->getAbsolutePosition(shownWidgetX, shownWidgetY);
1060
1061 Rectangle const r(shownWidgetX, shownWidgetY, shownWidget->getWidth(), shownWidget->getHeight());
1062
1063 if (r.isContaining(mLastMouseX, mLastMouseY) && shownWidget->isEnabled()) {
1064 // find which widget had the mouse before and distribute that the mouse exited it
1065 Widget* underMouseCursorBefore = getWidgetAt(mLastMouseX, mLastMouseY, shownWidget);
1066
1068 underMouseCursorBefore,
1069 MouseEvent::Type::Exited,
1070 MouseEvent::Button::Empty,
1073 true,
1074 true);
1075
1076 // find which specific widget in the shown widget had the mouse before
1077 // and distribute that the mouse exited it
1078 Widget* underMouseCursorNow = getWidgetAt(mLastMouseX, mLastMouseY);
1079
1081 underMouseCursorNow,
1082 MouseEvent::Type::Entered,
1083 MouseEvent::Button::Empty,
1086 true,
1087 true);
1088 }
1089
1090 mShownWidgets.pop();
1091 }
1092 }
1093} // namespace fcn
Base class for all GUI event objects.
Definition event.hpp:24
Widget * getSource() const
Gets the source widget of the event.
Definition event.cpp:11
Manages focus navigation and assignment among widgets within a Gui instance.
virtual Widget * getFocused() const
Gets the widget with focus.
Abstract interface providing primitive drawing functions (lines, rectangles, etc.).
Definition graphics.hpp:57
Internal listener that forwards death notifications from widgets to the owning Gui so it can clean up...
Definition gui.cpp:38
GuiDeathListener(Gui *gui)
Construct a GuiDeathListener bound to a Gui instance.
Definition gui.cpp:41
void death(Event const &event) override
Called when a widget dies.
Definition gui.cpp:44
The central GUI manager.
Definition gui.hpp:101
Graphics * mGraphics
Holds the graphics implementation used.
Definition gui.hpp:544
virtual void distributeKeyEvent(KeyEvent &keyEvent)
Distributes a key event.
Definition gui.cpp:831
Widget * mTop
Holds the top widget.
Definition gui.hpp:529
Input * mInput
Holds the input implementation used.
Definition gui.hpp:549
virtual void distributeKeyEventToGlobalKeyListeners(KeyEvent &keyEvent)
Distributes a key event to the global key listeners.
Definition gui.cpp:880
virtual Graphics * getGraphics() const
Gets the graphics object used for drawing.
Definition gui.cpp:152
virtual Input * getInput() const
Gets the input object being used for input handling.
Definition gui.cpp:171
virtual void handleModalMouseInputFocus()
Handles modal mouse input focus.
Definition gui.cpp:902
virtual void focusNone()
Focuses none of the widgets in the Gui.
Definition gui.cpp:245
Widget * getWidgetAt(int x, int y)
Gets the widget at a certain position.
Definition gui.hpp:489
FocusHandler * mFocusHandler
Holds the focus handler for the Gui.
Definition gui.hpp:554
virtual void setRoot(Widget *top)
Alias for setTop.
Definition gui.cpp:123
KeyListenerList mKeyListeners
Holds the global key listeners of the Gui.
Definition gui.hpp:579
bool mTabbing
True if tabbing is enabled, false otherwise.
Definition gui.hpp:564
virtual void draw()
Draws the GUI.
Definition gui.cpp:226
std::queue< Widget * > mShownWidgets
Holds shown widgets.
Definition gui.hpp:539
int mLastMouseDragButton
Holds the last button used when a drag of a widget was initiated.
Definition gui.hpp:632
virtual std::set< Widget * > getWidgetsAt(int x, int y)
Gets all widgets a certain coordinate in the Gui.
Definition gui.cpp:690
virtual void handleModalFocus()
Handles modal focus.
Definition gui.cpp:916
virtual FocusHandler * getFocusHandler() const
Returns the focus handler used by this GUI.
Definition gui.cpp:184
void distributeMouseEvent(Widget *source, MouseEvent::Type type, MouseEvent::Button button, int x, int y)
Convenience overload: distribute a mouse event forwarding to the full overload with force and toSourc...
Definition gui.hpp:432
int mLastMousePressTimeStamp
Holds the last mouse press time stamp.
Definition gui.hpp:609
virtual Widget * getMouseEventSource(int x, int y)
Gets the source of the mouse event.
Definition gui.cpp:707
unsigned int mLastMousePressButton
Holds the last mouse button pressed.
Definition gui.hpp:604
virtual void handleMouseInput()
Handles all mouse input.
Definition gui.cpp:303
virtual void setGraphics(Graphics *graphics)
Sets the graphics object to use for drawing.
Definition gui.cpp:138
virtual void handleModalFocusReleased()
Handles modal focus released.
Definition gui.cpp:953
bool mControlPressed
True if control is pressed, false otherwise.
Definition gui.hpp:594
bool mMetaPressed
True if meta is pressed, false otherwise.
Definition gui.hpp:589
virtual void initialize(std::unique_ptr< Graphics > graphics, std::unique_ptr< Input > input, int width, int height)
Initializes GUI backends in one call.
Definition gui.cpp:176
std::unique_ptr< Widget > mOwnedTop
Optional owned top widget (when Gui takes ownership).
Definition gui.hpp:638
std::unique_ptr< Graphics > mOwnedGraphics
Optional owned graphics backend instance.
Definition gui.hpp:641
virtual Widget * getKeyEventSource()
Gets the source of the key event.
Definition gui.cpp:718
int mLastMouseX
Holds the last mouse x coordinate.
Definition gui.hpp:614
virtual void handleMouseWheelMovedRight(MouseInput const &mouseInput)
Handles mouse wheel moved right input.
Definition gui.cpp:589
virtual void setInput(Input *input)
Sets the input object to use for input handling.
Definition gui.cpp:157
void enqueueShownWidget(Widget *widget)
Inform gui that a widget was shown.
Definition gui.cpp:275
virtual void setTabbingEnabled(bool tabbing)
Sets tabbing enabled, or not.
Definition gui.cpp:250
virtual void handleModalMouseInputFocusReleased()
Handles modal mouse input focus released.
Definition gui.cpp:996
virtual Widget * getTop() const
Gets the top widget.
Definition gui.cpp:133
virtual void handleKeyInput()
Handles key input.
Definition gui.cpp:344
virtual void handleHiddenWidgets()
Handles hidden widgets.
Definition gui.cpp:1016
std::queue< Widget * > mHiddenWidgets
Holds hidden widgets.
Definition gui.hpp:534
bool mAltPressed
True if alt is pressed, false otherwise.
Definition gui.hpp:599
int mLastMouseY
Holds the last mouse y coordinate.
Definition gui.hpp:619
KeyListenerList::iterator KeyListenerListIterator
Typedef.
Definition gui.hpp:574
virtual void handleShownWidgets()
Handles shown widgets.
Definition gui.cpp:1050
GuiDeathListener * mDeathListener
Listener notified when the GUI or top widget is destroyed.
Definition gui.hpp:635
virtual void handleMouseWheelMovedDown(MouseInput const &mouseInput)
Handles mouse wheel moved down input.
Definition gui.cpp:549
virtual bool isTabbingEnabled()
Checks if tabbing is enabled.
Definition gui.cpp:255
std::unique_ptr< Input > mOwnedInput
Optional owned input backend instance.
Definition gui.hpp:644
virtual void removeGlobalKeyListener(KeyListener *keyListener)
Removes global key listener from the Gui.
Definition gui.cpp:265
std::shared_ptr< Font > mGlobalFont
Shared global font used by widgets when not overridden.
Definition gui.hpp:647
virtual void handleModalMouseInputFocusGained()
Handles modal mouse input focus gained.
Definition gui.cpp:973
virtual void handleMouseMoved(MouseInput const &mouseInput)
Handles mouse moved input.
Definition gui.cpp:413
virtual void handleMouseWheelMovedUp(MouseInput const &mouseInput)
Handles mouse wheel moved up input.
Definition gui.cpp:569
virtual void addGlobalKeyListener(KeyListener *keyListener)
Adds a global key listener to the Gui.
Definition gui.cpp:260
virtual void setTop(Widget *top)
Sets the top widget.
Definition gui.cpp:101
bool mShiftPressed
True if shift is pressed, false otherwise.
Definition gui.hpp:584
void enqueueHiddenWidget(Widget *widget)
Inform gui that a widget was hidden.
Definition gui.cpp:270
virtual void setGlobalFont(std::string const &filename, int size)
Loads a font using the active graphics backend and sets it as global widget font.
Definition gui.cpp:189
virtual void handleModalFocusGained()
Handles modal focus gained.
Definition gui.cpp:930
virtual void handleMouseWheelMovedLeft(MouseInput const &mouseInput)
Handles mouse wheel moved left input.
Definition gui.cpp:609
virtual void handleMousePressed(MouseInput const &mouseInput)
Handles mouse pressed input.
Definition gui.cpp:509
void widgetDied(Widget const *widget)
Inform gui that a widget was deleted.
Definition gui.cpp:280
virtual void logic()
Performs logic of the GUI.
Definition gui.cpp:204
int mClickCount
Holds the current click count.
Definition gui.hpp:625
VisibilityEventHandler * mVisibilityEventHandler
Holds the visibility event handler for the Gui.
Definition gui.hpp:559
virtual void handleMouseReleased(MouseInput const &mouseInput)
Handles mouse released input.
Definition gui.cpp:629
bool isConsumed() const
Checks if the input event is consumed.
Widget * mDistributor
Holds the distributor of the event.
Abstract interface for polling user input devices.
Definition input.hpp:28
Represents a key event.
Definition keyevent.hpp:22
Type getType() const
Gets the type of the event.
Definition keyevent.cpp:29
Type
Key event types.
Definition keyevent.hpp:28
Internal class representing raw keyboard input data.
Definition keyinput.hpp:28
bool isAltPressed() const
Checks if alt is pressed.
Definition keyinput.cpp:53
bool isNumericPad() const
Checks if the key was pressed at the numeric pad.
Definition keyinput.cpp:73
Type getType() const
Gets the type of the key input.
Definition keyinput.cpp:18
bool isControlPressed() const
Checks if control is pressed.
Definition keyinput.cpp:43
bool isMetaPressed() const
Checks if meta is pressed.
Definition keyinput.cpp:63
Key const & getKey() const
Gets the key of the key input.
Definition keyinput.cpp:28
bool isShiftPressed() const
Checks if shift is pressed.
Definition keyinput.cpp:33
Interface for listening to keyboard events.
int getValue() const
Gets the value of the key.
Definition key.cpp:28
Represents a mouse event.
MouseEvent::Type getType() const
Gets the type of the event.
Button
Mouse button types.
int mY
Holds the y-coordinate of the mouse event.
Type
Mouse event types.
int mX
Holds the x-coordinate of the mouse event.
Internal class representing raw mouse input data.
Type getType() const
Gets the type of the mouse input.
Button getButton() const
Gets the button pressed.
int getX() const
Gets the x coordinate of the mouse input.
int getY() const
Gets the y coordinate of the mouse input.
int getTimeStamp() const
Gets the time stamp of the input.
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:20
bool isContaining(int x, int y) const
Checks the rectangle contains a point.
Definition rectangle.cpp:52
Abstract base class defining the common behavior, properties, and lifecycle of all GUI elements.
Definition widget.hpp:45
virtual bool isModalMouseInputFocused() const
Checks if the widget or it's parent has modal mouse input focus.
Definition widget.cpp:941
virtual bool isModalFocused() const
Checks if the widget or it's parent has modal focus.
Definition widget.cpp:928
int getWidth() const
Gets the width of the widget.
Definition widget.cpp:170
virtual Widget * getParent() const
Gets the widget's parent container.
Definition widget.cpp:157
virtual std::list< KeyListener * > const & _getKeyListeners()
Gets the key listeners of the widget.
Definition widget.cpp:982
bool isEnabled() const
Checks if the widget is enabled.
Definition widget.cpp:869
virtual void requestFocus()
Requests focus for the widget.
Definition widget.cpp:520
virtual FocusHandler * _getInternalFocusHandler()
Gets the internal focus handler used.
Definition widget.cpp:997
Widget * getWidgetAt(int x, int y)
Gets a widget at a certain position in the widget.
Definition widget.hpp:1135
static bool widgetExists(Widget const *widget)
Checks if a widget exists or not, that is if it still exists an instance of the object.
Definition widget.cpp:827
static void _setGuiDeathListener(DeathListener *deathListener)
Set the global GUI death listener used to observe widget deletions.
Definition widget.cpp:704
virtual std::list< MouseListener * > const & _getMouseListeners()
Gets the mouse listeners of the widget.
Definition widget.cpp:977
static void _setVisibilityEventHandler(VisibilityEventHandler *visibilityEventHandler)
Sets the visibility event handler to be used.
Definition widget.cpp:694
static void setGlobalFont(Font *font)
Sets the global font to be used by default for all widgets.
Definition widget.cpp:809
virtual void getAbsolutePosition(int &x, int &y) const
Gets the absolute position on the screen for the widget.
Definition widget.cpp:774
int getHeight() const
Gets the height of the widget.
Definition widget.cpp:183
virtual void _setFocusHandler(FocusHandler *focusHandler)
Sets the focus handler to be used.
Definition widget.cpp:661