Documentation Getting started with GPUI.
Installation Add gpui-ce as a git dependency in your Cargo.toml. It uses wgpu for GPU abstraction and winit for windowing, so no platform-specific setup is required.
Cargo.toml copy
[dependencies]
gpui-ce = { git = "https://github.com/Far-Beyond-Pulsar/WGPUI" }
Your First Window Create a window with a basic element tree. The entry point is Application::new().run().
src/main.rs copy
use gpui::*;
struct HelloApp;
impl Render for HelloApp {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
cx.notify();
div()
.flex()
.size_full()
.justify_center()
.items_center()
.bg(rgb(0x0c0c0c))
.text_color(rgb(0xffffff))
.child(
div()
.px_6()
.py_3()
.rounded_xl()
.bg(rgb(0x0ea5e9))
.child("Hello, WGPUI!")
)
}
}
fn main() {
Application::new().run(|cx: &mut App| {
cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|_| HelloApp)
}).unwrap();
});
}
Run with cargo run. The same code compiles on Windows, macOS, Linux, and WASM. On WASM, call cx.activate(true) after opening the window.
Element Tree GPUI uses a builder pattern for constructing UI element trees. Every element returns impl IntoElement, enabling composition. Text is added via .child("text") — there are no span()or h1() element functions.
rust copy
use gpui::*;
fn metric_card(label: &str, value: &str) -> impl IntoElement {
div()
.flex_1()
.p_4()
.rounded_lg()
.bg(rgb(0x1a1a1a))
.child(
div().flex().flex_col().gap_1()
.child(div().text_xs().text_color(rgb(0x888888)).child(label.to_string()))
.child(div().text_2xl().font_weight(FontWeight::BOLD).text_color(rgb(0xffffff)).child(value.to_string()))
)
}
fn dashboard() -> impl IntoElement {
div()
.flex()
.flex_col()
.gap_4()
.p_6()
.child(div().text_xl().font_weight(FontWeight::BOLD).child("Dashboard"))
.child(div().flex().gap_3()
.child(metric_card("Users", "1,234"))
.child(metric_card("Revenue", "$8,910"))
.child(metric_card("Active", "89%"))
)
}
Style shortcuts: flex_1(), px_4(),py_2(), gap_4(),rounded_lg(), border_1(),w_full(), text_size(px(24.0)),hover(|s| s.bg(rgb(0x...)))
Interactivity & State Elements need .id() for on_clickto work. Listeners receive (this, event, window, cx) — 4 arguments. Always call cx.notify() after state changes.
rust copy
use gpui::*;
struct Counter {
count: i32,
}
impl Render for Counter {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
cx.notify();
div()
.flex()
.size_full()
.justify_center()
.items_center()
.gap_4()
.bg(rgb(0x0c0c0c))
.text_color(rgb(0xffffff))
.child(
div().flex().items_center().gap_4()
.child(
div()
.id("dec")
.px_4().py_2().rounded_lg()
.bg(rgb(0x333333))
.hover(|s| s.bg(rgb(0x444444)))
.on_click(cx.listener(|this, _event, _window, cx| {
this.count -= 1;
cx.notify();
}))
.child("-")
)
.child(
div()
.text_size(px(32.0))
.font_weight(FontWeight::BOLD)
.child(self.count.to_string())
)
.child(
div()
.id("inc")
.px_4().py_2().rounded_lg()
.bg(rgb(0x0ea5e9))
.hover(|s| s.bg(rgb(0x0284c7)))
.on_click(cx.listener(|this, _event, _window, cx| {
this.count += 1;
cx.notify();
}))
.child("+")
)
)
}
}
fn main() {
Application::new().run(|cx: &mut App| {
cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|_| Counter { count: 0 })
}).unwrap();
});
}
Gotchas: on WASM, the render() method should callcx.notify() at the top to keep the frame loop running. Without it, events may not fire.
WASM Build Compile GPUI apps to WebAssembly with wasm-pack. The entry point must use #[wasm_bindgen(start)] and call console_error_panic_hook::set_once().
Cargo.toml copy
[package]
name = "my-app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
gpui-ce = { git = "https://github.com/Far-Beyond-Pulsar/WGPUI" }
wasm-bindgen = "0.2"
console_error_panic_hook = "0.1"
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
src/lib.rs copy
use gpui::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
console_error_panic_hook::set_once();
Application::new().run(|cx: &mut App| {
// On WASM, winit throws for control flow — catch in JS
cx.activate(true);
cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|_| MyView)
}).unwrap();
});
}
index.html copy
<!DOCTYPE html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; background: #000; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<script type="module">
import init from "./pkg/my_app.js";
try { await init(); }
catch (e) {
if (!String(e).includes("exceptions for control flow")) throw e;
}
</script>
</body>
</html>
Build: wasm-pack build --target web --out-dir pkg