# Advanced Element Patterns Advanced techniques and patterns for implementing sophisticated GPUI elements. ## Custom Layout Algorithms Implementing custom layout algorithms not supported by GPUI's built-in layouts. ### Masonry Layout (Pinterest-Style) ```rust pub struct MasonryLayout { id: ElementId, columns: usize, gap: Pixels, children: Vec, } struct MasonryLayoutState { column_layouts: Vec>, column_heights: Vec, } struct MasonryPaintState { child_bounds: Vec>, } impl Element for MasonryLayout { type RequestLayoutState = MasonryLayoutState; type PrepaintState = MasonryPaintState; fn id(&self) -> Option { Some(self.id.clone()) } fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None } fn request_layout( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App ) -> (LayoutId, MasonryLayoutState) { // Initialize columns let mut columns: Vec> = vec![Vec::new(); self.columns]; let mut column_heights = vec![px(0.); self.columns]; // Distribute children across columns for child in &mut self.children { let (child_layout_id, _) = child.request_layout( global_id, inspector_id, window, cx ); let child_size = window.layout_bounds(child_layout_id).size; // Find shortest column let min_column_idx = column_heights .iter() .enumerate() .min_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .unwrap() .0; // Add child to shortest column columns[min_column_idx].push(child_layout_id); column_heights[min_column_idx] += child_size.height + self.gap; } // Calculate total layout size let column_width = px(200.); // Fixed column width let total_width = column_width * self.columns as f32 + self.gap * (self.columns - 1) as f32; let total_height = column_heights.iter() .max_by(|a, b| a.partial_cmp(b).unwrap()) .copied() .unwrap_or(px(0.)); let layout_id = window.request_layout( Style { size: size(total_width, total_height), ..default() }, columns.iter().flatten().copied().collect(), cx ); (layout_id, MasonryLayoutState { column_layouts: columns, column_heights, }) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, layout_state: &mut MasonryLayoutState, window: &mut Window, cx: &mut App ) -> MasonryPaintState { let column_width = px(200.); let mut child_bounds = Vec::new(); // Position children in columns for (col_idx, column) in layout_state.column_layouts.iter().enumerate() { let x_offset = bounds.left() + (column_width + self.gap) * col_idx as f32; let mut y_offset = bounds.top(); for (child_idx, layout_id) in column.iter().enumerate() { let child_size = window.layout_bounds(*layout_id).size; let child_bound = Bounds::new( point(x_offset, y_offset), size(column_width, child_size.height) ); self.children[child_idx].prepaint( global_id, inspector_id, child_bound, window, cx ); child_bounds.push(child_bound); y_offset += child_size.height + self.gap; } } MasonryPaintState { child_bounds } } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, _bounds: Bounds, _layout_state: &mut MasonryLayoutState, paint_state: &mut MasonryPaintState, window: &mut Window, cx: &mut App ) { for (child, bounds) in self.children.iter_mut().zip(&paint_state.child_bounds) { child.paint(global_id, inspector_id, *bounds, window, cx); } } } ``` ### Circular Layout ```rust pub struct CircularLayout { id: ElementId, radius: Pixels, children: Vec, } impl Element for CircularLayout { type RequestLayoutState = Vec; type PrepaintState = Vec>; fn request_layout( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App ) -> (LayoutId, Vec) { let child_layouts: Vec<_> = self.children .iter_mut() .map(|child| child.request_layout(global_id, inspector_id, window, cx).0) .collect(); let diameter = self.radius * 2.; let layout_id = window.request_layout( Style { size: size(diameter, diameter), ..default() }, child_layouts.clone(), cx ); (layout_id, child_layouts) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, layout_ids: &mut Vec, window: &mut Window, cx: &mut App ) -> Vec> { let center = bounds.center(); let angle_step = 2.0 * std::f32::consts::PI / self.children.len() as f32; let mut child_bounds = Vec::new(); for (i, (child, layout_id)) in self.children.iter_mut() .zip(layout_ids.iter()) .enumerate() { let angle = angle_step * i as f32; let child_size = window.layout_bounds(*layout_id).size; // Position child on circle let x = center.x + self.radius * angle.cos() - child_size.width / 2.; let y = center.y + self.radius * angle.sin() - child_size.height / 2.; let child_bound = Bounds::new(point(x, y), child_size); child.prepaint(global_id, inspector_id, child_bound, window, cx); child_bounds.push(child_bound); } child_bounds } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, _bounds: Bounds, _layout_ids: &mut Vec, child_bounds: &mut Vec>, window: &mut Window, cx: &mut App ) { for (child, bounds) in self.children.iter_mut().zip(child_bounds) { child.paint(global_id, inspector_id, *bounds, window, cx); } } } ``` ## Element Composition with Traits Create reusable behaviors via traits for element composition. ### Hoverable Trait ```rust pub trait Hoverable: Element { fn on_hover(&mut self, f: F) -> &mut Self where F: Fn(&mut Window, &mut App) + 'static; fn on_hover_end(&mut self, f: F) -> &mut Self where F: Fn(&mut Window, &mut App) + 'static; } // Implementation for custom element pub struct HoverableElement { id: ElementId, content: AnyElement, hover_handlers: Vec>, hover_end_handlers: Vec>, was_hovered: bool, } impl Hoverable for HoverableElement { fn on_hover(&mut self, f: F) -> &mut Self where F: Fn(&mut Window, &mut App) + 'static { self.hover_handlers.push(Box::new(f)); self } fn on_hover_end(&mut self, f: F) -> &mut Self where F: Fn(&mut Window, &mut App) + 'static { self.hover_end_handlers.push(Box::new(f)); self } } impl Element for HoverableElement { type RequestLayoutState = LayoutId; type PrepaintState = Hitbox; fn paint( &mut self, _global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, _layout: &mut LayoutId, hitbox: &mut Hitbox, window: &mut Window, cx: &mut App ) { let is_hovered = hitbox.is_hovered(window); // Trigger hover events if is_hovered && !self.was_hovered { for handler in &self.hover_handlers { handler(window, cx); } } else if !is_hovered && self.was_hovered { for handler in &self.hover_end_handlers { handler(window, cx); } } self.was_hovered = is_hovered; // Paint content self.content.paint(bounds, window, cx); } // ... other methods } ``` ### Clickable Trait ```rust pub trait Clickable: Element { fn on_click(&mut self, f: F) -> &mut Self where F: Fn(&MouseUpEvent, &mut Window, &mut App) + 'static; fn on_double_click(&mut self, f: F) -> &mut Self where F: Fn(&MouseUpEvent, &mut Window, &mut App) + 'static; } pub struct ClickableElement { id: ElementId, content: AnyElement, click_handlers: Vec>, double_click_handlers: Vec>, last_click_time: Option, } impl Clickable for ClickableElement { fn on_click(&mut self, f: F) -> &mut Self where F: Fn(&MouseUpEvent, &mut Window, &mut App) + 'static { self.click_handlers.push(Box::new(f)); self } fn on_double_click(&mut self, f: F) -> &mut Self where F: Fn(&MouseUpEvent, &mut Window, &mut App) + 'static { self.double_click_handlers.push(Box::new(f)); self } } ``` ## Async Element Updates Elements that update based on async operations. ```rust pub struct AsyncElement { id: ElementId, state: Entity, loading: bool, data: Option, } pub struct AsyncState { loading: bool, data: Option, } impl Element for AsyncElement { type RequestLayoutState = (); type PrepaintState = Hitbox; fn paint( &mut self, _global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, _layout: &mut (), hitbox: &mut Hitbox, window: &mut Window, cx: &mut App ) { // Display loading or data if self.loading { // Paint loading indicator self.paint_loading(bounds, window, cx); } else if let Some(data) = &self.data { // Paint data self.paint_data(data, bounds, window, cx); } // Trigger async update on click window.on_mouse_event({ let state = self.state.clone(); let hitbox = hitbox.clone(); move |event: &MouseUpEvent, phase, window, cx| { if hitbox.is_hovered(window) && phase.bubble() { // Spawn async task cx.spawn({ let state = state.clone(); async move { // Perform async operation let result = fetch_data_async().await; // Update state on completion state.update(cx, |state, cx| { state.loading = false; state.data = Some(result); cx.notify(); }); } }).detach(); // Set loading state immediately state.update(cx, |state, cx| { state.loading = true; cx.notify(); }); cx.stop_propagation(); } } }); } // ... other methods } async fn fetch_data_async() -> String { // Simulate async operation tokio::time::sleep(Duration::from_secs(1)).await; "Data loaded!".to_string() } ``` ## Element Memoization Optimize performance by memoizing expensive element computations. ```rust pub struct MemoizedElement { id: ElementId, value: T, render_fn: Box AnyElement>, cached_element: Option, last_value: Option, } impl MemoizedElement { pub fn new(id: ElementId, value: T, render_fn: F) -> Self where F: Fn(&T) -> AnyElement + 'static, { Self { id, value, render_fn: Box::new(render_fn), cached_element: None, last_value: None, } } } impl Element for MemoizedElement { type RequestLayoutState = LayoutId; type PrepaintState = (); fn id(&self) -> Option { Some(self.id.clone()) } fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None } fn request_layout( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App ) -> (LayoutId, LayoutId) { // Check if value changed if self.last_value.as_ref() != Some(&self.value) || self.cached_element.is_none() { // Recompute element self.cached_element = Some((self.render_fn)(&self.value)); self.last_value = Some(self.value.clone()); } // Request layout for cached element let (layout_id, _) = self.cached_element .as_mut() .unwrap() .request_layout(global_id, inspector_id, window, cx); (layout_id, layout_id) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, _layout_id: &mut LayoutId, window: &mut Window, cx: &mut App ) -> () { self.cached_element .as_mut() .unwrap() .prepaint(global_id, inspector_id, bounds, window, cx); } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, _layout_id: &mut LayoutId, _: &mut (), window: &mut Window, cx: &mut App ) { self.cached_element .as_mut() .unwrap() .paint(global_id, inspector_id, bounds, window, cx); } } // Usage fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { MemoizedElement::new( ElementId::Name("memoized".into()), self.expensive_value.clone(), |value| { // Expensive rendering function only called when value changes div().child(format!("Computed: {}", value)) } ) } ``` ## Virtual List Pattern Efficiently render large lists by only rendering visible items. ```rust pub struct VirtualList { id: ElementId, item_count: usize, item_height: Pixels, viewport_height: Pixels, scroll_offset: Pixels, render_item: Box AnyElement>, } struct VirtualListState { visible_range: Range, visible_item_layouts: Vec, } impl Element for VirtualList { type RequestLayoutState = VirtualListState; type PrepaintState = Hitbox; fn request_layout( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App ) -> (LayoutId, VirtualListState) { // Calculate visible range let start_idx = (self.scroll_offset / self.item_height).floor() as usize; let end_idx = ((self.scroll_offset + self.viewport_height) / self.item_height) .ceil() as usize; let visible_range = start_idx..end_idx.min(self.item_count); // Request layout only for visible items let visible_item_layouts: Vec<_> = visible_range.clone() .map(|i| { let mut item = (self.render_item)(i); item.request_layout(global_id, inspector_id, window, cx).0 }) .collect(); let total_height = self.item_height * self.item_count as f32; let layout_id = window.request_layout( Style { size: size(relative(1.0), self.viewport_height), overflow: Overflow::Hidden, ..default() }, visible_item_layouts.clone(), cx ); (layout_id, VirtualListState { visible_range, visible_item_layouts, }) } fn prepaint( &mut self, _global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, state: &mut VirtualListState, window: &mut Window, _cx: &mut App ) -> Hitbox { // Prepaint visible items at correct positions for (i, layout_id) in state.visible_item_layouts.iter().enumerate() { let item_idx = state.visible_range.start + i; let y = item_idx as f32 * self.item_height - self.scroll_offset; let item_bounds = Bounds::new( point(bounds.left(), bounds.top() + y), size(bounds.width(), self.item_height) ); // Prepaint if visible if item_bounds.intersects(&bounds) { // Prepaint item... } } window.insert_hitbox(bounds, HitboxBehavior::Normal) } fn paint( &mut self, _global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, state: &mut VirtualListState, hitbox: &mut Hitbox, window: &mut Window, cx: &mut App ) { // Paint visible items for (i, _layout_id) in state.visible_item_layouts.iter().enumerate() { let item_idx = state.visible_range.start + i; let y = item_idx as f32 * self.item_height - self.scroll_offset; let item_bounds = Bounds::new( point(bounds.left(), bounds.top() + y), size(bounds.width(), self.item_height) ); if item_bounds.intersects(&bounds) { let mut item = (self.render_item)(item_idx); item.paint(item_bounds, window, cx); } } // Handle scroll window.on_mouse_event({ let hitbox = hitbox.clone(); let total_height = self.item_height * self.item_count as f32; move |event: &ScrollWheelEvent, phase, window, cx| { if hitbox.is_hovered(window) && phase.bubble() { self.scroll_offset -= event.delta.y; self.scroll_offset = self.scroll_offset .max(px(0.)) .min(total_height - self.viewport_height); cx.notify(); cx.stop_propagation(); } } }); } } // Usage: Efficiently render 10,000 items let virtual_list = VirtualList { id: ElementId::Name("large-list".into()), item_count: 10_000, item_height: px(40.), viewport_height: px(400.), scroll_offset: px(0.), render_item: Box::new(|index| { div().child(format!("Item {}", index)) }), }; ``` These advanced patterns enable sophisticated element implementations while maintaining performance and code quality.