use gpui::{
AnyElement, App, ElementId, IntoElement, Length, ParentElement, RenderOnce, Styled, Window,
div, prelude::*, px,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ScrollDirection {
#[default]
Vertical,
Horizontal,
Both,
}
pub fn scroll_area(id: impl Into<ElementId>) -> ScrollArea {
ScrollArea::new(id)
}
#[derive(IntoElement)]
pub struct ScrollArea {
id: ElementId,
direction: ScrollDirection,
max_height: Option<Length>,
max_width: Option<Length>,
children: Vec<AnyElement>,
full_width: bool,
full_height: bool,
}
impl ScrollArea {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
direction: ScrollDirection::Vertical,
max_height: None,
max_width: None,
children: Vec::new(),
full_width: false,
full_height: false,
}
}
pub fn vertical(mut self) -> Self {
self.direction = ScrollDirection::Vertical;
self
}
pub fn horizontal(mut self) -> Self {
self.direction = ScrollDirection::Horizontal;
self
}
pub fn both(mut self) -> Self {
self.direction = ScrollDirection::Both;
self
}
pub fn direction(mut self, direction: ScrollDirection) -> Self {
self.direction = direction;
self
}
pub fn max_h(mut self, height: impl Into<Length>) -> Self {
self.max_height = Some(height.into());
self
}
pub fn max_h_px(self, height: f32) -> Self {
self.max_h(px(height))
}
pub fn max_w(mut self, width: impl Into<Length>) -> Self {
self.max_width = Some(width.into());
self
}
pub fn max_w_px(self, width: f32) -> Self {
self.max_w(px(width))
}
pub fn full_width(mut self, full_width: bool) -> Self {
self.full_width = full_width;
self
}
pub fn full_height(mut self, full_height: bool) -> Self {
self.full_height = full_height;
self
}
pub fn child(mut self, child: impl IntoElement) -> Self {
self.children.push(child.into_any_element());
self
}
pub fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self {
self.children
.extend(children.into_iter().map(|c| c.into_any_element()));
self
}
}
impl RenderOnce for ScrollArea {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let container = div()
.id(self.id)
.flex()
.when(self.direction == ScrollDirection::Vertical, |this| {
this.flex_col()
})
.when(self.full_width, |this| this.w_full())
.when(self.full_height, |this| this.h_full())
.when_some(self.max_height, |this, height| this.max_h(height))
.when_some(self.max_width, |this, width| this.max_w(width));
let container = match self.direction {
ScrollDirection::Vertical => container.overflow_y_scroll().overflow_x_hidden(),
ScrollDirection::Horizontal => container.overflow_x_scroll().overflow_y_hidden(),
ScrollDirection::Both => container.overflow_y_scroll().overflow_x_scroll(),
};
container
.on_scroll_wheel(|_, _, cx| {
cx.stop_propagation();
})
.children(self.children)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scroll_direction_default() {
assert_eq!(ScrollDirection::default(), ScrollDirection::Vertical);
}
#[test]
fn test_scroll_area_builder() {
let area = scroll_area("test-id").vertical();
assert_eq!(area.direction, ScrollDirection::Vertical);
let area = scroll_area("test-id").horizontal();
assert_eq!(area.direction, ScrollDirection::Horizontal);
let area = scroll_area("test-id").both();
assert_eq!(area.direction, ScrollDirection::Both);
}
}