From f8d8d1734d6b29ea451260ab0e58667ec889e721 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 11 Feb 2023 00:20:51 +0800 Subject: [PATCH] Improve menu mouse selection, add scroll mappings to menu controls --- src/app/sdl/sdl-input-manager.cpp | 5 +- src/game/controls.cpp | 165 +++++++++++++----------------- src/game/state/boot.cpp | 5 +- 3 files changed, 78 insertions(+), 97 deletions(-) diff --git a/src/app/sdl/sdl-input-manager.cpp b/src/app/sdl/sdl-input-manager.cpp index c3b657d..12eaf47 100644 --- a/src/app/sdl/sdl-input-manager.cpp +++ b/src/app/sdl/sdl-input-manager.cpp @@ -257,7 +257,7 @@ void sdl_input_manager::update() ::uuid gamepad_uuid; std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size()); - debug::log::info("Connected gamepad \"{}\" with UUID {}", controller_name, gamepad_uuid.string()); + debug::log::info("Connected gamepad \"{}\" (UUID: {})", controller_name, gamepad_uuid.string()); // Create new gamepad input::gamepad* gamepad = new input::gamepad(); @@ -276,6 +276,7 @@ void sdl_input_manager::update() else { debug::log::error("Failed to connected gamepad \"{}\": {}", controller_name, SDL_GetError()); + SDL_ClearError(); } } @@ -316,6 +317,7 @@ void sdl_input_manager::show_cursor() if (SDL_ShowCursor(SDL_ENABLE) < 0) { debug::log::error("Failed to show cursor: \"{}\"", SDL_GetError()); + SDL_ClearError(); } } @@ -324,6 +326,7 @@ void sdl_input_manager::hide_cursor() if (SDL_ShowCursor(SDL_DISABLE) < 0) { debug::log::error("Failed to hide cursor: \"{}\"", SDL_GetError()); + SDL_ClearError(); } } diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 16a01a2..0bde120 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -79,6 +79,8 @@ void setup_menu_controls(game::context& ctx) ctx.menu_controls.add_mapping(ctx.menu_left_control, input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, true)); ctx.menu_controls.add_mapping(ctx.menu_left_control, input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_x, true)); ctx.menu_controls.add_mapping(ctx.menu_left_control, input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_left)); + ctx.menu_controls.add_mapping(ctx.menu_left_control, input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, true)); + ctx.menu_controls.add_mapping(ctx.menu_left_control, input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, true)); ctx.menu_controls.add_mapping(ctx.menu_right_control, input::key_mapping(nullptr, input::scancode::right, true)); ctx.menu_controls.add_mapping(ctx.menu_right_control, input::key_mapping(nullptr, input::scancode::d, true)); @@ -86,6 +88,8 @@ void setup_menu_controls(game::context& ctx) ctx.menu_controls.add_mapping(ctx.menu_right_control, input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, false)); ctx.menu_controls.add_mapping(ctx.menu_right_control, input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_x, false)); ctx.menu_controls.add_mapping(ctx.menu_right_control, input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_right)); + ctx.menu_controls.add_mapping(ctx.menu_right_control, input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, false)); + ctx.menu_controls.add_mapping(ctx.menu_right_control, input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, false)); ctx.menu_controls.add_mapping(ctx.menu_select_control, input::key_mapping(nullptr, input::scancode::enter, false)); ctx.menu_controls.add_mapping(ctx.menu_select_control, input::key_mapping(nullptr, input::scancode::space, false)); @@ -198,53 +202,60 @@ void enable_menu_controls(game::context& ctx) { ctx.menu_controls.connect(ctx.input_manager->get_event_queue()); + // Function to select menu item at mouse position + auto select_menu_item = [&ctx](const math::vector& mouse_position) + { + const float padding = config::menu_mouseover_padding * ctx.menu_font.get_font_metrics().size; + + for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i) + { + auto [name, value] = ctx.menu_item_texts[i]; + + const auto& name_bounds = static_cast&>(name->get_world_bounds()); + float min_x = name_bounds.min_point.x(); + float min_y = name_bounds.min_point.y(); + float max_x = name_bounds.max_point.x(); + float max_y = name_bounds.max_point.y(); + if (value) + { + const auto& value_bounds = static_cast&>(value->get_world_bounds()); + min_x = std::min(min_x, value_bounds.min_point.x()); + min_y = std::min(min_y, value_bounds.min_point.y()); + max_x = std::max(max_x, value_bounds.max_point.x()); + max_y = std::max(max_y, value_bounds.max_point.y()); + } + + min_x -= padding; + min_y -= padding; + max_x += padding; + max_y += padding; + + const auto& viewport = ctx.window->get_viewport_size(); + const float x = mouse_position.x(); + const float y = static_cast((viewport[1] - mouse_position.y() + 1)); + + if (x >= min_x && x <= max_x) + { + if (y >= min_y && y <= max_y) + { + *ctx.menu_item_index = static_cast(i); + game::menu::update_text_color(ctx); + break; + } + } + } + }; + // Enable menu mouse tracking ctx.menu_mouse_subscriptions.clear(); ctx.menu_mouse_subscriptions.emplace_back ( ctx.input_manager->get_event_queue().subscribe ( - [&ctx](const auto& event) + [&ctx, select_menu_item](const auto& event) { - const float padding = config::menu_mouseover_padding * ctx.menu_font.get_font_metrics().size; - - for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i) - { - auto [name, value] = ctx.menu_item_texts[i]; - - const auto& name_bounds = static_cast&>(name->get_world_bounds()); - float min_x = name_bounds.min_point.x(); - float min_y = name_bounds.min_point.y(); - float max_x = name_bounds.max_point.x(); - float max_y = name_bounds.max_point.y(); - if (value) - { - const auto& value_bounds = static_cast&>(value->get_world_bounds()); - min_x = std::min(min_x, value_bounds.min_point.x()); - min_y = std::min(min_y, value_bounds.min_point.y()); - max_x = std::max(max_x, value_bounds.max_point.x()); - max_y = std::max(max_y, value_bounds.max_point.y()); - } - - min_x -= padding; - min_y -= padding; - max_x += padding; - max_y += padding; - - const auto& viewport = ctx.window->get_viewport_size(); - const float x = static_cast(event.position.x()); - const float y = static_cast((viewport[1] - event.position.y() + 1)); - - if (x >= min_x && x <= max_x) - { - if (y >= min_y && y <= max_y) - { - *ctx.menu_item_index = i; - game::menu::update_text_color(ctx); - break; - } - } - } + // Select menu item at mouse position (if any) + select_menu_item(math::vector(event.position)); } ) ); @@ -252,69 +263,33 @@ void enable_menu_controls(game::context& ctx) ( ctx.input_manager->get_event_queue().subscribe ( - [&ctx](const auto& event) + [&ctx, select_menu_item](const auto& event) { - const float padding = config::menu_mouseover_padding * ctx.menu_font.get_font_metrics().size; + // Select menu item at mouse position (if any) + select_menu_item(math::vector(event.position)); - for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i) + // Determine appropriate menu item callback + auto callback = ctx.menu_select_callbacks[*ctx.menu_item_index]; + if (event.button == input::mouse_button::left) { - auto [name, value] = ctx.menu_item_texts[i]; - - const auto& name_bounds = static_cast&>(name->get_world_bounds()); - float min_x = name_bounds.min_point.x(); - float min_y = name_bounds.min_point.y(); - float max_x = name_bounds.max_point.x(); - float max_y = name_bounds.max_point.y(); - if (value) + if (ctx.menu_left_callbacks[*ctx.menu_item_index]) { - const auto& value_bounds = static_cast&>(value->get_world_bounds()); - min_x = std::min(min_x, value_bounds.min_point.x()); - min_y = std::min(min_y, value_bounds.min_point.y()); - max_x = std::max(max_x, value_bounds.max_point.x()); - max_y = std::max(max_y, value_bounds.max_point.y()); + callback = ctx.menu_left_callbacks[*ctx.menu_item_index]; } - - min_x -= padding; - min_y -= padding; - max_x += padding; - max_y += padding; - - const auto& viewport = ctx.window->get_viewport_size(); - const float x = static_cast(event.position.x()); - const float y = static_cast((viewport[1] - event.position.y() + 1)); - - if (x >= min_x && x <= max_x) + } + else if (event.button == input::mouse_button::right) + { + if (ctx.menu_right_callbacks[*ctx.menu_item_index]) { - if (y >= min_y && y <= max_y) - { - *ctx.menu_item_index = i; - game::menu::update_text_color(ctx); - - auto callback = ctx.menu_select_callbacks[i]; - if (event.button == input::mouse_button::left) - { - if (ctx.menu_left_callbacks[i]) - { - callback = ctx.menu_left_callbacks[i]; - } - } - else if (event.button == input::mouse_button::right) - { - if (ctx.menu_right_callbacks[i]) - { - callback = ctx.menu_right_callbacks[i]; - } - } - - if (callback) - { - callback(); - } - - return; - } + callback = ctx.menu_right_callbacks[*ctx.menu_item_index]; } } + + // Invoke menu item callback + if (callback) + { + callback(); + } } ) ); diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 1310feb..1f4632a 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -460,6 +460,9 @@ void boot::setup_input() // Construct input manager ctx.input_manager = app::input_manager::instance(); + // Process initial input events, such as connecting gamepads + ctx.input_manager->update(); + // Setup application quit callback ctx.application_quit_subscription = ctx.input_manager->get_event_queue().subscribe ( @@ -484,7 +487,7 @@ void boot::setup_input() ( [&](const auto& event) { - if (!ctx.gamepad_active && std::abs(event.position) > 0.1f) + if (!ctx.gamepad_active && std::abs(event.position) > 0.5f) { ctx.gamepad_active = true; ctx.input_manager->hide_cursor();