divider.rs

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