Public Access
Add widget kit, input layer with Compose Key, Launcher and demo apps
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
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace roro::utf8 {
|
||||
|
||||
inline bool isContinuation(unsigned char c) { return (c & 0xC0) == 0x80; }
|
||||
|
||||
inline std::string encode(uint32_t cp) {
|
||||
std::string out;
|
||||
if (cp < 0x80) {
|
||||
out += static_cast<char>(cp);
|
||||
} else if (cp < 0x800) {
|
||||
out += static_cast<char>(0xC0 | (cp >> 6));
|
||||
out += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
} else if (cp < 0x10000) {
|
||||
out += static_cast<char>(0xE0 | (cp >> 12));
|
||||
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||
out += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
} else {
|
||||
out += static_cast<char>(0xF0 | (cp >> 18));
|
||||
out += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
|
||||
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||
out += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Byte index of the character start before / after `pos`.
|
||||
inline size_t previous(const std::string& s, size_t pos) {
|
||||
if (pos == 0) return 0;
|
||||
do pos--;
|
||||
while (pos > 0 && isContinuation(static_cast<unsigned char>(s[pos])));
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline size_t next(const std::string& s, size_t pos) {
|
||||
if (pos >= s.size()) return s.size();
|
||||
do pos++;
|
||||
while (pos < s.size() && isContinuation(static_cast<unsigned char>(s[pos])));
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Longest prefix of at most `maxBytes` that doesn't cut a character.
|
||||
inline size_t clampToBoundary(const std::string& s, size_t maxBytes) {
|
||||
if (s.size() <= maxBytes) return s.size();
|
||||
size_t pos = maxBytes;
|
||||
while (pos > 0 && isContinuation(static_cast<unsigned char>(s[pos]))) pos--;
|
||||
return pos;
|
||||
}
|
||||
|
||||
} // namespace roro::utf8
|
||||
Reference in New Issue
Block a user