Layout (pocketmage_layout)

Layout API

The layout module centralizes display geometry and the text-fitting helpers used by the OS and the display primitives. It commits to one hardware pair: a 256x32 SSD1326 OLED and a 320x240 GDEQ031T10 e-ink panel.

Geometry constants

constexpr int kOledWidth  = 256;
constexpr int kOledHeight = 32;
constexpr int kEinkWidth  = 320;
constexpr int kEinkHeight = 240;

constexpr int kEinkStatusH  = 26;                 // bottom status band
constexpr int kEinkContentH = kEinkHeight - kEinkStatusH;  // 214

The e-ink content region is kEinkHeight - kEinkStatusH tall; the status band is owned by EINK().drawStatusBar().

Text frames

constexpr int kFrameTextPadX  = 4;   // left text pad inside an e-ink frame
constexpr int kFrameCursorPad = 16;  // right/center clearance for the cursor

OLED baselines and the info line

constexpr int kOledWordBaseline      = 16;  // oledWord/sysMessage center
constexpr int kOledWordLargeBaseline = 21;  // oledWord allowLarge heading
constexpr int kOledEditBaseline      = 20;  // oledLine input/cursor
constexpr int kOledEditRightPad      = 8;
constexpr int kOledEditCursorX       = 0;
constexpr int kOledEditCursorY       = 1;
constexpr int kOledEditCursorH       = 22;
constexpr int kOledInfoBaseline      = kOledHeight;       // status line
constexpr int kOledInfoBatteryW      = 10;
constexpr int kOledInfoBatteryH      = 6;
constexpr int kOledInfoBatteryY      = kOledHeight - kOledInfoBatteryH;
constexpr int kOledInfoGap           = 6;
constexpr int kOledInfoFirstX        = kOledInfoBatteryW + kOledInfoGap;

Progress bar and sysMessage overlay geometry:

constexpr int kOledProgressMaxW  = kEinkWidth - 5;  // reference width
constexpr float kOledProgressFullFrac = 0.8f;       // "full" arrow threshold
constexpr int kOledSysMsgPadX   = 8;
constexpr int kOledSysMsgFrameH = kOledHeight + 16;
constexpr int kOledSysMsgRadius = 10;
constexpr int kOledSysMsgRaise  = 5;

Scroll preview

constexpr int kOledScrollPreviewW = 128;   // preview strip width
constexpr int kOledScrollRowPitch = 4;     // bar vertical pitch
constexpr int kOledScrollBarH     = 2;
constexpr int kOledScrollBaseY    = 28;
constexpr int kOledScrollNormX    = 61;
constexpr int kOledScrollTabX     = 68;
constexpr int kOledScrollNormMaxW = 56;
constexpr int kOledScrollTabMaxW  = 49;
constexpr int kOledScrollTextX    = 140;
constexpr int kOledScrollLabelY   = 12;
constexpr int kOledScrollValueY   = 24;

Icon grid and label cascade

constexpr int kIconCellSize = 40;   // app icon cell
constexpr int kIconNameGap  = 13;   // icon-to-name baseline gap
constexpr int kGridLabelMaxW = 60;  // max label width = cell pitch

static constexpr FontStyle kLabelCascade[] = {
  FontStyle::Body, FontStyle::BodyNarrow, FontStyle::Small,
};

kLabelCascade is the standard fit cascade (all serif: ncenR10, timR10, ncenR08) used by the HOME/APPLOADER grid and the SETTINGS list. einkRowPitch() converts a style to a canonical row height:

inline int einkRowPitch(FontStyle s) {
  return FontEngine::fontHeight(DisplayTarget::EINK, s) + EINK().getLineSpacing();
}

Free functions

sliceThatFits

size_t sliceThatFits(const char* s, size_t n, int maxTextWidth, FontStyle style);

Returns the byte length of the longest prefix of s[0..n) that fits in maxTextWidth at style, word-aware: a trailing space boundary is preferred, so a caller can slice line by line without mid-word cuts. Stops at a newline (returns the fit before it, or 1 for a bare newline). Returns 0 when nothing fits.

truncateWithEllipsis

String truncateWithEllipsis(const String& text, int maxWidthPx, FontStyle style,
                            DisplayTarget target = DisplayTarget::EINK);

Truncates text to maxWidthPx and appends "..." (binary-search cut, so it is fast even on long strings). The cut lands on a UTF-8 character boundary: continuation bytes are backed off until the prefix ends cleanly. Returns text unchanged when it already fits.

wordWrap

std::vector<String> wordWrap(const String& text, int maxWidthPx, FontStyle style);

Wraps to whole words, one string per line, using sliceThatFits. Lines are trimmed; an overlong word pushes as a single (n) line.

Example

#include <pocketmage.h>

void drawBudget(std::vector<String>& lines) {
  int y = einkRowPitch(FontStyle::Body);
  for (const String& line : lines) {
    FontEngine::drawText(DisplayTarget::EINK, kFrameTextPadX, y, line,
                         FontStyle::Body);
    y += einkRowPitch(FontStyle::Body);
    if (y > kEinkContentH) break;
  }
}