Skip to main content

egui/widgets/
separator.rs

1use crate::{
2    Response, Sense, Ui, Vec2, Widget, vec2,
3    widget_style::{Classes, HasClasses, SeparatorStyle},
4};
5
6/// A visual separator. A horizontal or vertical line (depending on [`crate::Layout`]).
7///
8/// Usually you'd use the shorter version [`Ui::separator`].
9///
10/// ```
11/// # egui::__run_test_ui(|ui| {
12/// // These are equivalent:
13/// ui.separator();
14/// ui.add(egui::Separator::default());
15/// # });
16/// ```
17#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
18pub struct Separator {
19    spacing: Option<f32>,
20    grow: f32,
21    is_horizontal_line: Option<bool>,
22    classes: Classes,
23}
24
25impl Default for Separator {
26    fn default() -> Self {
27        Self {
28            spacing: None,
29            grow: 0.0,
30            is_horizontal_line: None,
31            classes: Classes::default(),
32        }
33    }
34}
35
36impl Separator {
37    /// How much space we take up. The line is painted in the middle of this.
38    ///
39    /// In a vertical layout, with a horizontal Separator,
40    /// this is the height of the separator widget.
41    ///
42    /// In a horizontal layout, with a vertical Separator,
43    /// this is the width of the separator widget.
44    #[inline]
45    pub fn spacing(mut self, spacing: f32) -> Self {
46        self.spacing = Some(spacing);
47        self
48    }
49
50    /// Explicitly ask for a horizontal line.
51    ///
52    /// By default you will get a horizontal line in vertical layouts,
53    /// and a vertical line in horizontal layouts.
54    #[inline]
55    pub fn horizontal(mut self) -> Self {
56        self.is_horizontal_line = Some(true);
57        self
58    }
59
60    /// Explicitly ask for a vertical line.
61    ///
62    /// By default you will get a horizontal line in vertical layouts,
63    /// and a vertical line in horizontal layouts.
64    #[inline]
65    pub fn vertical(mut self) -> Self {
66        self.is_horizontal_line = Some(false);
67        self
68    }
69
70    /// Extend each end of the separator line by this much.
71    ///
72    /// The default is to take up the available width/height of the parent.
73    ///
74    /// This will make the line extend outside the parent ui.
75    #[inline]
76    pub fn grow(mut self, extra: f32) -> Self {
77        self.grow += extra;
78        self
79    }
80
81    /// Contract each end of the separator line by this much.
82    ///
83    /// The default is to take up the available width/height of the parent.
84    ///
85    /// This effectively adds margins to the line.
86    #[inline]
87    pub fn shrink(mut self, shrink: f32) -> Self {
88        self.grow -= shrink;
89        self
90    }
91}
92
93impl Widget for Separator {
94    fn ui(self, ui: &mut Ui) -> Response {
95        let Self {
96            spacing,
97            grow,
98            is_horizontal_line,
99            classes,
100        } = self;
101
102        // Get the widget style by reading the response from the previous pass
103        let id = ui.next_auto_id();
104        let response: Option<Response> = ui.ctx().read_response(id);
105        let state = response.map(|r| r.widget_state()).unwrap_or_default();
106        let SeparatorStyle {
107            spacing: spacing_style,
108            stroke,
109        } = ui.style().separator_style(&classes, state);
110
111        // override the spacing if not set
112        let spacing = spacing.unwrap_or(spacing_style);
113
114        let is_horizontal_line = is_horizontal_line
115            .unwrap_or_else(|| ui.is_grid() || !ui.layout().main_dir().is_horizontal());
116
117        let available_space = if ui.is_sizing_pass() {
118            Vec2::ZERO
119        } else {
120            ui.available_size_before_wrap()
121        };
122
123        let size = if is_horizontal_line {
124            vec2(available_space.x, spacing)
125        } else {
126            vec2(spacing, available_space.y)
127        };
128
129        let (rect, response) = ui.allocate_at_least(size, Sense::hover());
130
131        if ui.is_rect_visible(response.rect) {
132            let painter = ui.painter();
133            if is_horizontal_line {
134                painter.hline(
135                    (rect.left() - grow)..=(rect.right() + grow),
136                    rect.center().y,
137                    stroke,
138                );
139            } else {
140                painter.vline(
141                    rect.center().x,
142                    (rect.top() - grow)..=(rect.bottom() + grow),
143                    stroke,
144                );
145            }
146        }
147
148        response
149    }
150}
151
152impl HasClasses for Separator {
153    fn classes(&self) -> &Classes {
154        &self.classes
155    }
156
157    fn classes_mut(&mut self) -> &mut Classes {
158        &mut self.classes
159    }
160}