dear_imgui/
layout.rs

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