Repaints only what changed
Every frame works out which pixels a change touched and redraws exactly those. A frame that changes one small box rasterizes and presents one small box.
Rust GUI library v0.1
Smudge is a native Rust GUI library with a retained widget tree, fine-grained reactive signals and damage-tracked repainting. It is built to stay fast where there is no usable GPU, and it runs on the desktop and in the browser.
cargo add smudgeMedian frame on the CPU renderer, one thread of a Ryzen 7 7700, headless. How these are measured
Every frame works out which pixels a change touched and redraws exactly those. A frame that changes one small box rasterizes and presents one small box.
State lives in signals. A closure that reads a signal binds that one property of that one widget, so a write updates what depends on it and nothing else.
A CPU renderer that is fast on its own, for virtual machines, remote desktops and older laptops. A GPU renderer takes over when a real adapter is there.
Widgets are built once and kept. A change that only affects color skips layout, and each node keeps its paint until something it shows changes.
Shaped text, a text input with selection, undo and redo, input methods for composing languages, and the system clipboard.
One window API for the desktop, tested on Linux in this release, and the CPU renderer in a browser canvas. Desktop builds expose the widget tree to screen readers.
A button and a label bound to one signal. Each press writes the signal, the label's text closure runs again, and the next frame repaints the label and the pressed button. Nothing else in the window is touched.
use smudge::prelude::*;
fn main() -> Result<(), PlatformError> {
let count = RwSignal::new(0_u32);
let press = count.clone();
let shown = count.clone();
run_with(
WindowOptions {
title: String::from("Counter"),
size: Size { width: 280.0, height: 140.0 },
clear_color: Color::rgb(16, 17, 21),
..WindowOptions::default()
},
move || {
column()
.padding(px(24.0))
.gap(Size { width: 0.0, height: 12.0 })
.align_items(Align::Center)
.child(
button("Count up")
.primary()
.on_press(move || press.update(|count| *count += 1)),
)
.child(label(move || format!("Clicked {} times", shown.get())))
},
|app| app.set_theme(Theme::dark()),
)
}