use std::marker::PhantomData; use gpui3::AbsoluteLength; use crate::{prelude::*, theme}; use crate::{token, v_stack}; #[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum PanelAllowedSides { LeftOnly, RightOnly, BottomOnly, #[default] LeftAndRight, All, } impl PanelAllowedSides { /// Return a `HashSet` that contains the allowable `PanelSide`s. pub fn allowed_sides(&self) -> HashSet { match self { Self::LeftOnly => HashSet::from_iter([PanelSide::Left]), Self::RightOnly => HashSet::from_iter([PanelSide::Right]), Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]), Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]), Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]), } } } #[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum PanelSide { #[default] Left, Right, Bottom, } use std::collections::HashSet; #[derive(Element)] pub struct Panel { state_type: PhantomData, scroll_state: ScrollState, current_side: PanelSide, /// Defaults to PanelAllowedSides::LeftAndRight allowed_sides: PanelAllowedSides, initial_width: AbsoluteLength, width: Option, children: HackyChildren, payload: HackyChildrenPayload, } impl Panel { pub fn new( scroll_state: ScrollState, children: HackyChildren, payload: HackyChildrenPayload, ) -> Self { let token = token(); Self { state_type: PhantomData, scroll_state, current_side: PanelSide::default(), allowed_sides: PanelAllowedSides::default(), initial_width: token.default_panel_size, width: None, children, payload, } } pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self { self.initial_width = initial_width; self } pub fn width(mut self, width: AbsoluteLength) -> Self { self.width = Some(width); self } pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self { self.allowed_sides = allowed_sides; self } pub fn side(mut self, side: PanelSide) -> Self { let allowed_sides = self.allowed_sides.allowed_sides(); if allowed_sides.contains(&side) { self.current_side = side; } else { panic!( "The panel side {:?} was not added as allowed before it was set.", side ); } self } fn render(&mut self, cx: &mut ViewContext) -> impl Element { let token = token(); let theme = theme(cx); let panel_base; let current_width = self.width.unwrap_or(self.initial_width); match self.current_side { PanelSide::Left => { panel_base = v_stack() .flex_initial() .h_full() // .w(current_width) .w_64() .fill(theme.middle.base.default.background) .border_r() .border_color(theme.middle.base.default.border); } PanelSide::Right => { panel_base = v_stack() .flex_initial() .h_full() // .w(current_width) .w_64() .fill(theme.middle.base.default.background) .border_l() .border_color(theme.middle.base.default.border); } PanelSide::Bottom => { panel_base = v_stack() .flex_initial() .w_full() // .h(current_width) .h_64() .fill(theme.middle.base.default.background) .border_t() .border_color(theme.middle.base.default.border); } } panel_base.children_any((self.children)(cx, self.payload.as_ref())) } } #[cfg(feature = "stories")] pub use stories::*; #[cfg(feature = "stories")] mod stories { use crate::{Label, Story}; use super::*; #[derive(Element)] pub struct PanelStory { state_type: PhantomData, } impl PanelStory { pub fn new() -> Self { Self { state_type: PhantomData, } } fn render(&mut self, cx: &mut ViewContext) -> impl Element { Story::container(cx) .child(Story::title_for::<_, Panel>(cx)) .child(Story::label(cx, "Default")) .child(Panel::new( ScrollState::default(), |_, _| { vec![div() .overflow_y_scroll(ScrollState::default()) .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))) .into_any()] }, Box::new(()), )) } } }