diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/crepe/api/Button.cpp | 3 | ||||
-rw-r--r-- | src/crepe/api/Button.h | 12 | ||||
-rw-r--r-- | src/crepe/api/Config.h | 5 | ||||
-rw-r--r-- | src/crepe/api/Event.h | 97 | ||||
-rw-r--r-- | src/crepe/api/KeyCodes.h | 6 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 88 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.h | 61 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.cpp | 235 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.h | 13 | ||||
-rw-r--r-- | src/example/button.cpp | 1 | ||||
-rw-r--r-- | src/test/EventTest.cpp | 55 | ||||
-rw-r--r-- | src/test/InputTest.cpp | 84 |
12 files changed, 420 insertions, 240 deletions
diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp index 76f74f0..305922c 100644 --- a/src/crepe/api/Button.cpp +++ b/src/crepe/api/Button.cpp @@ -3,9 +3,8 @@ namespace crepe { Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::function<void()> & on_click, bool is_toggle) + const std::function<void()> & on_click) : UIObject(id, dimensions, offset), - is_toggle(is_toggle), on_click(on_click) {} } // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h index 61b18d7..08f5dec 100644 --- a/src/crepe/api/Button.h +++ b/src/crepe/api/Button.h @@ -15,19 +15,11 @@ public: * \param id The unique ID of the game object associated with this button. * \param dimensions The width and height of the UIObject * \param offset The offset relative this GameObjects Transform - * \param is_toggle Optional flag to indicate if the button is a toggle button. Defaults to false. * \param on_click callback function that will be invoked when the button is clicked. */ Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::function<void()> & on_click, bool is_toggle = false); + const std::function<void()> & on_click); - /** - * \brief Indicates if the button is a toggle button (can be pressed and released). - * - * A toggle button allows for a pressed/released state, whereas a regular button - * typically only has an on-click state. - */ - bool is_toggle = false; // TODO: create separate toggle button class /** * \brief The callback function to be executed when the button is clicked. @@ -56,8 +48,6 @@ public: private: //! friend relation for is_pressed and hover variables friend class InputSystem; - //! Indicates whether the toggle button is pressed - bool is_pressed = false; //! Indicates whether the mouse is currently hovering over the button bool hover = false; diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 6472270..925ded8 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -76,6 +76,11 @@ struct Config final { */ std::string root_pattern = ".crepe-root"; } asset; + //! Configuration for click tolerance. + struct { + //! The maximum number of pixels the mouse can move between MouseDown and MouseUp events to be considered a click. + int click_tolerance = 5; + } input; //! Audio system settings struct { diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index f2f3daf..d3e99af 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -4,7 +4,7 @@ #include <string> #include "KeyCodes.h" - +#include "types.h" namespace crepe { /** @@ -38,11 +38,8 @@ public: */ class MousePressEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position + ivec2 mouse_pos = {0, 0}; //! The mouse button that was pressed. MouseButton button = MouseButton::NONE; @@ -53,11 +50,8 @@ public: */ class MouseClickEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position + ivec2 mouse_pos = {0, 0}; //! The mouse button that was clicked. MouseButton button = MouseButton::NONE; @@ -68,11 +62,8 @@ public: */ class MouseReleaseEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position + ivec2 mouse_pos = {0, 0}; //! The mouse button that was released. MouseButton button = MouseButton::NONE; @@ -83,17 +74,10 @@ public: */ class MouseMoveEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; - - // Movement since last event in x - int delta_x = 0; - - // Movement since last event in y - int delta_y = 0; + //! new mouse position + ivec2 mouse_pos = {0, 0}; + //! The change in mouse position relative to the last position (in pixels). + ivec2 mouse_delta = {0, 0}; }; /** @@ -101,12 +85,8 @@ public: */ class MouseScrollEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; - + //! mouse position when the scroll happened. + ivec2 mouse_pos = {0, 0}; //! scroll direction (-1 = down, 1 = up) int scroll_direction = 0; //! scroll amount in y axis (from and away from the person). @@ -127,4 +107,55 @@ public: */ class ShutDownEvent : public Event {}; +/** + * \brief Event triggered to indicate the window is overlapped by another window. + * + * When two windows overlap the bottom window gets distorted and that window has to be redrawn. + */ +class WindowExposeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window is resized. + */ +class WindowResizeEvent : public Event { +public: + //! new window dimensions + ivec2 dimensions = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is moved. + */ +class WindowMoveEvent : public Event { +public: + //! The change in position relative to the last position (in pixels). + ivec2 delta_move = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is minimized. + */ +class WindowMinimizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window is maximized + */ +class WindowMaximizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window gained focus + * + * This event is triggered when the window receives focus, meaning it becomes the active window + * for user interaction. + */ +class WindowFocusGainEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window lost focus + * + * This event is triggered when the window loses focus, meaning it is no longer the active window + * for user interaction. + */ +class WindowFocusLostEvent : public Event {}; + } // namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index fcfc080..f704fbb 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -13,7 +13,7 @@ enum class MouseButton { }; //! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. -enum class Keycode { +typedef enum { NONE = 0, //!< No key input. SPACE = 32, //!< Spacebar. APOSTROPHE = 39, //!< Apostrophe ('). @@ -150,5 +150,7 @@ enum class Keycode { RIGHT_SUPER = 347, /// \} MENU = 348, //!< Menu key. -}; + //! Not actually a key instead its the amount of keycodes there are for array indexing + NUM_KEYCODES = 512, +} Keycode; } // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 20bb030..4552605 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -75,11 +75,12 @@ SDLContext::~SDLContext() { SDL_Quit(); } -Keycode SDLContext::sdl_to_keycode(SDL_Keycode sdl_key) { +Keycode SDLContext::sdl_to_keycode(SDL_Scancode sdl_key) { static const std::array<Keycode, SDL_NUM_SCANCODES> LOOKUP_TABLE = [] { std::array<Keycode, SDL_NUM_SCANCODES> table{}; table.fill(Keycode::NONE); + // Map all SDL scancodes to Keycodes table[SDL_SCANCODE_SPACE] = Keycode::SPACE; table[SDL_SCANCODE_APOSTROPHE] = Keycode::APOSTROPHE; table[SDL_SCANCODE_COMMA] = Keycode::COMMA; @@ -181,13 +182,27 @@ Keycode SDLContext::sdl_to_keycode(SDL_Keycode sdl_key) { return table; }(); - if (sdl_key < 0 || sdl_key >= SDL_NUM_SCANCODES) { return Keycode::NONE; } - return LOOKUP_TABLE[sdl_key]; } +std::array<bool, Keycode::NUM_KEYCODES> SDLContext::get_keyboard_state() { + // Array to hold the key states (true if pressed, false if not) + std::array<bool, Keycode::NUM_KEYCODES> keyState{}; + SDL_PumpEvents(); + const Uint8 * current_state = SDL_GetKeyboardState(nullptr); + + for (int i = 0; i < SDL_NUM_SCANCODES; ++i) { + Keycode key = sdl_to_keycode(static_cast<SDL_Scancode>(i)); + + if (key != Keycode::NONE) { + keyState[key] = current_state[i] != 0; + } + } + + return keyState; +} MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) { static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] { @@ -368,35 +383,32 @@ ivec2 SDLContext::get_size(const Texture & ctx) { std::vector<SDLContext::EventData> SDLContext::get_events() { std::vector<SDLContext::EventData> event_list; SDL_Event event; - const CameraAuxiliaryData & cam = this->cam_aux_data; while (SDL_PollEvent(&event)) { ivec2 mouse_pos; mouse_pos.x = (event.button.x - cam.bar_size.x) / cam.render_scale.x; mouse_pos.y = (event.button.y - cam.bar_size.y) / cam.render_scale.y; switch (event.type) { case SDL_QUIT: - event_list.push_back(EventData{ - .event_type = SDLContext::EventType::SHUTDOWN, - }); + event_list.push_back({SDLContext::EventType::SHUTDOWN, {}, {}, {}}); break; case SDL_KEYDOWN: - event_list.push_back(EventData{ - .event_type = SDLContext::EventType::KEYDOWN, - .key = sdl_to_keycode(event.key.keysym.scancode), - .key_repeat = (event.key.repeat != 0), - }); + event_list.push_back( + {SDLContext::EventType::KEYDOWN, + {sdl_to_keycode(event.key.keysym.scancode), event.key.repeat != 0}, + {}, + {}}); break; case SDL_KEYUP: - event_list.push_back(EventData{ - .event_type = SDLContext::EventType::KEYUP, - .key = sdl_to_keycode(event.key.keysym.scancode), - }); + event_list.push_back({SDLContext::EventType::KEYUP, + {sdl_to_keycode(event.key.keysym.scancode), false}, + {}, + {}}); break; case SDL_MOUSEBUTTONDOWN: event_list.push_back(EventData{ .event_type = SDLContext::EventType::MOUSEDOWN, .mouse_button = sdl_to_mousebutton(event.button.button), - .mouse_position = mouse_pos, + .mouse_position = {event.button.x, event.button.y}, }); break; case SDL_MOUSEBUTTONUP: { @@ -405,21 +417,21 @@ std::vector<SDLContext::EventData> SDLContext::get_events() { event_list.push_back(EventData{ .event_type = SDLContext::EventType::MOUSEUP, .mouse_button = sdl_to_mousebutton(event.button.button), - .mouse_position = mouse_pos, + .mouse_position = {event.button.x, event.button.y}, }); } break; case SDL_MOUSEMOTION: { event_list.push_back( EventData{.event_type = SDLContext::EventType::MOUSEMOVE, - .mouse_position = mouse_pos, + .mouse_position = {event.motion.x, event.motion.y}, .rel_mouse_move = {event.motion.xrel, event.motion.yrel}}); } break; case SDL_MOUSEWHEEL: { event_list.push_back(EventData{ .event_type = SDLContext::EventType::MOUSEWHEEL, - .mouse_position = mouse_pos, + .mouse_position = {event.motion.x, event.motion.y}, // TODO: why is this needed? .scroll_direction = event.wheel.y < 0 ? -1 : 1, .scroll_delta = event.wheel.preciseY, @@ -427,8 +439,44 @@ std::vector<SDLContext::EventData> SDLContext::get_events() { } break; } } + return event_list; } + +// Separate function for SDL_WINDOWEVENT subtypes +void SDLContext::handle_window_event(const SDL_WindowEvent & window_event, + std::vector<SDLContext::EventData> & event_list) { + switch (window_event.event) { + case SDL_WINDOWEVENT_EXPOSED: + event_list.push_back({SDLContext::EventType::WINDOW_EXPOSE, {}, {}, {}}); + break; + case SDL_WINDOWEVENT_RESIZED: + event_list.push_back({SDLContext::EventType::WINDOW_RESIZE, + {}, + {}, + {{}, {window_event.data1, window_event.data2}}}); + break; + case SDL_WINDOWEVENT_MOVED: + event_list.push_back({SDLContext::EventType::WINDOW_MOVE, + {}, + {}, + {{window_event.data1, window_event.data2}, {}}}); + break; + case SDL_WINDOWEVENT_MINIMIZED: + event_list.push_back({SDLContext::EventType::WINDOW_MINIMIZE, {}, {}, {}}); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + event_list.push_back({SDLContext::EventType::WINDOW_MAXIMIZE, {}, {}, {}}); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + event_list.push_back({SDLContext::EventType::WINDOW_FOCUS_GAIN, {}, {}, {}}); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + event_list.push_back({SDLContext::EventType::WINDOW_FOCUS_LOST, {}, {}, {}}); + break; + } +} + void SDLContext::set_color_texture(const Texture & texture, const Color & color) { SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b); SDL_SetTextureAlphaMod(texture.get_img(), color.a); diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index bcadf87..0a2456d 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -5,6 +5,7 @@ #include <SDL2/SDL_rect.h> #include <SDL2/SDL_render.h> #include <SDL2/SDL_video.h> +#include <array> #include <cmath> #include <functional> #include <memory> @@ -79,19 +80,54 @@ public: KEYUP, KEYDOWN, SHUTDOWN, - + WINDOW_MINIMIZE, + WINDOW_MAXIMIZE, + WINDOW_FOCUS_GAIN, + WINDOW_FOCUS_LOST, + WINDOW_MOVE, + WINDOW_RESIZE, + WINDOW_EXPOSE, }; - //! EventData struct for passing event data from facade - struct EventData { - SDLContext::EventType event_type = SDLContext::EventType::NONE; + struct KeyData { Keycode key = Keycode::NONE; bool key_repeat = false; + }; + struct MouseData { MouseButton mouse_button = MouseButton::NONE; ivec2 mouse_position = {-1, -1}; int scroll_direction = -1; float scroll_delta = INFINITY; ivec2 rel_mouse_move = {-1, -1}; }; + struct WindowData { + ivec2 move_delta; + ivec2 resize_dimension; + }; + //! EventData struct for passing event data from facade + struct EventData { + SDLContext::EventType event_type = SDLContext::EventType::NONE; + KeyData key_data; + MouseData mouse_data; + WindowData window_data; + }; + /** + * \brief Retrieves the current state of the keyboard. + * + * This method returns the state of all keys on the keyboard, represented as a + * `std::array` of boolean values. Each element of the array corresponds to a + * specific key defined in the `Keycode` enum, and the value indicates whether + * the key is currently pressed (true) or not pressed (false). + * + * \return A `std::array<bool, Keycode::NUM_KEYCODES>` representing the state of + * each key on the keyboard, where `true` means the key is pressed, and + * `false` means it is not pressed. + */ + std::array<bool, Keycode::NUM_KEYCODES> get_keyboard_state(); + /** + * \brief Gets the singleton instance of SDLContext. + * \return Reference to the SDLContext instance. + */ + static SDLContext & get_instance(); public: SDLContext(const SDLContext &) = delete; @@ -123,17 +159,24 @@ public: * \return Events that occurred since last call to `get_events()` */ std::vector<SDLContext::EventData> get_events(); - /** - * \brief Converts an SDL key code to the custom Keycode type. + * \brief Fills event_list with triggered window events + * + * This method checks if any window events are triggered and adds them to the event_list. + * + */ + void handle_window_event(const SDL_WindowEvent & window_event, + std::vector<SDLContext::EventData> & event_list); + /** + * \brief Converts an SDL scan code to the custom Keycode type. * - * This method maps an SDL key code to the corresponding `Keycode` enum value, + * This method maps an SDL scan code to the corresponding `Keycode` enum value, * which is used internally by the system to identify the keys. * - * \param sdl_key The SDL key code to convert. + * \param sdl_key The SDL scan code to convert. * \return The corresponding `Keycode` value or `Keycode::NONE` if the key is unrecognized. */ - Keycode sdl_to_keycode(SDL_Keycode sdl_key); + Keycode sdl_to_keycode(SDL_Scancode sdl_key); /** * \brief Converts an SDL mouse button code to the custom MouseButton type. diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp index a710ae2..459feb3 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -1,3 +1,4 @@ + #include "../api/Button.h" #include "../manager/ComponentManager.h" #include "../manager/EventManager.h" @@ -16,6 +17,7 @@ void InputSystem::update() { RefVector<Button> buttons = mgr.get_components_by_type<Button>(); RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); OptionalRef<Camera> curr_cam_ref; + // Find the active camera for (Camera & cam : cameras) { if (!cam.active) continue; @@ -23,149 +25,182 @@ void InputSystem::update() { break; } if (!curr_cam_ref) return; + Camera & current_cam = curr_cam_ref; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(current_cam.game_object_id); Transform & cam_transform = transform_vec.front().get(); - int camera_origin_x = cam_transform.position.x + current_cam.data.postion_offset.x - - (current_cam.viewport_size.x / 2); - int camera_origin_y = cam_transform.position.y + current_cam.data.postion_offset.y - - (current_cam.viewport_size.y / 2); + + ivec2 camera_origin; + camera_origin.y = cam_transform.position.y + current_cam.data.postion_offset.y + - (current_cam.viewport_size.y / 2); + camera_origin.x = cam_transform.position.x + current_cam.data.postion_offset.x + - (current_cam.viewport_size.x / 2); for (const SDLContext::EventData & event : event_list) { - int world_mouse_x = event.mouse_position.x + camera_origin_x; - int world_mouse_y = event.mouse_position.y + camera_origin_y; - // check if the mouse is within the viewport - bool mouse_in_viewport - = !(world_mouse_x < camera_origin_x - || world_mouse_x > camera_origin_x + current_cam.viewport_size.x - || world_mouse_y < camera_origin_y - || world_mouse_y > camera_origin_y + current_cam.viewport_size.y); - - switch (event.event_type) { - case SDLContext::EventType::KEYDOWN: - event_mgr.queue_event<KeyPressEvent>(KeyPressEvent{ - .repeat = event.key_repeat, - .key = event.key, - }); - break; - case SDLContext::EventType::KEYUP: - event_mgr.queue_event<KeyReleaseEvent>(KeyReleaseEvent{ - .key = event.key, - }); - break; - case SDLContext::EventType::MOUSEDOWN: - if (!mouse_in_viewport) { + // Only calculate mouse coordinates for relevant events + if (event.event_type == SDLContext::EventType::MOUSEDOWN + || event.event_type == SDLContext::EventType::MOUSEUP + || event.event_type == SDLContext::EventType::MOUSEMOVE + || event.event_type == SDLContext::EventType::MOUSEWHEEL) { + + ivec2 adjusted_mouse; + adjusted_mouse.x = event.mouse_data.mouse_position.x + camera_origin.x; + adjusted_mouse.y = event.mouse_data.mouse_position.y + camera_origin.y; + // Check if the mouse is within the viewport + bool mouse_in_viewport + = !(adjusted_mouse.x < camera_origin.x + || adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x + || adjusted_mouse.y < camera_origin.y + || adjusted_mouse.y > camera_origin.y + current_cam.viewport_size.y); + + if (!mouse_in_viewport) continue; + + // Handle mouse-specific events + switch (event.event_type) { + case SDLContext::EventType::MOUSEDOWN: + event_mgr.queue_event<MousePressEvent>({ + .mouse_pos = {adjusted_mouse.x, adjusted_mouse.y}, + .button = event.mouse_data.mouse_button, + }); + this->last_mouse_down_position = {adjusted_mouse.x, adjusted_mouse.y}; + this->last_mouse_button = event.mouse_data.mouse_button; break; - } - event_mgr.queue_event<MousePressEvent>(MousePressEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - this->last_mouse_down_position = {world_mouse_x, world_mouse_y}; - this->last_mouse_button = event.mouse_button; - break; - case SDLContext::EventType::MOUSEUP: { - if (!mouse_in_viewport) { + + case SDLContext::EventType::MOUSEUP: { + event_mgr.queue_event<MouseReleaseEvent>({ + .mouse_pos = {adjusted_mouse.x, adjusted_mouse.y}, + .button = event.mouse_data.mouse_button, + }); + int delta_x = adjusted_mouse.x - this->last_mouse_down_position.x; + int delta_y = adjusted_mouse.y - this->last_mouse_down_position.y; + + if (this->last_mouse_button == event.mouse_data.mouse_button + && std::abs(delta_x) <= click_tolerance + && std::abs(delta_y) <= click_tolerance) { + event_mgr.queue_event<MouseClickEvent>({ + .mouse_pos = {adjusted_mouse.x, adjusted_mouse.y}, + .button = event.mouse_data.mouse_button, + }); + this->handle_click(event.mouse_data.mouse_button, adjusted_mouse); + } break; } - event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - //check if its a click by checking the last button down - int delta_x = world_mouse_x - this->last_mouse_down_position.x; - int delta_y = world_mouse_y - this->last_mouse_down_position.y; - - if (this->last_mouse_button == event.mouse_button - && std::abs(delta_x) <= click_tolerance - && std::abs(delta_y) <= click_tolerance) { - event_mgr.queue_event<MouseClickEvent>(MouseClickEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, + + case SDLContext::EventType::MOUSEMOVE: + event_mgr.queue_event<MouseMoveEvent>({ + .mouse_pos = {adjusted_mouse.x, adjusted_mouse.y}, + .mouse_delta = event.mouse_data.rel_mouse_move, }); + this->handle_move(event, adjusted_mouse); + break; - this->handle_click(event.mouse_button, world_mouse_x, world_mouse_y); - } - } break; - case SDLContext::EventType::MOUSEMOVE: - if (!mouse_in_viewport) { + case SDLContext::EventType::MOUSEWHEEL: + event_mgr.queue_event<MouseScrollEvent>({ + .mouse_pos = {adjusted_mouse.x, adjusted_mouse.y}, + .scroll_direction = event.mouse_data.scroll_direction, + .scroll_delta = event.mouse_data.scroll_delta, + }); break; - } - event_mgr.queue_event<MouseMoveEvent>(MouseMoveEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .delta_x = event.rel_mouse_move.x, - .delta_y = event.rel_mouse_move.y, - }); - this->handle_move(event, world_mouse_x, world_mouse_y); - break; - case SDLContext::EventType::MOUSEWHEEL: - event_mgr.queue_event<MouseScrollEvent>(MouseScrollEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .scroll_direction = event.scroll_direction, - .scroll_delta = event.scroll_delta, - }); - break; - case SDLContext::EventType::SHUTDOWN: - event_mgr.queue_event<ShutDownEvent>(ShutDownEvent{}); - break; - default: - break; + + default: + break; + } + } else { + // Handle non-mouse events + switch (event.event_type) { + case SDLContext::EventType::KEYDOWN: + event_mgr.queue_event<KeyPressEvent>( + {.repeat = event.key_data.key_repeat, .key = event.key_data.key}); + break; + case SDLContext::EventType::KEYUP: + event_mgr.queue_event<KeyReleaseEvent>({.key = event.key_data.key}); + break; + case SDLContext::EventType::SHUTDOWN: + event_mgr.queue_event<ShutDownEvent>({}); + break; + case SDLContext::EventType::WINDOW_EXPOSE: + event_mgr.queue_event<WindowExposeEvent>({}); + break; + case SDLContext::EventType::WINDOW_RESIZE: + event_mgr.queue_event<WindowResizeEvent>( + WindowResizeEvent{.dimensions = event.window_data.resize_dimension}); + break; + case SDLContext::EventType::WINDOW_MOVE: + event_mgr.queue_event<WindowMoveEvent>( + {.delta_move = event.window_data.move_delta}); + break; + case SDLContext::EventType::WINDOW_MINIMIZE: + event_mgr.queue_event<WindowMinimizeEvent>({}); + break; + case SDLContext::EventType::WINDOW_MAXIMIZE: + event_mgr.queue_event<WindowMaximizeEvent>({}); + break; + case SDLContext::EventType::WINDOW_FOCUS_GAIN: + event_mgr.queue_event<WindowFocusGainEvent>({}); + break; + case SDLContext::EventType::WINDOW_FOCUS_LOST: + event_mgr.queue_event<WindowFocusLostEvent>({}); + break; + default: + break; + } } } } + void InputSystem::handle_move(const SDLContext::EventData & event_data, - const int world_mouse_x, const int world_mouse_y) { + const ivec2& mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if(!button.active) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform(transform_vec.front().get()); bool was_hovering = button.hover; - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { + if (this->is_mouse_inside_button(mouse_pos, button, + transform)) { button.hover = true; - if (!was_hovering && button.on_mouse_enter) { + if(!button.on_mouse_enter) continue; + if (!was_hovering) { button.on_mouse_enter(); } } else { button.hover = false; // Trigger the on_exit callback if the hover state just changed to false - if (was_hovering && button.on_mouse_exit) { + if(!button.on_mouse_exit) continue; + if (was_hovering) { button.on_mouse_exit(); } } } } -void InputSystem::handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y) { +void InputSystem::handle_click(const MouseButton & mouse_button, const ivec2& mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if(!button.active) continue; + if (!button.on_click) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform = transform_vec.front().get(); - - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { - this->handle_button_press(button); + + if (this->is_mouse_inside_button(mouse_pos, button, + transform)) { + + button.on_click(); } } } -bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, +bool InputSystem::is_mouse_inside_button(const ivec2& mouse_pos, const Button & button, const Transform & transform) { int actual_x = transform.position.x + button.offset.x; int actual_y = transform.position.y + button.offset.y; @@ -174,17 +209,7 @@ bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, int half_height = button.dimensions.y / 2; // Check if the mouse is within the button's boundaries - return mouse_x >= actual_x - half_width && mouse_x <= actual_x + half_width - && mouse_y >= actual_y - half_height && mouse_y <= actual_y + half_height; + return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width + && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height; } -void InputSystem::handle_button_press(Button & button) { - if (button.is_toggle) { - if (!button.is_pressed && button.on_click) { - button.on_click(); - } - button.is_pressed = !button.is_pressed; - } else if (button.on_click) { - button.on_click(); - } -} diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index 87e86f8..3703635 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -1,5 +1,6 @@ #pragma once +#include "../api/Config.h" #include "../facade/SDLContext.h" #include "../types.h" #include "../util/OptionalRef.h" @@ -37,8 +38,8 @@ private: //! Stores the last mouse button pressed. MouseButton last_mouse_button = MouseButton::NONE; - //! The maximum allowable distance between mouse down and mouse up to register as a click. - const int click_tolerance = 5; + //! The maximum allowable distance between mouse down and mouse up to register as a click. This can be changed using the Config. + int click_tolerance = Config::get_instance().input.click_tolerance; /** * \brief Handles the mouse click event. @@ -48,8 +49,7 @@ private: * * This method processes the mouse click event and triggers the corresponding button action. */ - void handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y); + void handle_click(const MouseButton & mouse_button,const ivec2& mouse_pos); /** * \brief Handles the mouse movement event. @@ -59,8 +59,7 @@ private: * * This method processes the mouse movement event and updates the button hover state. */ - void handle_move(const SDLContext::EventData & event_data, const int world_mouse_x, - const int world_mouse_y); + void handle_move(const SDLContext::EventData & event_data, const ivec2& mouse_pos); /** * \brief Checks if the mouse position is inside the bounds of the button. @@ -70,7 +69,7 @@ private: * \param transform The transform component of the button. * \return True if the mouse is inside the button, false otherwise. */ - bool is_mouse_inside_button(const int world_mouse_x, const int world_mouse_y, + bool is_mouse_inside_button(const ivec2& mouse_pos, const Button & button, const Transform & transform); /** diff --git a/src/example/button.cpp b/src/example/button.cpp index 00bdc28..f2e77f6 100644 --- a/src/example/button.cpp +++ b/src/example/button.cpp @@ -15,7 +15,6 @@ #include <crepe/system/InputSystem.h> #include <crepe/system/RenderSystem.h> #include <crepe/types.h> -#include <iostream> using namespace crepe; using namespace std; diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 82272b5..f57a6de 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -48,16 +48,16 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) { triggered = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; event_mgr.subscribe<MouseClickEvent>(mouse_handler, EventManager::CHANNEL_ALL); - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, + EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } @@ -66,16 +66,16 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { int test_channel = 1; EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) { triggered = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; event_mgr.subscribe<MouseClickEvent>(mouse_handler, test_channel); - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, + EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); event_mgr.trigger_event<MouseClickEvent>(click_event, test_channel); @@ -90,25 +90,24 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Handlers EventHandler<MouseClickEvent> mouse_handler_true = [&](const MouseClickEvent & e) { triggered_true = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return true; // Stops propagation }; EventHandler<MouseClickEvent> mouse_handler_false = [&](const MouseClickEvent & e) { triggered_false = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; // Test event - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); - event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); + MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); + event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); // Trigger event event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); @@ -172,16 +171,16 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { // Define EventHandlers EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) { triggered1 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) { triggered2 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; @@ -190,8 +189,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { subscription_t handler2_id = event_mgr.subscribe<MouseClickEvent>(mouse_handler2); // Queue events - event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_manager.queue_event<MouseClickEvent>( + MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - both handlers should be triggered event_mgr.dispatch_events(); @@ -206,8 +205,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { event_mgr.unsubscribe(handler1_id); // Queue the same event again - event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_manager.queue_event<MouseClickEvent>( + MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - only handler 2 should be triggered, handler 1 should NOT event_mgr.dispatch_events(); @@ -221,8 +220,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { event_mgr.unsubscribe(handler2_id); // Queue the event again - event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_manager.queue_event<MouseClickEvent>( + MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - no handler should be triggered event_mgr.dispatch_events(); diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp index 29ef941..a6e60b5 100644 --- a/src/test/InputTest.cpp +++ b/src/test/InputTest.cpp @@ -31,11 +31,7 @@ public: //GameObject camera; protected: - void SetUp() override { - mediator.event_manager = event_manager; - mediator.component_manager = mgr; - event_manager.clear(); - } + void SetUp() override { event_manager.clear(); } void simulate_mouse_click(int mouse_x, int mouse_y, Uint8 mouse_button) { SDL_Event event; @@ -67,8 +63,8 @@ TEST_F(InputTest, MouseDown) { EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { mouse_triggered = true; //middle of the screen = 0,0 - EXPECT_EQ(event.mouse_x, 0); - EXPECT_EQ(event.mouse_y, 0); + EXPECT_EQ(event.mouse_pos.x, 0); + EXPECT_EQ(event.mouse_pos.y, 0); EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); return false; }; @@ -96,8 +92,8 @@ TEST_F(InputTest, MouseUp) { bool function_triggered = false; EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { function_triggered = true; - EXPECT_EQ(e.mouse_x, 0); - EXPECT_EQ(e.mouse_y, 0); + EXPECT_EQ(e.mouse_pos.x, 0); + EXPECT_EQ(e.mouse_pos.y, 0); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; @@ -124,10 +120,10 @@ TEST_F(InputTest, MouseMove) { bool function_triggered = false; EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { function_triggered = true; - EXPECT_EQ(e.mouse_x, 0); - EXPECT_EQ(e.mouse_y, 0); - EXPECT_EQ(e.delta_x, 10); - EXPECT_EQ(e.delta_y, 10); + EXPECT_EQ(e.mouse_pos.x, 0); + EXPECT_EQ(e.mouse_pos.y, 0); + EXPECT_EQ(e.mouse_delta.x, 10); + EXPECT_EQ(e.mouse_delta.y, 10); return false; }; event_manager.subscribe<MouseMoveEvent>(on_mouse_move); @@ -210,8 +206,8 @@ TEST_F(InputTest, MouseClick) { EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { on_click_triggered = true; EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); - EXPECT_EQ(event.mouse_x, 0); - EXPECT_EQ(event.mouse_y, 0); + EXPECT_EQ(event.mouse_pos.x, 0); + EXPECT_EQ(event.mouse_pos.y, 0); return false; }; event_manager.subscribe<MouseClickEvent>(on_mouse_click); @@ -231,13 +227,10 @@ TEST_F(InputTest, testButtonClick) { bool button_clicked = false; std::function<void()> on_click = [&]() { button_clicked = true; }; auto & button - = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click, false); + = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click); bool hover = false; button.active = true; - - button.is_pressed = false; - button.is_toggle = false; this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); input_system.update(); event_manager.dispatch_events(); @@ -258,10 +251,8 @@ TEST_F(InputTest, testButtonHover) { bool button_clicked = false; std::function<void()> on_click = [&]() { button_clicked = true; }; auto & button - = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click, false); + = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}, on_click); button.active = true; - button.is_pressed = false; - button.is_toggle = false; // Mouse not on button SDL_Event event; @@ -291,3 +282,52 @@ TEST_F(InputTest, testButtonHover) { event_manager.dispatch_events(); EXPECT_TRUE(button.hover); } + +TEST_F(InputTest, WindowResizeTest) { + GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); + auto & camera = obj.add_component<Camera>( + ivec2{0, 0}, vec2{500, 500}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + camera.active = true; + bool callback_triggered = false; + EventHandler<WindowResizeEvent> on_window_resize = [&](const WindowResizeEvent & event) { + callback_triggered = true; + EXPECT_EQ(event.dimensions.x, 800); + EXPECT_EQ(event.dimensions.y, 600); + return false; + }; + event_manager.subscribe<WindowResizeEvent>(on_window_resize); + SDL_Event resize_event; + SDL_zero(resize_event); + resize_event.type = SDL_WINDOWEVENT; + resize_event.window.event = SDL_WINDOWEVENT_RESIZED; + resize_event.window.data1 = 800; // new width + resize_event.window.data2 = 600; // new height + SDL_PushEvent(&resize_event); + input_system.update(); + event_manager.dispatch_events(); + EXPECT_TRUE(callback_triggered); +} +TEST_F(InputTest, WindowMoveTest) { + GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); + auto & camera = obj.add_component<Camera>( + ivec2{0, 0}, vec2{500, 500}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + camera.active = true; + bool callback_triggered = false; + EventHandler<WindowMoveEvent> on_window_move = [&](const WindowMoveEvent & event) { + callback_triggered = true; + EXPECT_EQ(event.delta_move.x, 800); + EXPECT_EQ(event.delta_move.y, 600); + return false; + }; + event_manager.subscribe<WindowMoveEvent>(on_window_move); + SDL_Event resize_event; + SDL_zero(resize_event); + resize_event.type = SDL_WINDOWEVENT; + resize_event.window.event = SDL_WINDOWEVENT_MOVED; + resize_event.window.data1 = 800; // new width + resize_event.window.data2 = 600; // new height + SDL_PushEvent(&resize_event); + input_system.update(); + event_manager.dispatch_events(); + EXPECT_TRUE(callback_triggered); +} |