Skip to main content

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.

CustomScreen gives you a blank canvas and full control over what gets rendered. Use it any time you need a screen that goes beyond a list of menu items — data visualizations, about pages, status dashboards, or any bespoke UI layout that the built-in MenuScreen and SettingsScreen types can’t express. When the user selects a CustomScreen item, the library calls your customDraw function every frame and renders the result. Pressing the select button returns to the previous screen automatically via the built-in handleInput() implementation.

Implementation flow

1

Declare the CustomScreen globally

Create the object at file scope, the same way you would declare a MenuScreen.
#include "OpenMenuOS.h"

OpenMenuOS menu;
MenuScreen mainMenu("Main Menu");
CustomScreen customScreen("Custom Screen"); // optional title
2

Set customDraw in setup()

Assign a lambda to customScreen.customDraw inside setup(). The lambda captures whatever variables you need from the surrounding scope.
void setup() {
  customScreen.customDraw = []() {
    canvas.drawSmoothRoundRect(-15, 50, 40, 0, 50, 50, TFT_BLUE, TFT_BLACK);
    canvas.drawSmoothRoundRect(10, 10, 200, 100, 20, 5, TFT_ORANGE, TFT_BLACK);
    canvas.drawSmoothRoundRect(120, -25, 40, 0, 40, 40, TFT_DARKGREEN, TFT_BLACK);
    canvas.drawString("V" + String(menu.getLibraryVersion()), 10, 10);
    canvas.setTextColor(TFT_WHITE, TFT_BLACK);
    canvas.drawString("Press UP for popup demo", 10, 30);
  };

  // ... rest of setup
  menu.begin(&mainMenu);
}
customDraw must be assigned in setup(), not at global scope during object construction. At global initialization time, canvas and other library globals are not yet initialized, so drawing calls made there will have no effect or will crash.
3

Link the screen to a MenuScreen

Add the CustomScreen as a navigation target from any MenuScreen item.
// Option A — explicit label
mainMenu.addItem("Custom Screen", &customScreen);

// Option B — use the CustomScreen's own title automatically
mainMenu.addItem(&customScreen);
4

Initialize the menu

Call menu.begin() with your root menu. No extra configuration is needed for CustomScreen.
menu.begin(&mainMenu);

The global canvas object

All drawing in OpenMenuOS goes through the canvas sprite, declared in the library header:
extern TFT_eSprite canvas;
canvas is a TFT_eSprite backed by an off-screen buffer. The library composites canvas onto the physical display after every frame. Inside customDraw, you draw to canvas exactly as you would with any TFT_eSPI sprite.

Drawing primitives

The full TFT_eSPI sprite API is available on canvas. Common primitives used inside customDraw:
// Fill the entire canvas with a solid color
canvas.fillScreen(TFT_BLACK);

// Set text foreground / background colors
canvas.setTextColor(TFT_WHITE, TFT_BLACK);

// Draw a string at pixel coordinates
canvas.drawString("Hello World", x, y);

// Filled circle
canvas.fillCircle(cx, cy, radius, TFT_BLUE);

// Outline rectangle
canvas.drawRect(x, y, width, height, TFT_GREEN);

// Smooth (anti-aliased) rounded rectangle
// drawSmoothRoundRect(x, y, r, ir, w, h, fg, bg)
canvas.drawSmoothRoundRect(10, 10, 200, 100, 20, 5, TFT_ORANGE, TFT_BLACK);
drawSmoothRoundRect parameters: (x, y, outerRadius, innerRadius, width, height, fgColor, bgColor). Setting innerRadius to 0 produces a solid shape.

Accessing menu data inside customDraw

Because customDraw is a lambda defined inside the sketch, it can access sketch-level globals directly — including the menu instance.
customScreen.customDraw = []() {
  // Read the library version from the menu instance
  canvas.drawString("V" + String(menu.getLibraryVersion()), 10, 10);

  // Read display dimensions
  int w = menu.getTftWidth();
  int h = menu.getTftHeight();
  canvas.drawString("Display: " + String(w) + "x" + String(h), 10, 30);
};
You can also read settings values from a SettingsScreen:
customScreen.customDraw = []() {
  uint8_t brightness = settingsScreen.getSettingValue("Brightness");
  canvas.drawString("Brightness: " + String(brightness) + "%", 10, 50);
};

Input handling

CustomScreen overrides handleInput() internally. The select button navigates back to the previous screen — no code required on your part. Encoder rotation and button presses for the up/down actions are not forwarded to customDraw; if you need to react to input from within a custom screen, trigger a popup or use a callback-based item on an intermediate MenuScreen instead.

Redirecting to a CustomScreen programmatically

If you need to navigate to a CustomScreen from a callback rather than through a menu item selection, use menu.redirectToScreen():
void redirectToCustomScreen() {
  menu.redirectToScreen(&customScreen);
}

// Register as a menu item action
mainMenu.addItem("Redirect To Screen", nullptr, redirectToCustomScreen);

Complete example

#include "OpenMenuOS.h"

OpenMenuOS menu;
MenuScreen mainMenu("Main Menu");
CustomScreen customScreen("Custom Screen");
SettingsScreen settingsScreen("Settings");

void setup() {
  Serial.begin(115200);

  customScreen.customDraw = []() {
    canvas.drawSmoothRoundRect(-15, 50, 40, 0, 50, 50, TFT_BLUE, TFT_BLACK);
    canvas.drawSmoothRoundRect(10, 10, 200, 100, 20, 5, TFT_ORANGE, TFT_BLACK);
    canvas.drawSmoothRoundRect(120, -25, 40, 0, 40, 40, TFT_DARKGREEN, TFT_BLACK);
    canvas.drawString("V" + String(menu.getLibraryVersion()), 10, 10);
    canvas.setTextColor(TFT_WHITE, TFT_BLACK);
    canvas.drawString("Press UP for popup demo", 10, 30);
  };

  mainMenu.addItem("Settings", &settingsScreen);
  mainMenu.addItem(&customScreen); // uses "Custom Screen" title automatically

  menu.useStylePreset("Rabbit_R1");
  menu.setEncoderPin(5, 2);
  menu.setSelectPin(19);
  menu.begin(&mainMenu);
}

void loop() {
  PopupResult result = PopupManager::update();
  menu.loop();
}

Build docs developers (and LLMs) love