dear_imgui_rs/
ui.rs

1//! Per-frame UI entry point
2//!
3//! The `Ui` type exposes most user-facing Dear ImGui APIs for a single frame:
4//! creating windows, drawing widgets, accessing draw lists, showing built-in
5//! tools and more. Obtain it from [`Context::frame`].
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("Hello, world!");
13//! ```
14//!
15use crate::Id;
16use crate::draw::DrawListMut;
17use crate::input::MouseCursor;
18use crate::internal::RawWrapper;
19use crate::string::UiBuffer;
20use crate::sys;
21use crate::texture::TextureRef;
22use std::cell::UnsafeCell;
23
24/// Represents the Dear ImGui user interface for one frame
25#[derive(Debug)]
26pub struct Ui {
27    /// Internal buffer for string operations
28    buffer: UnsafeCell<UiBuffer>,
29}
30
31impl Ui {
32    /// Returns a reference to the main Dear ImGui viewport (safe wrapper)
33    ///
34    /// Same viewport used by `dockspace_over_main_viewport()`.
35    ///
36    /// The returned reference is owned by the currently active ImGui context and
37    /// must not be used after the context is destroyed.
38    #[doc(alias = "GetMainViewport")]
39    pub fn main_viewport(&self) -> &crate::platform_io::Viewport {
40        unsafe {
41            let ptr = sys::igGetMainViewport();
42            if ptr.is_null() {
43                panic!("Ui::main_viewport() requires an active ImGui context");
44            }
45            crate::platform_io::Viewport::from_raw(ptr as *const sys::ImGuiViewport)
46        }
47    }
48    /// Creates a new Ui instance
49    ///
50    /// This should only be called by Context::create()
51    pub(crate) fn new() -> Self {
52        Ui {
53            buffer: UnsafeCell::new(UiBuffer::new(1024)),
54        }
55    }
56
57    /// Returns an immutable reference to the inputs/outputs object
58    #[doc(alias = "GetIO")]
59    pub fn io(&self) -> &crate::io::Io {
60        unsafe {
61            let io = sys::igGetIO_Nil();
62            if io.is_null() {
63                panic!("Ui::io() requires an active ImGui context");
64            }
65            &*(io as *const crate::io::Io)
66        }
67    }
68
69    /// Internal method to push a single text to our scratch buffer.
70    pub(crate) fn scratch_txt(&self, txt: impl AsRef<str>) -> *const std::os::raw::c_char {
71        unsafe {
72            let handle = &mut *self.buffer.get();
73            handle.scratch_txt(txt)
74        }
75    }
76
77    /// Internal method to push an option text to our scratch buffer.
78    pub(crate) fn scratch_txt_opt(
79        &self,
80        txt: Option<impl AsRef<str>>,
81    ) -> *const std::os::raw::c_char {
82        unsafe {
83            let handle = &mut *self.buffer.get();
84            handle.scratch_txt_opt(txt)
85        }
86    }
87
88    /// Helper method for two strings
89    pub(crate) fn scratch_txt_two(
90        &self,
91        txt_0: impl AsRef<str>,
92        txt_1: impl AsRef<str>,
93    ) -> (*const std::os::raw::c_char, *const std::os::raw::c_char) {
94        unsafe {
95            let handle = &mut *self.buffer.get();
96            handle.scratch_txt_two(txt_0, txt_1)
97        }
98    }
99
100    /// Helper method with one optional value
101    pub(crate) fn scratch_txt_with_opt(
102        &self,
103        txt_0: impl AsRef<str>,
104        txt_1: Option<impl AsRef<str>>,
105    ) -> (*const std::os::raw::c_char, *const std::os::raw::c_char) {
106        unsafe {
107            let handle = &mut *self.buffer.get();
108            handle.scratch_txt_with_opt(txt_0, txt_1)
109        }
110    }
111
112    /// Get access to the scratch buffer for complex string operations
113    pub(crate) fn scratch_buffer(&self) -> &UnsafeCell<UiBuffer> {
114        &self.buffer
115    }
116
117    /// Display text
118    #[doc(alias = "TextUnformatted")]
119    pub fn text<T: AsRef<str>>(&self, text: T) {
120        let s = text.as_ref();
121        unsafe {
122            let start = s.as_ptr();
123            let end = start.add(s.len());
124            crate::sys::igTextUnformatted(
125                start as *const std::os::raw::c_char,
126                end as *const std::os::raw::c_char,
127            );
128        }
129    }
130
131    /// Set the viewport for the next window.
132    ///
133    /// This is a convenience wrapper over `ImGui::SetNextWindowViewport`.
134    /// Useful when hosting a fullscreen DockSpace window inside the main viewport.
135    #[doc(alias = "SetNextWindowViewport")]
136    pub fn set_next_window_viewport(&self, viewport_id: Id) {
137        unsafe { sys::igSetNextWindowViewport(viewport_id.into()) }
138    }
139
140    /// Returns an ID from a string label in the current ID scope.
141    ///
142    /// This mirrors `ImGui::GetID(label)`. Useful for building stable IDs
143    /// for widgets or dockspaces inside the current window/scope.
144    #[doc(alias = "GetID")]
145    pub fn get_id(&self, label: &str) -> Id {
146        unsafe { Id::from(sys::igGetID_Str(self.scratch_txt(label))) }
147    }
148
149    /// Access to the current window's draw list
150    #[doc(alias = "GetWindowDrawList")]
151    pub fn get_window_draw_list(&self) -> DrawListMut<'_> {
152        DrawListMut::window(self)
153    }
154
155    /// Access to the background draw list
156    #[doc(alias = "GetBackgroundDrawList")]
157    pub fn get_background_draw_list(&self) -> DrawListMut<'_> {
158        DrawListMut::background(self)
159    }
160
161    /// Access to the foreground draw list
162    #[doc(alias = "GetForegroundDrawList")]
163    pub fn get_foreground_draw_list(&self) -> DrawListMut<'_> {
164        DrawListMut::foreground(self)
165    }
166
167    /// Creates a window builder
168    pub fn window<'ui>(
169        &'ui self,
170        name: impl Into<std::borrow::Cow<'ui, str>>,
171    ) -> crate::window::Window<'ui> {
172        crate::window::Window::new(self, name)
173    }
174
175    /// Renders a demo window (previously called a test window), which demonstrates most
176    /// Dear ImGui features.
177    #[doc(alias = "ShowDemoWindow")]
178    pub fn show_demo_window(&self, opened: &mut bool) {
179        unsafe {
180            crate::sys::igShowDemoWindow(opened);
181        }
182    }
183
184    /// Convenience: draw an image with background and tint (ImGui 1.92+)
185    ///
186    /// Equivalent to using `image_config(...).build_with_bg(bg, tint)` but in one call.
187    #[doc(alias = "ImageWithBg")]
188    pub fn image_with_bg(
189        &self,
190        texture: impl Into<TextureRef>,
191        size: [f32; 2],
192        bg_color: [f32; 4],
193        tint_color: [f32; 4],
194    ) {
195        crate::widget::image::Image::new(self, texture, size).build_with_bg(bg_color, tint_color)
196    }
197
198    /// Renders an about window.
199    ///
200    /// Displays the Dear ImGui version/credits, and build/system information.
201    #[doc(alias = "ShowAboutWindow")]
202    pub fn show_about_window(&self, opened: &mut bool) {
203        unsafe {
204            crate::sys::igShowAboutWindow(opened);
205        }
206    }
207
208    /// Renders a metrics/debug window.
209    ///
210    /// Displays Dear ImGui internals: draw commands (with individual draw calls and vertices),
211    /// window list, basic internal state, etc.
212    #[doc(alias = "ShowMetricsWindow")]
213    pub fn show_metrics_window(&self, opened: &mut bool) {
214        unsafe {
215            crate::sys::igShowMetricsWindow(opened);
216        }
217    }
218
219    /// Renders a style editor block (not a window) for the given `Style` structure
220    #[doc(alias = "ShowStyleEditor")]
221    pub fn show_style_editor(&self, style: &mut crate::style::Style) {
222        unsafe {
223            crate::sys::igShowStyleEditor(style.raw_mut());
224        }
225    }
226
227    /// Renders a style editor block (not a window) for the currently active style
228    #[doc(alias = "ShowStyleEditor")]
229    pub fn show_default_style_editor(&self) {
230        unsafe {
231            crate::sys::igShowStyleEditor(std::ptr::null_mut());
232        }
233    }
234
235    /// Renders a basic help/info block (not a window)
236    #[doc(alias = "ShowUserGuide")]
237    pub fn show_user_guide(&self) {
238        unsafe {
239            crate::sys::igShowUserGuide();
240        }
241    }
242
243    // Drag widgets
244
245    /// Creates a drag float slider
246    #[doc(alias = "DragFloat")]
247    pub fn drag_float(&self, label: impl AsRef<str>, value: &mut f32) -> bool {
248        crate::widget::drag::Drag::new(label).build(self, value)
249    }
250
251    /// Creates a drag float slider with configuration
252    #[doc(alias = "DragFloat")]
253    pub fn drag_float_config<L: AsRef<str>>(&self, label: L) -> crate::widget::drag::Drag<f32, L> {
254        crate::widget::drag::Drag::new(label)
255    }
256
257    /// Creates a drag int slider
258    #[doc(alias = "DragInt")]
259    pub fn drag_int(&self, label: impl AsRef<str>, value: &mut i32) -> bool {
260        crate::widget::drag::Drag::new(label).build(self, value)
261    }
262
263    /// Creates a drag int slider with configuration
264    #[doc(alias = "DragInt")]
265    pub fn drag_int_config<L: AsRef<str>>(&self, label: L) -> crate::widget::drag::Drag<i32, L> {
266        crate::widget::drag::Drag::new(label)
267    }
268
269    /// Creates a drag float range slider
270    #[doc(alias = "DragFloatRange2")]
271    pub fn drag_float_range2(&self, label: impl AsRef<str>, min: &mut f32, max: &mut f32) -> bool {
272        crate::widget::drag::DragRange::<f32, _>::new(label).build(self, min, max)
273    }
274
275    /// Creates a drag float range slider with configuration
276    #[doc(alias = "DragFloatRange2")]
277    pub fn drag_float_range2_config<L: AsRef<str>>(
278        &self,
279        label: L,
280    ) -> crate::widget::drag::DragRange<f32, L> {
281        crate::widget::drag::DragRange::new(label)
282    }
283
284    /// Creates a drag int range slider
285    #[doc(alias = "DragIntRange2")]
286    pub fn drag_int_range2(&self, label: impl AsRef<str>, min: &mut i32, max: &mut i32) -> bool {
287        crate::widget::drag::DragRange::<i32, _>::new(label).build(self, min, max)
288    }
289
290    /// Creates a drag int range slider with configuration
291    #[doc(alias = "DragIntRange2")]
292    pub fn drag_int_range2_config<L: AsRef<str>>(
293        &self,
294        label: L,
295    ) -> crate::widget::drag::DragRange<i32, L> {
296        crate::widget::drag::DragRange::new(label)
297    }
298
299    /// Returns the currently desired mouse cursor type
300    ///
301    /// Returns `None` if no cursor should be displayed
302    #[doc(alias = "GetMouseCursor")]
303    pub fn mouse_cursor(&self) -> Option<MouseCursor> {
304        unsafe {
305            match sys::igGetMouseCursor() {
306                sys::ImGuiMouseCursor_Arrow => Some(MouseCursor::Arrow),
307                sys::ImGuiMouseCursor_TextInput => Some(MouseCursor::TextInput),
308                sys::ImGuiMouseCursor_ResizeAll => Some(MouseCursor::ResizeAll),
309                sys::ImGuiMouseCursor_ResizeNS => Some(MouseCursor::ResizeNS),
310                sys::ImGuiMouseCursor_ResizeEW => Some(MouseCursor::ResizeEW),
311                sys::ImGuiMouseCursor_ResizeNESW => Some(MouseCursor::ResizeNESW),
312                sys::ImGuiMouseCursor_ResizeNWSE => Some(MouseCursor::ResizeNWSE),
313                sys::ImGuiMouseCursor_Hand => Some(MouseCursor::Hand),
314                sys::ImGuiMouseCursor_NotAllowed => Some(MouseCursor::NotAllowed),
315                _ => None,
316            }
317        }
318    }
319
320    /// Sets the desired mouse cursor type
321    ///
322    /// Passing `None` hides the mouse cursor
323    #[doc(alias = "SetMouseCursor")]
324    pub fn set_mouse_cursor(&self, cursor_type: Option<MouseCursor>) {
325        unsafe {
326            let val: sys::ImGuiMouseCursor = cursor_type
327                .map(|x| x as sys::ImGuiMouseCursor)
328                .unwrap_or(sys::ImGuiMouseCursor_None);
329            sys::igSetMouseCursor(val);
330        }
331    }
332
333    // ============================================================================
334    // Focus and Navigation
335    // ============================================================================
336
337    /// Focuses keyboard on the next widget.
338    ///
339    /// This is the equivalent to [set_keyboard_focus_here_with_offset](Self::set_keyboard_focus_here_with_offset)
340    /// with `offset` set to 0.
341    #[doc(alias = "SetKeyboardFocusHere")]
342    pub fn set_keyboard_focus_here(&self) {
343        self.set_keyboard_focus_here_with_offset(0);
344    }
345
346    /// Focuses keyboard on a widget relative to current position.
347    ///
348    /// Use positive offset to focus on next widgets, negative offset to focus on previous widgets.
349    #[doc(alias = "SetKeyboardFocusHere")]
350    pub fn set_keyboard_focus_here_with_offset(&self, offset: i32) {
351        unsafe {
352            sys::igSetKeyboardFocusHere(offset);
353        }
354    }
355
356    /// Set next item to be open by default.
357    ///
358    /// This is useful for tree nodes, collapsing headers, etc.
359    #[doc(alias = "SetNextItemOpen")]
360    pub fn set_next_item_open(&self, is_open: bool) {
361        unsafe {
362            sys::igSetNextItemOpen(is_open, 0); // 0 = ImGuiCond_Always
363        }
364    }
365
366    /// Set next item to be open by default with condition.
367    #[doc(alias = "SetNextItemOpen")]
368    pub fn set_next_item_open_with_cond(&self, is_open: bool, cond: crate::Condition) {
369        unsafe { sys::igSetNextItemOpen(is_open, cond as sys::ImGuiCond) }
370    }
371
372    /// Set next item width.
373    ///
374    /// Set to 0.0 for default width, >0.0 for explicit width, <0.0 for relative width.
375    #[doc(alias = "SetNextItemWidth")]
376    pub fn set_next_item_width(&self, item_width: f32) {
377        unsafe {
378            sys::igSetNextItemWidth(item_width);
379        }
380    }
381
382    // ============================================================================
383    // Style Access
384    // ============================================================================
385
386    /// Returns a shared reference to the current [`Style`].
387    ///
388    /// ## Safety
389    ///
390    /// This function is tagged as `unsafe` because pushing via
391    /// [`push_style_color`](crate::Ui::push_style_color) or
392    /// [`push_style_var`](crate::Ui::push_style_var) or popping via
393    /// [`ColorStackToken::pop`](crate::ColorStackToken::pop) or
394    /// [`StyleStackToken::pop`](crate::StyleStackToken::pop) will modify the values in the returned
395    /// shared reference. Therefore, you should not retain this reference across calls to push and
396    /// pop. The [`clone_style`](Ui::clone_style) version may instead be used to avoid `unsafe`.
397    #[doc(alias = "GetStyle")]
398    pub unsafe fn style(&self) -> &crate::Style {
399        unsafe {
400            // safe because Style is a transparent wrapper around sys::ImGuiStyle
401            &*(sys::igGetStyle() as *const crate::Style)
402        }
403    }
404
405    /// Returns a copy of the current style.
406    ///
407    /// This is a safe alternative to [`style`](Self::style) that avoids the lifetime issues.
408    #[doc(alias = "GetStyle")]
409    pub fn clone_style(&self) -> crate::Style {
410        unsafe { self.style().clone() }
411    }
412
413    /// Apply the built-in Dark style to the current style.
414    #[doc(alias = "StyleColorsDark")]
415    pub fn style_colors_dark(&self) {
416        unsafe { sys::igStyleColorsDark(std::ptr::null_mut()) }
417    }
418
419    /// Apply the built-in Light style to the current style.
420    #[doc(alias = "StyleColorsLight")]
421    pub fn style_colors_light(&self) {
422        unsafe { sys::igStyleColorsLight(std::ptr::null_mut()) }
423    }
424
425    /// Apply the built-in Classic style to the current style.
426    #[doc(alias = "StyleColorsClassic")]
427    pub fn style_colors_classic(&self) {
428        unsafe { sys::igStyleColorsClassic(std::ptr::null_mut()) }
429    }
430
431    /// Write the Dark style values into the provided [`Style`] object.
432    #[doc(alias = "StyleColorsDark")]
433    pub fn style_colors_dark_into(&self, dst: &mut crate::Style) {
434        unsafe { sys::igStyleColorsDark(dst.raw_mut() as *mut sys::ImGuiStyle) }
435    }
436
437    /// Write the Light style values into the provided [`Style`] object.
438    #[doc(alias = "StyleColorsLight")]
439    pub fn style_colors_light_into(&self, dst: &mut crate::Style) {
440        unsafe { sys::igStyleColorsLight(dst.raw_mut() as *mut sys::ImGuiStyle) }
441    }
442
443    /// Write the Classic style values into the provided [`Style`] object.
444    #[doc(alias = "StyleColorsClassic")]
445    pub fn style_colors_classic_into(&self, dst: &mut crate::Style) {
446        unsafe { sys::igStyleColorsClassic(dst.raw_mut() as *mut sys::ImGuiStyle) }
447    }
448
449    /// Returns DPI scale currently associated to the current window's viewport.
450    #[doc(alias = "GetWindowDpiScale")]
451    pub fn window_dpi_scale(&self) -> f32 {
452        unsafe { sys::igGetWindowDpiScale() }
453    }
454
455    /// Display a text label with a boolean value (for quick debug UIs).
456    #[doc(alias = "Value")]
457    pub fn value_bool(&self, prefix: impl AsRef<str>, v: bool) {
458        unsafe { sys::igValue_Bool(self.scratch_txt(prefix), v) }
459    }
460
461    /// Get current window width (shortcut for `GetWindowSize().x`).
462    #[doc(alias = "GetWindowWidth")]
463    pub fn window_width(&self) -> f32 {
464        unsafe { sys::igGetWindowWidth() }
465    }
466
467    /// Get current window height (shortcut for `GetWindowSize().y`).
468    #[doc(alias = "GetWindowHeight")]
469    pub fn window_height(&self) -> f32 {
470        unsafe { sys::igGetWindowHeight() }
471    }
472
473    /// Get current window position in screen space.
474    #[doc(alias = "GetWindowPos")]
475    pub fn window_pos(&self) -> [f32; 2] {
476        let v = unsafe { sys::igGetWindowPos() };
477        [v.x, v.y]
478    }
479
480    /// Get current window size.
481    #[doc(alias = "GetWindowSize")]
482    pub fn window_size(&self) -> [f32; 2] {
483        let v = unsafe { sys::igGetWindowSize() };
484        [v.x, v.y]
485    }
486
487    // ============================================================================
488    // Additional Demo, Debug, Information (non-duplicate methods)
489    // ============================================================================
490
491    /// Renders a debug log window.
492    ///
493    /// Displays a simplified log of important dear imgui events.
494    #[doc(alias = "ShowDebugLogWindow")]
495    pub fn show_debug_log_window(&self, opened: &mut bool) {
496        unsafe {
497            sys::igShowDebugLogWindow(opened);
498        }
499    }
500
501    /// Renders an ID stack tool window.
502    ///
503    /// Hover items with mouse to query information about the source of their unique ID.
504    #[doc(alias = "ShowIDStackToolWindow")]
505    pub fn show_id_stack_tool_window(&self, opened: &mut bool) {
506        unsafe {
507            sys::igShowIDStackToolWindow(opened);
508        }
509    }
510
511    /// Renders a style selector combo box.
512    ///
513    /// Returns true when a different style was selected.
514    #[doc(alias = "ShowStyleSelector")]
515    pub fn show_style_selector(&self, label: impl AsRef<str>) -> bool {
516        unsafe { sys::igShowStyleSelector(self.scratch_txt(label)) }
517    }
518
519    /// Renders a font selector combo box.
520    #[doc(alias = "ShowFontSelector")]
521    pub fn show_font_selector(&self, label: impl AsRef<str>) {
522        unsafe {
523            sys::igShowFontSelector(self.scratch_txt(label));
524        }
525    }
526
527    /// Returns the Dear ImGui version string
528    #[doc(alias = "GetVersion")]
529    pub fn get_version(&self) -> &str {
530        unsafe {
531            let version_ptr = sys::igGetVersion();
532            if version_ptr.is_null() {
533                return "Unknown";
534            }
535            let c_str = std::ffi::CStr::from_ptr(version_ptr);
536            c_str.to_str().unwrap_or("Unknown")
537        }
538    }
539}