Launcher: a grid of icons, with badges; the list stays as a Setting

The Launcher shows the Apps as a 4 x 3 grid of 32 x 32 icons, the
selected one's name under it (issue #9). The icons are the website's
own, doubled, and two drawn the same way for SSH and the Shell; they
are PNGs in assets/icons, turned into arrays by scripts/make_icons.py,
which CI checks. The selected tile is the website's: dark blue on cyan,
with notched corners.

- GridModel (lib/ui, host-tested) holds the selection and the scrolling.
- App::badge(): a dot on the tile for unread IRC messages, a Track
  recording, a Capture running, an SSH session open.
- Settings > Launcher: Grid or List.

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 aadfd8cb37
commit e2326e113b
40 changed files with 907 additions and 24 deletions
+4
View File
@@ -50,6 +50,10 @@ class App {
virtual bool retainsContent() const { return false; }
virtual void contentLost() {}
// True while something goes on in the App that is worth a look, whether it is in front or
// not (unread messages, a recording running): the Launcher marks its tile (issue #9).
virtual bool badge() const { return false; }
void requestRedraw() { redraw_ = true; }
bool consumeRedraw() {
+1 -1
View File
@@ -48,7 +48,7 @@ inline constexpr KeyHelp kText[] = {
// launcher: The Launcher
inline constexpr KeyHelp kLauncher[] = {
{"; .", "up, down"},
{"; . , /", "move"},
{"Enter", "open the App"},
};
+3
View File
@@ -1,5 +1,7 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
@@ -12,6 +14,7 @@ struct AppInfo {
const char* title;
bool hidden; // not listed in the Launcher, but can still be opened
App* app;
const uint8_t* icon = nullptr; // the Launcher's picture of it (ui/icons.h); none: its first letter
};
// Owns which App is in the foreground and routes keys. Home always returns to the Launcher;