diff --git a/crates/story/examples/tiles.rs b/crates/story/examples/tiles.rs index 4088e3e1..232cc3c4 100644 --- a/crates/story/examples/tiles.rs +++ b/crates/story/examples/tiles.rs @@ -94,11 +94,11 @@ impl Panel for ContainerPanel { "ContainerPanel" } - fn title(&self, window: &Window, cx: &App) -> AnyElement { + fn title(&mut self, window: &Window, cx: &mut Context) -> AnyElement { self.panel.title(window, cx) } - fn title_suffix(&self, _: &mut Window, cx: &mut App) -> Option { + fn title_suffix(&mut self, _: &mut Window, cx: &mut Context) -> Option { Some( div() .w_24() diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 2f45f520..b61a2b29 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -692,7 +692,7 @@ impl Panel for StoryContainer { "StoryContainer" } - fn title(&self, _window: &Window, _cx: &App) -> AnyElement { + fn title(&mut self, _window: &Window, _cx: &mut Context) -> AnyElement { self.name.clone().into_any_element() } @@ -722,11 +722,11 @@ impl Panel for StoryContainer { .contains(&self.name) } - fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, _cx: &mut App) { + fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, _cx: &mut Context) { println!("panel: {} zoomed: {}", self.name, zoomed); } - fn set_active(&mut self, active: bool, _window: &mut Window, cx: &mut App) { + fn set_active(&mut self, active: bool, _window: &mut Window, cx: &mut Context) { println!("panel: {} active: {}", self.name, active); if let Some(on_active) = self.on_active { if let Some(story) = self.story.clone() { @@ -735,11 +735,20 @@ impl Panel for StoryContainer { } } - fn dropdown_menu(&self, menu: PopupMenu, _window: &Window, _cx: &App) -> PopupMenu { + fn dropdown_menu( + &mut self, + menu: PopupMenu, + _window: &mut Window, + _cx: &mut Context, + ) -> PopupMenu { menu.menu("Info", Box::new(ShowPanelInfo)) } - fn toolbar_buttons(&self, _window: &mut Window, _cx: &mut App) -> Option> { + fn toolbar_buttons( + &mut self, + _window: &mut Window, + _cx: &mut Context, + ) -> Option> { Some(vec![ Button::new("info") .icon(IconName::Info) diff --git a/crates/ui/src/dock/panel.rs b/crates/ui/src/dock/panel.rs index 6781a92a..e21baaaf 100644 --- a/crates/ui/src/dock/panel.rs +++ b/crates/ui/src/dock/panel.rs @@ -1,12 +1,10 @@ -use std::{collections::HashMap, sync::Arc}; - use crate::{button::Button, dock::TabPanel, menu::PopupMenu}; use gpui::{ - AnyElement, AnyView, App, AppContext as _, Entity, EntityId, EventEmitter, FocusHandle, - Focusable, Global, Hsla, IntoElement, Render, SharedString, WeakEntity, Window, + AnyElement, AnyView, App, AppContext as _, Context, Entity, EntityId, EventEmitter, + FocusHandle, Focusable, Global, Hsla, IntoElement, Render, SharedString, WeakEntity, Window, }; - use rust_i18n::t; +use std::{collections::HashMap, sync::Arc}; use super::{DockArea, PanelInfo, PanelState, invalid_panel::InvalidPanel}; @@ -68,7 +66,7 @@ pub trait Panel: EventEmitter + Render + Focusable { } /// The title of the panel - fn title(&self, window: &Window, cx: &App) -> AnyElement { + fn title(&mut self, window: &Window, cx: &mut Context) -> AnyElement { SharedString::from(t!("Dock.Unnamed")).into_any_element() } @@ -80,7 +78,7 @@ pub trait Panel: EventEmitter + Render + Focusable { /// The suffix of the panel title, default is `None`. /// /// This is used to add a suffix element to the panel title. - fn title_suffix(&self, window: &mut Window, cx: &mut App) -> Option { + fn title_suffix(&mut self, window: &mut Window, cx: &mut Context) -> Option { None } @@ -110,28 +108,43 @@ pub trait Panel: EventEmitter + Render + Focusable { /// This method will be called when the panel is active or inactive. /// /// The last_active_panel and current_active_panel will be touched when the panel is active. - fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut App) {} + fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context) {} /// Set zoomed state of the panel. /// /// This method will be called when the panel is zoomed or unzoomed. /// /// Only current Panel will touch this method. - fn set_zoomed(&mut self, zoomed: bool, window: &mut Window, cx: &mut App) {} + fn set_zoomed(&mut self, zoomed: bool, window: &mut Window, cx: &mut Context) {} /// When this Panel is added to a TabPanel, this will be called. - fn on_added_to(&mut self, tab_panel: WeakEntity, window: &mut Window, cx: &mut App) {} + fn on_added_to( + &mut self, + tab_panel: WeakEntity, + window: &mut Window, + cx: &mut Context, + ) { + } /// When this Panel is removed from a TabPanel, this will be called. - fn on_removed(&mut self, window: &mut Window, cx: &mut App) {} + fn on_removed(&mut self, window: &mut Window, cx: &mut Context) {} /// The addition dropdown menu of the panel, default is `None`. - fn dropdown_menu(&self, this: PopupMenu, window: &Window, cx: &App) -> PopupMenu { + fn dropdown_menu( + &mut self, + this: PopupMenu, + window: &mut Window, + cx: &mut Context, + ) -> PopupMenu { this } /// The addition toolbar buttons of the panel used to show in the right of the title bar, default is `None`. - fn toolbar_buttons(&self, window: &mut Window, cx: &mut App) -> Option> { + fn toolbar_buttons( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> Option> { None } @@ -152,7 +165,7 @@ pub trait PanelView: 'static + Send + Sync { fn panel_name(&self, cx: &App) -> &'static str; fn panel_id(&self, cx: &App) -> EntityId; fn tab_name(&self, cx: &App) -> Option; - fn title(&self, window: &Window, cx: &App) -> AnyElement; + fn title(&self, window: &Window, cx: &mut App) -> AnyElement; fn title_suffix(&self, window: &mut Window, cx: &mut App) -> Option; fn title_style(&self, cx: &App) -> Option; fn closable(&self, cx: &App) -> bool; @@ -162,7 +175,7 @@ pub trait PanelView: 'static + Send + Sync { fn set_zoomed(&self, zoomed: bool, window: &mut Window, cx: &mut App); fn on_added_to(&self, tab_panel: WeakEntity, window: &mut Window, cx: &mut App); fn on_removed(&self, window: &mut Window, cx: &mut App); - fn dropdown_menu(&self, menu: PopupMenu, window: &Window, cx: &App) -> PopupMenu; + fn dropdown_menu(&self, menu: PopupMenu, window: &mut Window, cx: &mut App) -> PopupMenu; fn toolbar_buttons(&self, window: &mut Window, cx: &mut App) -> Option>; fn view(&self) -> AnyView; fn focus_handle(&self, cx: &App) -> FocusHandle; @@ -183,8 +196,8 @@ impl PanelView for Entity { self.read(cx).tab_name(cx) } - fn title(&self, window: &Window, cx: &App) -> AnyElement { - self.read(cx).title(window, cx) + fn title(&self, window: &Window, cx: &mut App) -> AnyElement { + self.update(cx, |this, cx| this.title(window, cx)) } fn title_suffix(&self, window: &mut Window, cx: &mut App) -> Option { @@ -227,8 +240,8 @@ impl PanelView for Entity { self.update(cx, |this, cx| this.on_removed(window, cx)); } - fn dropdown_menu(&self, menu: PopupMenu, window: &Window, cx: &App) -> PopupMenu { - self.read(cx).dropdown_menu(menu, window, cx) + fn dropdown_menu(&self, menu: PopupMenu, window: &mut Window, cx: &mut App) -> PopupMenu { + self.update(cx, |this, cx| this.dropdown_menu(menu, window, cx)) } fn toolbar_buttons(&self, window: &mut Window, cx: &mut App) -> Option> { diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index a2dc7960..bc2b6149 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -1,13 +1,13 @@ use std::sync::Arc; use crate::{ + ActiveTheme, AxisExt as _, Placement, dock::PanelInfo, h_flex, resizable::{ - resizable_panel, ResizablePanelEvent, ResizablePanelGroup, ResizablePanelState, - ResizableState, PANEL_MIN_SIZE, + PANEL_MIN_SIZE, ResizablePanelEvent, ResizablePanelGroup, ResizablePanelState, + ResizableState, resizable_panel, }, - ActiveTheme, AxisExt as _, Placement, }; use super::{DockArea, Panel, PanelEvent, PanelState, PanelView, TabPanel}; @@ -32,10 +32,10 @@ impl Panel for StackPanel { "StackPanel" } - fn title(&self, _window: &gpui::Window, _cx: &gpui::App) -> gpui::AnyElement { + fn title(&mut self, _window: &gpui::Window, _cx: &mut Context) -> gpui::AnyElement { "StackPanel".into_any_element() } - fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut App) { + fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context) { for panel in &self.panels { panel.set_active(active, window, cx); } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 04f72f2f..e6766ad3 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -91,7 +91,7 @@ impl Panel for TabPanel { "TabPanel" } - fn title(&self, window: &Window, cx: &App) -> gpui::AnyElement { + fn title(&mut self, window: &Window, cx: &mut Context) -> gpui::AnyElement { self.active_panel(cx) .map(|panel| panel.title(window, cx)) .unwrap_or("Empty Tab".into_any_element()) @@ -121,7 +121,12 @@ impl Panel for TabPanel { self.visible_panels(cx).next().is_some() } - fn dropdown_menu(&self, menu: PopupMenu, window: &Window, cx: &App) -> PopupMenu { + fn dropdown_menu( + &mut self, + menu: PopupMenu, + window: &mut Window, + cx: &mut Context, + ) -> PopupMenu { if let Some(panel) = self.active_panel(cx) { panel.dropdown_menu(menu, window, cx) } else { @@ -129,7 +134,11 @@ impl Panel for TabPanel { } } - fn toolbar_buttons(&self, window: &mut Window, cx: &mut App) -> Option> { + fn toolbar_buttons( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> Option> { self.active_panel(cx) .and_then(|panel| panel.toolbar_buttons(window, cx)) } @@ -422,7 +431,7 @@ impl TabPanel { } fn render_toolbar( - &self, + &mut self, state: &TabState, window: &mut Window, cx: &mut Context, @@ -481,23 +490,24 @@ impl TabPanel { let zoomable = state.zoomable.map_or(false, |v| v.menu_visible()); let closable = state.closable; - move |this, window, cx| { - view.read(cx) - .dropdown_menu(this, window, cx) - .separator() - .menu_with_disabled( - if zoomed { - t!("Dock.Zoom Out") - } else { - t!("Dock.Zoom In") - }, - Box::new(ToggleZoom), - !zoomable, - ) - .when(closable, |this| { - this.separator() - .menu(t!("Dock.Close"), Box::new(ClosePanel)) - }) + move |menu, window, cx| { + view.update(cx, |this, cx| { + this.dropdown_menu(menu, window, cx) + .separator() + .menu_with_disabled( + if zoomed { + t!("Dock.Zoom Out") + } else { + t!("Dock.Zoom In") + }, + Box::new(ToggleZoom), + !zoomable, + ) + .when(closable, |this| { + this.separator() + .menu(t!("Dock.Close"), Box::new(ClosePanel)) + }) + }) } }) .anchor(Corner::TopRight), @@ -591,7 +601,7 @@ impl TabPanel { } fn render_title_bar( - &self, + &mut self, state: &TabState, window: &mut Window, cx: &mut Context, diff --git a/crates/ui/src/dock/tiles.rs b/crates/ui/src/dock/tiles.rs index eaf2ed83..7217ed66 100644 --- a/crates/ui/src/dock/tiles.rs +++ b/crates/ui/src/dock/tiles.rs @@ -148,7 +148,7 @@ impl Panel for Tiles { "Tiles" } - fn title(&self, _window: &Window, _cx: &App) -> AnyElement { + fn title(&mut self, _window: &Window, _cx: &mut Context) -> AnyElement { "Tiles".into_any_element() } diff --git a/docs/docs/components/list.md b/docs/docs/components/list.md index c704ccc5..14c93e39 100644 --- a/docs/docs/components/list.md +++ b/docs/docs/components/list.md @@ -34,10 +34,10 @@ impl ListDelegate for MyListDelegate { } fn render_item( - &self, + &mut self, ix: IndexPath, _window: &mut Window, - _cx: &mut App, + _cx: &mut Context>, ) -> Option { self.items.get(ix.row).map(|item| { ListItem::new(ix) @@ -93,10 +93,10 @@ impl ListDelegate for MyListDelegate { } fn render_section_header( - &self, + &mut self, section: usize, _window: &mut Window, - cx: &mut App, + cx: &mut Context>, ) -> Option { let title = match section { 0 => "Section 1", @@ -118,10 +118,10 @@ impl ListDelegate for MyListDelegate { } fn render_section_footer( - &self, + &mut self, section: usize, _window: &mut Window, - cx: &mut App, + cx: &mut Context>, ) -> Option { Some( div() @@ -139,10 +139,10 @@ impl ListDelegate for MyListDelegate { ```rust fn render_item( - &self, + &mut self, ix: IndexPath, _window: &mut Window, - cx: &mut App, + cx: &mut Context>, ) -> Option { self.items.get(ix.row).map(|item| { ListItem::new(ix) @@ -205,9 +205,9 @@ impl ListDelegate for MyListDelegate { } fn render_loading( - &self, + &mut self, _window: &mut Window, - _cx: &mut App, + _cx: &mut Context>, ) -> impl IntoElement { // Custom loading view v_flex() @@ -305,7 +305,7 @@ ListSeparatorItem::new() ```rust impl ListDelegate for MyListDelegate { - fn render_empty(&self, _window: &mut Window, cx: &mut App) -> impl IntoElement { + fn render_empty(&mut self, _window: &mut Window, cx: &mut Context>) -> impl IntoElement { v_flex() .size_full() .justify_center() @@ -380,7 +380,7 @@ struct FileInfo { impl ListDelegate for FileBrowserDelegate { type Item = ListItem; - fn render_item(&self, ix: IndexPath, window: &mut Window, cx: &mut App) -> Option { + fn render_item(&mut self, ix: IndexPath, window: &mut Window, cx: &mut Context>) -> Option { self.files.get(ix.row).map(|file| { let icon = if file.is_directory { IconName::Folder @@ -430,7 +430,7 @@ impl ListDelegate for ContactListDelegate { self.contacts_by_letter.len() } - fn render_section_header(&self, section: usize, _window: &mut Window, cx: &mut App) -> Option { + fn render_section_header(&mut self, section: usize, _window: &mut Window, cx: &mut Context>) -> Option { let letter = self.contacts_by_letter.keys().nth(section)?; Some( @@ -450,11 +450,3 @@ impl ListDelegate for ContactListDelegate { } } ``` - -## Performance - -- **Virtualization**: Only renders visible items for large datasets -- **Efficient Updates**: Optimized re-rendering with proper change detection -- **Memory Management**: Automatic cleanup of off-screen items -- **Smooth Scrolling**: Hardware-accelerated scrolling with momentum -- **Lazy Loading**: Built-in support for infinite scrolling and pagination diff --git a/docs/docs/components/table.md b/docs/docs/components/table.md index a0ddc50c..8142d948 100644 --- a/docs/docs/components/table.md +++ b/docs/docs/components/table.md @@ -66,7 +66,7 @@ impl TableDelegate for MyTableDelegate { &self.columns[col_ix] } - fn render_td(&self, row_ix: usize, col_ix: usize, _: &mut Window, _: &mut App) -> impl IntoElement { + fn render_td(&mut self, row_ix: usize, col_ix: usize, _: &mut Window, _: &mut Context>) -> impl IntoElement { let row = &self.data[row_ix]; let col = &self.columns[col_ix]; @@ -143,7 +143,7 @@ impl TableDelegate for LargeDataDelegate { } // Only visible rows are rendered - fn render_td(&self, row_ix: usize, col_ix: usize, _: &mut Window, _: &mut Context>) -> impl IntoElement { + fn render_td(&mut self, row_ix: usize, col_ix: usize, _: &mut Window, _: &mut Context>) -> impl IntoElement { // This is only called for visible rows // Efficiently render cell content let row = &self.data[row_ix]; @@ -191,26 +191,12 @@ impl TableDelegate for MyTableDelegate { } ``` -### Row Selection - -Handle row selection and interaction: +### ContextMenu ```rust impl TableDelegate for MyTableDelegate { - fn render_tr(&self, row_ix: usize, _: &mut Window, cx: &mut App) -> Stateful
{ - div() - .id(row_ix) - .on_click(move |ev, _, _| { - if ev.modifiers().secondary() { - println!("Right-clicked row {}", row_ix); - } else { - println!("Selected row {}", row_ix); - } - }) - } - // Context menu for right-click - fn context_menu(&self, row_ix: usize, menu: PopupMenu, _: &mut Window, _: &mut App) -> PopupMenu { + fn context_menu(&mut self, row_ix: usize, menu: PopupMenu, _: &mut Window, _: &mut Context>) -> PopupMenu { let row = &self.data[row_ix]; menu.menu(format!("Edit {}", row.name), Box::new(EditRowAction(row_ix))) .menu("Delete", Box::new(DeleteRowAction(row_ix))) @@ -218,32 +204,15 @@ impl TableDelegate for MyTableDelegate { .menu("Duplicate", Box::new(DuplicateRowAction(row_ix))) } } - -// Handle table events -cx.subscribe_in(&state, window, |view, table, event, _, cx| { - match event { - TableEvent::SelectRow(row_ix) => { - println!("Row {} selected", row_ix); - } - TableEvent::DoubleClickedRow(row_ix) => { - println!("Row {} double-clicked", row_ix); - // Open detail view or edit mode - } - TableEvent::SelectColumn(col_ix) => { - println!("Column {} selected", col_ix); - } - _ => {} - } -}).detach(); ``` -### Custom Cell Rendering +### Cell Rendering Create rich cell content with custom rendering: ```rust impl TableDelegate for MyTableDelegate { - fn render_td(&self, row_ix: usize, col_ix: usize, _: &mut Window, cx: &mut App) -> impl IntoElement { + fn render_td(&mut self, row_ix: usize, col_ix: usize, _: &mut Window, cx: &mut Context>) -> impl IntoElement { let row = &self.data[row_ix]; let col = &self.columns[col_ix]; @@ -413,7 +382,7 @@ struct StockData { } impl TableDelegate for StockTableDelegate { - fn render_td(&self, row_ix: usize, col_ix: usize, _: &mut Window, cx: &mut App) -> impl IntoElement { + fn render_td(&mut self, row_ix: usize, col_ix: usize, _: &mut Window, cx: &mut Context>) -> impl IntoElement { let stock = &self.stocks[row_ix]; let col = &self.columns[col_ix];