nandi/jolt-nativepublic Fork 0
460541b3803224e2b0199f6384c01f9635c6d618
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

README.md · 233 lines · 11.5 KBmarkdown Blame HistoryRaw
Bring vidya in cfd3e36 nandi 19d ago1# Vidya
2
3GNOME/HIG-inspired **theme layer for [egui](https://github.com/emilk/egui)** — no GTK.
4
5Calm charcoal shells, a clear blue accent, soft borders, and a short type/spacing scale. **Dark is the product default** — ship `apply_dark` / `Theme::dark()` and stop there. Light (`Theme::light` / `apply_light`) exists for the showcase and for apps that explicitly need it; do not copy the demo’s dark/light toggle into every app.
6
7**Repo:** [tangled.org/nandi.uk/vidya](https://tangled.org/nandi.uk/vidya)
8
9Screenshots below are **Waydroid** (portrait Android) captures of the demo APK.
10
11<p align="center">
12 <img src="docs/screenshots/mobile/vidya-home.png" alt="Vidya — Overview" width="320" />
13 <img src="docs/screenshots/mobile/vidya-forms.png" alt="Vidya — Forms (themed checkbox)" width="320" />
14 <img src="docs/screenshots/mobile/vidya-actions.png" alt="Vidya — Actions" width="320" />
15</p>
16
17<p align="center">
18 <img src="docs/screenshots/mobile/vidya-colors.png" alt="Vidya — Palette" width="320" />
19 <img src="docs/screenshots/mobile/vidya-type.png" alt="Vidya — Typography" width="320" />
20 <img src="docs/screenshots/mobile/vidya-surfaces.png" alt="Vidya — Surfaces" width="320" />
21</p>
22
23## Aesthetic
24
25| Layer | Role |
26|-------|------|
27| Window / view / card / popover | Stacked surfaces (`#242424``#383838` in dark) |
28| Accent | Adwaita-like blue (`#3584e4`) for primary actions, selection, and checked controls |
29| Feedback | Destructive red, success green, warning amber |
30| Type | Title 20 · title₂ 16 · body 14 · caption 12 |
31| Spacing | 4 · 6 · 12 · 18 · 24 · control height 34 |
32| Radius | 6 · 9 · 12 |
33
34`apply()` installs palette + spacing on the egui context so text fields, sliders, and combos inherit the shell. Checkboxes use a dedicated themed control (`checkbox`) with accent fill and a drawn checkmark. It also registers a tiny **symbol font fallback** (DejaVu subset) so UI punctuation such as `→`, `●`/`○`, disclosure `▾`/`▴`, dashes, and curly quotes does not render as hollow boxes on Android.
35
36**Emoji → color icons:** the full **Twemoji** 72×72 set (~3.8k glyphs) is embedded. Any reaction (flags, ZWJ sequences, skin tones) resolves via `emoji_icon` / `paint_emoji_in` / `reaction_chip`. Prefer these over raw Unicode on Android — egui’s default fonts only ship monochrome Noto Emoji.
37
38On **Android** (edge-to-edge NativeActivity), call `reserve_system_chrome(ctx, &theme)` once per frame **before** other panels, or use `top_header(ctx, &theme, |ui| { … })` for the app chrome. Prefer `sync_system_chrome_from_android(ctx, &android_app)` each frame so insets track `content_rect` (including when the soft keyboard is open). The IME reserve passes pointer events through so swipe typing on the system keyboard is not blocked.
39
40## Demo
41
42An interactive showcase walks through overview, typography, actions, surfaces, palette swatches, and forms. The demo includes a dark/light toggle so you can compare palettes — that control is for the showcase, not a recommended app pattern.
43
44### raylib and Jolt port
45
46The first native C/raylib port lives in [`raylib/`](raylib/README.md), with
47Jolt FFI bindings and a Jolt showcase in [`jolt/`](jolt/README.md). The same ABI
48is also implemented on **this** crate in [`ffi/`](ffi/README.md), so Jolt can
49render through the Rust/egui layer instead of the C reimplementation — same
50symbols, same bindings, selected by library search path. The C ABI
51keeps raylib types private, so Jolt and Zig callers only exchange primitive
52values and UTF-8 strings. This is an independent backend; the established Rust
53implementation remains available while controls and layout facilities are
54ported incrementally.
55
56### glimmer
57
58[`glimmer/`](glimmer/README.md) is a backend for
59[glimmer](https://github.com/jolt-lang/glimmer), Jolt's reactive toolkit — so
60components written as hiccup, with reagent-style reactive cells, render as Vidya
61widgets. egui has no widgets for a reconciler to patch, so the widget tree lives
62in Rust behind a second C ABI
63([`ffi/include/vidya_tree.h`](ffi/include/vidya_tree.h)) that the
64Rust/egui build alone implements: the caller mutates integer node handles,
65one call a frame paints the whole tree, and interactions come back as events.
66
67```bash
68nix run # apps.default → vidya-demo
69nix run .#demo
70# or from a remote flake:
71nix run git+https://tangled.org/nandi.uk/vidya
72```
73
74```bash
75nix develop # rust (+ android target) · just · adb
76
77just waydroid # in-tree cargo apk → install → launch on Waydroid
78just host # desktop egui window
79just install # same as waydroid (rebuild APK)
80just launch # start installed activity only
81just shots # Waydroid screencaps → docs/screenshots/mobile/
82```
83
84```bash
85just ffi # buck2 → build/libvidya.so (host)
86just ffi-android # buck2 → build/android/arm64-v8a/libvidya.so
87```
88
89Both are the **same** Rust/egui sources; only the target configuration moves.
90Android is the sole backend on device now — the earlier cimgui/raylib
91NativeActivity port is gone. `just ffi-android` needs `ANDROID_NDK_HOME` for the
92linker (r27+); rustc and the `aarch64-linux-android` libstd are DotSlash-pinned
93like everything else, so there is no rustup target to add.
94
95`just waydroid` builds **in `android-demo/`** with the nix develop toolchain (no temp/isolated copy).
96
97Sections:
98
99- **Overview** — hero card, design tokens + **grid DSL** metrics table (demo can flip to light)
100- **Typography** — type scale samples (grid) and hierarchy
101- **Actions** — primary / default / destructive buttons, dialog footer, status pills
102- **Surfaces** — layer stack table, card & header frames
103- **Palette** — live semantic swatches (flip the shell to compare)
104- **Forms** — themed inputs, accent checkbox, slider, combo, progress
105
106### Refreshing screenshots
107
108With a running Waydroid session:
109
110```bash
111just install # rebuild + install APK
112just shots # adb screencap per section → docs/screenshots/mobile/
113```
114
115## Use
116
117Apps should start dark-only:
118
119```toml
120[dependencies]
121vidya = { git = "https://tangled.org/nandi.uk/vidya" }
122# or: vidya = { git = "ssh://git@tangled.org/nandi.uk/vidya" }
123egui = "0.31"
124```
125
126```rust
127use vidya::{apply_dark, checkbox, primary_button, Theme};
128
129apply_dark(ctx);
130let th = Theme::dark();
131if primary_button(ui, &th, "Open").clicked() {
132 // …
133}
134checkbox(ui, &th, &mut sync, "Sync preferences");
135```
136
137`Theme::light()` / `apply_light` are available when an app truly needs a light shell. Prefer not to expose a theme toggle unless that is a deliberate product choice (the Vidya demo does it to exercise both palettes).
138
139### Window icon
140
141Embed a PNG and attach it to the viewport (set Wayland `app_id` to match your `.desktop`):
142
143```rust
144use egui::ViewportBuilder;
145use vidya::with_app_icon_id;
146
147let viewport = with_app_icon_id(
148 ViewportBuilder::default().with_title("I/O Usage"),
149 "usage",
150 include_bytes!("../assets/usage-256.png"),
151);
152```
153
154## Nix flake
155
156| Output | Role |
157|--------|------|
158| `apps.default` / `apps.demo` | Desktop showcase via **`cargo run`** (rustup + egui libs) |
159| `packages.default` / `packages.demo` | Same launcher + `.desktop` entry |
160| `packages.vidya` | Theme library sources + rlib bundle |
161| `devShells.default` | just · adb · egui runtime libs (rustup for cargo) |
162
163```bash
164nix run # cargo run --manifest-path host/Cargo.toml
165nix run .#demo
166nix build .#vidya
167nix develop
168```
169
170`nix run` keeps your cwd: from the checkout it rebuilds the live tree (same as `just host`). The packaged `.desktop` falls back to the flake source with `CARGO_TARGET_DIR` under `~/.cache/vidya/`.
171
172As a flake input:
173
174```nix
175inputs.vidya.url = "git+https://tangled.org/nandi.uk/vidya";
176```
177
178## API
179
180| Item | Role |
181|------|------|
182| `Theme::dark()` / `light()` | Palette + spacing + type scale (`dark` for apps; `light` optional) |
183| `apply` / `apply_dark` / `apply_light` | Install on `egui::Context` (+ symbol font); prefer `apply_dark` |
184| `install_symbol_font` | Fallback glyphs for arrows / disclosure triangles / bullets / quotes |
185| `emoji_icon` / `paint_emoji_in` / `has_emoji_icon` | Full Twemoji color set by codepoint |
186| `Icon` / `icon` / `paint_icon_in` | Named shortcuts + stroke Plus / Copy |
187| `icon_button` | Square tool button with a stroke/emoji icon |
188| `reaction_chip` | Themed count chip with color emoji |
189| `primary_button` / `button` / `destructive_button` | Styled actions |
190| `checkbox` | Accent-filled checkbox with drawn checkmark |
191| `status_dot` | Live/offline circle (drawn — no Unicode tofu on Android) |
192| `text_field_singleline` / `text_field_multiline` | Text inputs with field padding (fill parent width) |
193| `consume_command` / `consume_escape` | Consume Cmd/Ctrl+key or Esc (platform-aware) |
194| `command_shortcut_label` / `escape_label` | `"Ctrl+F"` / `"⌘F"` / `"Esc"` for tooltips |
195| `title` / `title_2` / `body` / `dim_label` | Text roles |
196| `Theme::header_frame` / `card_frame` / `page_frame` | Layout chrome |
197| `dialog` | Centered resizable window with `card_frame` (chain size / `.show`) |
198| `Theme::text_edit_margin` | Inner field padding (12×8 default) |
199| **Layout composition** | Prefer these over raw `set_max_width` / `Layout` plumbing |
200| `fit_width` / `fill_width` | Pin children to residual width (no edge overflow) |
201| `vstack` | Non-justified vertical stack (no giant gaps in tall parents) |
202| `card` | Fill-width themed card + `vstack` content |
203| `compact_card` | Fixed-width card that **hugs content** (gauge / anomaly tiles) |
204| `pack` | Wrap compact tiles without stretching leftover horizontal space |
205| `hflow` | Wrapping horizontal row (toolbars / chips) |
206| `lead_trail` | Leading field + trailing actions without clipping the actions |
207| `two_col` / `side_by_side` | Responsive two-column / stack (+ pure breakpoint policy) |
208| `page_body` / `central_page` | **Enforced** scrollable page: top-level content is a **grid** (`GridCtx`) |
209| `page_body_cols` / `central_page_cols` | Same, with explicit top-level `ColSpec`s |
210| `page_scroll` | Escape hatch: scroll + width pin only (no grid) |
211| `GridCtx::section` | Full-width page section (one row / one cell) — preferred page building block |
212| `inset_row` | Soft inset row capped to parent width |
213| `grid` / `grid_cols` | **Grid DSL** — pinned to residual width; cols share viewport budget |
214| `grid_cols_with` / `GridOpts` | Grid with striping / spacing options (page shell uses non-striped) |
215| `distribute_col_max` | Pure policy: per-column max widths so sum + gaps ≤ available |
216| `ColSpec` | `Flex` / `Fixed` / `MetricBps` / `MetricRate` (floors; capped by residual) |
217| `metric_cell` | Right-edge mono metric; clipped to column budget |
218| `RowDsl` | `heading` / `text` / `dim` / `warn` / `metric` / `metric_bps` / `metric_rate` |
219| `metric_bps` / `metric_rate` | Fixed-width monospace rate strings (no staircase columns) |
220| `metric_cell` / `table_metric` / `table_text` | Low-level cells (prefer the row DSL) |
221| `data_table` | Index-callback table helper on top of the grid DSL |
222| `reserve_system_chrome` / `system_chrome` | Android status + nav safe areas |
223| `sync_system_chrome_from_android` | Measured insets from `AndroidApp::content_rect` (Android) |
224| `top_header` | Header panel with system chrome already reserved |
225| **App icon** | Embed a PNG as the native window icon |
226| `icon_data_from_png` | Decode PNG bytes → `egui::IconData` |
227| `with_app_icon` | `ViewportBuilder` + embedded PNG icon |
228| `with_app_icon_id` | Same + Wayland `app_id` (match `.desktop` / `StartupWMClass`) |
229| `try_with_app_icon` / `try_with_app_icon_id` | Fallible variants |
230
231## License
232
233MIT