Skip to main content

dear_imgui_rs/layout/
spacing.rs

1use super::validation::{assert_finite_f32, assert_finite_vec2};
2use crate::scope::{NativeScopePop, NativeScopeToken};
3use crate::{Ui, sys};
4
5/// Tracks an indentation scope started with [`Ui::begin_indent`] or
6/// [`Ui::begin_indent_by`].
7///
8/// Tokens from the same window may be ended in any order because each token removes its own
9/// captured width. Cross-window cleanup is deferred until the originating window becomes current
10/// again. [`Ui::with_indent`] and [`Ui::with_indent_by`] remain the canonical APIs for ordinary
11/// scoped use.
12#[must_use]
13pub struct IndentToken<'ui> {
14    scope: NativeScopeToken<'ui>,
15}
16
17impl IndentToken<'_> {
18    /// Ends the indentation scope explicitly.
19    pub fn end(self) {}
20}
21
22impl Drop for IndentToken<'_> {
23    fn drop(&mut self) {
24        self.scope.finish();
25    }
26}
27
28impl Ui {
29    /// Call between widgets or groups to layout them horizontally.
30    ///
31    /// X position is given in window coordinates.
32    ///
33    /// This is equivalent to calling [same_line_with_pos](Self::same_line_with_pos)
34    /// with the `pos` set to 0.0, which uses `Style::item_spacing`.
35    #[doc(alias = "SameLine")]
36    pub fn same_line(&self) {
37        self.same_line_with_pos(0.0);
38    }
39
40    /// Call between widgets or groups to layout them horizontally.
41    ///
42    /// X position is given in window coordinates.
43    ///
44    /// This is equivalent to calling [same_line_with_spacing](Self::same_line_with_spacing)
45    /// with the `spacing` set to -1.0, which means no extra spacing.
46    #[doc(alias = "SameLine")]
47    pub fn same_line_with_pos(&self, pos_x: f32) {
48        self.same_line_with_spacing(pos_x, -1.0)
49    }
50
51    /// Call between widgets or groups to layout them horizontally.
52    ///
53    /// X position is given in window coordinates.
54    #[doc(alias = "SameLine")]
55    pub fn same_line_with_spacing(&self, pos_x: f32, spacing_w: f32) {
56        assert_finite_f32("Ui::same_line_with_spacing()", "pos_x", pos_x);
57        assert_finite_f32("Ui::same_line_with_spacing()", "spacing_w", spacing_w);
58        self.run_with_bound_context(|| unsafe { sys::igSameLine(pos_x, spacing_w) });
59    }
60
61    /// Undo a `same_line` call or force a new line when in horizontal layout mode
62    #[doc(alias = "NewLine")]
63    pub fn new_line(&self) {
64        self.run_with_bound_context(|| unsafe { sys::igNewLine() });
65    }
66
67    /// Adds vertical spacing
68    #[doc(alias = "Spacing")]
69    pub fn spacing(&self) {
70        self.run_with_bound_context(|| unsafe { sys::igSpacing() });
71    }
72
73    /// Fills a space of `size` in pixels with nothing on the current window.
74    ///
75    /// Can be used to move the cursor on the window.
76    #[doc(alias = "Dummy")]
77    pub fn dummy(&self, size: impl Into<[f32; 2]>) {
78        let size = size.into();
79        assert_finite_vec2("Ui::dummy()", "size", size);
80        let size_vec: sys::ImVec2 = size.into();
81        self.run_with_bound_context(|| unsafe { sys::igDummy(size_vec) });
82    }
83
84    /// Moves content position to the right by `Style::indent_spacing`
85    ///
86    /// This is equivalent to [indent_by](Self::indent_by) with `width` set to
87    /// `Style::indent_spacing`.
88    #[doc(alias = "Indent")]
89    pub fn indent(&self) {
90        self.indent_by(0.0)
91    }
92
93    /// Moves content position to the right by `width`
94    #[doc(alias = "Indent")]
95    pub fn indent_by(&self, width: f32) {
96        assert_finite_f32("Ui::indent_by()", "width", width);
97        self.run_with_bound_context(|| unsafe { sys::igIndent(width) });
98    }
99
100    /// Starts an indentation scope using [`Style::indent_spacing`](crate::Style::indent_spacing).
101    ///
102    /// The returned token restores the exact width captured at creation and may be dropped in any
103    /// order relative to other indentation tokens from the same window.
104    #[doc(alias = "Indent")]
105    pub fn begin_indent(&self) -> IndentToken<'_> {
106        self.begin_indent_by(0.0)
107    }
108
109    /// Starts an indentation scope with a custom width.
110    ///
111    /// Passing `0.0` snapshots the current [`Style::indent_spacing`](crate::Style::indent_spacing)
112    /// so a later style change cannot alter restoration.
113    #[doc(alias = "Indent")]
114    pub fn begin_indent_by(&self, width: f32) -> IndentToken<'_> {
115        assert_finite_f32("Ui::begin_indent_by()", "width", width);
116        let width = self.run_with_bound_context(|| unsafe {
117            let width = if width == 0.0 {
118                (*sys::igGetStyle()).IndentSpacing
119            } else {
120                width
121            };
122            sys::igIndent(width);
123            width
124        });
125        IndentToken {
126            scope: self
127                .begin_provenance_native_scope(NativeScopePop::Unindent(width), "IndentToken"),
128        }
129    }
130
131    /// Runs `f` in an indentation scope using [`Style::indent_spacing`](crate::Style::indent_spacing).
132    ///
133    /// The indentation is restored if `f` returns early or panics. Prefer this closure-based
134    /// scope over manually pairing [`Self::indent`] and [`Self::unindent`].
135    #[doc(alias = "Indent", alias = "Unindent")]
136    pub fn with_indent<R>(&self, f: impl FnOnce() -> R) -> R {
137        let indent = self.begin_indent();
138        let result = f();
139        drop(indent);
140        result
141    }
142
143    /// Runs `f` in an indentation scope with a custom width.
144    ///
145    /// The indentation is restored if `f` returns early or panics.
146    #[doc(alias = "Indent", alias = "Unindent")]
147    pub fn with_indent_by<R>(&self, width: f32, f: impl FnOnce() -> R) -> R {
148        let indent = self.begin_indent_by(width);
149        let result = f();
150        drop(indent);
151        result
152    }
153
154    /// Moves content position to the left by `Style::indent_spacing`
155    ///
156    /// This is equivalent to [unindent_by](Self::unindent_by) with `width` set to
157    /// `Style::indent_spacing`.
158    #[doc(alias = "Unindent")]
159    pub fn unindent(&self) {
160        self.unindent_by(0.0)
161    }
162
163    /// Moves content position to the left by `width`
164    #[doc(alias = "Unindent")]
165    pub fn unindent_by(&self, width: f32) {
166        assert_finite_f32("Ui::unindent_by()", "width", width);
167        self.run_with_bound_context(|| unsafe { sys::igUnindent(width) });
168    }
169}
170
171impl Ui {
172    /// Vertically align upcoming text baseline to FramePadding.y (align text to framed items).
173    #[doc(alias = "AlignTextToFramePadding")]
174    pub fn align_text_to_frame_padding(&self) {
175        self.run_with_bound_context(|| unsafe { sys::igAlignTextToFramePadding() });
176    }
177}