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}