makepad/widgets/themes/README.md
Admin 53e0e85b92 draw and widgets: Slug text everywhere, no blank glyphs, slimmer widgets
All outline text draws with Slug curves on every platform (SDF/MSDF stays
selectable per size); a frame never presents a blank glyph and retained draw
lists keep theirs. One coverage model for vector feathering, widget families
split out of the core crate, fewer steady-state allocations, video keeps its
aspect (Contain/Cover), render and SSAO pass unification.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

181 lines
10 KiB
Markdown

# Application styles
These Splash files are the library's own sheets, one per desktop family the
window manager lays out: `omarchy`, `black-orange`, `macos`, `macos-dark`,
`windows`, `windows-dark`, `windows-2000`, `nextstep`, `ios`, `ios-dark`,
`android` and `android-dark`.
## The sheet catalogue
Every style is a sheet in one catalogue (`widgets/core/src/desktop_style.rs`):
a `SheetEntry` holds its id, its label, the desktop family it lays out as
(`DesktopStyle`: floating or tiled, shelf, title bar, icons, the WM's style
tween), the id of its dark variant, both Splash halves embedded, the source
directory a checkout hotloads them from, and the crate its
`crate_resource("self:...")` resolves against. The sheets above are the
catalogue's first entries, made with `sheet_entry!`. An app brings its own
sheets the same way and adds them at startup, before the first style load:
```rust
static SHEETS: [SheetEntry; 1] = [makepad_widgets::sheet_entry!(
dir: "themes", id: "harbour", label: "Harbour",
family: DesktopStyle::Omarchy, dark: None, resources: makepad_widgets::widgets_dir,
)];
desktop_style::register(&SHEETS);
```
Every list of styles -- the pickers, the WM's style menu, the theme lab and
store, the storybook's theme list -- reads the catalogue
(`desktop_style::catalogue`, `picks`, `find`), so a registered sheet turns up
everywhere without being named anywhere else. Sheets that belong to one app
live with that app, in a crate of its own, not here. The
checks every sheet is held to are `sheet_checks` in widgets-core (the
`sheet-checks` feature for an app's own tests).
Each style has two phases:
- `theme.splash` installs semantic roles, typography, spacing, radii, and bevels
before stock widgets are registered.
- `widgets.splash` applies component geometry and materials after registration.
It can replace a widget's shader as well as its properties. Windows 2000's
raised buttons and sunken edit fields are examples.
A replaced button face still shows the colour an app paints on it: apps light
a latch (the page they are on, a mute that is down) by setting `draw_bg.color`.
Each `Button*` face a sheet draws goes through `self.host_over(face, m)` (`m`
the room the sheet grew its quad by) or fills with `self.host_face(...)` or
`self.face_fill()`. At the variant's own `color_default` these return the
sheet's face unchanged; `sheet_checks::button_faces_take_the_host_colour`
holds every sheet to it.
## The surface material
The rounded view, the panel, the button, the check box, the toggle, the
slider and the knob carry a moulded material behind one uniform, `material`,
whose default is the theme's `material_level`. Every stock theme leaves it at
0, so those shaders draw exactly what they always did; a sheet built on it
raises it in its `theme.splash` before any widget registers (1 relief, 2
relief with rim, gloss and specular), and the `material_*` tokens beside it
set the key light, the shoulder, the finish, the cast and inner shadows, the
glow and the press. The shading is `mod.sdf.Material` in
`draw/src/shader/surface.rs`, the library `ReliefView` is lit by too.
A material control keeps its quad and insets its face by
`theme.material_margin`: the room its cast shadow falls into, which a sheet
pays for again in the control's padding. The press is one signed number,
`material_press_depth`: past `-material_raise` a held face inverts, short of
it the cap deepens, near zero the glow carries the state (with
`material_ink_glow` lighting the label). The storybook's Containers >
Material page shows all three.
The loader embeds both files for installed/wasm builds. In a native source
checkout it reads the sheet's source directory (`widgets/themes/<style>/` for
these) on each selection. Edit either file
and choose the same style again from the WM's style picker to hotload it.
End a generated assignment-only Splash module with `true` so its last
assignment is evaluated as a statement.
The WM keeps its Omarchy top bar. Click the current style name to open the
dropdown. Modern desktop and mobile styles also have a Light/Dark button. Its Applications
launcher, Windows Start menus, window chrome, and hosted apps use the selected
appearance.
`desktop_style::StyleSheet` carries both phases to existing process clients
through `StudioToApp::Custom`. `Window` receives the message and requests an
application Splash reload with `Apply::ScriptReapply`. Module clients re-evaluate
their registrations in their existing isolates with the same apply mode.
Widget identities, editable text, and application Rust state are retained.
Initialize persistent script models only when `!vm.is_reload()`; read the existing
`mod.state` from application Splash. See `examples/counter/src/main.rs`. Dynamic
`View.on_render` children and embedded Splash isolates reapply the style too.
Embedded `Splash` widgets inherit the stylesheet and reapply their existing
widget tree in their own isolate.
Applications should inherit the stock component styles and read `theme.*` for
custom drawing. Rust drawing reads `makepad_widgets::theme_tokens::color(vm, role)`
during registration or through the owning VM with `Cx::with_vm`. Re-read these
roles after each style reload: appearances can change while an app is open.
The WM provides `MAKEPAD_WIDGET_STYLE` at spawn and the `makepad_style` sheet
event for live changes; apps do not read the WM's desk palette file.
The framebuffer crossfade lives in `apps/wm/src/scene.rs`. It freezes the old
GPU render target while the live scene renders the new style; it does not take
screenshots, rebuild app instances, or put transitions in the widget system.
Shell geometry interpolates over the same 650 ms interval. Floating desktop
rectangles are stored separately from the Omarchy tiling tree, so switching back
restores the tiling arrangement.
The macOS dock overlays the desktop; dragging keeps the title bar reachable but
allows window bodies behind the dock or partly offscreen. Terminals render only
their background with opacity (`MAKEPAD_WM_TERM_OPACITY`, default `0.78 0.70` for
focused/unfocused); text and ANSI cell colors remain crisp. Their foreground and
background follow the active light/dark stylesheet without restarting the PTY.
`color_terminal_bg` and `color_terminal_text` are separate semantic roles:
Windows 2000 and Android use opaque black consoles, while NeXTSTEP uses white.
Rust terminal drawing reads these tokens directly, using `color_bg_app` and
`color_text` when the terminal-specific roles are absent. Declare both in each
style so a reload resets the previous console palette.
The terminal opts into `Window.body.keyboard_resize: true`; this KeyboardView
mode reflows content above the animated keyboard instead of panning the whole
terminal out of view. Its grid and PTY resize with the available space.
`widgets/core/src/backdrop.rs` supplies ordered compositor checkpoints. Windows are
composited from back to front, and a glass surface samples a checkpoint below it.
Disjoint sampling footprints share a Gaussian stack, including the kernel's
support outside the visible surface. Intervening opaque or translucent content
that overlaps the footprint starts a new checkpoint. The dock is the final
consumer. Requested blur levels are combined before drawing the pyramid, so
unused deeper passes are skipped. Explicit producer/consumer links preserve GPU
ordering when pass IDs are recycled. `MAKEPAD_WM_TRACE_BLUR=1` logs stack and pass
counts when they change; normal operation does not log each frame.
In a checkout, build with `cargo build --release -p makepad-app-wm`, then launch
`./target/release/makepad-app-wm --remote` from the checkout root.
Applications launch on demand with `cargo run --release -p <package>`; there is
no binary collection to prepare. The app's window shows Cargo's compiling or
build-wait stage before the process connects, then fades into its first frame.
Installed distributions without a source checkout use sibling executables.
NeXTSTEP uses black focused title bars, gray beveled window controls, square
widgets, a right-hand vertical application dock, and its own icon family. Its
launcher opens a draggable Workspace palette with attached submenu columns.
Hold the secondary mouse button on the desktop or a window title bar, drag
through the menu and release to choose a command. The popup follows the pointer
and opens submenus to the left near the screen edge. The Omarchy top bar
and shared application state remain in place while switching styles.
Application icons use `AppIcon{name: "files" width: 32 height: 32}`. The default
`style: "auto"` follows the application's Splash stylesheet; standalone apps use
the host OS family. An explicit style ID is available for galleries. `color`
retints the Omarchy artwork; `opacity` preserves the other families' own colors.
Window captions derive their icon name from `window.app_id` (or the binary name),
and the WM's dock/taskbar/launcher use the same `AppIconDraw` renderer.
Each family has editable `icons/<app-id>.svg` assets. macOS dark and light share
app artwork, as on the OS. Unknown IDs use that family's `app.svg`. Add or edit
an SVG and reselect the style to hotload it; the complete icon sources travel in
the stylesheet to existing process and module apps. Unchanged sources keep their
parsed geometry cache. `python3 widgets/themes/build_icons.py` rebuilds the
bundled artwork families using only Python's standard library.
Use original application symbols within each OS's visual style. Files uses a
folder with documents, and Browser uses a web window with a globe; do not use
the Finder face or Safari compass artwork.
Application surfaces should use the shared `theme` roles (or app-specific aliases
that resolve to those roles), including `corner_radius` and
`container_corner_radius`. Keep content colors such as chart series and model
axes separate. Filled accent actions use `color_text_on_accent`.
Mark live fields that contain runtime UI state with `#[apply_state]` alongside
`#[live]`: stylesheet `ScriptReapply` preserves these values, while explicit
`Eval` edits and ordinary source reloads still apply. Standard panel visibility,
checkbox state, and dropdown selection use this path.
On macOS, the WM captures the complete window into a GPU texture and warps
that surface into its application icon in the dock. Minimize and restore share
the same reversible progress, without resizing or relaying out the application
during the animation. A style change invalidates minimized snapshots so the
restored window uses the current theme. `MAKEPAD_WM_TRACE_WARP=1` enables the
capture diagnostics using ordinary logs. For frame inspection,
`MAKEPAD_WM_WARP_SECONDS=5` slows the default 0.62-second animation.