Skip to main content

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