From 879549da39f1d1bbcb6347d8d24cf86473afdf31 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 26 Feb 2026 12:28:50 +0000 Subject: [PATCH] [dynui] Allow for duplicate shortcut keys When searching for a shortcut key, search first from the currently selected menu item and then from the start of the list. This allows several ways for a shortcut key to be meaningfully used multiple times within the same menu. For example, two sections may have the same shortcut key: item --key s --gap (S)ection 1 item ... item ... item --key s --gap (S)ection 2 item ... With the above menu, repeated "s" keypresses would cycle through the sections. As another example, entries within different sections may have the same shortcut keys. For example: item --key d --gap (D)ebian item --key s debst Debian (s)table release item --key u debun Debian (u)nstable release item --key f --gap (F)edora item --key s fedst Fedora (s)table release item --key u fedun Fedora (u)nstable release With the above menu, a shortcut key sequence such as "f", "s" can be used to select an entry within a specific section, avoiding the need to choose shortcut keys that are globally unique within the menu. Signed-off-by: Michael Brown --- src/core/dynui.c | 15 +++++++++++++-- src/hci/tui/form_ui.c | 3 ++- src/hci/tui/menu_ui.c | 3 ++- src/include/ipxe/dynui.h | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/dynui.c b/src/core/dynui.c index c2af95f86..921716a72 100644 --- a/src/core/dynui.c +++ b/src/core/dynui.c @@ -207,15 +207,26 @@ struct dynamic_item * dynui_item ( struct dynamic_ui *dynui, * * @v dynui Dynamic user interface * @v key Shortcut key + * @v index Starting index * @ret item User interface item, or NULL if not found */ -struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui, int key ) { +struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui, int key, + unsigned int index ) { struct dynamic_item *item; + /* Do nothing unless shortcut key is set */ + if ( ! key ) + return NULL; + + /* Search from current list index */ list_for_each_entry ( item, &dynui->items, list ) { - if ( key && ( key == item->shortcut ) ) + if ( ( key == item->shortcut ) && ( item->index >= index ) ) return item; } + /* If not found, search again from start of list */ + if ( index ) + return dynui_shortcut ( dynui, key, 0 ); + return NULL; } diff --git a/src/hci/tui/form_ui.c b/src/hci/tui/form_ui.c index 2bce952fa..9fa0dbd12 100644 --- a/src/hci/tui/form_ui.c +++ b/src/hci/tui/form_ui.c @@ -480,7 +480,8 @@ static int form_loop ( struct form *form ) { break; default: /* Move to input with matching shortcut key, if any */ - item = dynui_shortcut ( form->dynui, key ); + item = dynui_shortcut ( form->dynui, key, + scroll->current ); if ( item ) { scroll->current = item->index; if ( ! item->name ) diff --git a/src/hci/tui/menu_ui.c b/src/hci/tui/menu_ui.c index f789a298f..b085be1bd 100644 --- a/src/hci/tui/menu_ui.c +++ b/src/hci/tui/menu_ui.c @@ -200,7 +200,8 @@ static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) { chosen = 1; break; default: - item = dynui_shortcut ( ui->dynui, key ); + item = dynui_shortcut ( ui->dynui, key, + ui->scroll.current ); if ( item ) { ui->scroll.current = item->index; if ( item->name ) { diff --git a/src/include/ipxe/dynui.h b/src/include/ipxe/dynui.h index e50c6ab49..2dd23873d 100644 --- a/src/include/ipxe/dynui.h +++ b/src/include/ipxe/dynui.h @@ -59,7 +59,7 @@ extern struct dynamic_ui * find_dynui ( const char *name ); extern struct dynamic_item * dynui_item ( struct dynamic_ui *dynui, unsigned int index ); extern struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui, - int key ); + int key, unsigned int index ); extern int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, unsigned long retimeout, const char *select, struct dynamic_item **selected );