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}