Control Design For A UI Menu

a graphic art image that depicts that concept of a menu that is under design, with text beneath that says Designing a UI Menu (LCD Screen Style)

This installment of How To Program a Raspberry Pi Pico with Rust covers the process of designing the structures and functions that drive menu navigation and control UI Interactions.

Included will be explanations on:

  • Identifying what a menu needs to do

  • Designing a control structure for menu navigation

  • Designing a control structure for sharing navigation & selection information to the program that the menu needs to interface with

What Should The Menu Do?

The simpler the better, not only for the end user but also for programming and maintaining it.

 


My Main Menu Control design considerations:

  • What aspects of the system can this menu control?

    • How can each of these aspects be controlled in as few screens as possible, without being too leaky or obscure?

  • How complex does the data structure that this menu generates need to be in order to convey selections back to the program

  • How big is the screen that will show the menu?

  • How many interface types does the user have to the menu?

    • How many physical buttons or inputs?

    • Is it a touch screen?

    • Are there any other sorts of events that need to be evaluated?

  • What is the most appropriate way to create data structures that define each screen of the menu to the system that also provides the appropriate selection actions?

 

By asking and answering these questions, the role, look, and feel of the menu should be pretty clearly defined.

Let’s look at an example.

If there is a system that has four outputs, and the job of the menu is to give the user the ability to toggle those outputs on and off, then at a minimum there must be:

  • a page that will reference an output and give the ability to toggle that specific output

  • an option on that page to ‘browse’ the outputs, giving the ability to select one of the outputs

  • The menu should definitely show the state that the menu thinks the output pin is in

  • the page that toggles the output should show the label of the pin that is being toggled

  • ideally to reduce user button pressing the toggle option should just change beneath the cursor.

    • meaning if the pin is off, the option should say ‘toggle-on’ or if the pin is on the option should say ‘toggle-off’ or something similar that gives the relevant command based on the state of the pin.

Designing a Menu Navigation Structure

a drawn diagram of a menu navigation node which is configured to have a next, prev, select, and back options.

In Rust, I liked the idea of using a custom struct to represent my menu ‘map’. A configurable relationship between page nodes if you will.

I created a menu page ‘node’ struct that contains reference information about the menu ‘page’ as well as menu pages that are reachable from this page. It also has meta data that describes cursor location, and a ‘selection buffer’ for when a selection action is made. It’s mostly just a linked list :-)

I’m in no way claiming that this is the best way! I did it this way because I just wanted to solve this problem one time and use this code for all my future Rust menu work. I’ll be extending the menu for PicoPonics over time, and I’ll have menus in other projects so I wanted it done where I can drop it in and configure it.

 

pub struct MenuNode {

// ID for this menu node
pub node_id: u16,

// reachable menu nodes from THIS menu node
pub next_ref: u16,
pub prev_ref: u16,
pub select_ref: u16,
pub deselect_ref: u16,


// Option and Selection Indexes make system aware of what
// user intends to select

pub option_index: u8,
pub selection_index: u8,

// ranges are added to make sure no erroneous index is ever
// attempted to be referenced.

pub selection_range: u8,
pub option_range: u8
}

 

This node struct above is a navigation control structure. It’s a map of menu pages and gives each page of the menu a way to know of other pages that can be navigated to. But outside of that there are still some concepts to grok before moving on.

Options

any entity which can be a place where a cursor COULD land, is an option (a menu nav option)

Options are places for a ‘cursor’ to land. Or better said:

If a cursor could be put next to it, it’s a menu option.

So even if the menu is a touch screen and there is no cursor, items that are used to change the state of the menu, are “menu options”. Options that change menu pages are menu nav options. Options that update selections are “MenuSelectionUpdateOptions”.

Selections

The next option will change the active selection from 12 to 13. Cycling through until the end of the list, and moving back to the beginning. The prev option does the same in reverse.

Selections are different from Options. A Selection is a currently active value that is what will be chosen if a ‘submit’ level action is invoked. Let’s use an example.

Let’s say that The selection is 10. And 10 is just one number out of a list. The list may look like this:

[10, 12, 14, 20, 22, 24]

Now let’s say there is an option next. If the next option is invoked, the value will go from 10 to 12, because 12 is the next item in the list. If the cursored option is “back”, then the selected value would move to 10, if the current selection were sitting on 12.

But what if the cursored option is “Select”? This is interesting because it totally depends on the program! This could toggle on power to a pin (pin 10 if pin 10 was the selected pin) it could navigate to a different page dedicated to pin 10. The menu doesn’t care what is done with the selection, the only thing it needs to do is provide that selection information to the parent program.

The key takeaways are:

  1. Options are places for the cursor to land, and are used for updating the menu context only.

  2. Selections are values that represent something meaningful to the menu AND the program. Selections contain or reference information that is valuable to other parts of the application.

Getting Menu Selection Data Back Into The Program

a diagram of a mapping between a menu context, to an application state context, mapped onto a physical hardware output pin

Making a light switch on with a menu selection

In the simplest form, there are 3 distinct contexts that are unavoidable when implementing a menu.

  1. The menu control context (the updates or changes users trigger)

  2. The context of the application being controlled context

  3. The hardware abstraction layer (the code that connects application state to physical components)

These 3 things are unavoidable because if there is code for a menu, that’s menu context. If there is code for application state, that’s application state context.

Even if there is no struct or class for application state, or a “menuController“ or something like that, it doesn’t matter, these contexts will exist, whether they are named or not.

Since this is the case, it’s best to think of a clean way to interface between the contexts that is as performant and simple as possible.

My solution at the time of writing was to create a value in the MenuController that contains a ‘readMeRequest’. The main loop of the program evaluates the MenuController on each loop, but only the read me variable.

If that variable is true, then the main loop extracts data from the menu controller. If it’s false, the main loop ignores the menuController, and continues with its other responsibilities.

The main loop should have visibility to all three contexts. So it should be capable of moving the menu updates into the application state, and then using the application state to update hardware states.

Note: It’s common to carry out hardware updates after program and other logic is carried out.

Conclusion

Menu design is the foundation of the menu-building effort. It’s possible to design a menu control system that is viable for multiple physical screens.

There are a lot of considerations for crafting a well-designed menu, and user effort + required knowledge should likely be priority number one other than user safety.

Options are tools for the menu context itself, and selections are values that are stored within the menu context that need to be available in other contexts.

Regarding menu page navigation, any individual menu page only needs to be aware of references to menu pages that are accessible via options from its own page. There is usually no need for each menu page to have the ability to navigate to any other page.

Previous
Previous

Rust 365 Challenge: Progress Report

Next
Next

Enums: Programming with Rust