divider.rs

  1#![allow(missing_docs)]
  2use gpui::{Hsla, IntoElement};
  3
  4use crate::prelude::*;
  5
  6#[derive(Clone, Copy, PartialEq)]
  7enum DividerStyle {
  8    Solid,
  9    Dashed,
 10}
 11
 12#[derive(Clone, Copy, PartialEq)]
 13enum DividerDirection {
 14    Horizontal,
 15    Vertical,
 16}
 17
 18/// The color of a [`Divider`].
 19#[derive(Default)]
 20pub enum DividerColor {
 21    Border,
 22    #[default]
 23    BorderVariant,
 24}
 25
 26impl DividerColor {
 27    pub fn hsla(self, cx: &mut App) -> Hsla {
 28        match self {
 29            DividerColor::Border => cx.theme().colors().border,
 30            DividerColor::BorderVariant => cx.theme().colors().border_variant,
 31        }
 32    }
 33}
 34
 35#[derive(IntoElement)]
 36pub struct Divider {
 37    style: DividerStyle,
 38    direction: DividerDirection,
 39    color: DividerColor,
 40    inset: bool,
 41}
 42
 43impl RenderOnce for Divider {
 44    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
 45        match self.style {
 46            DividerStyle::Solid => self.render_solid(cx).into_any_element(),
 47            DividerStyle::Dashed => self.render_dashed(cx).into_any_element(),
 48        }
 49    }
 50}
 51
 52impl Divider {
 53    pub fn horizontal() -> Self {
 54        Self {
 55            style: DividerStyle::Solid,
 56            direction: DividerDirection::Horizontal,
 57            color: DividerColor::default(),
 58            inset: false,
 59        }
 60    }
 61
 62    pub fn vertical() -> Self {
 63        Self {
 64            style: DividerStyle::Solid,
 65            direction: DividerDirection::Vertical,
 66            color: DividerColor::default(),
 67            inset: false,
 68        }
 69    }
 70
 71    pub fn horizontal_dashed() -> Self {
 72        Self {
 73            style: DividerStyle::Dashed,
 74            direction: DividerDirection::Horizontal,
 75            color: DividerColor::default(),
 76            inset: false,
 77        }
 78    }
 79
 80    pub fn vertical_dashed() -> Self {
 81        Self {
 82            style: DividerStyle::Dashed,
 83            direction: DividerDirection::Vertical,
 84            color: DividerColor::default(),
 85            inset: false,
 86        }
 87    }
 88
 89    pub fn inset(mut self) -> Self {
 90        self.inset = true;
 91        self
 92    }
 93
 94    pub fn color(mut self, color: DividerColor) -> Self {
 95        self.color = color;
 96        self
 97    }
 98
 99    pub fn render_solid(self, cx: &mut App) -> impl IntoElement {
100        div()
101            .map(|this| match self.direction {
102                DividerDirection::Horizontal => {
103                    this.h_px().w_full().when(self.inset, |this| this.mx_1p5())
104                }
105                DividerDirection::Vertical => {
106                    this.w_px().h_full().when(self.inset, |this| this.my_1p5())
107                }
108            })
109            .bg(self.color.hsla(cx))
110    }
111
112    // TODO: Use canvas or a shader here
113    // This obviously is a short term approach
114    pub fn render_dashed(self, cx: &mut App) -> impl IntoElement {
115        let segment_count = 128;
116        let segment_count_f = segment_count as f32;
117        let segment_min_w = 6.;
118        let base = match self.direction {
119            DividerDirection::Horizontal => h_flex(),
120            DividerDirection::Vertical => v_flex(),
121        };
122        let (w, h) = match self.direction {
123            DividerDirection::Horizontal => (px(segment_min_w), px(1.)),
124            DividerDirection::Vertical => (px(1.), px(segment_min_w)),
125        };
126        let color = self.color.hsla(cx);
127        let total_min_w = segment_min_w * segment_count_f * 2.; // * 2 because of the gap
128
129        base.min_w(px(total_min_w))
130            .map(|this| {
131                if self.direction == DividerDirection::Horizontal {
132                    this.w_full().h_px()
133                } else {
134                    this.w_px().h_full()
135                }
136            })
137            .gap(px(segment_min_w))
138            .overflow_hidden()
139            .children(
140                (0..segment_count).map(|_| div().flex_grow().flex_shrink_0().w(w).h(h).bg(color)),
141            )
142    }
143}