diff options
| -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); +} |