dear_imgui_rs/
layout.rs

1//! Layout and cursor helpers
2//!
3//! Spacing, separators, horizontal layout (`same_line`), grouping, cursor
4//! positioning and clipping helpers. These functions help arrange widgets and
5//! content within windows.
6//!
7//! Example:
8//! ```no_run
9//! # use dear_imgui_rs::*;
10//! # let mut ctx = Context::create();
11//! # let ui = ctx.frame();
12//! ui.text("Left");
13//! ui.same_line();
14//! ui.text("Right");
15//! ```
16//!
17#![allow(
18    clippy::cast_possible_truncation,
19    clippy::cast_sign_loss,
20    clippy::as_conversions
21)]
22use crate::Ui;
23use crate::sys;
24
25create_token!(
26    /// Tracks a layout group that can be ended with `end` or by dropping.
27    pub struct GroupToken<'ui>;
28
29    /// Drops the layout group manually. You can also just allow this token
30    /// to drop on its own.
31    drop { unsafe { sys::igEndGroup() } }
32);
33
34create_token!(
35    /// Tracks a pushed clip rect that will be popped on drop.
36    pub struct ClipRectToken<'ui>;
37
38    /// Pops a clip rect pushed with [`Ui::push_clip_rect`].
39    drop { unsafe { sys::igPopClipRect() } }
40);
41
42/// # Cursor / Layout
43impl Ui {
44    /// Renders a separator (generally horizontal).
45    ///
46    /// This becomes a vertical separator inside a menu bar or in horizontal layout mode.
47    #[doc(alias = "Separator")]
48    pub fn separator(&self) {
49        unsafe { sys::igSeparator() }
50    }
51
52    /// Renders a separator with text.
53    #[doc(alias = "SeparatorText")]
54    pub fn separator_with_text(&self, text: impl AsRef<str>) {
55        unsafe { sys::igSeparatorText(self.scratch_txt(text)) }
56    }
57
58    /// Creates a vertical separator
59    #[doc(alias = "SeparatorEx")]
60    pub fn separator_vertical(&self) {
61        unsafe { sys::igSeparatorEx(sys::ImGuiSeparatorFlags_Vertical as i32, 1.0) }
62    }
63
64    /// Creates a horizontal separator
65    #[doc(alias = "SeparatorEx")]
66    pub fn separator_horizontal(&self) {
67        unsafe { sys::igSeparatorEx(sys::ImGuiSeparatorFlags_Horizontal as i32, 1.0) }
68    }
69
70    /// Call between widgets or groups to layout them horizontally.
71    ///
72    /// X position is given in window coordinates.
73    ///
74    /// This is equivalent to calling [same_line_with_pos](Self::same_line_with_pos)
75    /// with the `pos` set to 0.0, which uses `Style::item_spacing`.
76    #[doc(alias = "SameLine")]
77    pub fn same_line(&self) {
78        self.same_line_with_pos(0.0);
79    }
80
81    /// Call between widgets or groups to layout them horizontally.
82    ///
83    /// X position is given in window coordinates.
84    ///
85    /// This is equivalent to calling [same_line_with_spacing](Self::same_line_with_spacing)
86    /// with the `spacing` set to -1.0, which means no extra spacing.
87    #[doc(alias = "SameLine")]
88    pub fn same_line_with_pos(&self, pos_x: f32) {
89        self.same_line_with_spacing(pos_x, -1.0)
90    }
91
92    /// Call between widgets or groups to layout them horizontally.
93    ///
94    /// X position is given in window coordinates.
95    #[doc(alias = "SameLine")]
96    pub fn same_line_with_spacing(&self, pos_x: f32, spacing_w: f32) {
97        unsafe { sys::igSameLine(pos_x, spacing_w) }
98    }
99
100    /// Undo a `same_line` call or force a new line when in horizontal layout mode
101    #[doc(alias = "NewLine")]
102    pub fn new_line(&self) {
103        unsafe { sys::igNewLine() }
104    }
105
106    /// Adds vertical spacing
107    #[doc(alias = "Spacing")]
108    pub fn spacing(&self) {
109        unsafe { sys::igSpacing() }
110    }
111
112    /// Fills a space of `size` in pixels with nothing on the current window.
113    ///
114    /// Can be used to move the cursor on the window.
115    #[doc(alias = "Dummy")]
116    pub fn dummy(&self, size: impl Into<[f32; 2]>) {
117        let size_vec: sys::ImVec2 = size.into().into();
118        unsafe { sys::igDummy(size_vec) }
119    }
120
121    /// Moves content position to the right by `Style::indent_spacing`
122    ///
123    /// This is equivalent to [indent_by](Self::indent_by) with `width` set to
124    /// `Style::indent_spacing`.
125    #[doc(alias = "Indent")]
126    pub fn indent(&self) {
127        self.indent_by(0.0)
128    }
129
130    /// Moves content position to the right by `width`
131    #[doc(alias = "Indent")]
132    pub fn indent_by(&self, width: f32) {
133        unsafe { sys::igIndent(width) };
134    }
135
136    /// Moves content position to the left by `Style::indent_spacing`
137    ///
138    /// This is equivalent to [unindent_by](Self::unindent_by) with `width` set to
139    /// `Style::indent_spacing`.
140    #[doc(alias = "Unindent")]
141    pub fn unindent(&self) {
142        self.unindent_by(0.0)
143    }
144
145    /// Moves content position to the left by `width`
146    #[doc(alias = "Unindent")]
147    pub fn unindent_by(&self, width: f32) {
148        unsafe { sys::igUnindent(width) };
149    }
150
151    /// Creates a layout group and starts appending to it.
152    ///
153    /// Returns a `GroupToken` that must be ended by calling `.end()`.
154    #[doc(alias = "BeginGroup")]
155    pub fn begin_group(&self) -> GroupToken<'_> {
156        unsafe { sys::igBeginGroup() };
157        GroupToken::new(self)
158    }
159
160    /// Creates a layout group and runs a closure to construct the contents.
161    ///
162    /// May be useful to handle the same mouse event on a group of items, for example.
163    #[doc(alias = "BeginGroup")]
164    pub fn group<R, F: FnOnce() -> R>(&self, f: F) -> R {
165        let group = self.begin_group();
166        let result = f();
167        group.end();
168        result
169    }
170
171    /// Returns the cursor position (in window coordinates)
172    #[doc(alias = "GetCursorPos")]
173    pub fn cursor_pos(&self) -> [f32; 2] {
174        let pos = unsafe { sys::igGetCursorPos() };
175        [pos.x, pos.y]
176    }
177
178    /// Returns the cursor position (in absolute screen coordinates)
179    #[doc(alias = "GetCursorScreenPos")]
180    pub fn cursor_screen_pos(&self) -> [f32; 2] {
181        let pos = unsafe { sys::igGetCursorScreenPos() };
182        [pos.x, pos.y]
183    }
184
185    /// Sets the cursor position (in window coordinates)
186    #[doc(alias = "SetCursorPos")]
187    pub fn set_cursor_pos(&self, pos: impl Into<[f32; 2]>) {
188        let pos_array = pos.into();
189        let pos_vec = sys::ImVec2 {
190            x: pos_array[0],
191            y: pos_array[1],
192        };
193        unsafe { sys::igSetCursorPos(pos_vec) };
194    }
195
196    /// Sets the cursor position (in absolute screen coordinates)
197    #[doc(alias = "SetCursorScreenPos")]
198    pub fn set_cursor_screen_pos(&self, pos: impl Into<[f32; 2]>) {
199        let pos_array = pos.into();
200        let pos_vec = sys::ImVec2 {
201            x: pos_array[0],
202            y: pos_array[1],
203        };
204        unsafe { sys::igSetCursorScreenPos(pos_vec) };
205    }
206
207    /// Returns the X cursor position (in window coordinates)
208    #[doc(alias = "GetCursorPosX")]
209    pub fn cursor_pos_x(&self) -> f32 {
210        unsafe { sys::igGetCursorPosX() }
211    }
212
213    /// Returns the Y cursor position (in window coordinates)
214    #[doc(alias = "GetCursorPosY")]
215    pub fn cursor_pos_y(&self) -> f32 {
216        unsafe { sys::igGetCursorPosY() }
217    }
218
219    /// Sets the X cursor position (in window coordinates)
220    #[doc(alias = "SetCursorPosX")]
221    pub fn set_cursor_pos_x(&self, x: f32) {
222        unsafe { sys::igSetCursorPosX(x) };
223    }
224
225    /// Sets the Y cursor position (in window coordinates)
226    #[doc(alias = "SetCursorPosY")]
227    pub fn set_cursor_pos_y(&self, y: f32) {
228        unsafe { sys::igSetCursorPosY(y) };
229    }
230
231    /// Returns the initial cursor position (in window coordinates)
232    #[doc(alias = "GetCursorStartPos")]
233    pub fn cursor_start_pos(&self) -> [f32; 2] {
234        let pos = unsafe { sys::igGetCursorStartPos() };
235        [pos.x, pos.y]
236    }
237}
238
239// ============================================================================
240// Metrics helpers & clip rect stack
241// ============================================================================
242
243impl Ui {
244    /// Return ~ FontSize.
245    #[doc(alias = "GetTextLineHeight")]
246    pub fn text_line_height(&self) -> f32 {
247        unsafe { sys::igGetTextLineHeight() }
248    }
249
250    /// Return ~ FontSize + style.ItemSpacing.y.
251    #[doc(alias = "GetTextLineHeightWithSpacing")]
252    pub fn text_line_height_with_spacing(&self) -> f32 {
253        unsafe { sys::igGetTextLineHeightWithSpacing() }
254    }
255
256    /// Return ~ FontSize + style.FramePadding.y * 2.
257    #[doc(alias = "GetFrameHeight")]
258    pub fn frame_height(&self) -> f32 {
259        unsafe { sys::igGetFrameHeight() }
260    }
261
262    /// Return ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y.
263    #[doc(alias = "GetFrameHeightWithSpacing")]
264    pub fn frame_height_with_spacing(&self) -> f32 {
265        unsafe { sys::igGetFrameHeightWithSpacing() }
266    }
267
268    /// Push a clipping rectangle in screen space.
269    #[doc(alias = "PushClipRect")]
270    pub fn push_clip_rect(
271        &self,
272        min: impl Into<[f32; 2]>,
273        max: impl Into<[f32; 2]>,
274        intersect_with_current: bool,
275    ) {
276        let min = min.into();
277        let max = max.into();
278        let min_v = sys::ImVec2 {
279            x: min[0],
280            y: min[1],
281        };
282        let max_v = sys::ImVec2 {
283            x: max[0],
284            y: max[1],
285        };
286        unsafe { sys::igPushClipRect(min_v, max_v, intersect_with_current) }
287    }
288
289    /// Pop a clipping rectangle from the stack.
290    #[doc(alias = "PopClipRect")]
291    pub fn pop_clip_rect(&self) {
292        unsafe { sys::igPopClipRect() }
293    }
294
295    /// Run a closure with a clip rect pushed and automatically popped.
296    pub fn with_clip_rect<R>(
297        &self,
298        min: impl Into<[f32; 2]>,
299        max: impl Into<[f32; 2]>,
300        intersect_with_current: bool,
301        f: impl FnOnce() -> R,
302    ) -> R {
303        self.push_clip_rect(min, max, intersect_with_current);
304        let _t = ClipRectToken::new(self);
305        f()
306    }
307
308    /// Returns true if the specified rectangle (min,max) is visible (not clipped).
309    #[doc(alias = "IsRectVisible")]
310    pub fn is_rect_visible_min_max(
311        &self,
312        rect_min: impl Into<[f32; 2]>,
313        rect_max: impl Into<[f32; 2]>,
314    ) -> bool {
315        let mn = rect_min.into();
316        let mx = rect_max.into();
317        let mn_v = sys::ImVec2 { x: mn[0], y: mn[1] };
318        let mx_v = sys::ImVec2 { x: mx[0], y: mx[1] };
319        unsafe { sys::igIsRectVisible_Vec2(mn_v, mx_v) }
320    }
321
322    /// Returns true if a rectangle of given size at the current cursor pos is visible.
323    #[doc(alias = "IsRectVisible")]
324    pub fn is_rect_visible_with_size(&self, size: impl Into<[f32; 2]>) -> bool {
325        let s = size.into();
326        let v = sys::ImVec2 { x: s[0], y: s[1] };
327        unsafe { sys::igIsRectVisible_Nil(v) }
328    }
329
330    /// Vertically align upcoming text baseline to FramePadding.y (align text to framed items).
331    #[doc(alias = "AlignTextToFramePadding")]
332    pub fn align_text_to_frame_padding(&self) {
333        unsafe { sys::igAlignTextToFramePadding() }
334    }
335}