Add durable TempRadio, ACL, clock, bridge, GPS/I2C, USB logging, and Indicator recovery safeguards together with expanded automation, build/profile contracts, release metadata, and the hardware validation ledger.
82 lines
2.6 KiB
C++
82 lines
2.6 KiB
C++
#pragma once
|
|
|
|
#include <helpers/ui/DisplayDriver.h>
|
|
|
|
namespace mesh {
|
|
namespace ui {
|
|
|
|
// Draw complete text across a bounded number of rows. Lines may break in the
|
|
// middle of a word (which is important for SSIDs, where every space is
|
|
// significant), but never in the middle of a UTF-8 codepoint. If the text
|
|
// cannot fit in max_lines, only the final row is ellipsized.
|
|
inline int drawTextWrapped(DisplayDriver& display, int x, int y, int max_width,
|
|
int line_height, int max_lines, const char* str) {
|
|
if (str == nullptr || str[0] == 0 || max_width <= 0
|
|
|| line_height <= 0 || max_lines <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
int lines = 0;
|
|
size_t offset = 0;
|
|
while (str[offset] != 0 && lines < max_lines) {
|
|
// A display row is necessarily much shorter than this, but retain the
|
|
// same generous bound used by DisplayDriver::drawTextEllipsized().
|
|
char row[256];
|
|
size_t row_length = 0;
|
|
|
|
while (str[offset + row_length] != 0) {
|
|
size_t codepoint_length = 1;
|
|
const uint8_t first = (uint8_t)str[offset + row_length];
|
|
if (first >= 0xC2 && first <= 0xDF) {
|
|
codepoint_length = 2;
|
|
} else if (first >= 0xE0 && first <= 0xEF) {
|
|
codepoint_length = 3;
|
|
} else if (first >= 0xF0 && first <= 0xF4) {
|
|
codepoint_length = 4;
|
|
}
|
|
for (size_t i = 1; i < codepoint_length; ++i) {
|
|
if (str[offset + row_length + i] == 0
|
|
|| ((uint8_t)str[offset + row_length + i] & 0xC0) != 0x80) {
|
|
codepoint_length = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const size_t candidate_length = row_length + codepoint_length;
|
|
if (candidate_length >= sizeof(row)) break;
|
|
memcpy(row, str + offset, candidate_length);
|
|
row[candidate_length] = 0;
|
|
if (display.getTextWidth(row) > max_width) break;
|
|
row_length = candidate_length;
|
|
}
|
|
|
|
// Ensure forward progress even for a glyph wider than the viewport.
|
|
if (row_length == 0) {
|
|
row_length = 1;
|
|
while (str[offset + row_length] != 0
|
|
&& ((uint8_t)str[offset + row_length] & 0xC0) == 0x80
|
|
&& row_length + 1 < sizeof(row)) {
|
|
++row_length;
|
|
}
|
|
}
|
|
|
|
if (lines + 1 == max_lines && str[offset + row_length] != 0) {
|
|
display.drawTextEllipsized(x, y + lines * line_height, max_width,
|
|
str + offset);
|
|
++lines;
|
|
break;
|
|
}
|
|
|
|
memcpy(row, str + offset, row_length);
|
|
row[row_length] = 0;
|
|
display.setCursor(x, y + lines * line_height);
|
|
display.print(row);
|
|
offset += row_length;
|
|
++lines;
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace mesh
|