dear_imgui_rs/widget/
tooltip.rs

1//! Tooltips
2//!
3//! Tooltip helpers with automatic lifetime management via tokens and
4//! convenient `with_tooltip` patterns.
5//!
6#![allow(
7    clippy::cast_possible_truncation,
8    clippy::cast_sign_loss,
9    clippy::as_conversions
10)]
11use crate::input::MouseButton;
12use crate::sys;
13use crate::ui::Ui;
14
15/// # Tooltip Widgets
16impl Ui {
17    /// Construct a tooltip window that can have any kind of content.
18    ///
19    /// Typically used with `Ui::is_item_hovered()` or some other conditional check.
20    ///
21    /// # Examples
22    ///
23    /// ```no_run
24    /// # use dear_imgui_rs::*;
25    /// # let mut ctx = Context::create();
26    /// # let ui = ctx.frame();
27    /// ui.text("Hover over me");
28    /// if ui.is_item_hovered() {
29    ///     ui.tooltip(|| {
30    ///         ui.text_colored([1.0, 0.0, 0.0, 1.0], "I'm red!");
31    ///     });
32    /// }
33    /// ```
34    #[doc(alias = "BeginTooltip", alias = "EndTooltip")]
35    pub fn tooltip<F: FnOnce()>(&self, f: F) {
36        if let Some(_token) = self.begin_tooltip() {
37            f();
38        }
39    }
40
41    /// Construct a tooltip window that can have any kind of content.
42    ///
43    /// Returns a `TooltipToken` that must be ended by calling `.end()` or by dropping.
44    #[doc(alias = "BeginTooltip")]
45    pub fn begin_tooltip(&self) -> Option<TooltipToken<'_>> {
46        if unsafe { sys::igBeginTooltip() } {
47            Some(TooltipToken::new(self))
48        } else {
49            None
50        }
51    }
52
53    /// Shortcut to call [`Self::tooltip`] with simple text content.
54    ///
55    /// # Examples
56    ///
57    /// ```no_run
58    /// # use dear_imgui_rs::*;
59    /// # let mut ctx = Context::create();
60    /// # let ui = ctx.frame();
61    /// ui.text("Hover over me");
62    /// if ui.is_item_hovered() {
63    ///     ui.tooltip_text("I'm a tooltip!");
64    /// }
65    /// ```
66    #[doc(alias = "BeginTooltip", alias = "EndTooltip", alias = "SetTooltip")]
67    pub fn tooltip_text(&self, text: impl AsRef<str>) {
68        self.tooltip(|| self.text(text));
69    }
70
71    /// Sets a tooltip with simple text content.
72    ///
73    /// This renders unformatted text (no `%`-style formatting) and avoids calling C variadic APIs.
74    #[doc(alias = "SetTooltip")]
75    pub fn set_tooltip(&self, text: impl AsRef<str>) {
76        let s = text.as_ref();
77        unsafe {
78            // Avoid calling C variadic APIs: build a tooltip window and render unformatted text.
79            if sys::igBeginTooltip() {
80                let begin = s.as_ptr() as *const std::os::raw::c_char;
81                let end = begin.add(s.len());
82                sys::igTextUnformatted(begin, end);
83                sys::igEndTooltip();
84            }
85        }
86    }
87
88    /// Sets a tooltip with formatted text content.
89    #[doc(alias = "SetTooltip")]
90    pub fn set_tooltip_formatted(&self, text: impl AsRef<str>) {
91        self.set_tooltip(text);
92    }
93
94    /// Sets a tooltip for the last item with simple text content.
95    ///
96    /// Uses the non-variadic `BeginItemTooltip` path and renders unformatted text.
97    #[doc(alias = "SetItemTooltip")]
98    pub fn set_item_tooltip(&self, text: impl AsRef<str>) {
99        let s = text.as_ref();
100        unsafe {
101            // Prefer the non-variadic ImGui 1.9x+ API when available.
102            if sys::igBeginItemTooltip() {
103                let begin = s.as_ptr() as *const std::os::raw::c_char;
104                let end = begin.add(s.len());
105                sys::igTextUnformatted(begin, end);
106                sys::igEndTooltip();
107            }
108        }
109    }
110}
111
112/// # Item/Widget Utilities and Query Functions
113impl Ui {
114    /// Returns true if the last item is being hovered by mouse (and usable).
115    /// This is typically used to show tooltips.
116    #[doc(alias = "IsItemHovered")]
117    pub fn is_item_hovered(&self) -> bool {
118        unsafe { sys::igIsItemHovered(crate::HoveredFlags::NONE.bits()) }
119    }
120
121    /// Returns true if the last item is being hovered by mouse with specific flags.
122    #[doc(alias = "IsItemHovered")]
123    pub fn is_item_hovered_with_flags(&self, flags: crate::HoveredFlags) -> bool {
124        unsafe { sys::igIsItemHovered(flags.bits()) }
125    }
126
127    /// Returns true if the last item is active (e.g. button being held, text field being edited).
128    #[doc(alias = "IsItemActive")]
129    pub fn is_item_active(&self) -> bool {
130        unsafe { sys::igIsItemActive() }
131    }
132
133    /// Returns true if the last item is focused (e.g. text input field).
134    #[doc(alias = "IsItemFocused")]
135    pub fn is_item_focused(&self) -> bool {
136        unsafe { sys::igIsItemFocused() }
137    }
138
139    /// Returns true if the last item was just clicked.
140    #[doc(alias = "IsItemClicked")]
141    pub fn is_item_clicked(&self) -> bool {
142        unsafe { sys::igIsItemClicked(crate::input::MouseButton::Left as i32) }
143    }
144
145    /// Returns true if the last item was clicked with specific mouse button.
146    #[doc(alias = "IsItemClicked")]
147    pub fn is_item_clicked_with_button(&self, mouse_button: MouseButton) -> bool {
148        unsafe { sys::igIsItemClicked(mouse_button as i32) }
149    }
150
151    /// Returns true if the last item is visible (not clipped).
152    #[doc(alias = "IsItemVisible")]
153    pub fn is_item_visible(&self) -> bool {
154        unsafe { sys::igIsItemVisible() }
155    }
156
157    /// Returns true if the last item was just made active (e.g. button was pressed).
158    #[doc(alias = "IsItemActivated")]
159    pub fn is_item_activated(&self) -> bool {
160        unsafe { sys::igIsItemActivated() }
161    }
162
163    /// Returns true if the last item was just made inactive (e.g. button was released).
164    #[doc(alias = "IsItemDeactivated")]
165    pub fn is_item_deactivated(&self) -> bool {
166        unsafe { sys::igIsItemDeactivated() }
167    }
168
169    /// Returns true if the last item was just made inactive and was edited.
170    #[doc(alias = "IsItemDeactivatedAfterEdit")]
171    pub fn is_item_deactivated_after_edit(&self) -> bool {
172        unsafe { sys::igIsItemDeactivatedAfterEdit() }
173    }
174
175    /// Returns true if any item is active.
176    #[doc(alias = "IsAnyItemActive")]
177    pub fn is_any_item_active(&self) -> bool {
178        unsafe { sys::igIsAnyItemActive() }
179    }
180
181    /// Returns true if any item is focused.
182    #[doc(alias = "IsAnyItemFocused")]
183    pub fn is_any_item_focused(&self) -> bool {
184        unsafe { sys::igIsAnyItemFocused() }
185    }
186
187    /// Returns true if any item is hovered.
188    #[doc(alias = "IsAnyItemHovered")]
189    pub fn is_any_item_hovered(&self) -> bool {
190        unsafe { sys::igIsAnyItemHovered() }
191    }
192
193    /// Gets the bounding rectangle of the last item in screen space.
194    #[doc(alias = "GetItemRectMin", alias = "GetItemRectMax")]
195    pub fn item_rect(&self) -> ([f32; 2], [f32; 2]) {
196        let min = unsafe { sys::igGetItemRectMin() };
197        let max = unsafe { sys::igGetItemRectMax() };
198        ([min.x, min.y], [max.x, max.y])
199    }
200
201    /// Gets the size of the last item.
202    #[doc(alias = "GetItemRectSize")]
203    pub fn item_rect_size(&self) -> [f32; 2] {
204        let size = unsafe { sys::igGetItemRectSize() };
205        [size.x, size.y]
206    }
207}
208
209/// Tracks a tooltip that can be ended by calling `.end()` or by dropping
210#[must_use]
211pub struct TooltipToken<'ui> {
212    ui: &'ui Ui,
213}
214
215impl<'ui> TooltipToken<'ui> {
216    /// Creates a new tooltip token
217    fn new(ui: &'ui Ui) -> Self {
218        TooltipToken { ui }
219    }
220
221    /// Ends the tooltip
222    pub fn end(self) {
223        // The drop implementation will handle the actual ending
224    }
225}
226
227impl<'ui> Drop for TooltipToken<'ui> {
228    fn drop(&mut self) {
229        unsafe {
230            sys::igEndTooltip();
231        }
232    }
233}