Skip to main content

gpui-fundamentals

Core GPUI concepts including contexts, windows, entities, elements, and rendering. Use when learning GPUI basics, understanding the framework architecture, or implementing core UI patterns.

Source facts

Repository
zerx-lab/rmx
Last source activity
February 5, 2026 at 09:49
Detected SKILL.md language
English
Stars
70
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
gpui-fundamentals
description
Core GPUI concepts including contexts, windows, entities, elements, and rendering. Use when learning GPUI basics, understanding the framework architecture, or implementing core UI patterns.
# GPUI Fundamentals This skill covers the core concepts of GPUI framework for building desktop UI applications in Rust. ## Overview GPUI is a UI framework that provides: - **Entities**: Handles to state with lifecycle management - **Contexts**: Access to global state, windows, and system services - **Elements**: Composable UI building blocks - **Rendering**: `Render` trait for creating element trees - **Concurrency**: Async primitives for background work ## Context Types Context types allow interaction with global state, windows, entities, and system services. They are passed as the argument named `cx`. ### App `App` is the root context type, providing access to global state and read/update of entities. ```rust fn do_something(cx: &mut App) { // Access global state // Read/update entities } ``` ### Context<T> Provided when updating an `Entity<T>`. This context dereferences into `App`, so functions which take `&App` can also take `&Context<T>`. ```rust struct MyView { count: usize, } impl MyView { fn increment(&mut self, cx: &mut Context<Self>) { self.count += 1; cx.notify(); // Tell GPUI to re-render } } ``` ### AsyncApp and AsyncWindowContext Provided by `cx.spawn()` for async operations. These can be held across await points. ```rust fn start_async_work(&mut self, cx: &mut Context<Self>) { cx.spawn(async move |this, cx| { // this: WeakEntity<Self> // cx: &mut AsyncApp // Do async work Ok(()) }).detach(); } ``` ## Window `Window` provides access to the state of an application window. It is passed as an argument named `window` and comes **before** `cx` when present. ```rust impl Render for MyView { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div().child("Hello") } } ``` Used for: - Managing focus - Dispatching actions - Directly drawing - Getting user input state ## Entities An `Entity<T>` is a handle to state of type `T`. Entities enable: - Shared ownership of UI state - Automatic lifecycle management - Safe concurrent access ### Creating Entities ```rust app.run(move |cx| { cx.spawn(async move |cx| { cx.open_window(WindowOptions::default(), |window, cx| { // Create an entity cx.new(|_cx| MyView { count: 0 }) })?; Ok::<_, anyhow::Error>(()) }).detach(); }); ``` ### Entity Operations ```rust // Given: thing: Entity<MyView> // Get entity ID let id = thing.entity_id(); // Downgrade to weak reference let weak = thing.downgrade(); // Read (immutable access) let value = thing.read(cx); println!("Count: {}", value.count); // Read with closure let count = thing.read_with(cx, |view, cx| view.count); // Update (mutable access) thing.update(cx, |view, cx| { view.count += 1; cx.notify(); }); // Update with window access thing.update_in(cx, |view, window, cx| { view.count += 1; window.dispatch_action(SomeAction.boxed_clone(), cx); cx.notify(); }); ``` ### Important Rules 1. **Use inner cx**: Within closures, use the inner `cx` provided to the closure, not the outer `cx` ```rust // ❌ WRONG entity.update(cx, |view, inner_cx| { view.count += 1; cx.notify(); // Using outer cx - WRONG! }); // ✅ CORRECT entity.update(cx, |view, inner_cx| { view.count += 1; inner_cx.notify(); // Using inner cx }); ``` 2. **Avoid update-while-updating**: Never update an entity while it's already being updated (causes panic) ```rust // ❌ WRONG - will panic entity.update(cx, |view, cx| { entity.update(cx, |view2, cx2| { // Nested update - PANIC! // ... }); }); ``` ## Elements The `Render` trait is used to render state into an element tree with flexbox layout. ### Render Trait ```rust use gpui::*; struct MyView { text: SharedString, } impl Render for MyView { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { div() .flex() .flex_col() .gap_2() .child(self.text.clone()) .child("More text") } } ``` ### RenderOnce Trait For components constructed just to be turned into elements: ```rust use gpui::*; #[derive(IntoElement)] struct Card { title: SharedString, content: SharedString, } impl RenderOnce for Card { fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { div() .p_4() .bg(rgb(0x1a1a1a)) .rounded(px(8.0)) .child( div().font_bold().child(self.title) ) .child( div().text_sm().child(self.content) ) } } // Usage impl Render for MyView { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { div().child(Card { title: "Hello".into(), content: "World".into(), }) } } ``` ### SharedString Use `SharedString` to avoid copying strings. It's either `&'static str` or `Arc<str>`. ```rust use gpui::*; struct MyView { // Efficient string storage title: SharedString, } impl MyView { fn new() -> Self { Self { title: "Hello".into(), // From &str } } fn set_title(&mut self, title: String) { self.title = title.into(); // From String } } // SharedString implements IntoElement impl Render for MyView { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { div().child(self.title.clone()) } } ``` ## Element Composition ### Basic Composition ```rust div() .child("Text") .child(div().child("Nested")) .child(another_element()) ``` ### Conditional Rendering Use `.when()` for conditional attributes/children: ```rust div() .when(is_active, |this| { this.bg(rgb(0x3b82f6)) }) .when_some(maybe_text, |this, text| { this.child(text) }) ``` ### Multiple Children ```rust div() .children(vec![ div().child("Item 1"), div().child("Item 2"), div().child("Item 3"), ]) ``` ## Application Lifecycle ### Basic Application ```rust use gpui::*; struct AppView; impl Render for AppView { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { div().size_full().child("My App") } } fn main() { let app = Application::new(); app.run(move |cx| { cx.spawn(async move |cx| { cx.open_window(WindowOptions::default(), |window, cx| { cx.new(|_| AppView) })?; Ok::<_, anyhow::Error>(()) }) .detach(); }); } ``` ### Window Options ```rust use gpui::*; let options = WindowOptions { window_bounds: Some(WindowBounds::Windowed(Bounds { origin: Point { x: px(100.0), y: px(100.0) }, size: Size { width: px(1024.0), height: px(768.0) }, })), titlebar: Some(TitlebarOptions { title: Some("My Application".into()), appears_transparent: false, ..Default::default() }), focus: true, show: true, ..Default::default() }; cx.open_window(options, |window, cx| { cx.new(|_| MyView::new()) })?; ``` ## Common Patterns ### State Update with Notify When state changes in a way that affects rendering: ```rust struct Counter { count: usize, } impl Counter { fn increment(&mut self, cx: &mut Context<Self>) { self.count += 1; cx.notify(); // Trigger re-render } } ``` ### Event Handlers ```rust impl Render for Counter { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .child(format!("Count: {}", self.count)) .child( div() .child("Increment") .on_click(cx.listener(|this, _event, _window, cx| { this.increment(cx); })) ) } } ``` ### Using cx.listener The `cx.listener()` method creates event handlers that receive `&mut Self`: ```rust .on_click(cx.listener(|this: &mut Self, event, window, cx| { // this: mutable reference to the entity // event: the click event // window: window reference ### Todo List with Entity Management ```rust use gpui::*; #[derive(Clone)] struct TodoItem { id: usize, text: String, completed: bool, } struct TodoList { items: Vec<TodoItem>, next_id: usize, input_text: SharedString, } impl TodoList { fn new() -> Self { Self { items: Vec::new(), next_id: 1, input_text: "".into(), } } fn add_item(&mut self, cx: &mut Context<Self>) { if !self.input_text.is_empty() { self.items.push(TodoItem { id: self.next_id, text: self.input_text.to_string(), completed: false, }); self.next_id += 1; self.input_text = "".into(); cx.notify(); } } fn toggle_item(&mut self, id: usize, cx: &mut Context<Self>) { if let Some(item) = self.items.iter_mut().find(|i| i.id == id) { item.completed = !item.completed; cx.notify(); } } fn remove_item(&mut self, id: usize, cx: &mut Context<Self>) { self.items.retain(|item| item.id != id); cx.notify(); } } impl Render for TodoList { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .size_full() .p_6()
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub