use gpui::prelude::*;
use gpui::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StackSpacing {
None,
Xs,
Sm,
#[default]
Md,
Lg,
Xl,
Xxl,
Custom(Pixels),
}
impl StackSpacing {
fn to_pixels(&self) -> Pixels {
match self {
StackSpacing::None => px(0.0),
StackSpacing::Xs => px(2.0),
StackSpacing::Sm => px(4.0),
StackSpacing::Md => px(8.0),
StackSpacing::Lg => px(16.0),
StackSpacing::Xl => px(24.0),
StackSpacing::Xxl => px(32.0),
StackSpacing::Custom(p) => *p,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StackAlign {
Start,
#[default]
Center,
End,
Stretch,
Baseline,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StackJustify {
#[default]
Start,
Center,
End,
SpaceBetween,
SpaceAround,
SpaceEvenly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StackOverflow {
#[default]
Visible,
Hidden,
Scroll,
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StackSize {
Auto,
Full,
Fixed(Pixels),
Fraction(f32),
}
pub struct VStack {
children: Vec<AnyElement>,
spacing: StackSpacing,
align: StackAlign,
justify: StackJustify,
width: Option<StackSize>,
height: Option<StackSize>,
flex_grow: Option<f32>,
flex_shrink: Option<f32>,
flex_basis: Option<Pixels>,
overflow_x: StackOverflow,
overflow_y: StackOverflow,
min_width: Option<Pixels>,
min_height: Option<Pixels>,
max_width: Option<Pixels>,
max_height: Option<Pixels>,
}
impl VStack {
pub fn new() -> Self {
Self {
children: Vec::new(),
spacing: StackSpacing::default(),
align: StackAlign::Stretch,
justify: StackJustify::default(),
width: None,
height: None,
flex_grow: None,
flex_shrink: None,
flex_basis: None,
overflow_x: StackOverflow::default(),
overflow_y: StackOverflow::default(),
min_width: None,
min_height: None,
max_width: None,
max_height: None,
}
}
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
}
pub fn spacing(mut self, spacing: StackSpacing) -> Self {
self.spacing = spacing;
self
}
pub fn align(mut self, align: StackAlign) -> Self {
self.align = align;
self
}
pub fn justify(mut self, justify: StackJustify) -> Self {
self.justify = justify;
self
}
pub fn width(mut self, size: StackSize) -> Self {
self.width = Some(size);
self
}
pub fn height(mut self, size: StackSize) -> Self {
self.height = Some(size);
self
}
pub fn full(mut self) -> Self {
self.width = Some(StackSize::Full);
self.height = Some(StackSize::Full);
self
}
pub fn grow(mut self, factor: f32) -> Self {
self.flex_grow = Some(factor);
self
}
pub fn flex_1(mut self) -> Self {
self.flex_grow = Some(1.0);
self.flex_shrink = Some(1.0);
self.flex_basis = Some(px(0.0));
self
}
pub fn shrink(mut self, factor: f32) -> Self {
self.flex_shrink = Some(factor);
self
}
pub fn basis(mut self, size: Pixels) -> Self {
self.flex_basis = Some(size);
self
}
pub fn overflow_x(mut self, overflow: StackOverflow) -> Self {
self.overflow_x = overflow;
self
}
pub fn overflow_y(mut self, overflow: StackOverflow) -> Self {
self.overflow_y = overflow;
self
}
pub fn overflow(mut self, overflow: StackOverflow) -> Self {
self.overflow_x = overflow;
self.overflow_y = overflow;
self
}
pub fn min_w(mut self, size: Pixels) -> Self {
self.min_width = Some(size);
self
}
pub fn min_h(mut self, size: Pixels) -> Self {
self.min_height = Some(size);
self
}
pub fn max_w(mut self, size: Pixels) -> Self {
self.max_width = Some(size);
self
}
pub fn max_h(mut self, size: Pixels) -> Self {
self.max_height = Some(size);
self
}
pub fn build(self) -> Div {
let mut stack = div().flex().flex_col().gap(self.spacing.to_pixels());
stack = match self.width {
Some(StackSize::Auto) => stack,
Some(StackSize::Full) => stack.w_full(),
Some(StackSize::Fixed(px)) => stack.w(px),
Some(StackSize::Fraction(f)) => stack.w(relative(f)),
None => stack,
};
stack = match self.height {
Some(StackSize::Auto) => stack,
Some(StackSize::Full) => stack.h_full(),
Some(StackSize::Fixed(px)) => stack.h(px),
Some(StackSize::Fraction(f)) => stack.h(relative(f)),
None => stack,
};
if let Some(grow) = self.flex_grow {
stack = stack.flex_grow();
if grow != 1.0 {
}
}
if let Some(_shrink) = self.flex_shrink {
stack = stack.flex_shrink();
}
if let Some(basis) = self.flex_basis {
stack = stack.flex_basis(basis);
}
if let Some(min_w) = self.min_width {
stack = stack.min_w(min_w);
}
if let Some(min_h) = self.min_height {
stack = stack.min_h(min_h);
}
if let Some(max_w) = self.max_width {
stack = stack.max_w(max_w);
}
if let Some(max_h) = self.max_height {
stack = stack.max_h(max_h);
}
stack = match self.overflow_x {
StackOverflow::Visible => stack,
StackOverflow::Hidden | StackOverflow::Scroll | StackOverflow::Auto => {
stack.overflow_x_hidden()
}
};
stack = match self.overflow_y {
StackOverflow::Visible => stack,
StackOverflow::Hidden | StackOverflow::Scroll | StackOverflow::Auto => {
stack.overflow_y_hidden()
}
};
stack = match self.align {
StackAlign::Start => stack.items_start(),
StackAlign::Center => stack.items_center(),
StackAlign::End => stack.items_end(),
StackAlign::Stretch => stack,
StackAlign::Baseline => stack, };
stack = match self.justify {
StackJustify::Start => stack.justify_start(),
StackJustify::Center => stack.justify_center(),
StackJustify::End => stack.justify_end(),
StackJustify::SpaceBetween => stack.justify_between(),
StackJustify::SpaceAround => stack.justify_around(),
StackJustify::SpaceEvenly => stack,
};
for child in self.children {
stack = stack.child(child);
}
stack
}
}
impl Default for VStack {
fn default() -> Self {
Self::new()
}
}
impl IntoElement for VStack {
type Element = Div;
fn into_element(self) -> Self::Element {
self.build()
}
}
pub struct HStack {
children: Vec<AnyElement>,
spacing: StackSpacing,
align: StackAlign,
justify: StackJustify,
wrap: bool,
width: Option<StackSize>,
height: Option<StackSize>,
flex_grow: Option<f32>,
flex_shrink: Option<f32>,
flex_basis: Option<Pixels>,
overflow_x: StackOverflow,
overflow_y: StackOverflow,
min_width: Option<Pixels>,
min_height: Option<Pixels>,
max_width: Option<Pixels>,
max_height: Option<Pixels>,
}
impl HStack {
pub fn new() -> Self {
Self {
children: Vec::new(),
spacing: StackSpacing::default(),
align: StackAlign::Center,
justify: StackJustify::default(),
wrap: false,
width: None,
height: None,
flex_grow: None,
flex_shrink: None,
flex_basis: None,
overflow_x: StackOverflow::default(),
overflow_y: StackOverflow::default(),
min_width: None,
min_height: None,
max_width: None,
max_height: None,
}
}
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
}
pub fn spacing(mut self, spacing: StackSpacing) -> Self {
self.spacing = spacing;
self
}
pub fn align(mut self, align: StackAlign) -> Self {
self.align = align;
self
}
pub fn justify(mut self, justify: StackJustify) -> Self {
self.justify = justify;
self
}
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = wrap;
self
}
pub fn width(mut self, size: StackSize) -> Self {
self.width = Some(size);
self
}
pub fn height(mut self, size: StackSize) -> Self {
self.height = Some(size);
self
}
pub fn full(mut self) -> Self {
self.width = Some(StackSize::Full);
self.height = Some(StackSize::Full);
self
}
pub fn grow(mut self, factor: f32) -> Self {
self.flex_grow = Some(factor);
self
}
pub fn flex_1(mut self) -> Self {
self.flex_grow = Some(1.0);
self.flex_shrink = Some(1.0);
self.flex_basis = Some(px(0.0));
self
}
pub fn shrink(mut self, factor: f32) -> Self {
self.flex_shrink = Some(factor);
self
}
pub fn basis(mut self, size: Pixels) -> Self {
self.flex_basis = Some(size);
self
}
pub fn overflow_x(mut self, overflow: StackOverflow) -> Self {
self.overflow_x = overflow;
self
}
pub fn overflow_y(mut self, overflow: StackOverflow) -> Self {
self.overflow_y = overflow;
self
}
pub fn overflow(mut self, overflow: StackOverflow) -> Self {
self.overflow_x = overflow;
self.overflow_y = overflow;
self
}
pub fn min_w(mut self, size: Pixels) -> Self {
self.min_width = Some(size);
self
}
pub fn min_h(mut self, size: Pixels) -> Self {
self.min_height = Some(size);
self
}
pub fn max_w(mut self, size: Pixels) -> Self {
self.max_width = Some(size);
self
}
pub fn max_h(mut self, size: Pixels) -> Self {
self.max_height = Some(size);
self
}
pub fn build(self) -> Div {
let mut stack = div().flex().gap(self.spacing.to_pixels());
if self.wrap {
stack = stack.flex_wrap();
}
stack = match self.width {
Some(StackSize::Auto) => stack,
Some(StackSize::Full) => stack.w_full(),
Some(StackSize::Fixed(px)) => stack.w(px),
Some(StackSize::Fraction(f)) => stack.w(relative(f)),
None => stack,
};
stack = match self.height {
Some(StackSize::Auto) => stack,
Some(StackSize::Full) => stack.h_full(),
Some(StackSize::Fixed(px)) => stack.h(px),
Some(StackSize::Fraction(f)) => stack.h(relative(f)),
None => stack,
};
if let Some(grow) = self.flex_grow {
stack = stack.flex_grow();
if grow != 1.0 {
}
}
if let Some(_shrink) = self.flex_shrink {
stack = stack.flex_shrink();
}
if let Some(basis) = self.flex_basis {
stack = stack.flex_basis(basis);
}
if let Some(min_w) = self.min_width {
stack = stack.min_w(min_w);
}
if let Some(min_h) = self.min_height {
stack = stack.min_h(min_h);
}
if let Some(max_w) = self.max_width {
stack = stack.max_w(max_w);
}
if let Some(max_h) = self.max_height {
stack = stack.max_h(max_h);
}
stack = match self.overflow_x {
StackOverflow::Visible => stack,
StackOverflow::Hidden | StackOverflow::Scroll | StackOverflow::Auto => {
stack.overflow_x_hidden()
}
};
stack = match self.overflow_y {
StackOverflow::Visible => stack,
StackOverflow::Hidden | StackOverflow::Scroll | StackOverflow::Auto => {
stack.overflow_y_hidden()
}
};
stack = match self.align {
StackAlign::Start => stack.items_start(),
StackAlign::Center => stack.items_center(),
StackAlign::End => stack.items_end(),
StackAlign::Stretch => stack,
StackAlign::Baseline => stack, };
stack = match self.justify {
StackJustify::Start => stack.justify_start(),
StackJustify::Center => stack.justify_center(),
StackJustify::End => stack.justify_end(),
StackJustify::SpaceBetween => stack.justify_between(),
StackJustify::SpaceAround => stack.justify_around(),
StackJustify::SpaceEvenly => stack,
};
for child in self.children {
stack = stack.child(child);
}
stack
}
}
impl Default for HStack {
fn default() -> Self {
Self::new()
}
}
impl IntoElement for HStack {
type Element = Div;
fn into_element(self) -> Self::Element {
self.build()
}
}
pub struct Spacer;
impl Spacer {
pub fn new() -> Self {
Self
}
pub fn build(self) -> Div {
div().flex_1()
}
}
impl Default for Spacer {
fn default() -> Self {
Self::new()
}
}
impl IntoElement for Spacer {
type Element = Div;
fn into_element(self) -> Self::Element {
self.build()
}
}
pub struct Divider {
id: Option<SharedString>,
vertical: bool,
color: Option<Rgba>,
hover_color: Option<Rgba>,
thickness: Option<Pixels>,
interactive: bool,
}
impl Divider {
pub fn new() -> Self {
Self {
id: None,
vertical: false,
color: None,
hover_color: None,
thickness: None,
interactive: false,
}
}
pub fn vertical() -> Self {
Self {
id: None,
vertical: true,
color: None,
hover_color: None,
thickness: None,
interactive: false,
}
}
pub fn id(mut self, id: impl Into<SharedString>) -> Self {
self.id = Some(id.into());
self
}
pub fn color(mut self, color: Rgba) -> Self {
self.color = Some(color);
self
}
pub fn hover_color(mut self, color: Rgba) -> Self {
self.hover_color = Some(color);
self
}
pub fn thickness(mut self, thickness: Pixels) -> Self {
self.thickness = Some(thickness);
self
}
pub fn interactive(mut self) -> Self {
self.interactive = true;
self
}
pub fn build(self) -> Stateful<Div> {
let color = self.color.unwrap_or(rgb(0x3a3a3a));
let id = self.id.unwrap_or_else(|| SharedString::from("divider"));
let base = if self.vertical {
let thickness = self.thickness.unwrap_or(px(1.0));
div().id(id).w(thickness).h_full().bg(color)
} else {
let thickness = self.thickness.unwrap_or(px(1.0));
div().id(id).h(thickness).w_full().bg(color)
};
if self.interactive {
let hover_color = self.hover_color.unwrap_or(rgb(0x007acc));
let cursor = if self.vertical {
gpui::CursorStyle::ResizeLeftRight
} else {
gpui::CursorStyle::ResizeUpDown
};
base.cursor(cursor)
.hover(move |style| style.bg(hover_color))
} else {
base
}
}
pub fn build_simple(self) -> Div {
let color = self.color.unwrap_or(rgb(0x3a3a3a));
if self.vertical {
let thickness = self.thickness.unwrap_or(px(1.0));
div().w(thickness).h_full().bg(color)
} else {
let thickness = self.thickness.unwrap_or(px(1.0));
div().h(thickness).w_full().bg(color)
}
}
}
impl Default for Divider {
fn default() -> Self {
Self::new()
}
}
impl IntoElement for Divider {
type Element = Stateful<Div>;
fn into_element(self) -> Self::Element {
self.build()
}
}