6#include "fifechan/gui.hpp"
20#include "fifechan/platform.hpp"
23#include "fifechan/events/keyevent.hpp"
24#include "fifechan/events/mouseevent.hpp"
25#include "fifechan/events/textinputevent.hpp"
26#include "fifechan/exception.hpp"
27#include "fifechan/focushandler.hpp"
28#include "fifechan/graphics.hpp"
29#include "fifechan/input.hpp"
30#include "fifechan/keyinput.hpp"
31#include "fifechan/listeners/deathlistener.hpp"
32#include "fifechan/listeners/keylistener.hpp"
33#include "fifechan/listeners/mouselistener.hpp"
34#include "fifechan/listeners/visibilityeventhandler.hpp"
35#include "fifechan/mouseinput.hpp"
36#include "fifechan/rectangle.hpp"
37#include "fifechan/widget.hpp"
73 mFocusHandler(new FocusHandler()),
74 mVisibilityEventHandler(new VisibilityEventHandler(this)),
77 mControlPressed(false),
79 mLastMousePressButton(0),
80 mLastMousePressTimeStamp(0),
84 mLastMouseDragButton(0),
85 mDeathListener(new GuiDeathListener(this))
87 assert(
"focus handler must be allocated" && mFocusHandler !=
nullptr);
88 assert(
"visibility handler must be allocated" && mVisibilityEventHandler !=
nullptr);
89 assert(
"death listener must be allocated" && mDeathListener !=
nullptr);
99 if (
mTop !=
nullptr) {
100 mTop->_setFocusHandler(
nullptr);
120 if (
mTop !=
nullptr) {
121 mTop->_setFocusHandler(
nullptr);
123 if (top !=
nullptr) {
193 void Gui::initialize(std::unique_ptr<Graphics> graphics, std::unique_ptr<Input> input,
int width,
int height)
212 auto createdFont =
mGraphics->createFont(filename, size);
214 throwException(
"Failed to create font using active graphics backend");
223 assert(
"Top widget must be set" &&
mTop !=
nullptr);
225 if (
mTop ==
nullptr) {
248 assert(
"Top widget must be set" &&
mTop !=
nullptr);
249 assert(
"Graphics must be set" &&
mGraphics !=
nullptr);
251 if (
mTop ==
nullptr) {
259 if (!
mTop->isVisible()) {
311 while (!
mInput->isMouseQueueEmpty()) {
314 switch (mouseInput.
getType()) {
315 case MouseInput::Type::Pressed:
318 case MouseInput::Type::Released:
321 case MouseInput::Type::Moved:
324 case MouseInput::Type::WheelMovedDown:
327 case MouseInput::Type::WheelMovedUp:
330 case MouseInput::Type::WheelMovedRight:
333 case MouseInput::Type::WheelMovedLeft:
352 while (!
mInput->isTextQueueEmpty()) {
353 std::string
const text =
mInput->dequeueTextInput();
356 if (focused ==
nullptr || !focused->
isEnabled()) {
376 while (!
mInput->isKeyQueueEmpty()) {
385 KeyEvent keyEventToGlobalKeyListeners(
400 if (keyEventToGlobalKeyListeners.
isConsumed()) {
404 bool keyEventConsumed =
false;
432 if (!keyEventConsumed &&
mTabbing && keyInput.
getKey().getValue() == fcn::Key::TAB &&
433 keyInput.
getType() == KeyInput::Type::Pressed) {
449 if (mouseInput.
getX() < 0 || mouseInput.
getY() < 0 ||
450 !
mTop->getDimension().isContaining(mouseInput.
getX(), mouseInput.
getY())) {
451 for (
auto const & w : mLastWidgetsWithMouse) {
454 MouseEvent::Type::Exited,
467 std::vector<Widget*>
const mWidgetsWithMouse =
getWidgetsAt(mouseInput.
getX(), mouseInput.
getY());
468 std::vector<Widget*> mWidgetsWithMouseExited;
469 std::vector<Widget*> mWidgetsWithMouseEntered;
472 std::ranges::copy_if(
473 mLastWidgetsWithMouse,
475 std::back_inserter(mWidgetsWithMouseExited),
477 return std::ranges::find(mWidgetsWithMouse, w) == mWidgetsWithMouse.end();
480 std::ranges::copy_if(
483 std::back_inserter(mWidgetsWithMouseEntered),
485 return std::ranges::find(mLastWidgetsWithMouse, w) == mLastWidgetsWithMouse.end();
488 for (
auto const & w : mWidgetsWithMouseExited) {
491 MouseEvent::Type::Exited,
503 for (
auto const & widget : mWidgetsWithMouseEntered) {
507 if (!
mFocusHandler->hasModalFocus() || widget->isUnderMouseModal()) {
510 MouseEvent::Type::Entered,
523 MouseEvent::Type::Dragged,
531 MouseEvent::Type::Moved,
546 int sourceWidgetX = 0;
547 int sourceWidgetY = 0;
559 MouseEvent::Type::Pressed,
585 int sourceWidgetX = 0;
586 int sourceWidgetY = 0;
591 MouseEvent::Type::WheelMovedDown,
605 int sourceWidgetX = 0;
606 int sourceWidgetY = 0;
611 MouseEvent::Type::WheelMovedUp,
625 int sourceWidgetX = 0;
626 int sourceWidgetY = 0;
631 MouseEvent::Type::WheelMovedRight,
645 int sourceWidgetX = 0;
646 int sourceWidgetY = 0;
651 MouseEvent::Type::WheelMovedLeft,
669 int sourceWidgetX = 0;
670 int sourceWidgetY = 0;
675 MouseEvent::Type::Released,
684 MouseEvent::Type::Clicked,
702 assert(
"Top widget must be set" &&
mTop !=
nullptr);
708 while (child !=
nullptr) {
713 child = parent->
getWidgetAt(x - parentX, y - parentY, exclude);
722 assert(
"Top widget must be set" &&
mTop !=
nullptr);
724 std::vector<Widget*> result;
728 while (widget !=
nullptr) {
729 result.push_back(widget);
733 widget = widget->
getWidgetAt(x - absoluteX, y - absoluteY);
751 if (child ==
nullptr || ancestor ==
nullptr) {
756 while (current !=
nullptr) {
757 if (current == ancestor) {
770 fcn::Widget* getMouseCapture()
782 if (widget !=
nullptr) {
789 if (widget !=
nullptr) {
801 if (leaf ==
nullptr) {
806 if (
Widget* explicitCapture = getMouseCapture()) {
807 if (isDescendantOf(leaf, explicitCapture)) {
810 return explicitCapture;
815 if (modalRoot ==
nullptr) {
820 if (isDescendantOf(leaf, modalRoot)) {
853 if (modalFocus !=
nullptr && !widget->
isModalFocused() && !force) {
863 source, source,
mShiftPressed,
mControlPressed,
mAltPressed,
mMetaPressed, type, button, x, y,
mClickCount);
865 while (parent !=
nullptr) {
879 mouseEvent.
mX = x - widgetX;
880 mouseEvent.
mY = y - widgetY;
885 for (
auto& mouseListener : mouseListeners) {
886 switch (mouseEvent.
getType()) {
887 case MouseEvent::Type::Entered:
888 mouseListener->mouseEntered(mouseEvent);
890 case MouseEvent::Type::Exited:
891 mouseListener->mouseExited(mouseEvent);
893 case MouseEvent::Type::Moved:
894 mouseListener->mouseMoved(mouseEvent);
896 case MouseEvent::Type::Pressed:
897 mouseListener->mousePressed(mouseEvent);
899 case MouseEvent::Type::Released:
900 mouseListener->mouseReleased(mouseEvent);
902 case MouseEvent::Type::WheelMovedUp:
903 mouseListener->mouseWheelMovedUp(mouseEvent);
905 case MouseEvent::Type::WheelMovedDown:
906 mouseListener->mouseWheelMovedDown(mouseEvent);
908 case MouseEvent::Type::WheelMovedRight:
909 mouseListener->mouseWheelMovedRight(mouseEvent);
911 case MouseEvent::Type::WheelMovedLeft:
912 mouseListener->mouseWheelMovedLeft(mouseEvent);
914 case MouseEvent::Type::Dragged:
915 mouseListener->mouseDragged(mouseEvent);
917 case MouseEvent::Type::Clicked:
918 mouseListener->mouseClicked(mouseEvent);
924 Widget const * swap = widget;
931 if (modalFocusLoop !=
nullptr && widget !=
nullptr && !widget->
isModalFocused()) {
938 if (modalMouseLoop !=
nullptr && widget !=
nullptr && !widget->
isUnderMouseModal()) {
954 while (parent !=
nullptr) {
968 for (
auto& keyListener : keyListeners) {
970 case KeyEvent::Type::Pressed:
971 keyListener->keyPressed(keyEvent);
973 case KeyEvent::Type::Released:
974 keyListener->keyReleased(keyEvent);
982 Widget const * swap = widget;
1001 case KeyEvent::Type::Pressed:
1002 (*it)->keyPressed(keyEvent);
1004 case KeyEvent::Type::Released:
1005 (*it)->keyReleased(keyEvent);
1021 (
mFocusHandler->getLastWidgetWithModalMouseInputFocus() ==
nullptr)) {
1026 (
mFocusHandler->getLastWidgetWithModalMouseInputFocus() !=
nullptr)) {
1035 (
mFocusHandler->getLastWidgetWithModalFocus() ==
nullptr)) {
1040 (
mFocusHandler->getLastWidgetWithModalFocus() !=
nullptr)) {
1051 for (
auto const & w : mWidgetsWithMouse) {
1052 if (w->isModalFocused() || w->isUnderMouseModal()) {
1057 MouseEvent::Type::Exited,
1073 for (
auto const & w : mWidgetsWithMouse) {
1076 MouseEvent::Type::Entered,
1092 for (
auto const & w : mWidgetsWithMouse) {
1093 if (w->isUnderMouseModal()) {
1098 MouseEvent::Type::Exited,
1114 for (
auto const & w : mWidgetsWithMouse) {
1117 MouseEvent::Type::Entered,
1124 mFocusHandler->setLastWidgetWithModalMouseInputFocus(
nullptr);
1133 for (
auto*
const hiddenWidget : pending) {
1136 int hiddenWidgetX = 0;
1137 int hiddenWidgetY = 0;
1138 hiddenWidget->getAbsolutePosition(hiddenWidgetX, hiddenWidgetY);
1140 Rectangle const r(hiddenWidgetX, hiddenWidgetY, hiddenWidget->getWidth(), hiddenWidget->getHeight());
1148 MouseEvent::Type::Entered,
1149 MouseEvent::Button::Empty,
1162 std::vector<Widget*>
const pending = std::move(
mShownWidgets);
1165 for (
auto* shownWidget : pending) {
1167 int shownWidgetX = 0;
1168 int shownWidgetY = 0;
1169 shownWidget->getAbsolutePosition(shownWidgetX, shownWidgetY);
1171 Rectangle const r(shownWidgetX, shownWidgetY, shownWidget->getWidth(), shownWidget->getHeight());
1178 underMouseCursorBefore,
1179 MouseEvent::Type::Exited,
1180 MouseEvent::Button::Empty,
1191 underMouseCursorNow,
1192 MouseEvent::Type::Entered,
1193 MouseEvent::Button::Empty,
Base class for all GUI event objects.
Widget * getSource() const
Gets the source widget of the event.
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.).
Internal listener that forwards death notifications from widgets to the owning Gui so it can clean up...
GuiDeathListener(Gui *gui)
Construct a GuiDeathListener bound to a Gui instance.
void death(Event const &event) override
Called when a widget dies.
Graphics * mGraphics
Holds the graphics implementation used.
virtual void distributeKeyEvent(KeyEvent &keyEvent)
Distributes a key event.
Widget * mTop
Holds the top widget.
Input * mInput
Holds the input implementation used.
virtual void distributeKeyEventToGlobalKeyListeners(KeyEvent &keyEvent)
Distributes a key event to the global key listeners.
virtual Graphics * getGraphics() const
Gets the graphics object used for drawing.
virtual Input * getInput() const
Gets the input object being used for input handling.
virtual void handleModalMouseInputFocus()
Handles modal mouse input focus.
virtual void focusNone()
Focuses none of the widgets in the GUI.
Widget * getWidgetAt(int x, int y)
Gets the widget at a certain position.
FocusHandler * mFocusHandler
Holds the focus handler for the GUI.
void addHiddenWidget(Widget *widget)
Inform gui that a widget was hidden.
virtual void setRoot(Widget *top)
Alias for setTop.
KeyListenerList mKeyListeners
Holds the global key listeners of the GUI.
bool mTabbing
True if tabbing is enabled, false otherwise.
virtual void draw()
Draws the GUI.
int mLastMouseDragButton
Holds the last button used when a drag of a widget was initiated.
virtual void handleModalFocus()
Handles modal focus.
virtual FocusHandler * getFocusHandler() const
Returns the focus handler used by this GUI.
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...
int mLastMousePressTimeStamp
Holds the last mouse press time stamp.
virtual Widget * getMouseEventSource(int x, int y)
Gets the source of the mouse event.
unsigned int mLastMousePressButton
Holds the last mouse button pressed.
void releaseMouse(Widget *widget)
Releases explicit mouse capture from a widget.
virtual void handleMouseInput()
Handles all mouse input.
virtual void setGraphics(Graphics *graphics)
Sets the graphics object to use for drawing.
virtual void handleModalFocusReleased()
Handles modal focus released.
bool mControlPressed
True if control is pressed, false otherwise.
virtual void handleTextInput()
Handles text input from the backend.
bool mMetaPressed
True if meta is pressed, false otherwise.
virtual void initialize(std::unique_ptr< Graphics > graphics, std::unique_ptr< Input > input, int width, int height)
Initializes GUI backends in one call.
std::unique_ptr< Widget > mOwnedTop
Optional owned top widget (when Gui takes ownership).
std::unique_ptr< Graphics > mOwnedGraphics
Optional owned graphics backend instance.
virtual Widget * getKeyEventSource()
Gets the source of the key event.
int mLastMouseX
Holds the last mouse x coordinate.
virtual void handleMouseWheelMovedRight(MouseInput const &mouseInput)
Handles mouse wheel moved right input.
virtual void setInput(Input *input)
Sets the input object to use for input handling.
virtual void setTabbingEnabled(bool tabbing)
Sets tabbing enabled, or not.
virtual void handleModalMouseInputFocusReleased()
Handles modal mouse input focus released.
virtual Widget * getTop() const
Gets the top widget.
virtual void handleKeyInput()
Handles key input.
virtual void handleHiddenWidgets()
Handles hidden widgets.
std::vector< Widget * > mShownWidgets
Holds shown widgets.
virtual std::vector< Widget * > getWidgetsAt(int x, int y)
Gets all widgets a certain coordinate in the GUI.
bool mAltPressed
True if alt is pressed, false otherwise.
std::vector< Widget * > mHiddenWidgets
Holds hidden widgets.
int mLastMouseY
Holds the last mouse y coordinate.
KeyListenerList::iterator KeyListenerListIterator
Iterator for KeyListenerList.
virtual void handleShownWidgets()
Handles shown widgets.
GuiDeathListener * mDeathListener
Listener notified when the GUI or top widget is destroyed.
virtual void handleMouseWheelMovedDown(MouseInput const &mouseInput)
Handles mouse wheel moved down input.
virtual bool isTabbingEnabled()
Checks if tabbing is enabled.
std::unique_ptr< Input > mOwnedInput
Optional owned input backend instance.
virtual void removeGlobalKeyListener(KeyListener *keyListener)
Removes global key listener from the GUI.
std::shared_ptr< Font > mGlobalFont
Shared global font used by widgets when not overridden.
void addShownWidget(Widget *widget)
Inform gui that a widget was shown.
virtual void handleModalMouseInputFocusGained()
Handles modal mouse input focus gained.
virtual void handleMouseMoved(MouseInput const &mouseInput)
Handles mouse moved input.
virtual void handleMouseWheelMovedUp(MouseInput const &mouseInput)
Handles mouse wheel moved up input.
virtual void addGlobalKeyListener(KeyListener *keyListener)
Adds a global key listener to the GUI.
virtual void setTop(Widget *top)
Sets the top widget.
bool mShiftPressed
True if shift is pressed, false otherwise.
virtual void setGlobalFont(std::string const &filename, int size)
Loads a font using the active graphics backend and sets it as global widget font.
virtual void handleModalFocusGained()
Handles modal focus gained.
virtual void handleMouseWheelMovedLeft(MouseInput const &mouseInput)
Handles mouse wheel moved left input.
virtual void handleMousePressed(MouseInput const &mouseInput)
Handles mouse pressed input.
void widgetDied(Widget const *widget)
Inform gui that a widget was deleted.
void captureMouse(Widget *widget)
Explicitly captures mouse input to a widget.
virtual void logic()
Performs logic of the GUI.
int mClickCount
Holds the current click count.
VisibilityEventHandler * mVisibilityEventHandler
Holds the visibility event handler for the GUI.
virtual void handleMouseReleased(MouseInput const &mouseInput)
Handles mouse released input.
Type getType() const
Gets the type of the event.
Interface for listening to keyboard events.
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.
int mX
Holds the x-coordinate of the mouse event.
Represents a rectangular area (X, Y, Width, Height).
bool isContaining(int x, int y) const
Checks the rectangle contains a point.
Text input event for IME (input method editor) composition, dead keys, and pasted text.
@ Input
Text was input (SDL_EVENT_TEXT_INPUT).
Used replacement tokens by configure_file():
void throwException(std::string const &message, std::source_location location=std::source_location::current())
Throw an Exception capturing the current source location.