UI: the website's colours, on every screen

The theme takes the website's palette, each a colour the RGB332 frame
buffer holds exactly: cyan for the accent, a selected row and a chosen
button in cyan with dark blue text, a slate Status Bar (the grey it
asked for was shown as a dull olive), the website's orange for
warnings and its pink for messages. "Good" (a Fix, the quietest
channel) keeps a green of its own, which the website doesn't have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
2026-10-09 23:14:37 +02:00
co-authored by Claude Opus 5.5
parent b1c80c912f
commit aadfd8cb37
10 changed files with 75 additions and 36 deletions
+17
View File
@@ -59,3 +59,20 @@ A screenshot could only be taken by typing `screenshot` in the Shell, where "now
**Checked on the device** (2026-10-07, with `key shot`): in the Launcher, a 33,383-byte PNG appears in `/screenshots` and is the Launcher; with the help panel open, the picture is of the panel (which now lists Fn p) and the panel stays open; on Settings > Debug Console, no file is written; back on the Settings list, one is. The test pictures were removed. **Checked on the device** (2026-10-07, with `key shot`): in the Launcher, a 33,383-byte PNG appears in `/screenshots` and is the Launcher; with the help panel open, the picture is of the panel (which now lists Fn p) and the panel stays open; on Settings > Debug Console, no file is written; back on the Settings list, one is. The test pictures were removed.
**Not checked:** the real Fn+p on the keyboard (the mapper is host-tested); the two Toasts that refuse, which weren't looked at (one of them is on the page that mustn't be photographed); a device with no card. **Not checked:** the real Fn+p on the keyboard (the mapper is host-tested); the two Toasts that refuse, which weren't looked at (one of them is on the page that mustn't be photographed); a device with no card.
## The website's colours, on every screen
Every screen takes its colours from `src/ui/theme.h`, so the palette changed there, to the website's (`site/static/css/site.css`, dark side). Each is a colour the RGB332 frame buffer holds exactly.
| | Was | Is |
|---|---|---|
| Accent | teal-blue | cyan `#00dbff` |
| A selected row, a dialog's chosen button | darker teal, white text | cyan, dark blue text (`#000055`) |
| Status Bar | `#202020`, which the frame buffer showed as a dull olive | slate `#494955`; what is idle on it in light grey `#b6b6aa` |
| Muted text | grey, as the frame buffer rounded it | the same grey, exact: `#9292aa` |
| Warning | orange | the website's orange `#ff9200` |
| Someone wrote (unread count, a mention, a message Toast) | green | pink `#ff92ff` |
| Good (a Fix, the quietest channel, a live task) | the same green | green `#49db55`, the one colour that isn't the website's: it has no green |
| Text on a Toast | black | dark blue |
Looked at on the device, by screenshot: the Launcher, IRC, Wi-Fi Tools (menu, channel occupancy), GNSS (position, sky), Gemini, the LoRa Scanner (Sniffer, Sweep, presets), Storage, Notes, SSH, the Shell, System, Settings and the help panel. Not looked at: dialogs, a warning or a message Toast, the SSH terminal (which keeps its own 16 ANSI colours), the Setup screens.
+17
View File
@@ -67,3 +67,20 @@ A screenshot could only be taken by typing `screenshot` in the Shell, where "now
**Checked on the device** (2026-10-07, with `key shot`): in the Launcher, a 33,383-byte PNG appears in `/screenshots` and is the Launcher; with the help panel open, the picture is of the panel (which now lists Fn p) and the panel stays open; on Settings > Debug Console, no file is written; back on the Settings list, one is. The test pictures were removed. **Checked on the device** (2026-10-07, with `key shot`): in the Launcher, a 33,383-byte PNG appears in `/screenshots` and is the Launcher; with the help panel open, the picture is of the panel (which now lists Fn p) and the panel stays open; on Settings > Debug Console, no file is written; back on the Settings list, one is. The test pictures were removed.
**Not checked:** the real Fn+p on the keyboard (the mapper is host-tested); the two Toasts that refuse, which weren't looked at (one of them is on the page that mustn't be photographed); a device with no card. **Not checked:** the real Fn+p on the keyboard (the mapper is host-tested); the two Toasts that refuse, which weren't looked at (one of them is on the page that mustn't be photographed); a device with no card.
## The website's colours, on every screen
Every screen takes its colours from `src/ui/theme.h`, so the palette changed there, to the website's (`site/static/css/site.css`, dark side). Each is a colour the RGB332 frame buffer holds exactly.
| | Was | Is |
|---|---|---|
| Accent | teal-blue | cyan `#00dbff` |
| A selected row, a dialog's chosen button | darker teal, white text | cyan, dark blue text (`#000055`) |
| Status Bar | `#202020`, which the frame buffer showed as a dull olive | slate `#494955`; what is idle on it in light grey `#b6b6aa` |
| Muted text | grey, as the frame buffer rounded it | the same grey, exact: `#9292aa` |
| Warning | orange | the website's orange `#ff9200` |
| Someone wrote (unread count, a mention, a message Toast) | green | pink `#ff92ff` |
| Good (a Fix, the quietest channel, a live task) | the same green | green `#49db55`, the one colour that isn't the website's: it has no green |
| Text on a Toast | black | dark blue |
Looked at on the device, by screenshot: the Launcher, IRC, Wi-Fi Tools (menu, channel occupancy), GNSS (position, sky), Gemini, the LoRa Scanner (Sniffer, Sweep, presets), Storage, Notes, SSH, the Shell, System, Settings and the help panel. Not looked at: dialogs, a warning or a message Toast, the SSH terminal (which keeps its own 16 ANSI colours), the Setup screens.
+1 -1
View File
@@ -534,7 +534,7 @@ void GeminiApp::drawLine(Canvas& c, size_t i, int firstRow, int y, int rowsLeft)
for (int r = firstRow; r < static_cast<int>(rows.size()) && r - firstRow < rowsLeft; r++) { for (int r = firstRow; r < static_cast<int>(rows.size()) && r - firstRow < rowsLeft; r++) {
int ry = y + (r - firstRow) * theme::kLineHeight; int ry = y + (r - firstRow) * theme::kLineHeight;
if (isSelected) c.fillRect(0, ry, theme::kWidth - 3, theme::kLineHeight, theme::kSelection); if (isSelected) c.fillRect(0, ry, theme::kWidth - 3, theme::kLineHeight, theme::kSelection);
c.setTextColor(isSelected ? theme::kText : st.color); c.setTextColor(isSelected ? theme::kOnAccent : st.color);
std::string text = rows[r]; std::string text = rows[r];
if (g.type == LineType::Preformatted) text = hscroll_ < static_cast<int>(text.size()) ? text.substr(hscroll_) : ""; if (g.type == LineType::Preformatted) text = hscroll_ < static_cast<int>(text.size()) ? text.substr(hscroll_) : "";
c.drawString(text.c_str(), kMargin, ry + 1); c.drawString(text.c_str(), kMargin, ry + 1);
+7 -7
View File
@@ -22,7 +22,7 @@ uint16_t colorOf(Constellation c) {
switch (c) { switch (c) {
case Constellation::Gps: return theme::kAccent; case Constellation::Gps: return theme::kAccent;
case Constellation::Glonass: return 0xF8C8; // red case Constellation::Glonass: return 0xF8C8; // red
case Constellation::Galileo: return theme::kMessage; case Constellation::Galileo: return theme::kGood;
case Constellation::BeiDou: return 0xFEA0; // yellow case Constellation::BeiDou: return 0xFEA0; // yellow
case Constellation::Qzss: return 0xC99F; // violet case Constellation::Qzss: return 0xC99F; // violet
default: return theme::kText; default: return theme::kText;
@@ -96,7 +96,7 @@ void GnssApp::drawPosition(Canvas& c) {
char line[64]; char line[64];
c.setFont(&fonts::bold); c.setFont(&fonts::bold);
c.setTextColor(s.fix == FixType::None ? theme::kWarning : theme::kMessage); c.setTextColor(s.fix == FixType::None ? theme::kWarning : theme::kGood);
if (!gnss_.receiving(now)) std::snprintf(line, sizeof line, "No data from the receiver"); if (!gnss_.receiving(now)) std::snprintf(line, sizeof line, "No data from the receiver");
else if (s.fix == FixType::None) else if (s.fix == FixType::None)
std::snprintf(line, sizeof line, "Searching: %u in view, %s", (unsigned)s.satellites.size(), std::snprintf(line, sizeof line, "Searching: %u in view, %s", (unsigned)s.satellites.size(),
@@ -155,10 +155,10 @@ void GnssApp::drawSky(Canvas& c) {
// The sky from above: horizon, 30° and 60° elevation rings, north up. // The sky from above: horizon, 30° and 60° elevation rings, north up.
c.drawCircle(cx, cy, radius, theme::kMuted); c.drawCircle(cx, cy, radius, theme::kMuted);
c.drawCircle(cx, cy, radius * 2 / 3, 0x4208); c.drawCircle(cx, cy, radius * 2 / 3, theme::kFaint);
c.drawCircle(cx, cy, radius / 3, 0x4208); c.drawCircle(cx, cy, radius / 3, theme::kFaint);
c.drawFastHLine(cx - radius, cy, 2 * radius, 0x4208); c.drawFastHLine(cx - radius, cy, 2 * radius, theme::kFaint);
c.drawFastVLine(cx, cy - radius, 2 * radius, 0x4208); c.drawFastVLine(cx, cy - radius, 2 * radius, theme::kFaint);
c.setFont(&fonts::small); c.setFont(&fonts::small);
c.setTextColor(theme::kMuted); c.setTextColor(theme::kMuted);
c.setTextDatum(middle_center); c.setTextDatum(middle_center);
@@ -201,7 +201,7 @@ void GnssApp::drawSky(Canvas& c) {
c.drawString("No satellites", x, y); c.drawString("No satellites", x, y);
y += theme::kLineHeight; y += theme::kLineHeight;
} }
c.setTextColor(s.fix == FixType::None ? theme::kWarning : theme::kMessage); c.setTextColor(s.fix == FixType::None ? theme::kWarning : theme::kGood);
c.drawString(fixLabel(s.fix), x, y + 4); c.drawString(fixLabel(s.fix), x, y + 4);
c.setFont(&fonts::small); c.setFont(&fonts::small);
c.setTextColor(theme::kMuted); c.setTextColor(theme::kMuted);
+1 -1
View File
@@ -327,7 +327,7 @@ void LoraScannerApp::drawSweep(Canvas& c) {
uint32_t hz = radio_.config().frequencyHz; uint32_t hz = radio_.config().frequencyHz;
if (hz >= f.fromHz && hz <= f.fromHz + (f.steps - 1) * f.stepHz) { if (hz >= f.fromHz && hz <= f.fromHz + (f.steps - 1) * f.stepHz) {
int x = x0 + static_cast<int>((hz - f.fromHz) / f.stepHz) * cell + cell / 2; int x = x0 + static_cast<int>((hz - f.fromHz) / f.stepHz) * cell + cell / 2;
for (int y = barTop; y < barBottom; y += 3) c.drawPixel(x, y, theme::kMessage); for (int y = barTop; y < barBottom; y += 3) c.drawPixel(x, y, theme::kGood);
} }
// The waterfall. // The waterfall.
+1 -1
View File
@@ -424,7 +424,7 @@ void SshApp::drawTerminal(Canvas& c) {
c.setFont(&fonts::small); c.setFont(&fonts::small);
c.setTextDatum(top_right); c.setTextDatum(top_right);
c.fillRect(area.x + area.w - 5 * static_cast<int>(where.size()) - 3, area.y, 5 * static_cast<int>(where.size()) + 3, 9, theme::kAccent); c.fillRect(area.x + area.w - 5 * static_cast<int>(where.size()) - 3, area.y, 5 * static_cast<int>(where.size()) + 3, 9, theme::kAccent);
c.setTextColor(0x0000); c.setTextColor(theme::kOnAccent);
c.drawString(where.c_str(), area.x + area.w - 1, area.y + 1); c.drawString(where.c_str(), area.x + area.w - 1, area.y + 1);
c.setTextDatum(top_left); c.setTextDatum(top_left);
} }
+4 -4
View File
@@ -232,7 +232,7 @@ void SystemApp::drawOverview(Canvas& c) {
const auto& h = loadHistory_[core]; const auto& h = loadHistory_[core];
for (size_t i = 1; i < h.size(); i++) { for (size_t i = 1; i < h.size(); i++) {
int x0 = gx + 1 + static_cast<int>((i - 1) * gw / kHistory), x1 = gx + 1 + static_cast<int>(i * gw / kHistory); int x0 = gx + 1 + static_cast<int>((i - 1) * gw / kHistory), x1 = gx + 1 + static_cast<int>(i * gw / kHistory);
c.drawLine(x0, gy + gh - h.at(i - 1) * gh / 100, x1, gy + gh - h.at(i) * gh / 100, core ? theme::kMessage : theme::kAccent); c.drawLine(x0, gy + gh - h.at(i - 1) * gh / 100, x1, gy + gh - h.at(i) * gh / 100, core ? theme::kGood : theme::kAccent);
} }
} }
} }
@@ -290,7 +290,7 @@ void SystemApp::drawMemory(Canvas& c) {
struct { struct {
size_t bytes; size_t bytes;
uint16_t color; uint16_t color;
} floors[] = {{GeminiService::kStartFloor, theme::kMuted}, {GeminiService::kSteadyFloor, theme::kWarning}, {GeminiService::kTransientFloor, theme::kMessage}}; } floors[] = {{GeminiService::kStartFloor, theme::kMuted}, {GeminiService::kSteadyFloor, theme::kWarning}, {GeminiService::kTransientFloor, theme::kGood}};
for (auto& f : floors) { for (auto& f : floors) {
int kb = static_cast<int>(f.bytes / 1024), y = yOf(kb); int kb = static_cast<int>(f.bytes / 1024), y = yOf(kb);
for (int x = gx + 1; x < gx + gw - 1; x += 4) c.drawPixel(x, y, f.color); for (int x = gx + 1; x < gx + gw - 1; x += 4) c.drawPixel(x, y, f.color);
@@ -335,13 +335,13 @@ void SystemApp::drawNetwork(Canvas& c) {
for (size_t i = 0; i < kUsers; i++) { for (size_t i = 0; i < kUsers; i++) {
bool active = rateIn_[i] || rateOut_[i]; bool active = rateIn_[i] || rateOut_[i];
c.setTextDatum(top_left); c.setTextDatum(top_left);
c.setTextColor(active ? theme::kMessage : theme::kText); c.setTextColor(active ? theme::kGood : theme::kText);
c.drawString(net::userName(static_cast<net::User>(i)), 4, y); c.drawString(net::userName(static_cast<net::User>(i)), 4, y);
c.setTextDatum(top_right); c.setTextDatum(top_right);
c.setTextColor(theme::kText); c.setTextColor(theme::kText);
c.drawString(net::formatTraffic(traffic_[i].in).c_str(), 124, y); c.drawString(net::formatTraffic(traffic_[i].in).c_str(), 124, y);
c.drawString(net::formatTraffic(traffic_[i].out).c_str(), 168, y); c.drawString(net::formatTraffic(traffic_[i].out).c_str(), 168, y);
c.setTextColor(active ? theme::kMessage : theme::kMuted); c.setTextColor(active ? theme::kGood : theme::kMuted);
c.drawString(active ? net::formatTraffic(rateIn_[i] + rateOut_[i]).c_str() : "-", area.w - 4, y); c.drawString(active ? net::formatTraffic(rateIn_[i] + rateOut_[i]).c_str() : "-", area.w - 4, y);
y += theme::kLineHeight; y += theme::kLineHeight;
} }
+3 -3
View File
@@ -232,9 +232,9 @@ void WifiToolsApp::drawChannels(Canvas& c) {
for (int ch = 1; ch <= 13; ch++) { for (int ch = 1; ch <= 13; ch++) {
int x = x0 + (ch - 1) * slot; int x = x0 + (ch - 1) * slot;
int h = static_cast<int>(load[ch] / maxLoad * barMaxH); int h = static_cast<int>(load[ch] / maxLoad * barMaxH);
uint16_t color = ch == best ? theme::kMessage : (ch == 1 || ch == 6 || ch == 11) ? theme::kAccent : theme::kMuted; uint16_t color = ch == best ? theme::kGood : (ch == 1 || ch == 6 || ch == 11) ? theme::kAccent : theme::kMuted;
if (h > 0) c.fillRect(x, baseY - h, slot - 4, h, color); if (h > 0) c.fillRect(x, baseY - h, slot - 4, h, color);
c.setTextColor(ch == best ? theme::kMessage : theme::kText); c.setTextColor(ch == best ? theme::kGood : theme::kText);
c.setTextDatum(top_center); c.setTextDatum(top_center);
c.drawString(std::to_string(ch).c_str(), x + (slot - 4) / 2, baseY + 3); c.drawString(std::to_string(ch).c_str(), x + (slot - 4) / 2, baseY + 3);
c.setTextDatum(top_left); c.setTextDatum(top_left);
@@ -265,7 +265,7 @@ void WifiToolsApp::drawTracker(Canvas& c) {
int strength = lost ? 0 : std::min(100, std::max(0, (tracker_.latest() + 95) * 100 / 60)); int strength = lost ? 0 : std::min(100, std::max(0, (tracker_.latest() + 95) * 100 / 60));
int barW = area.w - 8; int barW = area.w - 8;
c.drawRect(4, area.y + 60, barW, 8, theme::kMuted); c.drawRect(4, area.y + 60, barW, 8, theme::kMuted);
c.fillRect(5, area.y + 61, (barW - 2) * strength / 100, 6, theme::kMessage); c.fillRect(5, area.y + 61, (barW - 2) * strength / 100, 6, theme::kGood);
const int gy = area.y + 74, gh = area.h - 76; const int gy = area.y + 74, gh = area.h - 76;
const auto& h = tracker_.history(); const auto& h = tracker_.history();
+12 -7
View File
@@ -4,15 +4,20 @@
namespace roro::theme { namespace roro::theme {
// RGB565 // The website's palette (site/static/css/site.css, its dark side), as RGB565. Every one is a colour
// the 8-bit frame buffer (RGB332) holds exactly, so what is asked for is what is shown.
constexpr uint16_t kBackground = 0x0000; constexpr uint16_t kBackground = 0x0000;
constexpr uint16_t kText = 0xFFFF; constexpr uint16_t kText = 0xFFFF;
constexpr uint16_t kMuted = 0x8410; // grey constexpr uint16_t kAccent = 0x06DF; // cyan, #00dbff: what is chosen, what is live
constexpr uint16_t kAccent = 0x2CBF; // teal-blue constexpr uint16_t kOnAccent = 0x000A; // dark blue, #000055: text and pictures on a cyan fill
constexpr uint16_t kSelection = 0x1A6F; // darker accent behind selected rows constexpr uint16_t kSelection = kAccent; // behind a selected row, whose text is kOnAccent
constexpr uint16_t kBar = 0x2104; // Status Bar background constexpr uint16_t kMuted = 0x9495; // grey, #9292aa: between the website's two
constexpr uint16_t kWarning = 0xFC60; // orange constexpr uint16_t kFaint = 0x4A4A; // slate, #494955: grid lines, what stays behind
constexpr uint16_t kMessage = 0x3666; // green constexpr uint16_t kBar = kFaint; // Status Bar background
constexpr uint16_t kBarMuted = 0xB5B5; // light grey, #b6b6aa: what is idle, on the bar
constexpr uint16_t kWarning = 0xFC80; // orange, #ff9200
constexpr uint16_t kMessage = 0xFC9F; // pink, #ff92ff: someone wrote
constexpr uint16_t kGood = 0x4ECA; // green, #49db55: a Fix, the best of a set (not on the website)
constexpr int kWidth = 240; constexpr int kWidth = 240;
constexpr int kHeight = 135; constexpr int kHeight = 135;
+12 -12
View File
@@ -32,43 +32,43 @@ void statusBar(Canvas& c, const StatusInfo& info) {
if (info.unread > 0) right("[" + std::to_string(info.unread) + "]", kMessage); if (info.unread > 0) right("[" + std::to_string(info.unread) + "]", kMessage);
if (info.batteryPercent >= 0) if (info.batteryPercent >= 0)
right(std::to_string(info.batteryPercent) + "%", info.batteryPercent <= 15 ? kWarning : kText); right(std::to_string(info.batteryPercent) + "%", info.batteryPercent <= 15 ? kWarning : kText);
if (info.sdPresent) right("SD", info.sdLevel >= 80 ? kWarning : kMuted); if (info.sdPresent) right("SD", info.sdLevel >= 80 ? kWarning : kBarMuted);
switch (info.wifi) { switch (info.wifi) {
case StatusInfo::Wifi::Connected: { case StatusInfo::Wifi::Connected: {
// Three signal bars, then the label. // Three signal bars, then the label.
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
int h = 2 + 2 * i; int h = 2 + 2 * i;
c.fillRect(x - 10 + 4 * i, 9 - h, 3, h, i < info.wifiBars ? kText : kMuted); c.fillRect(x - 10 + 4 * i, 9 - h, 3, h, i < info.wifiBars ? kText : kBackground);
} }
x -= 14; x -= 14;
right("W", kText); right("W", kText);
break; break;
} }
case StatusInfo::Wifi::Searching: right("W?", kMuted); break; case StatusInfo::Wifi::Searching: right("W?", kBarMuted); break;
case StatusInfo::Wifi::Monitoring: right("MON", kAccent); break; case StatusInfo::Wifi::Monitoring: right("MON", kAccent); break;
case StatusInfo::Wifi::None: break; case StatusInfo::Wifi::None: break;
} }
switch (info.vpn) { // Q252: there while the tunnel is wanted, bright once the peer has answered switch (info.vpn) { // Q252: there while the tunnel is wanted, bright once the peer has answered
case StatusInfo::Vpn::Trying: right("VPN", kMuted); break; case StatusInfo::Vpn::Trying: right("VPN", kBarMuted); break;
case StatusInfo::Vpn::Up: right("VPN", kAccent); break; case StatusInfo::Vpn::Up: right("VPN", kAccent); break;
case StatusInfo::Vpn::Off: break; case StatusInfo::Vpn::Off: break;
} }
if (info.ssh) right("SSH", kAccent); // Q260: a session goes on, whatever App is in front if (info.ssh) right("SSH", kAccent); // Q260: a session goes on, whatever App is in front
switch (info.debug) { // Q190: there while the console listens, bright with someone connected switch (info.debug) { // Q190: there while the console listens, bright with someone connected
case StatusInfo::Debug::On: right("DBG", kMuted); break; case StatusInfo::Debug::On: right("DBG", kBarMuted); break;
case StatusInfo::Debug::Client: right("DBG", kAccent); break; case StatusInfo::Debug::Client: right("DBG", kAccent); break;
case StatusInfo::Debug::Off: break; case StatusInfo::Debug::Off: break;
} }
if (info.tracking) right("REC", kWarning); if (info.tracking) right("REC", kWarning);
if (info.capturing) right("CAP", kWarning); if (info.capturing) right("CAP", kWarning);
switch (info.radio) { // Q101: muted while listening, bright for a moment on each packet switch (info.radio) { // Q101: muted while listening, bright for a moment on each packet
case StatusInfo::Radio::Listening: right("L", kMuted); break; case StatusInfo::Radio::Listening: right("L", kBarMuted); break;
case StatusInfo::Radio::Packet: right("L", kAccent); break; case StatusInfo::Radio::Packet: right("L", kAccent); break;
case StatusInfo::Radio::Sweep: right("SW", kMuted); break; // the Sniffer is paused case StatusInfo::Radio::Sweep: right("SW", kBarMuted); break; // the Sniffer is paused
case StatusInfo::Radio::None: break; case StatusInfo::Radio::None: break;
} }
switch (info.gnss) { // Q61: muted while searching, normal with a Fix, the count with a 3D Fix switch (info.gnss) { // Q61: muted while searching, normal with a Fix, the count with a 3D Fix
case StatusInfo::Gnss::Searching: right("G", kMuted); break; case StatusInfo::Gnss::Searching: right("G", kBarMuted); break;
case StatusInfo::Gnss::TwoD: right("G", kText); break; case StatusInfo::Gnss::TwoD: right("G", kText); break;
case StatusInfo::Gnss::ThreeD: right("G" + std::to_string(info.gnssSatellites), kText); break; case StatusInfo::Gnss::ThreeD: right("G" + std::to_string(info.gnssSatellites), kText); break;
case StatusInfo::Gnss::None: break; case StatusInfo::Gnss::None: break;
@@ -88,10 +88,10 @@ void list(Canvas& c, const ListModel& model, Rect area, const std::function<std:
int y = area.y + row * kLineHeight; int y = area.y + row * kLineHeight;
bool selected = i == model.selected(); bool selected = i == model.selected();
if (selected) c.fillRect(area.x, y, area.w - 4, kLineHeight, kSelection); if (selected) c.fillRect(area.x, y, area.w - 4, kLineHeight, kSelection);
c.setTextColor(kText); c.setTextColor(selected ? kOnAccent : kText);
c.drawString(label(i).c_str(), area.x + 4, y + 1); c.drawString(label(i).c_str(), area.x + 4, y + 1);
if (value) { if (value) {
c.setTextColor(selected ? kText : kMuted); c.setTextColor(selected ? kOnAccent : kMuted);
c.setTextDatum(top_right); c.setTextDatum(top_right);
c.drawString(value(i).c_str(), area.x + area.w - 8, y + 1); c.drawString(value(i).c_str(), area.x + area.w - 8, y + 1);
c.setTextDatum(top_left); c.setTextDatum(top_left);
@@ -159,7 +159,7 @@ void dialog(Canvas& c, const std::string& title, const std::string& message, con
bool selected = i == model.selected(); bool selected = i == model.selected();
if (selected) c.fillRect(x, box.y + box.h - 19, w, 15, kAccent); if (selected) c.fillRect(x, box.y + box.h - 19, w, 15, kAccent);
else c.drawRect(x, box.y + box.h - 19, w, 15, kMuted); else c.drawRect(x, box.y + box.h - 19, w, 15, kMuted);
c.setTextColor(selected ? kBackground : kText); c.setTextColor(selected ? kOnAccent : kText);
c.drawString(buttons[i].c_str(), x + 6, box.y + box.h - 17); c.drawString(buttons[i].c_str(), x + 6, box.y + box.h - 17);
x -= 6; x -= 6;
} }
@@ -174,7 +174,7 @@ void toast(Canvas& c, const Toast& t) {
if (w > kWidth - 8) w = kWidth - 8; if (w > kWidth - 8) w = kWidth - 8;
int x = (kWidth - w) / 2, y = kHeight - 22; int x = (kWidth - w) / 2, y = kHeight - 22;
c.fillRect(x, y, w, 18, color); c.fillRect(x, y, w, 18, color);
c.setTextColor(kBackground); c.setTextColor(kOnAccent);
c.setTextDatum(top_left); c.setTextDatum(top_left);
c.setClipRect(x + 4, y, w - 8, 18); c.setClipRect(x + 4, y, w - 8, 18);
c.drawString(t.text.c_str(), x + 8, y + 3); c.drawString(t.text.c_str(), x + 8, y + 3);