Skip to main content

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        crate::utils::validate_item_hovered_flags("Ui::is_item_hovered_with_flags()", flags);
125        unsafe { sys::igIsItemHovered(flags.bits()) }
126    }
127
128    /// Returns true if the last item is active (e.g. button being held, text field being edited).
129    #[doc(alias = "IsItemActive")]
130    pub fn is_item_active(&self) -> bool {
131        unsafe { sys::igIsItemActive() }
132    }
133
134    /// Returns true if the last item is focused (e.g. text input field).
135    #[doc(alias = "IsItemFocused")]
136    pub fn is_item_focused(&self) -> bool {
137        unsafe { sys::igIsItemFocused() }
138    }
139
140    /// Returns true if the last item was just clicked.
141    #[doc(alias = "IsItemClicked")]
142    pub fn is_item_clicked(&self) -> bool {
143        unsafe { sys::igIsItemClicked(crate::input::MouseButton::Left as i32) }
144    }
145
146    /// Returns true if the last item was clicked with specific mouse button.
147    #[doc(alias = "IsItemClicked")]
148    pub fn is_item_clicked_with_button(&self, mouse_button: MouseButton) -> bool {
149        unsafe { sys::igIsItemClicked(mouse_button as i32) }
150    }
151
152    /// Returns true if the last item is visible (not clipped).
153    #[doc(alias = "IsItemVisible")]
154    pub fn is_item_visible(&self) -> bool {
155        unsafe { sys::igIsItemVisible() }
156    }
157
158    /// Returns true if the last item was just made active (e.g. button was pressed).
159    #[doc(alias = "IsItemActivated")]
160    pub fn is_item_activated(&self) -> bool {
161        unsafe { sys::igIsItemActivated() }
162    }
163
164    /// Returns true if the last item was just made inactive (e.g. button was released).
165    #[doc(alias = "IsItemDeactivated")]
166    pub fn is_item_deactivated(&self) -> bool {
167        unsafe { sys::igIsItemDeactivated() }
168    }
169
170    /// Returns true if the last item was just made inactive and was edited.
171    #[doc(alias = "IsItemDeactivatedAfterEdit")]
172    pub fn is_item_deactivated_after_edit(&self) -> bool {
173        unsafe { sys::igIsItemDeactivatedAfterEdit() }
174    }
175
176    /// Returns true if the last item was edited.
177    ///
178    /// This is typically used to detect value changes for widgets.
179    #[doc(alias = "IsItemEdited")]
180    pub fn is_item_edited(&self) -> bool {
181        unsafe { sys::igIsItemEdited() }
182    }
183
184    /// Returns true if any item is active.
185    #[doc(alias = "IsAnyItemActive")]
186    pub fn is_any_item_active(&self) -> bool {
187        unsafe { sys::igIsAnyItemActive() }
188    }
189
190    /// Returns true if any item is focused.
191    #[doc(alias = "IsAnyItemFocused")]
192    pub fn is_any_item_focused(&self) -> bool {
193        unsafe { sys::igIsAnyItemFocused() }
194    }
195
196    /// Returns true if any item is hovered.
197    #[doc(alias = "IsAnyItemHovered")]
198    pub fn is_any_item_hovered(&self) -> bool {
199        unsafe { sys::igIsAnyItemHovered() }
200    }
201
202    /// Gets the bounding rectangle of the last item in screen space.
203    #[doc(alias = "GetItemRectMin", alias = "GetItemRectMax")]
204    pub fn item_rect(&self) -> ([f32; 2], [f32; 2]) {
205        let min = unsafe { sys::igGetItemRectMin() };
206        let max = unsafe { sys::igGetItemRectMax() };
207        ([min.x, min.y], [max.x, max.y])
208    }
209
210    /// Gets the size of the last item.
211    #[doc(alias = "GetItemRectSize")]
212    pub fn item_rect_size(&self) -> [f32; 2] {
213        let size = unsafe { sys::igGetItemRectSize() };
214        [size.x, size.y]
215    }
216
217    /// Returns the ImGui ID of the last item.
218    #[doc(alias = "GetItemID")]
219    pub fn item_id(&self) -> crate::Id {
220        unsafe { crate::Id::from(sys::igGetItemID()) }
221    }
222}
223
224/// Tracks a tooltip that can be ended by calling `.end()` or by dropping
225#[must_use]
226pub struct TooltipToken<'ui> {
227    _ui: &'ui Ui,
228}
229
230impl<'ui> TooltipToken<'ui> {
231    /// Creates a new tooltip token
232    fn new(ui: &'ui Ui) -> Self {
233        TooltipToken { _ui: ui }
234    }
235
236    /// Ends the tooltip
237    pub fn end(self) {
238        // The drop implementation will handle the actual ending
239    }
240}
241
242impl<'ui> Drop for TooltipToken<'ui> {
243    fn drop(&mut self) {
244        unsafe {
245            sys::igEndTooltip();
246        }
247    }
248}