Public Access
Steps 5 and 6 of M0. - lib/ui (host-tested): ListModel, LineEditor (UTF-8, byte-limited), wrapText, ToastQueue, DialogModel - lib/input (host-tested): KeyMapper turns keyboard state into logical keys (Fn arrows, ` Back, Fn+` Home) and composes accents with opt - src/ui: off-screen 16-bit frame pushed in one transfer (fixes the flicker), Status Bar, list, text, line editor, dialog, Toast widgets, Notifier (Toast + beep + LED), vendored public-domain Latin-1 fonts - Apps: minimal Launcher, Widget demo, Diagnostics (SD erase now behind a confirmation dialog) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
61 lines
1.9 KiB
C++
61 lines
1.9 KiB
C++
#include "text_wrap.h"
|
|
|
|
#include "utf8.h"
|
|
|
|
namespace roro {
|
|
|
|
namespace {
|
|
|
|
void wrapParagraph(const std::string& para, int maxWidth, const MeasureText& measure,
|
|
std::vector<std::string>& out) {
|
|
std::string line;
|
|
size_t pos = 0;
|
|
while (pos < para.size()) {
|
|
size_t space = para.find(' ', pos);
|
|
std::string word = para.substr(pos, space == std::string::npos ? std::string::npos : space - pos);
|
|
pos = space == std::string::npos ? para.size() : space + 1;
|
|
|
|
std::string candidate = line.empty() ? word : line + " " + word;
|
|
if (measure(candidate) <= maxWidth) {
|
|
line = candidate;
|
|
continue;
|
|
}
|
|
if (!line.empty()) {
|
|
out.push_back(line);
|
|
line.clear();
|
|
}
|
|
// The word alone may still be too wide: cut it character by character.
|
|
while (measure(word) > maxWidth) {
|
|
size_t cut = 0;
|
|
while (true) {
|
|
size_t nextCut = utf8::next(word, cut);
|
|
if (measure(word.substr(0, nextCut)) > maxWidth) break;
|
|
cut = nextCut;
|
|
}
|
|
if (cut == 0) cut = utf8::next(word, 0); // a single character wider than the line
|
|
out.push_back(word.substr(0, cut));
|
|
word = word.substr(cut);
|
|
}
|
|
line = word;
|
|
}
|
|
out.push_back(line);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::vector<std::string> wrapText(const std::string& text, int maxWidth, const MeasureText& measure) {
|
|
std::vector<std::string> lines;
|
|
if (text.empty()) return lines;
|
|
size_t start = 0;
|
|
while (true) {
|
|
size_t nl = text.find('\n', start);
|
|
wrapParagraph(text.substr(start, nl == std::string::npos ? std::string::npos : nl - start), maxWidth,
|
|
measure, lines);
|
|
if (nl == std::string::npos) break;
|
|
start = nl + 1;
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
} // namespace roro
|