Skip to main content

guise/
scrollarea.rs

1//! `ScrollArea` — a bounded, scrollable container.
2//!
3//! Desktop UIs scroll; most builders assume their content fits. Wrap an
4//! overflowing column (or row) in a `ScrollArea` with a `max_height` to clip and
5//! scroll it. Each instance needs a unique id so gpui can track its scroll
6//! offset.
7
8use gpui::prelude::*;
9use gpui::{div, px, AnyElement, App, ElementId, IntoElement, Window};
10
11/// A scrollable region. `ScrollArea::new("id").max_height(240.0)`.
12#[derive(IntoElement)]
13pub struct ScrollArea {
14    id: ElementId,
15    children: Vec<AnyElement>,
16    max_height: Option<f32>,
17    horizontal: bool,
18}
19
20impl ScrollArea {
21    pub fn new(id: impl Into<ElementId>) -> Self {
22        ScrollArea {
23            id: id.into(),
24            children: Vec::new(),
25            max_height: None,
26            horizontal: false,
27        }
28    }
29
30    /// Clip to this height (px) and scroll past it.
31    pub fn max_height(mut self, height: f32) -> Self {
32        self.max_height = Some(height);
33        self
34    }
35
36    /// Scroll horizontally instead of vertically.
37    pub fn horizontal(mut self, horizontal: bool) -> Self {
38        self.horizontal = horizontal;
39        self
40    }
41}
42
43impl ParentElement for ScrollArea {
44    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
45        self.children.extend(elements);
46    }
47}
48
49impl RenderOnce for ScrollArea {
50    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
51        let mut el = div().id(self.id).flex();
52        el = if self.horizontal {
53            el.flex_row().overflow_x_scroll()
54        } else {
55            el.flex_col().overflow_y_scroll()
56        };
57        if let Some(height) = self.max_height {
58            el = el.max_h(px(height));
59        }
60        el.children(self.children)
61    }
62}