From 336dc0d262fa417ee4e6196ff01fbe39b3da9278 Mon Sep 17 00:00:00 2001 From: xshxdex98 Date: Sun, 11 Oct 2026 17:22:15 -0500 Subject: [PATCH] Server browsers: a game selected in yellow, as Custom Edition's In Join Game's server browser a click selects a game, its row in the yellow-outlined picture Custom Edition draws for it, and a click on the selected game, a double click, A, Enter or JOIN joins it; one click joined it before. The keys move the selection. (cherry picked from commit 472d232246568ed2e3ee56bc93d97a3530feea54, without that fork's Online Games screen; OpenCE's rows already take the focus only from a click, not from the pointer passing over them) --- port/linux/game/menu_functions.c | 58 ++++++++++++++++++++++++-------- source/interface/ui_widget.c | 17 ++++++++++ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/port/linux/game/menu_functions.c b/port/linux/game/menu_functions.c index f3118c1d..3e0e1e53 100644 --- a/port/linux/game/menu_functions.c +++ b/port/linux/game/menu_functions.c @@ -579,6 +579,7 @@ reached, and (map_data) a saved game on the level */ #define ROW_TEXT_LENGTH 64 #define MAXIMUM_PROFILES 32 #define SOUND_ERROR 4 +#define SOUND_CURSOR 1 #define SOUND_FORWARD 2 #define SOUND_BACK 3 @@ -1599,6 +1600,7 @@ static boolean color_list_initialize(struct widget_instance *list) /* (ui_widget.c) whether the pointer moved the menus' focus last */ boolean ui_widget_port_pointer_focused(void); +boolean ui_widget_port_hover_focused(void); /* Whether a list whose focus is on its first or last row moves on: when the d-pad put it there. The pointer resting there would make it move every @@ -3695,21 +3697,25 @@ static boolean browser_item_usable(struct widget_instance *child) } /* the browser's focus, off what cannot have it (at the start, or a row whose -game went): the first game, else the buttons (off those hidden) */ -static void browser_focus(struct widget_instance *list) +game went): the first game, else the buttons (off those hidden); the row of +the game chosen is `chosen_row` (NONE: none shown) */ +static void browser_focus(struct widget_instance *list, short chosen_row) { char const *const choices[] = { "server_item_1", "join_game_button_bar" }; struct widget_instance *focused = list->focused_child; struct widget_instance *row; short index; - /* (the rows' backgrounds: the focused one's outlined frame. The engine - shows a list item's focus only on a bitmap of two frames, and theirs has - three: normal, focused, selected) */ + /* (the rows' backgrounds, as Custom Edition's: the chosen game's + outlined in yellow, the focused one's in blue. The engine shows a list + item's focus only on a bitmap of two frames, and theirs has three: + normal, focused, selected) */ for (row = list->child; row; row = row->next) { - if (browser_row_index(row) != NONE) - row->animation.current_frame_index = row == focused ? 1 : 0; + short row_index = browser_row_index(row); + + if (row_index != NONE) + row->animation.current_frame_index = row_index == chosen_row ? 2 : row == focused ? 1 : 0; } focus_off_hidden(named(list, "join_game_button_bar", 0)); if (focused && focused->visible && !focused->disabled) @@ -3762,7 +3768,7 @@ static boolean browser_initialize(struct widget_instance *screen, struct event_r for (child = list ? list->child : NULL; child; child = child->next) child->disabled = !browser_item_usable(child); if (list) - browser_focus(list); + browser_focus(list, NONE); } visible_set(named(screen, "scroll_up_button", 0), FALSE); visible_set(named(screen, "scroll_down_button", 0), FALSE); @@ -3899,6 +3905,17 @@ static struct boolean password_joined; } lobby_browser; +/* the row of the browser's chosen game, NONE if it is not on the rows */ +static short browser_chosen_row(void) +{ + boolean server_browser = multiplayer.mode == _multiplayer_mode_server_browser; + short chosen = server_browser ? lobby_browser.chosen : multiplayer.game_chosen; + short row = server_browser ? (short)(chosen - lobby_browser.first) : chosen; + + return chosen < (server_browser ? lobby_browser.count : multiplayer.game_count) && row >= 0 && + row < BROWSER_ROWS ? row : NONE; +} + static void lobby_browser_begin(void) { lobby_browser.count = 0; @@ -4031,9 +4048,10 @@ static void lobby_browser_update(struct widget_instance *list) first_row->visible = TRUE; lobby_browser_focus_row(list, 0); focused = 0; + lobby_browser.chosen = lobby_browser.first; } } - if (focused != NONE) + if (focused != NONE && !ui_widget_port_hover_focused()) lobby_browser.chosen = (short)(lobby_browser.first + focused); chosen = lobby_browser.chosen; for (row = list->child; row; row = row->next) @@ -4187,7 +4205,7 @@ static void lobby_browser_update(struct widget_instance *list) usnprintf(text, ROW_TEXT_LENGTH - 1, L"%s %d", label, lobby_browser.count); text_set(named(stats, "server_count_label", 0), text); } - browser_focus(list); + browser_focus(list, browser_chosen_row()); } /* the game's host reached by its invite (joined, once it is reached: @@ -4385,7 +4403,7 @@ static void browser_update(struct widget_instance *list) return; } browser_games_read(); - if (focused != NONE && focused < multiplayer.game_count) + if (focused != NONE && focused < multiplayer.game_count && !ui_widget_port_hover_focused()) multiplayer.game_chosen = focused; for (row = list->child; row; row = row->next) { @@ -4453,7 +4471,7 @@ static void browser_update(struct widget_instance *list) usnprintf(text, ROW_TEXT_LENGTH - 1, L"%s %d", label, multiplayer.game_count); text_set(named(stats, "server_count_label", 0), text); } - browser_focus(list); + browser_focus(list, browser_chosen_row()); } /* "direct ip connect go" (Direct Link's PASTE LINK): the invite @@ -4489,13 +4507,25 @@ static boolean browser_join(struct widget_instance *widget, short controller, bo return ui_widget_port_join(widget, multiplayer.games[multiplayer.game_chosen], LOBBY_NAME, widget_deleted); } -/* "gamespy select item" (a row: joins its game) and "gamespy select button" -(Join) */ +/* "gamespy select item" (a row: chooses its game, or joins it once chosen) +and "gamespy select button" (Join) */ static boolean browser_select(struct widget_instance *widget, struct event_record *event, short controller, boolean *widget_deleted) { short row = browser_row_index(widget); + /* (a row not chosen, clicked after the pointer passed over it: chosen, + as Custom Edition's; a click on it chosen, a double click, A or JOIN + joins its game. Not the A press a join posts: lobby_browser_select) */ + if (row != NONE && row != browser_chosen_row() && !lobby_browser.ready) + { + if (multiplayer.mode == _multiplayer_mode_server_browser) + lobby_browser.chosen = (short)(lobby_browser.first + row); + else + multiplayer.game_chosen = row; + ui_play_audio_feedback_sound(SOUND_CURSOR); + return TRUE; + } if (multiplayer.mode == _multiplayer_mode_server_browser) return lobby_browser_select(widget, controller, row, widget_deleted); if (row != NONE) diff --git a/source/interface/ui_widget.c b/source/interface/ui_widget.c index b69ebf3a..6bb70156 100644 --- a/source/interface/ui_widget.c +++ b/source/interface/ui_widget.c @@ -5692,6 +5692,10 @@ static boolean ui_mouse_click_pending = FALSE; then doesn't scroll on at its end, which the pointer, moving, would make it do every frame (menu_functions.c) */ static boolean ui_mouse_focused_last = FALSE; +/* whether the pointer passing over a widget moved the focus last, not a +click (or the d-pad): the server browser's game stays chosen +(menu_functions.c) */ +static boolean ui_mouse_hovered_last = FALSE; static short ui_mouse_hover_x, ui_mouse_hover_y; static short ui_mouse_click_x, ui_mouse_click_y; /* whether the latest pointer read was the touchscreen: the taller legend @@ -6462,6 +6466,14 @@ boolean ui_widget_port_pointer_focused( return ui_mouse_focused_last; } +/* port: whether the pointer passing over a widget, not a click or the +d-pad, moved the menus' focus last (menu_functions.c's server browser) */ +boolean ui_widget_port_hover_focused( + void) +{ + return ui_mouse_hovered_last; +} + /* the d-pad buttons that step a widget back and forward */ static void ui_mouse_list_directions( struct widget_instance *widget, @@ -6881,7 +6893,10 @@ static void ui_widgets_process_mouse( /* (a selection list's row is chosen by a click, not by passing over it on the way to its buttons) */ if (!ui_mouse_selection_row(target->widget)) + { ui_mouse_give_focus(target->widget); + ui_mouse_hovered_last = TRUE; + } break; case _ui_mouse_target_list_slot: ui_mouse_step_list_to_slot(target->widget); @@ -6898,6 +6913,7 @@ static void ui_widgets_process_mouse( if (!ui_mouse_press_count && ui_mouse_click_pending) { ui_mouse_click_pending = FALSE; + ui_mouse_hovered_last = FALSE; target = ui_mouse_target_at(ui_mouse_click_x, ui_mouse_click_y); if (ui_debug_targets_enabled()) ui_debug_log_click(ui_mouse_click_x, ui_mouse_click_y, target, ui_debug_frame - ui_debug_click_frame); @@ -7729,6 +7745,7 @@ static void widget_instance_process_one_event_recursive( event->data.button.index <= _widget_event_dpad_right) { ui_mouse_focused_last = FALSE; + ui_mouse_hovered_last = FALSE; } if (event->type == _event_type_button && event->data.button.value > 1 &&