diff options
-rw-r--r-- | src/crepe/api/Button.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/Button.h | 52 | ||||
-rw-r--r-- | src/crepe/api/Event.h | 7 | ||||
-rw-r--r-- | src/crepe/api/UiObject.cpp | 2 | ||||
-rw-r--r-- | src/crepe/api/UiObject.h | 2 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.h | 14 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.cpp | 46 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.h | 7 | ||||
-rw-r--r-- | src/test/inputTest.cpp | 45 |
9 files changed, 109 insertions, 73 deletions
diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp index 547c0fc..077a5e7 100644 --- a/src/crepe/api/Button.cpp +++ b/src/crepe/api/Button.cpp @@ -1,5 +1,8 @@ #include "Button.h" -using namespace crepe; +namespace crepe { -Button::Button(game_object_id_t id) : UiObject(id) {} +Button::Button(game_object_id_t id, int width, int height, bool is_toggle, std::function<void()> on_click) + : UiObject(id, width, height), is_toggle(is_toggle), is_pressed(false), hover(false), on_click(on_click) {} + +} // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h index 0056238..df6f1e0 100644 --- a/src/crepe/api/Button.h +++ b/src/crepe/api/Button.h @@ -1,7 +1,6 @@ #pragma once #include <functional> - #include "UiObject.h" namespace crepe { @@ -9,34 +8,59 @@ namespace crepe { /** * \class Button * \brief Represents a clickable UI button, derived from the UiObject class. + * + * This class provides functionality for a button in the UI, including toggle state, + * click handling, and mouse hover detection. A callback function can be provided to + * handle button clicks. */ class Button : public UiObject { public: /** - * \brief Constructs a Button with the specified game object ID. + * \brief Constructs a Button with the specified game object ID and dimensions. + * * \param id The unique ID of the game object associated with this button. + * \param width The width of the button. + * \param height The height of the button. + * \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); - - //! Indicates if the button is interactable (can be clicked). - bool interactable = true; + Button(game_object_id_t id, int width, int height, bool is_toggle = false, std::function<void()> on_click = nullptr); - //! Indicates if the button is a toggle button (can be pressed and released). - bool is_toggle = false; + /** + * \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; - //! Indicates whether the button is currently pressed. - bool is_pressed = false; + /** + * \brief Indicates whether the button is currently pressed. + * + * This state is true when the button is actively pressed and false otherwise. + */ + bool is_pressed; - //! Indicates whether the mouse is currently hovering over the button. - bool hover = false; + /** + * \brief Indicates whether the mouse is currently hovering over the button. + * + * This is set to true when the mouse is over the button and false otherwise. + */ + bool hover; - //! The callback function to be executed when the button is clicked. + /** + * \brief The callback function to be executed when the button is clicked. + * + * This function is invoked whenever the button is clicked. It can be set to any + * function that matches the signature `void()`. Defaults to nullptr. + */ std::function<void()> on_click; public: /** * \brief Retrieves the maximum number of instances allowed for this button type. - * \return Always returns 1, as only a single instance is allowed. + * + * \return Always returns 1, as only a single instance of this type is allowed. */ virtual int get_instances_max() const override { return 1; } }; diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index b13abc1..a7d5511 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -88,10 +88,12 @@ public: //! Y-coordinate of the mouse position at the time of the event. int mouse_y = 0; + // Relative movement in x - int rel_x; + int rel_x = 0; + // Relative movement in y - int rel_y; + int rel_y = 0; }; /** @@ -104,6 +106,7 @@ public: //! Y-coordinate of the mouse position at the time of the event. int scroll_y = 0; + //! scroll direction (-1 = down, 1 = up) int direction = 0; }; diff --git a/src/crepe/api/UiObject.cpp b/src/crepe/api/UiObject.cpp index 1c11fc3..987fc06 100644 --- a/src/crepe/api/UiObject.cpp +++ b/src/crepe/api/UiObject.cpp @@ -2,4 +2,4 @@ using namespace crepe; -UiObject::UiObject(game_object_id_t id) : Component(id){}; +UiObject::UiObject(game_object_id_t id,int width,int height) : Component(id),width(width),height(height){}; diff --git a/src/crepe/api/UiObject.h b/src/crepe/api/UiObject.h index 7bd1c2e..6b0323e 100644 --- a/src/crepe/api/UiObject.h +++ b/src/crepe/api/UiObject.h @@ -14,7 +14,7 @@ public: * \brief Constructs a UiObject with the specified game object ID. * \param id The unique ID of the game object associated with this UI object. */ - UiObject(game_object_id_t id); + UiObject(game_object_id_t id,int width,int height); //! The width of the UI object. int width = 0; diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index 3e0b073..9f18728 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -11,20 +11,16 @@ #include <string> #include <utility> -#include "../api/Event.h" -#include "../api/KeyCodes.h" -#include "../api/Sprite.h" -#include "../api/Transform.h" -#include "../api/Vector2.h" +#include "api/Event.h" +#include "api/KeyCodes.h" +#include "api/Sprite.h" +#include "api/Transform.h" #include "api/Camera.h" #include "types.h" namespace crepe { -// TODO: SDL_Keycode is defined in a header not distributed with crepe, which means this -// typedef is unusable when crepe is packaged. Wouter will fix this later. -//typedef SDL_Keycode CREPE_KEYCODES; class LoopManager; class InputSystem; /** @@ -93,7 +89,7 @@ private: * @param sdlKey The SDL key code to convert. * @return The corresponding `Keycode` value. */ - Keycode sdl_to_keycode(SDL_Keycode sdlKey); + Keycode sdl_to_keycode(SDL_Keycode 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 9690fec..590be8d 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -1,8 +1,8 @@ -#include "../api/Button.h" -#include "../api/EventManager.h" +#include "api/Button.h" +#include "api/EventManager.h" #include "ComponentManager.h" -#include "system/InputSystem.h" +#include "InputSystem.h" using namespace crepe; @@ -36,17 +36,15 @@ void InputSystem::update() { break; } case SDLContext::EventType::MOUSEUP: { - MouseReleaseEvent mouse_release_event = MouseReleaseEvent{ + event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ .mouse_x = event.mouse_position.first, .mouse_y = event.mouse_position.second, .button = event.mouse_button, - }; - event_mgr.queue_event<MouseReleaseEvent>(mouse_release_event); + }); - // Calculate deltas for click detection int delta_x = event.mouse_position.first - last_mouse_down_position.first; int delta_y = event.mouse_position.second - last_mouse_down_position.second; - + if (last_mouse_button == event.mouse_button && std::abs(delta_x) <= click_tolerance && std::abs(delta_y) <= click_tolerance) { @@ -96,10 +94,10 @@ void InputSystem::handle_move(const SDLContext::EventData & event_data) { = mgr.get_components_by_type<Transform>(); for (Button & button : buttons) { - Transform * transform = find_transform_for_button(button, transforms); + OptionalRef<Transform> transform = find_transform_for_button(button, transforms); if (!transform) continue; - if (button.interactable && is_mouse_inside_button(event_data, button, *transform)) { + if (button.active && is_mouse_inside_button(event_data, button, transform)) { button.hover = true; } else { button.hover = false; @@ -111,29 +109,31 @@ void InputSystem::handle_click(const SDLContext::EventData & event_data) { ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<Button>> buttons = mgr.get_components_by_type<Button>(); - std::vector<std::reference_wrapper<Transform>> transforms - = mgr.get_components_by_type<Transform>(); + std::vector<std::reference_wrapper<Transform>> transforms = mgr.get_components_by_type<Transform>(); for (Button & button : buttons) { - Transform * transform = find_transform_for_button(button, transforms); - if (!transform) continue; + OptionalRef<Transform> transform_ref = find_transform_for_button(button, transforms); - if (button.interactable && is_mouse_inside_button(event_data, button, *transform)) { + if (button.active && is_mouse_inside_button(event_data, button, transform_ref)) { handle_button_press(button); } } } -Transform * InputSystem::find_transform_for_button( - Button & button, std::vector<std::reference_wrapper<Transform>> & transforms) { - for (Transform & transform : transforms) { - if (button.game_object_id == transform.game_object_id) { - return &transform; - } - } - return nullptr; + +OptionalRef<Transform> InputSystem::find_transform_for_button( + Button & button, std::vector<std::reference_wrapper<Transform>> & transforms) { + + for (auto& transform : transforms) { + if (button.game_object_id == transform.get().game_object_id) { + return OptionalRef<Transform>(transform); + } + } + + return OptionalRef<Transform>(); } + bool InputSystem::is_mouse_inside_button(const SDLContext::EventData & event_data, const Button & button, const Transform & transform) { return event_data.mouse_position.first >= transform.position.x diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index cd7ca77..8b47e49 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -1,11 +1,14 @@ #pragma once +#include "facade/SDLContext.h" +#include "util/OptionalRef.h" + #include "System.h" -#include "../facade/SDLContext.h" namespace crepe { class Button; + class Transform; /** * \class InputSystem @@ -57,7 +60,7 @@ private: * \param transforms A list of transforms to search through. * \return A pointer to the transform of the button, or nullptr if not found. */ - Transform * + OptionalRef<Transform> find_transform_for_button(Button & button, std::vector<std::reference_wrapper<Transform>> & transforms); diff --git a/src/test/inputTest.cpp b/src/test/inputTest.cpp index 3a9d341..0e3e097 100644 --- a/src/test/inputTest.cpp +++ b/src/test/inputTest.cpp @@ -20,12 +20,14 @@ using namespace crepe; class InputTest : public ::testing::Test { public: ComponentManager mgr{}; - InputSystem input_system{mgr}; + InputSystem input_system{mgr}; // Initializes the InputSystem with the ComponentManager - EventManager & event_manager = EventManager::get_instance(); + EventManager& event_manager = EventManager::get_instance(); protected: - void SetUp() override { 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; @@ -37,6 +39,8 @@ protected: event.button.y = mouse_y; event.button.button = mouse_button; SDL_PushEvent(&event); + + // Simulate Mouse Button Up event SDL_zero(event); event.type = SDL_MOUSEBUTTONUP; event.button.x = mouse_x; @@ -48,7 +52,7 @@ protected: TEST_F(InputTest, MouseDown) { bool mouse_triggered = false; - EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { + EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent& event) { mouse_triggered = true; EXPECT_EQ(event.mouse_x, 10); EXPECT_EQ(event.mouse_y, 10); @@ -64,6 +68,7 @@ TEST_F(InputTest, MouseDown) { event.button.y = 10; event.button.button = SDL_BUTTON_LEFT; SDL_PushEvent(&event); + input_system.update(); event_manager.dispatch_events(); EXPECT_TRUE(mouse_triggered); @@ -71,8 +76,7 @@ TEST_F(InputTest, MouseDown) { TEST_F(InputTest, MouseUp) { bool function_triggered = false; - EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { - // Handle the mouse click event here + EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent& e) { function_triggered = true; EXPECT_EQ(e.mouse_x, 10); EXPECT_EQ(e.mouse_y, 10); @@ -88,6 +92,7 @@ TEST_F(InputTest, MouseUp) { event.button.y = 10; event.button.button = SDL_BUTTON_LEFT; SDL_PushEvent(&event); + input_system.update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); @@ -95,8 +100,7 @@ TEST_F(InputTest, MouseUp) { TEST_F(InputTest, MouseMove) { bool function_triggered = false; - EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { - // Handle the mouse click event here + EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent& e) { function_triggered = true; EXPECT_EQ(e.mouse_x, 10); EXPECT_EQ(e.mouse_y, 10); @@ -114,6 +118,7 @@ TEST_F(InputTest, MouseMove) { event.motion.xrel = 10; event.motion.yrel = 10; SDL_PushEvent(&event); + input_system.update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); @@ -123,7 +128,7 @@ TEST_F(InputTest, KeyDown) { bool function_triggered = false; // Define event handler for KeyPressEvent - EventHandler<KeyPressEvent> on_key_press = [&](const KeyPressEvent & event) { + EventHandler<KeyPressEvent> on_key_press = [&](const KeyPressEvent& event) { function_triggered = true; EXPECT_EQ(event.key, Keycode::B); // Validate the key is 'B' EXPECT_EQ(event.repeat, true); // Validate repeat flag @@ -148,7 +153,7 @@ TEST_F(InputTest, KeyDown) { TEST_F(InputTest, KeyUp) { bool function_triggered = false; - EventHandler<KeyReleaseEvent> on_key_release = [&](const KeyReleaseEvent & event) { + EventHandler<KeyReleaseEvent> on_key_release = [&](const KeyReleaseEvent& event) { function_triggered = true; EXPECT_EQ(event.key, Keycode::B); return false; @@ -160,6 +165,7 @@ TEST_F(InputTest, KeyUp) { event.type = SDL_KEYUP; event.key.keysym.scancode = SDL_SCANCODE_B; SDL_PushEvent(&event); + input_system.update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); @@ -167,7 +173,7 @@ TEST_F(InputTest, KeyUp) { TEST_F(InputTest, MouseClick) { bool on_click_triggered = false; - EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { + EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent& event) { on_click_triggered = true; EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); EXPECT_EQ(event.mouse_x, 10); @@ -184,13 +190,11 @@ TEST_F(InputTest, MouseClick) { TEST_F(InputTest, testButtonClick) { GameObject obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - auto & button = obj.add_component<Button>(); + + auto& button = obj.add_component<Button>(100,100); bool button_clicked = false; bool hover = false; button.active = true; - button.interactable = true; - button.width = 100; - button.height = 100; std::function<void()> on_click = [&]() { button_clicked = true; }; button.on_click = on_click; button.is_pressed = false; @@ -199,6 +203,7 @@ TEST_F(InputTest, testButtonClick) { input_system.update(); event_manager.dispatch_events(); EXPECT_FALSE(button_clicked); + this->simulate_mouse_click(10, 10, SDL_BUTTON_LEFT); input_system.update(); event_manager.dispatch_events(); @@ -207,15 +212,15 @@ TEST_F(InputTest, testButtonClick) { TEST_F(InputTest, testButtonHover) { GameObject obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - auto & button = obj.add_component<Button>(); + auto& button = obj.add_component<Button>(100,100); bool button_clicked = false; button.active = true; - button.interactable = true; button.width = 100; button.height = 100; button.is_pressed = false; button.is_toggle = false; - //mouse not on button + + // Mouse not on button SDL_Event event; SDL_zero(event); event.type = SDL_MOUSEMOTION; @@ -228,7 +233,8 @@ TEST_F(InputTest, testButtonHover) { input_system.update(); event_manager.dispatch_events(); EXPECT_FALSE(button.hover); - //mouse on button + + // Mouse on button SDL_Event hover_event; SDL_zero(hover_event); hover_event.type = SDL_MOUSEMOTION; @@ -237,6 +243,7 @@ TEST_F(InputTest, testButtonHover) { hover_event.motion.xrel = 10; hover_event.motion.yrel = 10; SDL_PushEvent(&hover_event); + input_system.update(); event_manager.dispatch_events(); EXPECT_TRUE(button.hover); |