Documentation Index
Fetch the complete documentation index at: https://mintlify.com/The-Young-Maker/OpenMenuOS/llms.txt
Use this file to discover all available pages before exploring further.
The PopupManager class gives you a complete, non-blocking dialog system with five built-in visual styles. Each popup type carries its own color scheme and default title so you can surface feedback, confirmations, and errors with a single method call — no manual drawing required.
OpenMenuOS ships five popup types, each with a distinct color theme:
| Type | Color | Default title | Notes |
|---|
INFO | Blue | ”Information” | General messages |
SUCCESS | Green | ”Success” | Auto-closes after ~3 s |
WARNING | Orange | ”Warning” | Requires user dismissal |
ERROR | Red | ”Error” | Requires user dismissal |
QUESTION | Cyan | ”Question” | Shows Yes / No buttons |
Quick one-liner methods
Each type has a dedicated static method. The title parameter is optional on all of them.
Info
Success
Warning
Error
Question
PopupManager::showInfo("Operation completed successfully!", "Info Demo");
// Auto-closes after ~3 seconds by default
PopupManager::showSuccess("Settings saved!");
PopupManager::showWarning("This action cannot be undone", "Warning!");
PopupManager::showError("Failed to save settings", "Error");
// Returns PopupResult::NONE immediately — check the result in loop()
PopupManager::showQuestion("Are you sure you want to delete all data?", "Confirm Delete");
When the one-liners aren’t enough, build a PopupConfig struct and pass it to PopupManager::show().
PopupConfig config;
config.title = "Custom Popup";
config.message = "This popup closes on its own after 5 seconds.";
config.type = PopupType::INFO;
config.autoClose = true;
config.autoCloseDelay = 5000; // milliseconds
config.customColor = 0x07E0; // RGB565 green — overrides type default
PopupManager::show(config);
All fields with their defaults:
| Field | Type | Default | Purpose |
|---|
title | const char* | nullptr (uses type default) | Header text |
message | const char* | nullptr | Body text (required) |
type | PopupType | PopupType::INFO | Color scheme and icon |
showButtons | bool | true | Show action buttons |
showCancelButton | bool | false | Add a Cancel / No button |
autoClose | bool | false | Close automatically |
autoCloseDelay | uint32_t | 3000 | Auto-close delay (ms) |
customColor | uint16_t | 0 (uses type default) | Override header color |
customIcon | const uint16_t* | nullptr | Custom icon pixel data |
customIconWidth | uint16_t | 0 | Icon width in pixels |
customIconHeight | uint16_t | 0 | Icon height in pixels |
The non-blocking pattern
PopupManager is non-blocking. Calling showInfo() or showQuestion() queues the popup but does not wait for a response. You poll for the result on every iteration of loop() using PopupManager::update().
PopupManager::update() must be called before menu.loop(). The update method both renders the active popup and intercepts input. Calling it after menu.loop() can cause the underlying menu to process button presses that were meant for the popup.
void loop() {
PopupResult popupResult = PopupManager::update();
menu.loop(); // safe — popup has already consumed any relevant input
if (popupResult != PopupResult::NONE) {
switch (popupResult) {
case PopupResult::OK:
Serial.println("User clicked OK/Yes");
// your confirmation logic here
break;
case PopupResult::CANCEL:
Serial.println("User clicked Cancel/No");
// your cancellation logic here
break;
default:
break;
}
}
}
PopupManager::update() returns one value per call:
| Value | Meaning |
|---|
PopupResult::NONE | No popup is active, or the popup is still waiting for input |
PopupResult::OK | User pressed OK or Yes |
PopupResult::CANCEL | User pressed Cancel or No |
PopupResult::YES | Alias for OK in question dialogs |
PopupResult::NO | Alias for CANCEL in question dialogs |
A non-NONE result is delivered exactly once — on the loop iteration in which the user interacts with the popup. Subsequent calls return PopupResult::NONE until a new popup is shown.
While a popup is visible, PopupManager::update() captures all button and encoder events before menu.loop() sees them. This prevents accidental navigation in the underlying menu when the user dismisses a dialog.
You can also check PopupManager::isActive() if you want to gate other logic:
void loop() {
PopupResult popupResult = PopupManager::update();
if (!PopupManager::isActive()) {
menu.loop();
}
if (popupResult != PopupResult::NONE) {
// handle result
}
}
Encoder support
When a rotary encoder is configured with menu.setEncoderPin(clk, dt), it works inside popups too:
- Rotate the encoder to move the highlight between the OK and Cancel buttons.
- Press the encoder button (select) to confirm the highlighted choice.
No extra configuration is needed — encoder support in popups is automatic once the encoder pins are registered.
Confirmation dialogs with showQuestion
showQuestion is the standard pattern for any action that needs explicit user approval:
void resetDevice() {
PopupManager::showQuestion(
"This will restart the device.\nContinue?",
"Reset Device"
);
}
void loop() {
PopupResult result = PopupManager::update();
menu.loop();
if (result == PopupResult::OK) {
// user confirmed — proceed with the destructive action
ESP.restart();
}
}
showQuestion automatically adds both an OK/Yes and a Cancel/No button. You do not need to set showCancelButton in a PopupConfig manually when using this helper.
Additional state methods
// Check programmatically whether a popup is currently on screen
bool active = PopupManager::isActive();
// Dismiss the current popup from code (e.g., on a timeout you control yourself)
PopupManager::hide();