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 crate::devtools::Probed;
9use gpui::prelude::*;
10use gpui::{div, px, AnyElement, App, ElementId, IntoElement, Window};
11
12/// A scrollable region. `ScrollArea::new("id").max_height(240.0)`.
13#[derive(IntoElement)]
14pub struct ScrollArea {
15    id: ElementId,
16    children: Vec<AnyElement>,
17    max_height: Option<f32>,
18    horizontal: bool,
19}
20
21impl ScrollArea {
22    pub fn new(id: impl Into<ElementId>) -> Self {
23        ScrollArea {
24            id: id.into(),
25            children: Vec::new(),
26            max_height: None,
27            horizontal: false,
28        }
29    }
30
31    /// Clip to this height (px) and scroll past it.
32    pub fn max_height(mut self, height: f32) -> Self {
33        self.max_height = Some(height);
34        self
35    }
36
37    /// Scroll horizontally instead of vertically.
38    pub fn horizontal(mut self, horizontal: bool) -> Self {
39        self.horizontal = horizontal;
40        self
41    }
42}
43
44impl ParentElement for ScrollArea {
45    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
46        self.children.extend(elements);
47    }
48}
49
50impl RenderOnce for ScrollArea {
51    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
52        let mut el = div().id(self.id).flex();
53        el = if self.horizontal {
54            el.flex_row().overflow_x_scroll()
55        } else {
56            el.flex_col().overflow_y_scroll()
57        };
58        if let Some(height) = self.max_height {
59            el = el.max_h(px(height));
60        }
61        el.children(self.children).probe("ScrollArea")
62    }
63}