[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2026-02-26 12:28:50 +00:00
parent f00d2079d3
commit 879549da39
4 changed files with 18 additions and 5 deletions

View File

@ -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;
}

View File

@ -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 )

View File

@ -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 ) {

View File

@ -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 );