Public Access
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:
@@ -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++) {
|
||||
int ry = y + (r - firstRow) * theme::kLineHeight;
|
||||
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];
|
||||
if (g.type == LineType::Preformatted) text = hscroll_ < static_cast<int>(text.size()) ? text.substr(hscroll_) : "";
|
||||
c.drawString(text.c_str(), kMargin, ry + 1);
|
||||
|
||||
@@ -22,7 +22,7 @@ uint16_t colorOf(Constellation c) {
|
||||
switch (c) {
|
||||
case Constellation::Gps: return theme::kAccent;
|
||||
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::Qzss: return 0xC99F; // violet
|
||||
default: return theme::kText;
|
||||
@@ -96,7 +96,7 @@ void GnssApp::drawPosition(Canvas& c) {
|
||||
char line[64];
|
||||
|
||||
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");
|
||||
else if (s.fix == FixType::None)
|
||||
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.
|
||||
c.drawCircle(cx, cy, radius, theme::kMuted);
|
||||
c.drawCircle(cx, cy, radius * 2 / 3, 0x4208);
|
||||
c.drawCircle(cx, cy, radius / 3, 0x4208);
|
||||
c.drawFastHLine(cx - radius, cy, 2 * radius, 0x4208);
|
||||
c.drawFastVLine(cx, cy - radius, 2 * radius, 0x4208);
|
||||
c.drawCircle(cx, cy, radius * 2 / 3, theme::kFaint);
|
||||
c.drawCircle(cx, cy, radius / 3, theme::kFaint);
|
||||
c.drawFastHLine(cx - radius, cy, 2 * radius, theme::kFaint);
|
||||
c.drawFastVLine(cx, cy - radius, 2 * radius, theme::kFaint);
|
||||
c.setFont(&fonts::small);
|
||||
c.setTextColor(theme::kMuted);
|
||||
c.setTextDatum(middle_center);
|
||||
@@ -201,7 +201,7 @@ void GnssApp::drawSky(Canvas& c) {
|
||||
c.drawString("No satellites", x, y);
|
||||
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.setFont(&fonts::small);
|
||||
c.setTextColor(theme::kMuted);
|
||||
|
||||
@@ -327,7 +327,7 @@ void LoraScannerApp::drawSweep(Canvas& c) {
|
||||
uint32_t hz = radio_.config().frequencyHz;
|
||||
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;
|
||||
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.
|
||||
|
||||
@@ -424,7 +424,7 @@ void SshApp::drawTerminal(Canvas& c) {
|
||||
c.setFont(&fonts::small);
|
||||
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.setTextColor(0x0000);
|
||||
c.setTextColor(theme::kOnAccent);
|
||||
c.drawString(where.c_str(), area.x + area.w - 1, area.y + 1);
|
||||
c.setTextDatum(top_left);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ void SystemApp::drawOverview(Canvas& c) {
|
||||
const auto& h = loadHistory_[core];
|
||||
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);
|
||||
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 {
|
||||
size_t bytes;
|
||||
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) {
|
||||
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);
|
||||
@@ -335,13 +335,13 @@ void SystemApp::drawNetwork(Canvas& c) {
|
||||
for (size_t i = 0; i < kUsers; i++) {
|
||||
bool active = rateIn_[i] || rateOut_[i];
|
||||
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.setTextDatum(top_right);
|
||||
c.setTextColor(theme::kText);
|
||||
c.drawString(net::formatTraffic(traffic_[i].in).c_str(), 124, 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);
|
||||
y += theme::kLineHeight;
|
||||
}
|
||||
|
||||
@@ -232,9 +232,9 @@ void WifiToolsApp::drawChannels(Canvas& c) {
|
||||
for (int ch = 1; ch <= 13; ch++) {
|
||||
int x = x0 + (ch - 1) * slot;
|
||||
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);
|
||||
c.setTextColor(ch == best ? theme::kMessage : theme::kText);
|
||||
c.setTextColor(ch == best ? theme::kGood : theme::kText);
|
||||
c.setTextDatum(top_center);
|
||||
c.drawString(std::to_string(ch).c_str(), x + (slot - 4) / 2, baseY + 3);
|
||||
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 barW = area.w - 8;
|
||||
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 auto& h = tracker_.history();
|
||||
|
||||
+12
-7
@@ -4,15 +4,20 @@
|
||||
|
||||
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 kText = 0xFFFF;
|
||||
constexpr uint16_t kMuted = 0x8410; // grey
|
||||
constexpr uint16_t kAccent = 0x2CBF; // teal-blue
|
||||
constexpr uint16_t kSelection = 0x1A6F; // darker accent behind selected rows
|
||||
constexpr uint16_t kBar = 0x2104; // Status Bar background
|
||||
constexpr uint16_t kWarning = 0xFC60; // orange
|
||||
constexpr uint16_t kMessage = 0x3666; // green
|
||||
constexpr uint16_t kAccent = 0x06DF; // cyan, #00dbff: what is chosen, what is live
|
||||
constexpr uint16_t kOnAccent = 0x000A; // dark blue, #000055: text and pictures on a cyan fill
|
||||
constexpr uint16_t kSelection = kAccent; // behind a selected row, whose text is kOnAccent
|
||||
constexpr uint16_t kMuted = 0x9495; // grey, #9292aa: between the website's two
|
||||
constexpr uint16_t kFaint = 0x4A4A; // slate, #494955: grid lines, what stays behind
|
||||
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 kHeight = 135;
|
||||
|
||||
+12
-12
@@ -32,43 +32,43 @@ void statusBar(Canvas& c, const StatusInfo& info) {
|
||||
if (info.unread > 0) right("[" + std::to_string(info.unread) + "]", kMessage);
|
||||
if (info.batteryPercent >= 0)
|
||||
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) {
|
||||
case StatusInfo::Wifi::Connected: {
|
||||
// Three signal bars, then the label.
|
||||
for (int i = 0; i < 3; 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;
|
||||
right("W", kText);
|
||||
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::None: break;
|
||||
}
|
||||
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::Off: break;
|
||||
}
|
||||
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
|
||||
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::Off: break;
|
||||
}
|
||||
if (info.tracking) right("REC", kWarning);
|
||||
if (info.capturing) right("CAP", kWarning);
|
||||
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::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;
|
||||
}
|
||||
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::ThreeD: right("G" + std::to_string(info.gnssSatellites), kText); 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;
|
||||
bool selected = i == model.selected();
|
||||
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);
|
||||
if (value) {
|
||||
c.setTextColor(selected ? kText : kMuted);
|
||||
c.setTextColor(selected ? kOnAccent : kMuted);
|
||||
c.setTextDatum(top_right);
|
||||
c.drawString(value(i).c_str(), area.x + area.w - 8, y + 1);
|
||||
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();
|
||||
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);
|
||||
c.setTextColor(selected ? kBackground : kText);
|
||||
c.setTextColor(selected ? kOnAccent : kText);
|
||||
c.drawString(buttons[i].c_str(), x + 6, box.y + box.h - 17);
|
||||
x -= 6;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ void toast(Canvas& c, const Toast& t) {
|
||||
if (w > kWidth - 8) w = kWidth - 8;
|
||||
int x = (kWidth - w) / 2, y = kHeight - 22;
|
||||
c.fillRect(x, y, w, 18, color);
|
||||
c.setTextColor(kBackground);
|
||||
c.setTextColor(kOnAccent);
|
||||
c.setTextDatum(top_left);
|
||||
c.setClipRect(x + 4, y, w - 8, 18);
|
||||
c.drawString(t.text.c_str(), x + 8, y + 3);
|
||||
|
||||
Reference in New Issue
Block a user