Skip to main content

gpui-patterns

Common UI patterns and advanced techniques for GPUI applications. Use when implementing modals, lists, forms, state sharing, or complex component compositions.

Informations de source

Dépôt
zerx-lab/rmx
Dernière activité de la source
5 février 2026 à 09:49
Langue détectée de SKILL.md
anglais
Étoiles
70
Forks
2

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
gpui-patterns
description
Common UI patterns and advanced techniques for GPUI applications. Use when implementing modals, lists, forms, state sharing, or complex component compositions.
# GPUI Patterns This skill provides common UI patterns and advanced techniques for GPUI applications. ## Modal/Overlay Pattern ### Basic Modal ```rust use gpui::*; struct Modal { is_open: bool, content: SharedString, } impl Render for Modal { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .when(self.is_open, |this| { this.absolute() .inset_0() .flex() .items_center() .justify_center() .bg(rgba(0, 0, 0, 0.5)) .on_click(cx.listener(|this, _event, _window, cx| { this.close(cx); })) .child( div() .p_6() .bg(rgb(0x1a1a1a)) .rounded_lg() .min_w(px(300.0)) .child(self.content.clone()) ) }) } } impl Modal { fn open(&mut self, content: impl Into<SharedString>, cx: &mut Context<Self>) { self.is_open = true; self.content = content.into(); cx.notify(); } fn close(&mut self, cx: &mut Context<Self>) { self.is_open = false; cx.notify(); } } ``` ## List Pattern ### Dynamic List Rendering ```rust struct ListView { items: Vec<String>, } impl Render for ListView { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .v_flex() .gap_2() .children( self.items.iter().enumerate().map(|(index, item)| { self.render_item(index, item, cx) }) ) } } impl ListView { fn render_item( &self, index: usize, item: &str, cx: &Context<Self>, ) -> impl IntoElement { div() .p_3() .bg(rgb(0x1a1a1a)) .rounded(px(4.0)) .flex() .justify_between() .child(format!("{}. {}", index + 1, item)) .child( div() .px_3() .py_1() .bg(rgb(0xef4444)) .rounded(px(4.0)) .cursor_pointer() .child("Delete") .on_click(cx.listener(move |this, _event, _window, cx| { this.remove_item(index, cx); })) ) } fn remove_item(&mut self, index: usize, cx: &mut Context<Self>) { if index < self.items.len() { self.items.remove(index); cx.notify(); } } } ``` ## Form Pattern ### Form with Validation ```rust struct LoginForm { username: SharedString, password: SharedString, error: Option<SharedString>, } impl Render for LoginForm { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .v_flex() .gap_4() .p_6() .when_some(self.error.clone(), |this, error| { this.child( div() .p_3() .bg(rgb(0xef4444)) .rounded(px(4.0)) .child(error) ) }) .child(self.render_input("Username", &self.username, cx)) .child(self.render_input("Password", &self.password, cx)) .child( div() .px_6() .py_3() .bg(rgb(0x3b82f6)) .rounded(px(6.0)) .cursor_pointer() .child("Login") .on_click(cx.listener(|this, _event, _window, cx| { this.submit(cx); })) ) } } impl LoginForm { fn render_input( &self, label: &str, value: &SharedString, _cx: &Context<Self>, ) -> impl IntoElement { div() .v_flex() .gap_1() .child( div().text_sm().child(label) ) .child( div() .w_full() .px_3() .py_2() .bg(rgb(0x1a1a1a)) .border_1() .border_color(rgb(0x4a4a4a)) .rounded(px(4.0)) .child(value.clone()) ) } fn submit(&mut self, cx: &mut Context<Self>) { if self.username.is_empty() || self.password.is_empty() { self.error = Some("Please fill in all fields".into()); cx.notify(); return; } self.error = None; // Handle login... } } ``` ## Global State Pattern ### Shared Application State ```rust #[derive(Clone)] struct AppState { user: Option<String>, theme: String, } // Set global state fn init_app_state(cx: &mut App) { let state = AppState { user: None, theme: "dark".to_string(), }; cx.set_global(state); } // Access global state fn use_app_state(cx: &App) -> AppState { cx.global::<AppState>().clone() } // Update global state fn set_user(cx: &mut App, user: String) { let mut state = cx.global::<AppState>().clone(); state.user = Some(user); cx.set_global(state); } ``` ## Parent-Child Communication ### Child Notifying Parent ```rust #[derive(Clone, Debug)] enum ChildEvent { ValueChanged(i32), } impl EventEmitter<ChildEvent> for Child {} struct Parent { child: Entity<Child>, _subscription: Subscription, } impl Parent { fn new(cx: &mut Context<Self>) -> Self { let child = cx.new(|_| Child::new()); let subscription = cx.subscribe(&child, |this, _child, event, cx| { match event { ChildEvent::ValueChanged(value) => { println!("Child value changed to: {}", value); cx.notify(); } } }); Self { child, _subscription: subscription, } } } struct Child { value: i32, } impl Child { fn new() -> Self { Self { value: 0 } } fn set_value(&mut self, value: i32, cx: &mut Context<Self>) { self.value = value; cx.emit(ChildEvent::ValueChanged(value)); cx.notify(); } } ``` ## Tab Navigation ### Tab View Pattern ```rust #[derive(Clone, Copy, PartialEq)] enum Tab { Home, Settings, Profile, } struct TabView { active_tab: Tab, } impl Render for TabView { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { div() .v_flex() .size_full() .child(self.render_tabs(cx)) .child(self.render_content()) } } impl TabView { fn render_tabs(&self, cx: &Context<Self>) -> impl IntoElement { div() .h_flex() .gap_2() .p_2() .bg(rgb(0x1a1a1a)) .child(self.render_tab("Home", Tab::Home, cx)) .child(self.render_tab("Settings", Tab::Settings, cx)) .child(self.render_tab("Profile", Tab::Profile, cx)) } fn render_tab(&self, label: &str, tab: Tab, cx: &Context<Self>) -> impl IntoElement { let is_active = self.active_tab == tab; div() .px_4() .py_2() .rounded(px(4.0)) .cursor_pointer() .bg(if is_active { rgb(0x3b82f6) } else { rgb(0x2a2a2a) }) .child(label) .on_click(cx.listener(move |this, _event, _window, cx| { this.active_tab = tab; cx.notify(); })) } fn render_content(&self) -> impl IntoElement { match self.active_tab { Tab::Home => div().child("Home Content"), Tab::Settings => div().child("Settings Content"), Tab::Profile => div().child("Profile Content"), } } } ``` ## Loading State Pattern ### Async Data Loading ```rust enum LoadState<T> { Idle, Loading, Loaded(T), Error(String), } struct DataView { state: LoadState<Vec<String>>, } impl Render for DataView { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { match &self.state { LoadState::Idle => { div().child("Click to load") .on_click(cx.listener(|this, _event, _window, cx| { this.load_data(cx); })) } LoadState::Loading => { div().child("Loading...") } LoadState::Loaded(items) => { div() .v_flex() .gap_2() .children(items.iter().map(|item| { div().child(item.clone()) })) } LoadState::Error(error) => { div() .p_3() .bg(rgb(0xef4444)) .child(format!("Error: {}", error)) } } } } impl DataView { fn load_data(&mut self, cx: &mut Context<Self>) { self.state = LoadState::Loading; cx.notify(); cx.spawn(async move |this, cx| { match fetch_data().await { Ok(data) => { this.update(&mut *cx, |view, cx| { view.state = LoadState::Loaded(data); cx.notify(); })?; } Err(e) => { this.update(&mut *cx, |view, cx| { view.state = LoadState::Error(e.to_string()); cx.notify(); })?; } } Ok(()) }).detach(); } } async fn fetch_data() -> Result<Vec<String>, anyhow::Error> { // Simulate API call tokio::time::sleep(Duration::from_secs(1)).await; Ok(vec!["Item 1".to_string(), "Item 2".to_string()]) } ``` ## Dropdown Pattern ```rust struct Dropdown {
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub