gpui_kit_material/components/
divider.rs1use crate::{color::ColorRole, theme::color};
3use gpui::{prelude::*, *};
4use gpui_base::StyledExt;
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub enum DividerOrientation {
8 #[default]
9 Horizontal,
10 Vertical,
11}
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
13pub enum DividerVariant {
14 #[default]
15 FullWidth,
16 Inset,
17 MiddleInset,
18}
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub enum DividerDirection {
21 #[default]
22 Ltr,
23 Rtl,
24}
25#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
27pub enum DividerContext {
28 #[default]
29 Plain,
30 List,
31 Card,
32}
33
34#[derive(gpui::IntoElement)]
44pub struct MaterialDivider {
45 id: ElementId,
46 orientation: DividerOrientation,
47 variant: DividerVariant,
48 direction: DividerDirection,
49 context: DividerContext,
50 thickness: Pixels,
51 inset_start: Option<Length>,
52 inset_end: Option<Length>,
53 color_role: ColorRole,
54 style: StyleRefinement,
55}
56impl MaterialDivider {
57 pub fn new(id: impl Into<ElementId>) -> Self {
58 Self {
59 id: id.into(),
60 orientation: DividerOrientation::Horizontal,
61 variant: DividerVariant::FullWidth,
62 direction: DividerDirection::Ltr,
63 context: DividerContext::Plain,
64 thickness: px(1.),
65 inset_start: None,
66 inset_end: None,
67 color_role: ColorRole::OUTLINE_VARIANT,
68 style: StyleRefinement::default(),
69 }
70 }
71 pub fn orientation(mut self, orientation: DividerOrientation) -> Self {
72 self.orientation = orientation;
73 self
74 }
75 pub fn variant(mut self, variant: DividerVariant) -> Self {
76 self.variant = variant;
77 self
78 }
79 pub fn direction(mut self, direction: DividerDirection) -> Self {
81 self.direction = direction;
82 self
83 }
84 pub fn context(mut self, context: DividerContext) -> Self {
85 self.context = context;
86 self
87 }
88 pub fn thickness(mut self, thickness: Pixels) -> Self {
90 self.thickness = if f32::from(thickness).is_finite() && thickness > px(0.) {
91 thickness
92 } else {
93 px(1.)
94 };
95 self
96 }
97 pub fn inset_start(mut self, inset: impl Into<Length>) -> Self {
98 self.inset_start = Some(inset.into());
99 self
100 }
101 pub fn inset_end(mut self, inset: impl Into<Length>) -> Self {
102 self.inset_end = Some(inset.into());
103 self
104 }
105 pub fn color_role(mut self, role: ColorRole) -> Self {
106 self.color_role = role;
107 self
108 }
109
110 fn styled_line(&self) -> Div {
111 let horizontal = self.orientation == DividerOrientation::Horizontal;
112 let mut line = div()
113 .block()
114 .m_0()
115 .p_0()
116 .border_0()
117 .bg(color(self.color_role));
118 if self.context != DividerContext::Plain {
119 line = line.my(px(8.));
120 }
121 line = if horizontal {
122 line.w_full().h(self.thickness)
123 } else {
124 line.h_full().w(self.thickness)
125 };
126 if self.variant != DividerVariant::FullWidth {
127 let start = self.inset_start.unwrap_or(px(16.).into());
128 let end = self.inset_end.unwrap_or(
129 px(if self.variant == DividerVariant::MiddleInset {
130 16.
131 } else {
132 0.
133 })
134 .into(),
135 );
136 line = if horizontal {
137 let (left, right) = if self.direction == DividerDirection::Ltr {
138 (start, end)
139 } else {
140 (end, start)
141 };
142 line.w_auto().ml(left).mr(right)
143 } else {
144 line.h_auto().mt(start).mb(end)
145 };
146 }
147 line.refine_style(&self.style)
148 }
149}
150impl MaterialDivider {
151 fn into_line(self) -> Stateful<Div> {
152 self.styled_line()
153 .id(self.id)
154 .role(Role::Splitter)
155 .when(self.orientation == DividerOrientation::Vertical, |line| {
156 line.aria_orientation(accesskit::Orientation::Vertical)
157 })
158 }
159}
160impl Styled for MaterialDivider {
161 fn style(&mut self) -> &mut StyleRefinement {
162 &mut self.style
163 }
164}
165impl RenderOnce for MaterialDivider {
166 fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
167 self.into_line()
168 }
169}
170
171#[cfg(test)]
172#[path = "divider_tests.rs"]
173mod tests;