egui/widgets/
separator.rs

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