# Common Element Patterns

Reusable patterns for implementing common element types in GPUI.

## Text Rendering Elements

Elements that display and manipulate text content.

### Pattern Characteristics

- Use `StyledText` for text layout and rendering
- Handle text selection in `paint` phase with hitbox interaction
- Create hitboxes for text interaction in `prepaint`
- Support text highlighting and custom styling via runs

### Implementation Template

```rust
pub struct TextElement {
    id: ElementId,
    text: SharedString,
    style: TextStyle,
}

impl Element for TextElement {
    type RequestLayoutState = StyledText;
    type PrepaintState = Hitbox;

    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, StyledText)
    {
        let styled_text = StyledText::new(self.text.clone())
            .with_style(self.style);
        let (layout_id, _) = styled_text.request_layout(None, None, window, cx);
        (layout_id, styled_text)
    }

    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, styled_text: &mut StyledText,
                window: &mut Window, cx: &mut App) -> Hitbox
    {
        styled_text.prepaint(None, None, bounds, &mut (), window, cx);
        window.insert_hitbox(bounds, HitboxBehavior::Normal)
    }

    fn paint(&mut self, .., bounds: Bounds<Pixels>, styled_text: &mut StyledText,
             hitbox: &mut Hitbox, window: &mut Window, cx: &mut App)
    {
        styled_text.paint(None, None, bounds, &mut (), &mut (), window, cx);
        window.set_cursor_style(CursorStyle::IBeam, hitbox);
    }
}
```

### Use Cases

- Code editors with syntax highlighting
- Rich text displays
- Labels with custom formatting
- Selectable text areas

## Container Elements

Elements that manage and layout child elements.

### Pattern Characteristics

- Manage child element layouts and positions
- Handle scrolling and clipping when needed
- Implement flex/grid-like layouts
- Coordinate child interactions and event delegation

### Implementation Template

```rust
pub struct ContainerElement {
    id: ElementId,
    children: Vec<AnyElement>,
    direction: FlexDirection,
    gap: Pixels,
}

impl Element for ContainerElement {
    type RequestLayoutState = Vec<LayoutId>;
    type PrepaintState = Vec<Bounds<Pixels>>;

    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, Vec<LayoutId>)
    {
        let child_layout_ids: Vec<_> = self.children
            .iter_mut()
            .map(|child| child.request_layout(window, cx).0)
            .collect();

        let layout_id = window.request_layout(
            Style {
                flex_direction: self.direction,
                gap: self.gap,
                ..default()
            },
            child_layout_ids.clone(),
            cx
        );

        (layout_id, child_layout_ids)
    }

    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, layout_ids: &mut Vec<LayoutId>,
                window: &mut Window, cx: &mut App) -> Vec<Bounds<Pixels>>
    {
        let mut child_bounds = Vec::new();

        for (child, layout_id) in self.children.iter_mut().zip(layout_ids.iter()) {
            let child_bound = window.layout_bounds(*layout_id);
            child.prepaint(child_bound, window, cx);
            child_bounds.push(child_bound);
        }

        child_bounds
    }

    fn paint(&mut self, .., child_bounds: &mut Vec<Bounds<Pixels>>,
             window: &mut Window, cx: &mut App)
    {
        for (child, bounds) in self.children.iter_mut().zip(child_bounds.iter()) {
            child.paint(*bounds, window, cx);
        }
    }
}
```

### Use Cases

- Panels and split views
- List containers
- Grid layouts
- Tab containers

## Interactive Elements

Elements that respond to user input (mouse, keyboard, touch).

### Pattern Characteristics

- Create appropriate hitboxes for interaction areas
- Handle mouse/keyboard/touch events properly
- Manage focus and cursor styles
- Support hover, active, and disabled states

### Implementation Template

```rust
pub struct InteractiveElement {
    id: ElementId,
    content: AnyElement,
    on_click: Option<Box<dyn Fn(&MouseUpEvent, &mut Window, &mut App)>>,
    hover_style: Option<Style>,
}

impl Element for InteractiveElement {
    type RequestLayoutState = LayoutId;
    type PrepaintState = (Hitbox, bool); // hitbox and is_hovered

    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, LayoutId)
    {
        let (content_layout, _) = self.content.request_layout(window, cx);
        (content_layout, content_layout)
    }

    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, content_layout: &mut LayoutId,
                window: &mut Window, cx: &mut App) -> (Hitbox, bool)
    {
        let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
        let is_hovered = hitbox.is_hovered(window);

        self.content.prepaint(bounds, window, cx);

        (hitbox, is_hovered)
    }

    fn paint(&mut self, .., bounds: Bounds<Pixels>, content_layout: &mut LayoutId,
             prepaint: &mut (Hitbox, bool), window: &mut Window, cx: &mut App)
    {
        let (hitbox, is_hovered) = prepaint;

        // Paint hover background if hovered
        if *is_hovered {
            if let Some(hover_style) = &self.hover_style {
                window.paint_quad(paint_quad(
                    bounds,
                    Corners::all(px(4.)),
                    hover_style.background_color.unwrap_or(cx.theme().hover),
                ));
            }
        }

        // Paint content
        self.content.paint(bounds, window, cx);

        // Handle click
        if let Some(on_click) = self.on_click.as_ref() {
            window.on_mouse_event({
                let on_click = on_click.clone();
                let hitbox = hitbox.clone();
                move |event: &MouseUpEvent, phase, window, cx| {
                    if hitbox.is_hovered(window) && phase.bubble() {
                        on_click(event, window, cx);
                        cx.stop_propagation();
                    }
                }
            });
        }

        // Set cursor style
        window.set_cursor_style(CursorStyle::PointingHand, hitbox);
    }
}
```

### Use Cases

- Buttons
- Links
- Clickable cards
- Drag handles
- Menu items

## Composite Elements

Elements that combine multiple child elements with complex coordination.

### Pattern Characteristics

- Combine multiple child elements with different types
- Manage complex state across children
- Coordinate animations and transitions
- Handle focus delegation between children

### Implementation Template

```rust
pub struct CompositeElement {
    id: ElementId,
    header: AnyElement,
    content: AnyElement,
    footer: Option<AnyElement>,
}

struct CompositeLayoutState {
    header_layout: LayoutId,
    content_layout: LayoutId,
    footer_layout: Option<LayoutId>,
}

struct CompositePaintState {
    header_bounds: Bounds<Pixels>,
    content_bounds: Bounds<Pixels>,
    footer_bounds: Option<Bounds<Pixels>>,
}

impl Element for CompositeElement {
    type RequestLayoutState = CompositeLayoutState;
    type PrepaintState = CompositePaintState;

    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, CompositeLayoutState)
    {
        let (header_layout, _) = self.header.request_layout(window, cx);
        let (content_layout, _) = self.content.request_layout(window, cx);
        let footer_layout = self.footer.as_mut()
            .map(|f| f.request_layout(window, cx).0);

        let mut children = vec![header_layout, content_layout];
        if let Some(footer) = footer_layout {
            children.push(footer);
        }

        let layout_id = window.request_layout(
            Style {
                flex_direction: FlexDirection::Column,
                size: Size {
                    width: relative(1.0),
                    height: auto(),
                },
                ..default()
            },
            children,
            cx
        );

        (layout_id, CompositeLayoutState {
            header_layout,
            content_layout,
            footer_layout,
        })
    }

    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, layout: &mut CompositeLayoutState,
                window: &mut Window, cx: &mut App) -> CompositePaintState
    {
        let header_bounds = window.layout_bounds(layout.header_layout);
        let content_bounds = window.layout_bounds(layout.content_layout);
        let footer_bounds = layout.footer_layout
            .map(|id| window.layout_bounds(id));

        self.header.prepaint(header_bounds, window, cx);
        self.content.prepaint(content_bounds, window, cx);
        if let (Some(footer), Some(bounds)) = (&mut self.footer, footer_bounds) {
            footer.prepaint(bounds, window, cx);
        }

        CompositePaintState {
            header_bounds,
            content_bounds,
            footer_bounds,
        }
    }

    fn paint(&mut self, .., paint_state: &mut CompositePaintState,
             window: &mut Window, cx: &mut App)
    {
        self.header.paint(paint_state.header_bounds, window, cx);
        self.content.paint(paint_state.content_bounds, window, cx);
        if let (Some(footer), Some(bounds)) = (&mut self.footer, paint_state.footer_bounds) {
            footer.paint(bounds, window, cx);
        }
    }
}
```

### Use Cases

- Dialog boxes (header + content + footer)
- Cards with multiple sections
- Form layouts
- Panels with toolbars

## Scrollable Elements

Elements with scrollable content areas.

### Pattern Characteristics

- Manage scroll state (offset, velocity)
- Handle scroll events (wheel, drag, touch)
- Paint scrollbars (track and thumb)
- Clip content to visible area

### Implementation Template

```rust
pub struct ScrollableElement {
    id: ElementId,
    content: AnyElement,
    scroll_offset: Point<Pixels>,
    content_size: Size<Pixels>,
}

struct ScrollPaintState {
    hitbox: Hitbox,
    visible_bounds: Bounds<Pixels>,
}

impl Element for ScrollableElement {
    type RequestLayoutState = (LayoutId, Size<Pixels>);
    type PrepaintState = ScrollPaintState;

    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, (LayoutId, Size<Pixels>))
    {
        let (content_layout, _) = self.content.request_layout(window, cx);
        let content_size = window.layout_bounds(content_layout).size;

        let layout_id = window.request_layout(
            Style {
                size: Size {
                    width: relative(1.0),
                    height: px(400.), // Fixed viewport height
                },
                overflow: Overflow::Hidden,
                ..default()
            },
            vec![content_layout],
            cx
        );

        (layout_id, (content_layout, content_size))
    }

    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, layout: &mut (LayoutId, Size<Pixels>),
                window: &mut Window, cx: &mut App) -> ScrollPaintState
    {
        let (content_layout, content_size) = layout;

        // Calculate content bounds with scroll offset
        let content_bounds = Bounds::new(
            point(bounds.left(), bounds.top() - self.scroll_offset.y),
            *content_size
        );

        self.content.prepaint(content_bounds, window, cx);

        let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);

        ScrollPaintState {
            hitbox,
            visible_bounds: bounds,
        }
    }

    fn paint(&mut self, .., layout: &mut (LayoutId, Size<Pixels>),
             paint_state: &mut ScrollPaintState, window: &mut Window, cx: &mut App)
    {
        let (_, content_size) = layout;

        // Paint content
        self.content.paint(paint_state.visible_bounds, window, cx);

        // Paint scrollbar
        self.paint_scrollbar(paint_state.visible_bounds, *content_size, window, cx);

        // Handle scroll events
        window.on_mouse_event({
            let hitbox = paint_state.hitbox.clone();
            let content_height = content_size.height;
            let visible_height = paint_state.visible_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 to valid range
                    let max_scroll = (content_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 ScrollableElement {
    fn paint_scrollbar(
        &self,
        bounds: Bounds<Pixels>,
        content_size: Size<Pixels>,
        window: &mut Window,
        cx: &mut App
    ) {
        let visible_height = bounds.size.height;
        let content_height = content_size.height;

        if content_height <= visible_height {
            return; // No scrollbar needed
        }

        let scrollbar_width = px(8.);

        // Calculate thumb position and size
        let scroll_ratio = self.scroll_offset.y / (content_height - visible_height);
        let thumb_height = (visible_height / content_height) * visible_height;
        let thumb_y = scroll_ratio * (visible_height - thumb_height);

        // Paint track
        window.paint_quad(paint_quad(
            Bounds::new(
                point(bounds.right() - scrollbar_width, bounds.top()),
                size(scrollbar_width, visible_height)
            ),
            Corners::default(),
            cx.theme().scrollbar_track,
        ));

        // Paint thumb
        window.paint_quad(paint_quad(
            Bounds::new(
                point(bounds.right() - scrollbar_width, bounds.top() + thumb_y),
                size(scrollbar_width, thumb_height)
            ),
            Corners::all(px(4.)),
            cx.theme().scrollbar_thumb,
        ));
    }
}
```

### Use Cases

- Scrollable lists
- Code editors with large files
- Long-form text content
- Image galleries

## Pattern Selection Guide

| Need | Pattern | Complexity |
|------|---------|------------|
| Display styled text | Text Rendering | Low |
| Layout multiple children | Container | Low-Medium |
| Handle clicks/hovers | Interactive | Medium |
| Complex multi-part UI | Composite | Medium-High |
| Large content with scrolling | Scrollable | High |

Choose the simplest pattern that meets your requirements, then extend as needed.
