# Element Implementation Examples Complete examples of implementing custom elements for various scenarios. ## Table of Contents 1. [Simple Text Element](#simple-text-element) 2. [Interactive Element with Selection](#interactive-element-with-selection) 3. [Complex Element with Child Management](#complex-element-with-child-management) ## Simple Text Element A basic text element with syntax highlighting support. ```rust pub struct SimpleText { id: ElementId, text: SharedString, highlights: Vec<(Range, HighlightStyle)>, } impl IntoElement for SimpleText { type Element = Self; fn into_element(self) -> Self::Element { self } } impl Element for SimpleText { type RequestLayoutState = StyledText; type PrepaintState = Hitbox; 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, Self::RequestLayoutState) { // Create styled text with highlights let mut runs = Vec::new(); let mut ix = 0; for (range, highlight) in &self.highlights { // Add unstyled text before highlight if ix < range.start { runs.push(window.text_style().to_run(range.start - ix)); } // Add highlighted text runs.push( window.text_style() .highlight(*highlight) .to_run(range.len()) ); ix = range.end; } // Add remaining unstyled text if ix < self.text.len() { runs.push(window.text_style().to_run(self.text.len() - ix)); } let styled_text = StyledText::new(self.text.clone()).with_runs(runs); let (layout_id, _) = styled_text.request_layout( global_id, inspector_id, window, cx ); (layout_id, styled_text) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, styled_text: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App ) -> Self::PrepaintState { // Prepaint the styled text styled_text.prepaint( global_id, inspector_id, bounds, &mut (), window, cx ); // Create hitbox for interaction let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); hitbox } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, styled_text: &mut Self::RequestLayoutState, hitbox: &mut Self::PrepaintState, window: &mut Window, cx: &mut App ) { // Paint the styled text styled_text.paint( global_id, inspector_id, bounds, &mut (), &mut (), window, cx ); // Set cursor style for text window.set_cursor_style(CursorStyle::IBeam, hitbox); } } ``` ## Interactive Element with Selection A text element that supports text selection via mouse interaction. ```rust #[derive(Clone)] pub struct Selection { pub start: usize, pub end: usize, } pub struct SelectableText { id: ElementId, text: SharedString, selectable: bool, selection: Option, } impl IntoElement for SelectableText { type Element = Self; fn into_element(self) -> Self::Element { self } } impl Element for SelectableText { type RequestLayoutState = TextLayout; type PrepaintState = Option; 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, Self::RequestLayoutState) { let styled_text = StyledText::new(self.text.clone()); let (layout_id, _) = styled_text.request_layout( global_id, inspector_id, window, cx ); // Extract text layout for selection painting let text_layout = styled_text.layout().clone(); (layout_id, text_layout) } fn prepaint( &mut self, _global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, _text_layout: &mut Self::RequestLayoutState, window: &mut Window, _cx: &mut App ) -> Self::PrepaintState { // Only create hitbox if selectable if self.selectable { Some(window.insert_hitbox(bounds, HitboxBehavior::Normal)) } else { None } } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, text_layout: &mut Self::RequestLayoutState, hitbox: &mut Self::PrepaintState, window: &mut Window, cx: &mut App ) { // Paint text let styled_text = StyledText::new(self.text.clone()); styled_text.paint( global_id, inspector_id, bounds, &mut (), &mut (), window, cx ); // Paint selection if any if let Some(selection) = &self.selection { Self::paint_selection(selection, text_layout, &bounds, window, cx); } // Handle mouse events for selection if let Some(hitbox) = hitbox { window.set_cursor_style(CursorStyle::IBeam, hitbox); // Mouse down to start selection window.on_mouse_event({ let bounds = bounds.clone(); move |event: &MouseDownEvent, phase, window, cx| { if bounds.contains(&event.position) && phase.bubble() { // Start selection at mouse position let char_index = Self::position_to_index( event.position, &bounds, text_layout ); self.selection = Some(Selection { start: char_index, end: char_index, }); cx.notify(); cx.stop_propagation(); } } }); // Mouse drag to extend selection window.on_mouse_event({ let bounds = bounds.clone(); move |event: &MouseMoveEvent, phase, window, cx| { if let Some(selection) = &mut self.selection { if phase.bubble() { let char_index = Self::position_to_index( event.position, &bounds, text_layout ); selection.end = char_index; cx.notify(); } } } }); } } } impl SelectableText { fn paint_selection( selection: &Selection, text_layout: &TextLayout, bounds: &Bounds, window: &mut Window, cx: &mut App ) { // Calculate selection bounds from text layout let selection_rects = text_layout.rects_for_range( selection.start..selection.end ); // Paint selection background for rect in selection_rects { window.paint_quad(paint_quad( Bounds::new( point(bounds.left() + rect.origin.x, bounds.top() + rect.origin.y), rect.size ), Corners::default(), cx.theme().selection_background, )); } } fn position_to_index( position: Point, bounds: &Bounds, text_layout: &TextLayout ) -> usize { // Convert screen position to character index let relative_pos = point( position.x - bounds.left(), position.y - bounds.top() ); text_layout.index_for_position(relative_pos) } } ``` ## Complex Element with Child Management A container element that manages multiple children with scrolling support. ```rust pub struct ComplexElement { id: ElementId, children: Vec>>, scrollable: bool, scroll_offset: Point, } struct ComplexLayoutState { child_layouts: Vec, total_height: Pixels, } struct ComplexPaintState { child_bounds: Vec>, hitbox: Hitbox, } impl IntoElement for ComplexElement { type Element = Self; fn into_element(self) -> Self::Element { self } } impl Element for ComplexElement { type RequestLayoutState = ComplexLayoutState; type PrepaintState = ComplexPaintState; 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, Self::RequestLayoutState) { let mut child_layouts = Vec::new(); let mut total_height = px(0.); // Request layout for all children for child in &mut self.children { let (child_layout_id, _) = child.request_layout( global_id, inspector_id, window, cx ); child_layouts.push(child_layout_id); // Get child size from layout let child_size = window.layout_bounds(child_layout_id).size(); total_height += child_size.height; } // Create container layout let layout_id = window.request_layout( Style { flex_direction: FlexDirection::Column, gap: px(8.), size: Size { width: relative(1.0), height: if self.scrollable { // Fixed height for scrollable px(400.) } else { // Auto height for non-scrollable total_height }, }, ..default() }, child_layouts.clone(), cx ); (layout_id, ComplexLayoutState { child_layouts, total_height, }) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, layout_state: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App ) -> Self::PrepaintState { let mut child_bounds = Vec::new(); let mut y_offset = self.scroll_offset.y; // Calculate child bounds and prepaint children for (child, layout_id) in self.children.iter_mut() .zip(&layout_state.child_layouts) { let child_size = window.layout_bounds(*layout_id).size(); let child_bound = Bounds::new( point(bounds.left(), bounds.top() + y_offset), child_size ); // Only prepaint visible children if self.is_visible(&child_bound, &bounds) { child.prepaint( global_id, inspector_id, child_bound, &mut (), window, cx ); } child_bounds.push(child_bound); y_offset += child_size.height + px(8.); // gap } let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); ComplexPaintState { child_bounds, hitbox, } } fn paint( &mut self, global_id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds, layout_state: &mut Self::RequestLayoutState, paint_state: &mut Self::PrepaintState, window: &mut Window, cx: &mut App ) { // Paint background window.paint_quad(paint_quad( bounds, Corners::all(px(4.)), cx.theme().background, )); // Paint visible children only for (i, child) in self.children.iter_mut().enumerate() { let child_bounds = paint_state.child_bounds[i]; if self.is_visible(&child_bounds, &bounds) { child.paint( global_id, inspector_id, child_bounds, &mut (), &mut (), window, cx ); } } // Paint scrollbar if scrollable if self.scrollable { self.paint_scrollbar(bounds, layout_state, window, cx); } // Handle scroll events if self.scrollable { window.on_mouse_event({ let hitbox = paint_state.hitbox.clone(); let total_height = layout_state.total_height; let visible_height = bounds.size.height; move |event: &ScrollWheelEvent, phase, window, cx| { if hitbox.is_hovered(window) && phase.bubble() { // Update scroll offset self.scroll_offset.y -= event.delta.y; // Clamp scroll offset let max_scroll = (total_height - visible_height).max(px(0.)); self.scroll_offset.y = self.scroll_offset.y .max(px(0.)) .min(max_scroll); cx.notify(); cx.stop_propagation(); } } }); } } } impl ComplexElement { fn is_visible(&self, child_bounds: &Bounds, container_bounds: &Bounds) -> bool { // Check if child is within visible area child_bounds.bottom() >= container_bounds.top() && child_bounds.top() <= container_bounds.bottom() } fn paint_scrollbar( &self, bounds: Bounds, layout_state: &ComplexLayoutState, window: &mut Window, cx: &mut App ) { let scrollbar_width = px(8.); let visible_height = bounds.size.height; let total_height = layout_state.total_height; if total_height <= visible_height { return; // No need for scrollbar } // Calculate scrollbar position and size let scroll_ratio = self.scroll_offset.y / (total_height - visible_height); let thumb_height = (visible_height / total_height) * visible_height; let thumb_y = scroll_ratio * (visible_height - thumb_height); // Paint scrollbar track let track_bounds = Bounds::new( point(bounds.right() - scrollbar_width, bounds.top()), size(scrollbar_width, visible_height) ); window.paint_quad(paint_quad( track_bounds, Corners::default(), cx.theme().scrollbar_track, )); // Paint scrollbar thumb let thumb_bounds = Bounds::new( point(bounds.right() - scrollbar_width, bounds.top() + thumb_y), size(scrollbar_width, thumb_height) ); window.paint_quad(paint_quad( thumb_bounds, Corners::all(px(4.)), cx.theme().scrollbar_thumb, )); } } ``` ## Usage Examples ### Using SimpleText ```rust fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { div() .child(SimpleText { id: ElementId::Name("code-text".into()), text: "fn main() { println!(\"Hello\"); }".into(), highlights: vec![ (0..2, HighlightStyle::keyword()), (3..7, HighlightStyle::function()), ], }) } ``` ### Using SelectableText ```rust fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { div() .child(SelectableText { id: ElementId::Name("selectable-text".into()), text: "Select this text with your mouse".into(), selectable: true, selection: self.current_selection.clone(), }) } ``` ### Using ComplexElement ```rust fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let children: Vec>> = self.items .iter() .map(|item| Box::new(div().child(item.name.clone())) as Box<_>) .collect(); div() .child(ComplexElement { id: ElementId::Name("scrollable-list".into()), children, scrollable: true, scroll_offset: self.scroll_offset, }) } ```