egui/
response.rs

1use std::{any::Any, sync::Arc};
2
3use crate::{
4    emath::{Align, Pos2, Rect, Vec2},
5    menu, AreaState, Context, CursorIcon, Id, LayerId, Order, PointerButton, Sense, Ui, WidgetRect,
6    WidgetText,
7};
8
9// ----------------------------------------------------------------------------
10
11/// The result of adding a widget to a [`Ui`].
12///
13/// A [`Response`] lets you know whether or not a widget is being hovered, clicked or dragged.
14/// It also lets you easily show a tooltip on hover.
15///
16/// Whenever something gets added to a [`Ui`], a [`Response`] object is returned.
17/// [`ui.add`] returns a [`Response`], as does [`ui.button`], and all similar shortcuts.
18///
19/// ⚠️ The `Response` contains a clone of [`Context`], and many methods lock the `Context`.
20/// It can therefor be a deadlock to use `Context` from within a context-locking closures,
21/// such as [`Context::input`].
22#[derive(Clone, Debug)]
23pub struct Response {
24    // CONTEXT:
25    /// Used for optionally showing a tooltip and checking for more interactions.
26    pub ctx: Context,
27
28    // IN:
29    /// Which layer the widget is part of.
30    pub layer_id: LayerId,
31
32    /// The [`Id`] of the widget/area this response pertains.
33    pub id: Id,
34
35    /// The area of the screen we are talking about.
36    pub rect: Rect,
37
38    /// The rectangle sensing interaction.
39    ///
40    /// This is sometimes smaller than [`Self::rect`] because of clipping
41    /// (e.g. when inside a scroll area).
42    pub interact_rect: Rect,
43
44    /// The senses (click and/or drag) that the widget was interested in (if any).
45    ///
46    /// Note: if [`Self::enabled`] is `false`, then
47    /// the widget _effectively_ doesn't sense anything,
48    /// but can still have the same `Sense`.
49    /// This is because the sense informs the styling of the widget,
50    /// but we don't want to change the style when a widget is disabled
51    /// (that is handled by the `Painter` directly).
52    pub sense: Sense,
53
54    /// Was the widget enabled?
55    /// If `false`, there was no interaction attempted (not even hover).
56    #[doc(hidden)]
57    pub enabled: bool,
58
59    // OUT:
60    /// The pointer is above this widget with no other blocking it.
61    #[doc(hidden)]
62    pub contains_pointer: bool,
63
64    /// The pointer is hovering above this widget or the widget was clicked/tapped this frame.
65    #[doc(hidden)]
66    pub hovered: bool,
67
68    /// The widget is highlighted via a call to [`Self::highlight`] or [`Context::highlight_widget`].
69    #[doc(hidden)]
70    pub highlighted: bool,
71
72    /// This widget was clicked this frame.
73    ///
74    /// Which pointer and how many times we don't know,
75    /// and ask [`crate::InputState`] about at runtime.
76    ///
77    /// This is only set to true if the widget was clicked
78    /// by an actual mouse.
79    #[doc(hidden)]
80    pub clicked: bool,
81
82    /// This widget should act as if clicked due
83    /// to something else than a click.
84    ///
85    /// This is set to true if the widget has keyboard focus and
86    /// the user hit the Space or Enter key.
87    #[doc(hidden)]
88    pub fake_primary_click: bool,
89
90    /// This widget was long-pressed on a touch screen to simulate a secondary click.
91    #[doc(hidden)]
92    pub long_touched: bool,
93
94    /// The widget started being dragged this frame.
95    #[doc(hidden)]
96    pub drag_started: bool,
97
98    /// The widget is being dragged.
99    #[doc(hidden)]
100    pub dragged: bool,
101
102    /// The widget was being dragged, but now it has been released.
103    #[doc(hidden)]
104    pub drag_stopped: bool,
105
106    /// Is the pointer button currently down on this widget?
107    /// This is true if the pointer is pressing down or dragging a widget
108    #[doc(hidden)]
109    pub is_pointer_button_down_on: bool,
110
111    /// Where the pointer (mouse/touch) were when when this widget was clicked or dragged.
112    /// `None` if the widget is not being interacted with.
113    #[doc(hidden)]
114    pub interact_pointer_pos: Option<Pos2>,
115
116    /// Was the underlying data changed?
117    ///
118    /// e.g. the slider was dragged, text was entered in a [`TextEdit`](crate::TextEdit) etc.
119    /// Always `false` for something like a [`Button`](crate::Button).
120    #[doc(hidden)]
121    pub changed: bool,
122}
123
124impl Response {
125    /// Returns true if this widget was clicked this frame by the primary button.
126    ///
127    /// A click is registered when the mouse or touch is released within
128    /// a certain amount of time and distance from when and where it was pressed.
129    ///
130    /// This will also return true if the widget was clicked via accessibility integration,
131    /// or if the widget had keyboard focus and the use pressed Space/Enter.
132    ///
133    /// Note that the widget must be sensing clicks with [`Sense::click`].
134    /// [`crate::Button`] senses clicks; [`crate::Label`] does not (unless you call [`crate::Label::sense`]).
135    ///
136    /// You can use [`Self::interact`] to sense more things *after* adding a widget.
137    #[inline(always)]
138    pub fn clicked(&self) -> bool {
139        self.fake_primary_click || self.clicked_by(PointerButton::Primary)
140    }
141
142    /// Returns true if this widget was clicked this frame by the given mouse button.
143    ///
144    /// This will NOT return true if the widget was "clicked" via
145    /// some accessibility integration, or if the widget had keyboard focus and the
146    /// user pressed Space/Enter. For that, use [`Self::clicked`] instead.
147    ///
148    /// This will likewise ignore the press-and-hold action on touch screens.
149    /// Use [`Self::secondary_clicked`] instead to also detect that.
150    #[inline]
151    pub fn clicked_by(&self, button: PointerButton) -> bool {
152        self.clicked && self.ctx.input(|i| i.pointer.button_clicked(button))
153    }
154
155    /// Returns true if this widget was clicked this frame by the secondary mouse button (e.g. the right mouse button).
156    ///
157    /// This also returns true if the widget was pressed-and-held on a touch screen.
158    #[inline]
159    pub fn secondary_clicked(&self) -> bool {
160        self.long_touched || self.clicked_by(PointerButton::Secondary)
161    }
162
163    /// Was this long-pressed on a touch screen?
164    ///
165    /// Usually you want to check [`Self::secondary_clicked`] instead.
166    #[inline]
167    pub fn long_touched(&self) -> bool {
168        self.long_touched
169    }
170
171    /// Returns true if this widget was clicked this frame by the middle mouse button.
172    #[inline]
173    pub fn middle_clicked(&self) -> bool {
174        self.clicked_by(PointerButton::Middle)
175    }
176
177    /// Returns true if this widget was double-clicked this frame by the primary button.
178    #[inline]
179    pub fn double_clicked(&self) -> bool {
180        self.double_clicked_by(PointerButton::Primary)
181    }
182
183    /// Returns true if this widget was triple-clicked this frame by the primary button.
184    #[inline]
185    pub fn triple_clicked(&self) -> bool {
186        self.triple_clicked_by(PointerButton::Primary)
187    }
188
189    /// Returns true if this widget was double-clicked this frame by the given button.
190    #[inline]
191    pub fn double_clicked_by(&self, button: PointerButton) -> bool {
192        self.clicked && self.ctx.input(|i| i.pointer.button_double_clicked(button))
193    }
194
195    /// Returns true if this widget was triple-clicked this frame by the given button.
196    #[inline]
197    pub fn triple_clicked_by(&self, button: PointerButton) -> bool {
198        self.clicked && self.ctx.input(|i| i.pointer.button_triple_clicked(button))
199    }
200
201    /// `true` if there was a click *outside* the rect of this widget.
202    ///
203    /// Clicks on widgets contained in this one counts as clicks inside this widget,
204    /// so that clicking a button in an area will not be considered as clicking "elsewhere" from the area.
205    pub fn clicked_elsewhere(&self) -> bool {
206        // We do not use self.clicked(), because we want to catch all clicks within our frame,
207        // even if we aren't clickable (or even enabled).
208        // This is important for windows and such that should close then the user clicks elsewhere.
209        self.ctx.input(|i| {
210            let pointer = &i.pointer;
211
212            if pointer.any_click() {
213                if self.contains_pointer || self.hovered {
214                    false
215                } else if let Some(pos) = pointer.interact_pos() {
216                    !self.interact_rect.contains(pos)
217                } else {
218                    false // clicked without a pointer, weird
219                }
220            } else {
221                false
222            }
223        })
224    }
225
226    /// Was the widget enabled?
227    /// If false, there was no interaction attempted
228    /// and the widget should be drawn in a gray disabled look.
229    #[inline(always)]
230    pub fn enabled(&self) -> bool {
231        self.enabled
232    }
233
234    /// The pointer is hovering above this widget or the widget was clicked/tapped this frame.
235    ///
236    /// In contrast to [`Self::contains_pointer`], this will be `false` whenever some other widget is being dragged.
237    /// `hovered` is always `false` for disabled widgets.
238    #[inline(always)]
239    pub fn hovered(&self) -> bool {
240        self.hovered
241    }
242
243    /// Returns true if the pointer is contained by the response rect, and no other widget is covering it.
244    ///
245    /// In contrast to [`Self::hovered`], this can be `true` even if some other widget is being dragged.
246    /// This means it is useful for styling things like drag-and-drop targets.
247    /// `contains_pointer` can also be `true` for disabled widgets.
248    ///
249    /// This is slightly different from [`Ui::rect_contains_pointer`] and [`Context::rect_contains_pointer`], in that
250    /// [`Self::contains_pointer`] also checks that no other widget is covering this response rectangle.
251    #[inline(always)]
252    pub fn contains_pointer(&self) -> bool {
253        self.contains_pointer
254    }
255
256    /// The widget is highlighted via a call to [`Self::highlight`] or [`Context::highlight_widget`].
257    #[doc(hidden)]
258    #[inline(always)]
259    pub fn highlighted(&self) -> bool {
260        self.highlighted
261    }
262
263    /// This widget has the keyboard focus (i.e. is receiving key presses).
264    ///
265    /// This function only returns true if the UI as a whole (e.g. window)
266    /// also has the keyboard focus. That makes this function suitable
267    /// for style choices, e.g. a thicker border around focused widgets.
268    pub fn has_focus(&self) -> bool {
269        self.ctx.input(|i| i.focused) && self.ctx.memory(|mem| mem.has_focus(self.id))
270    }
271
272    /// True if this widget has keyboard focus this frame, but didn't last frame.
273    pub fn gained_focus(&self) -> bool {
274        self.ctx.memory(|mem| mem.gained_focus(self.id))
275    }
276
277    /// The widget had keyboard focus and lost it,
278    /// either because the user pressed tab or clicked somewhere else,
279    /// or (in case of a [`crate::TextEdit`]) because the user pressed enter.
280    ///
281    /// ```
282    /// # egui::__run_test_ui(|ui| {
283    /// # let mut my_text = String::new();
284    /// # fn do_request(_: &str) {}
285    /// let response = ui.text_edit_singleline(&mut my_text);
286    /// if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
287    ///     do_request(&my_text);
288    /// }
289    /// # });
290    /// ```
291    pub fn lost_focus(&self) -> bool {
292        self.ctx.memory(|mem| mem.lost_focus(self.id))
293    }
294
295    /// Request that this widget get keyboard focus.
296    pub fn request_focus(&self) {
297        self.ctx.memory_mut(|mem| mem.request_focus(self.id));
298    }
299
300    /// Surrender keyboard focus for this widget.
301    pub fn surrender_focus(&self) {
302        self.ctx.memory_mut(|mem| mem.surrender_focus(self.id));
303    }
304
305    /// Did a drag on this widgets begin this frame?
306    ///
307    /// This is only true if the widget sense drags.
308    /// If the widget also senses clicks, this will only become true if the pointer has moved a bit.
309    ///
310    /// This will only be true for a single frame.
311    #[inline]
312    pub fn drag_started(&self) -> bool {
313        self.drag_started
314    }
315
316    /// Did a drag on this widgets by the button begin this frame?
317    ///
318    /// This is only true if the widget sense drags.
319    /// If the widget also senses clicks, this will only become true if the pointer has moved a bit.
320    ///
321    /// This will only be true for a single frame.
322    #[inline]
323    pub fn drag_started_by(&self, button: PointerButton) -> bool {
324        self.drag_started() && self.ctx.input(|i| i.pointer.button_down(button))
325    }
326
327    /// The widget is being dragged.
328    ///
329    /// To find out which button(s), use [`Self::dragged_by`].
330    ///
331    /// If the widget is only sensitive to drags, this is `true` as soon as the pointer presses down on it.
332    /// If the widget is also sensitive to drags, this won't be true until the pointer has moved a bit,
333    /// or the user has pressed down for long enough.
334    /// See [`crate::input_state::PointerState::is_decidedly_dragging`] for details.
335    ///
336    /// If you want to avoid the delay, use [`Self::is_pointer_button_down_on`] instead.
337    ///
338    /// If the widget is NOT sensitive to drags, this will always be `false`.
339    /// [`crate::DragValue`] senses drags; [`crate::Label`] does not (unless you call [`crate::Label::sense`]).
340    /// You can use [`Self::interact`] to sense more things *after* adding a widget.
341    #[inline(always)]
342    pub fn dragged(&self) -> bool {
343        self.dragged
344    }
345
346    /// See [`Self::dragged`].
347    #[inline]
348    pub fn dragged_by(&self, button: PointerButton) -> bool {
349        self.dragged() && self.ctx.input(|i| i.pointer.button_down(button))
350    }
351
352    /// The widget was being dragged, but now it has been released.
353    #[inline]
354    pub fn drag_stopped(&self) -> bool {
355        self.drag_stopped
356    }
357
358    /// The widget was being dragged by the button, but now it has been released.
359    pub fn drag_stopped_by(&self, button: PointerButton) -> bool {
360        self.drag_stopped() && self.ctx.input(|i| i.pointer.button_released(button))
361    }
362
363    /// The widget was being dragged, but now it has been released.
364    #[inline]
365    #[deprecated = "Renamed 'drag_stopped'"]
366    pub fn drag_released(&self) -> bool {
367        self.drag_stopped
368    }
369
370    /// The widget was being dragged by the button, but now it has been released.
371    #[deprecated = "Renamed 'drag_stopped_by'"]
372    pub fn drag_released_by(&self, button: PointerButton) -> bool {
373        self.drag_stopped_by(button)
374    }
375
376    /// If dragged, how many points were we dragged and in what direction?
377    #[inline]
378    pub fn drag_delta(&self) -> Vec2 {
379        if self.dragged() {
380            let mut delta = self.ctx.input(|i| i.pointer.delta());
381            if let Some(scaling) = self
382                .ctx
383                .memory(|m| m.layer_transforms.get(&self.layer_id).map(|t| t.scaling))
384            {
385                delta /= scaling;
386            }
387            delta
388        } else {
389            Vec2::ZERO
390        }
391    }
392
393    /// If dragged, how far did the mouse move?
394    /// This will use raw mouse movement if provided by the integration, otherwise will fall back to [`Response::drag_delta`]
395    /// Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen.
396    /// This may be useful in certain situations such as draggable values and 3D cameras, where screen position does not matter.
397    #[inline]
398    pub fn drag_motion(&self) -> Vec2 {
399        if self.dragged() {
400            self.ctx
401                .input(|i| i.pointer.motion().unwrap_or(i.pointer.delta()))
402        } else {
403            Vec2::ZERO
404        }
405    }
406
407    /// If the user started dragging this widget this frame, store the payload for drag-and-drop.
408    #[doc(alias = "drag and drop")]
409    pub fn dnd_set_drag_payload<Payload: Any + Send + Sync>(&self, payload: Payload) {
410        if self.drag_started() {
411            crate::DragAndDrop::set_payload(&self.ctx, payload);
412        }
413
414        if self.hovered() && !self.sense.click {
415            // Things that can be drag-dropped should use the Grab cursor icon,
416            // but if the thing is _also_ clickable, that can be annoying.
417            self.ctx.set_cursor_icon(CursorIcon::Grab);
418        }
419    }
420
421    /// Drag-and-Drop: Return what is being held over this widget, if any.
422    ///
423    /// Only returns something if [`Self::contains_pointer`] is true,
424    /// and the user is drag-dropping something of this type.
425    #[doc(alias = "drag and drop")]
426    pub fn dnd_hover_payload<Payload: Any + Send + Sync>(&self) -> Option<Arc<Payload>> {
427        // NOTE: we use `response.contains_pointer` here instead of `hovered`, because
428        // `hovered` is always false when another widget is being dragged.
429        if self.contains_pointer() {
430            crate::DragAndDrop::payload::<Payload>(&self.ctx)
431        } else {
432            None
433        }
434    }
435
436    /// Drag-and-Drop: Return what is being dropped onto this widget, if any.
437    ///
438    /// Only returns something if [`Self::contains_pointer`] is true,
439    /// the user is drag-dropping something of this type,
440    /// and they released it this frame
441    #[doc(alias = "drag and drop")]
442    pub fn dnd_release_payload<Payload: Any + Send + Sync>(&self) -> Option<Arc<Payload>> {
443        // NOTE: we use `response.contains_pointer` here instead of `hovered`, because
444        // `hovered` is always false when another widget is being dragged.
445        if self.contains_pointer() && self.ctx.input(|i| i.pointer.any_released()) {
446            crate::DragAndDrop::take_payload::<Payload>(&self.ctx)
447        } else {
448            None
449        }
450    }
451
452    /// Where the pointer (mouse/touch) were when when this widget was clicked or dragged.
453    ///
454    /// `None` if the widget is not being interacted with.
455    #[inline]
456    pub fn interact_pointer_pos(&self) -> Option<Pos2> {
457        self.interact_pointer_pos
458    }
459
460    /// If it is a good idea to show a tooltip, where is pointer?
461    ///
462    /// None if the pointer is outside the response area.
463    #[inline]
464    pub fn hover_pos(&self) -> Option<Pos2> {
465        if self.hovered() {
466            let mut pos = self.ctx.input(|i| i.pointer.hover_pos())?;
467            if let Some(transform) = self
468                .ctx
469                .memory(|m| m.layer_transforms.get(&self.layer_id).copied())
470            {
471                pos = transform.inverse() * pos;
472            }
473            Some(pos)
474        } else {
475            None
476        }
477    }
478
479    /// Is the pointer button currently down on this widget?
480    ///
481    /// This is true if the pointer is pressing down or dragging a widget,
482    /// even when dragging outside the widget.
483    ///
484    /// This could also be thought of as "is this widget being interacted with?".
485    #[inline(always)]
486    pub fn is_pointer_button_down_on(&self) -> bool {
487        self.is_pointer_button_down_on
488    }
489
490    /// Was the underlying data changed?
491    ///
492    /// e.g. the slider was dragged, text was entered in a [`TextEdit`](crate::TextEdit) etc.
493    /// Always `false` for something like a [`Button`](crate::Button).
494    ///
495    /// Can sometimes be `true` even though the data didn't changed
496    /// (e.g. if the user entered a character and erased it the same frame).
497    ///
498    /// This is not set if the *view* of the data was changed.
499    /// For instance, moving the cursor in a [`TextEdit`](crate::TextEdit) does not set this to `true`.
500    #[inline(always)]
501    pub fn changed(&self) -> bool {
502        self.changed
503    }
504
505    /// Report the data shown by this widget changed.
506    ///
507    /// This must be called by widgets that represent some mutable data,
508    /// e.g. checkboxes, sliders etc.
509    ///
510    /// This should be called when the *content* changes, but not when the view does.
511    /// So we call this when the text of a [`crate::TextEdit`], but not when the cursors changes.
512    #[inline(always)]
513    pub fn mark_changed(&mut self) {
514        self.changed = true;
515    }
516
517    /// Show this UI if the widget was hovered (i.e. a tooltip).
518    ///
519    /// The text will not be visible if the widget is not enabled.
520    /// For that, use [`Self::on_disabled_hover_ui`] instead.
521    ///
522    /// If you call this multiple times the tooltips will stack underneath the previous ones.
523    ///
524    /// The widget can contain interactive widgets, such as buttons and links.
525    /// If so, it will stay open as the user moves their pointer over it.
526    /// By default, the text of a tooltip is NOT selectable (i.e. interactive),
527    /// but you can change this by setting [`style::Interaction::selectable_labels` from within the tooltip:
528    ///
529    /// ```
530    /// # egui::__run_test_ui(|ui| {
531    /// ui.label("Hover me").on_hover_ui(|ui| {
532    ///     ui.style_mut().interaction.selectable_labels = true;
533    ///     ui.label("This text can be selected");
534    /// });
535    /// # });
536    /// ```
537    #[doc(alias = "tooltip")]
538    pub fn on_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
539        if self.enabled && self.should_show_hover_ui() {
540            self.show_tooltip_ui(add_contents);
541        }
542        self
543    }
544
545    /// Show this UI when hovering if the widget is disabled.
546    pub fn on_disabled_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
547        if !self.enabled && self.should_show_hover_ui() {
548            crate::containers::show_tooltip_for(
549                &self.ctx,
550                self.layer_id,
551                self.id,
552                &self.rect,
553                add_contents,
554            );
555        }
556        self
557    }
558
559    /// Like `on_hover_ui`, but show the ui next to cursor.
560    pub fn on_hover_ui_at_pointer(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
561        if self.enabled && self.should_show_hover_ui() {
562            crate::containers::show_tooltip_at_pointer(
563                &self.ctx,
564                self.layer_id,
565                self.id,
566                add_contents,
567            );
568        }
569        self
570    }
571
572    /// Always show this tooltip, even if disabled and the user isn't hovering it.
573    ///
574    /// This can be used to give attention to a widget during a tutorial.
575    pub fn show_tooltip_ui(&self, add_contents: impl FnOnce(&mut Ui)) {
576        crate::containers::show_tooltip_for(
577            &self.ctx,
578            self.layer_id,
579            self.id,
580            &self.rect,
581            add_contents,
582        );
583    }
584
585    /// Always show this tooltip, even if disabled and the user isn't hovering it.
586    ///
587    /// This can be used to give attention to a widget during a tutorial.
588    pub fn show_tooltip_text(&self, text: impl Into<WidgetText>) {
589        self.show_tooltip_ui(|ui| {
590            ui.label(text);
591        });
592    }
593
594    /// Was the tooltip open last frame?
595    pub fn is_tooltip_open(&self) -> bool {
596        crate::popup::was_tooltip_open_last_frame(&self.ctx, self.id)
597    }
598
599    fn should_show_hover_ui(&self) -> bool {
600        if self.ctx.memory(|mem| mem.everything_is_visible()) {
601            return true;
602        }
603
604        let any_open_popups = self.ctx.prev_frame_state(|fs| {
605            fs.layers
606                .get(&self.layer_id)
607                .map_or(false, |layer| !layer.open_popups.is_empty())
608        });
609        if any_open_popups {
610            // Hide tooltips if the user opens a popup (menu, combo-box, etc) in the same layer.
611            return false;
612        }
613
614        let style = self.ctx.style();
615
616        let tooltip_delay = style.interaction.tooltip_delay;
617        let tooltip_grace_time = style.interaction.tooltip_grace_time;
618
619        let (
620            time_since_last_scroll,
621            time_since_last_click,
622            time_since_last_pointer_movement,
623            pointer_pos,
624            pointer_dir,
625        ) = self.ctx.input(|i| {
626            (
627                i.time_since_last_scroll(),
628                i.pointer.time_since_last_click(),
629                i.pointer.time_since_last_movement(),
630                i.pointer.hover_pos(),
631                i.pointer.direction(),
632            )
633        });
634
635        if time_since_last_scroll < tooltip_delay {
636            // See https://github.com/emilk/egui/issues/4781
637            // Note that this means we cannot have `ScrollArea`s in a tooltip.
638            self.ctx
639                .request_repaint_after_secs(tooltip_delay - time_since_last_scroll);
640            return false;
641        }
642
643        let is_our_tooltip_open = self.is_tooltip_open();
644
645        if is_our_tooltip_open {
646            // Check if we should automatically stay open:
647
648            let tooltip_id = crate::next_tooltip_id(&self.ctx, self.id);
649            let tooltip_layer_id = LayerId::new(Order::Tooltip, tooltip_id);
650
651            let tooltip_has_interactive_widget = self.ctx.viewport(|vp| {
652                vp.prev_frame
653                    .widgets
654                    .get_layer(tooltip_layer_id)
655                    .any(|w| w.enabled && w.sense.interactive())
656            });
657
658            if tooltip_has_interactive_widget {
659                // We keep the tooltip open if hovered,
660                // or if the pointer is on its way to it,
661                // so that the user can interact with the tooltip
662                // (i.e. click links that are in it).
663                if let Some(area) = AreaState::load(&self.ctx, tooltip_id) {
664                    let rect = area.rect();
665
666                    if let Some(pos) = pointer_pos {
667                        if rect.contains(pos) {
668                            return true; // hovering interactive tooltip
669                        }
670                        if pointer_dir != Vec2::ZERO
671                            && rect.intersects_ray(pos, pointer_dir.normalized())
672                        {
673                            return true; // on the way to interactive tooltip
674                        }
675                    }
676                }
677            }
678        }
679
680        let clicked_more_recently_than_moved =
681            time_since_last_click < time_since_last_pointer_movement + 0.1;
682        if clicked_more_recently_than_moved {
683            // It is common to click a widget and then rest the mouse there.
684            // It would be annoying to then see a tooltip for it immediately.
685            // Similarly, clicking should hide the existing tooltip.
686            // Only hovering should lead to a tooltip, not clicking.
687            // The offset is only to allow small movement just right after the click.
688            return false;
689        }
690
691        if is_our_tooltip_open {
692            // Check if we should automatically stay open:
693
694            if pointer_pos.is_some_and(|pointer_pos| self.rect.contains(pointer_pos)) {
695                // Handle the case of a big tooltip that covers the widget:
696                return true;
697            }
698        }
699
700        let is_other_tooltip_open = self.ctx.prev_frame_state(|fs| {
701            if let Some(already_open_tooltip) = fs
702                .layers
703                .get(&self.layer_id)
704                .and_then(|layer| layer.widget_with_tooltip)
705            {
706                already_open_tooltip != self.id
707            } else {
708                false
709            }
710        });
711        if is_other_tooltip_open {
712            // We only allow one tooltip per layer. First one wins. It is up to that tooltip to close itself.
713            return false;
714        }
715
716        // Fast early-outs:
717        if self.enabled {
718            if !self.hovered || !self.ctx.input(|i| i.pointer.has_pointer()) {
719                return false;
720            }
721        } else if !self.ctx.rect_contains_pointer(self.layer_id, self.rect) {
722            return false;
723        }
724
725        // There is a tooltip_delay before showing the first tooltip,
726        // but once one tooltips is show, moving the mouse cursor to
727        // another widget should show the tooltip for that widget right away.
728
729        // Let the user quickly move over some dead space to hover the next thing
730        let tooltip_was_recently_shown =
731            crate::popup::seconds_since_last_tooltip(&self.ctx) < tooltip_grace_time;
732
733        if !tooltip_was_recently_shown && !is_our_tooltip_open {
734            if style.interaction.show_tooltips_only_when_still {
735                // We only show the tooltip when the mouse pointer is still.
736                if !self
737                    .ctx
738                    .input(|i| i.pointer.is_still() && i.smooth_scroll_delta == Vec2::ZERO)
739                {
740                    // wait for mouse to stop
741                    self.ctx.request_repaint();
742                    return false;
743                }
744            }
745
746            let time_since_last_interaction = time_since_last_scroll
747                .min(time_since_last_pointer_movement)
748                .min(time_since_last_click);
749            let time_til_tooltip = tooltip_delay - time_since_last_interaction;
750
751            if 0.0 < time_til_tooltip {
752                // Wait until the mouse has been still for a while
753                self.ctx.request_repaint_after_secs(time_til_tooltip);
754                return false;
755            }
756        }
757
758        // We don't want tooltips of things while we are dragging them,
759        // but we do want tooltips while holding down on an item on a touch screen.
760        if self
761            .ctx
762            .input(|i| i.pointer.any_down() && i.pointer.has_moved_too_much_for_a_click)
763        {
764            return false;
765        }
766
767        // All checks passed: show the tooltip!
768
769        true
770    }
771
772    /// Like `on_hover_text`, but show the text next to cursor.
773    #[doc(alias = "tooltip")]
774    pub fn on_hover_text_at_pointer(self, text: impl Into<WidgetText>) -> Self {
775        self.on_hover_ui_at_pointer(|ui| {
776            ui.add(crate::widgets::Label::new(text));
777        })
778    }
779
780    /// Show this text if the widget was hovered (i.e. a tooltip).
781    ///
782    /// The text will not be visible if the widget is not enabled.
783    /// For that, use [`Self::on_disabled_hover_text`] instead.
784    ///
785    /// If you call this multiple times the tooltips will stack underneath the previous ones.
786    #[doc(alias = "tooltip")]
787    pub fn on_hover_text(self, text: impl Into<WidgetText>) -> Self {
788        self.on_hover_ui(|ui| {
789            ui.add(crate::widgets::Label::new(text));
790        })
791    }
792
793    /// Highlight this widget, to make it look like it is hovered, even if it isn't.
794    ///
795    /// The highlight takes one frame to take effect if you call this after the widget has been fully rendered.
796    ///
797    /// See also [`Context::highlight_widget`].
798    #[inline]
799    pub fn highlight(mut self) -> Self {
800        self.ctx.highlight_widget(self.id);
801        self.highlighted = true;
802        self
803    }
804
805    /// Show this text when hovering if the widget is disabled.
806    pub fn on_disabled_hover_text(self, text: impl Into<WidgetText>) -> Self {
807        self.on_disabled_hover_ui(|ui| {
808            ui.add(crate::widgets::Label::new(text));
809        })
810    }
811
812    /// When hovered, use this icon for the mouse cursor.
813    #[inline]
814    pub fn on_hover_cursor(self, cursor: CursorIcon) -> Self {
815        if self.hovered() {
816            self.ctx.set_cursor_icon(cursor);
817        }
818        self
819    }
820
821    /// When hovered or dragged, use this icon for the mouse cursor.
822    #[inline]
823    pub fn on_hover_and_drag_cursor(self, cursor: CursorIcon) -> Self {
824        if self.hovered() || self.dragged() {
825            self.ctx.set_cursor_icon(cursor);
826        }
827        self
828    }
829
830    /// Sense more interactions (e.g. sense clicks on a [`Response`] returned from a label).
831    ///
832    /// The interaction will occur on the same plane as the original widget,
833    /// i.e. if the response was from a widget behind button, the interaction will also be behind that button.
834    /// egui gives priority to the _last_ added widget (the one on top gets clicked first).
835    ///
836    /// Note that this call will not add any hover-effects to the widget, so when possible
837    /// it is better to give the widget a [`Sense`] instead, e.g. using [`crate::Label::sense`].
838    ///
839    /// Using this method on a `Response` that is the result of calling `union` on multiple `Response`s
840    /// is undefined behavior.
841    ///
842    /// ```
843    /// # egui::__run_test_ui(|ui| {
844    /// let horiz_response = ui.horizontal(|ui| {
845    ///     ui.label("hello");
846    /// }).response;
847    /// assert!(!horiz_response.clicked()); // ui's don't sense clicks by default
848    /// let horiz_response = horiz_response.interact(egui::Sense::click());
849    /// if horiz_response.clicked() {
850    ///     // The background behind the label was clicked
851    /// }
852    /// # });
853    /// ```
854    #[must_use]
855    pub fn interact(&self, sense: Sense) -> Self {
856        if (self.sense | sense) == self.sense {
857            // Early-out: we already sense everything we need to sense.
858            return self.clone();
859        }
860
861        self.ctx.create_widget(WidgetRect {
862            layer_id: self.layer_id,
863            id: self.id,
864            rect: self.rect,
865            interact_rect: self.interact_rect,
866            sense: self.sense | sense,
867            enabled: self.enabled,
868        })
869    }
870
871    /// Adjust the scroll position until this UI becomes visible.
872    ///
873    /// If `align` is [`Align::TOP`] it means "put the top of the rect at the top of the scroll area", etc.
874    /// If `align` is `None`, it'll scroll enough to bring the UI into view.
875    ///
876    /// See also: [`Ui::scroll_to_cursor`], [`Ui::scroll_to_rect`]. [`Ui::scroll_with_delta`].
877    ///
878    /// ```
879    /// # egui::__run_test_ui(|ui| {
880    /// egui::ScrollArea::vertical().show(ui, |ui| {
881    ///     for i in 0..1000 {
882    ///         let response = ui.button("Scroll to me");
883    ///         if response.clicked() {
884    ///             response.scroll_to_me(Some(egui::Align::Center));
885    ///         }
886    ///     }
887    /// });
888    /// # });
889    /// ```
890    pub fn scroll_to_me(&self, align: Option<Align>) {
891        self.ctx.frame_state_mut(|state| {
892            state.scroll_target[0] = Some((self.rect.x_range(), align));
893            state.scroll_target[1] = Some((self.rect.y_range(), align));
894        });
895    }
896
897    /// For accessibility.
898    ///
899    /// Call after interacting and potential calls to [`Self::mark_changed`].
900    pub fn widget_info(&self, make_info: impl Fn() -> crate::WidgetInfo) {
901        use crate::output::OutputEvent;
902
903        let event = if self.clicked() {
904            Some(OutputEvent::Clicked(make_info()))
905        } else if self.double_clicked() {
906            Some(OutputEvent::DoubleClicked(make_info()))
907        } else if self.triple_clicked() {
908            Some(OutputEvent::TripleClicked(make_info()))
909        } else if self.gained_focus() {
910            Some(OutputEvent::FocusGained(make_info()))
911        } else if self.changed {
912            Some(OutputEvent::ValueChanged(make_info()))
913        } else {
914            None
915        };
916
917        if let Some(event) = event {
918            self.output_event(event);
919        } else {
920            #[cfg(feature = "accesskit")]
921            self.ctx.accesskit_node_builder(self.id, |builder| {
922                self.fill_accesskit_node_from_widget_info(builder, make_info());
923            });
924
925            self.ctx.register_widget_info(self.id, make_info);
926        }
927    }
928
929    pub fn output_event(&self, event: crate::output::OutputEvent) {
930        #[cfg(feature = "accesskit")]
931        self.ctx.accesskit_node_builder(self.id, |builder| {
932            self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());
933        });
934
935        self.ctx
936            .register_widget_info(self.id, || event.widget_info().clone());
937
938        self.ctx.output_mut(|o| o.events.push(event));
939    }
940
941    #[cfg(feature = "accesskit")]
942    pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::NodeBuilder) {
943        if !self.enabled {
944            builder.set_disabled();
945        }
946        builder.set_bounds(accesskit::Rect {
947            x0: self.rect.min.x.into(),
948            y0: self.rect.min.y.into(),
949            x1: self.rect.max.x.into(),
950            y1: self.rect.max.y.into(),
951        });
952        if self.sense.focusable {
953            builder.add_action(accesskit::Action::Focus);
954        }
955        if self.sense.click && builder.default_action_verb().is_none() {
956            builder.set_default_action_verb(accesskit::DefaultActionVerb::Click);
957        }
958    }
959
960    #[cfg(feature = "accesskit")]
961    fn fill_accesskit_node_from_widget_info(
962        &self,
963        builder: &mut accesskit::NodeBuilder,
964        info: crate::WidgetInfo,
965    ) {
966        use crate::WidgetType;
967        use accesskit::{Checked, Role};
968
969        self.fill_accesskit_node_common(builder);
970        builder.set_role(match info.typ {
971            WidgetType::Label => Role::StaticText,
972            WidgetType::Link => Role::Link,
973            WidgetType::TextEdit => Role::TextInput,
974            WidgetType::Button | WidgetType::ImageButton | WidgetType::CollapsingHeader => {
975                Role::Button
976            }
977            WidgetType::Checkbox => Role::CheckBox,
978            WidgetType::RadioButton => Role::RadioButton,
979            WidgetType::SelectableLabel => Role::ToggleButton,
980            WidgetType::ComboBox => Role::ComboBox,
981            WidgetType::Slider => Role::Slider,
982            WidgetType::DragValue => Role::SpinButton,
983            WidgetType::ColorButton => Role::ColorWell,
984            WidgetType::ProgressIndicator => Role::ProgressIndicator,
985            WidgetType::Other => Role::Unknown,
986        });
987        if !info.enabled {
988            builder.set_disabled();
989        }
990        if let Some(label) = info.label {
991            builder.set_name(label);
992        }
993        if let Some(value) = info.current_text_value {
994            builder.set_value(value);
995        }
996        if let Some(value) = info.value {
997            builder.set_numeric_value(value);
998        }
999        if let Some(selected) = info.selected {
1000            builder.set_checked(if selected {
1001                Checked::True
1002            } else {
1003                Checked::False
1004            });
1005        } else if matches!(info.typ, WidgetType::Checkbox) {
1006            // Indeterminate state
1007            builder.set_checked(Checked::Mixed);
1008        }
1009    }
1010
1011    /// Associate a label with a control for accessibility.
1012    ///
1013    /// # Example
1014    ///
1015    /// ```
1016    /// # egui::__run_test_ui(|ui| {
1017    /// # let mut text = "Arthur".to_string();
1018    /// ui.horizontal(|ui| {
1019    ///     let label = ui.label("Your name: ");
1020    ///     ui.text_edit_singleline(&mut text).labelled_by(label.id);
1021    /// });
1022    /// # });
1023    /// ```
1024    pub fn labelled_by(self, id: Id) -> Self {
1025        #[cfg(feature = "accesskit")]
1026        self.ctx.accesskit_node_builder(self.id, |builder| {
1027            builder.push_labelled_by(id.accesskit_id());
1028        });
1029        #[cfg(not(feature = "accesskit"))]
1030        {
1031            let _ = id;
1032        }
1033
1034        self
1035    }
1036
1037    /// Response to secondary clicks (right-clicks) by showing the given menu.
1038    ///
1039    /// Make sure the widget senses clicks (e.g. [`crate::Button`] does, [`crate::Label`] does not).
1040    ///
1041    /// ```
1042    /// # use egui::{Label, Sense};
1043    /// # egui::__run_test_ui(|ui| {
1044    /// let response = ui.add(Label::new("Right-click me!").sense(Sense::click()));
1045    /// response.context_menu(|ui| {
1046    ///     if ui.button("Close the menu").clicked() {
1047    ///         ui.close_menu();
1048    ///     }
1049    /// });
1050    /// # });
1051    /// ```
1052    ///
1053    /// See also: [`Ui::menu_button`] and [`Ui::close_menu`].
1054    pub fn context_menu(&self, add_contents: impl FnOnce(&mut Ui)) -> Option<InnerResponse<()>> {
1055        menu::context_menu(self, add_contents)
1056    }
1057
1058    /// Returns whether a context menu is currently open for this widget.
1059    ///
1060    /// See [`Self::context_menu`].
1061    pub fn context_menu_opened(&self) -> bool {
1062        menu::context_menu_opened(self)
1063    }
1064
1065    /// Draw a debug rectangle over the response displaying the response's id and whether it is
1066    /// enabled and/or hovered.
1067    ///
1068    /// This function is intended for debugging purpose and can be useful, for example, in case of
1069    /// widget id instability.
1070    ///
1071    /// Color code:
1072    /// - Blue: Enabled but not hovered
1073    /// - Green: Enabled and hovered
1074    /// - Red: Disabled
1075    pub fn paint_debug_info(&self) {
1076        self.ctx.debug_painter().debug_rect(
1077            self.rect,
1078            if self.hovered {
1079                crate::Color32::DARK_GREEN
1080            } else if self.enabled {
1081                crate::Color32::BLUE
1082            } else {
1083                crate::Color32::RED
1084            },
1085            format!("{:?}", self.id),
1086        );
1087    }
1088}
1089
1090impl Response {
1091    /// A logical "or" operation.
1092    /// For instance `a.union(b).hovered` means "was either a or b hovered?".
1093    ///
1094    /// The resulting [`Self::id`] will come from the first (`self`) argument.
1095    ///
1096    /// You may not call [`Self::interact`] on the resulting `Response`.
1097    pub fn union(&self, other: Self) -> Self {
1098        assert!(self.ctx == other.ctx);
1099        debug_assert!(
1100            self.layer_id == other.layer_id,
1101            "It makes no sense to combine Responses from two different layers"
1102        );
1103        Self {
1104            ctx: other.ctx,
1105            layer_id: self.layer_id,
1106            id: self.id,
1107            rect: self.rect.union(other.rect),
1108            interact_rect: self.interact_rect.union(other.interact_rect),
1109            sense: self.sense.union(other.sense),
1110            enabled: self.enabled || other.enabled,
1111            contains_pointer: self.contains_pointer || other.contains_pointer,
1112            hovered: self.hovered || other.hovered,
1113            highlighted: self.highlighted || other.highlighted,
1114            clicked: self.clicked || other.clicked,
1115            fake_primary_click: self.fake_primary_click || other.fake_primary_click,
1116            long_touched: self.long_touched || other.long_touched,
1117            drag_started: self.drag_started || other.drag_started,
1118            dragged: self.dragged || other.dragged,
1119            drag_stopped: self.drag_stopped || other.drag_stopped,
1120            is_pointer_button_down_on: self.is_pointer_button_down_on
1121                || other.is_pointer_button_down_on,
1122            interact_pointer_pos: self.interact_pointer_pos.or(other.interact_pointer_pos),
1123            changed: self.changed || other.changed,
1124        }
1125    }
1126}
1127
1128impl Response {
1129    /// Returns a response with a modified [`Self::rect`].
1130    #[inline]
1131    pub fn with_new_rect(self, rect: Rect) -> Self {
1132        Self { rect, ..self }
1133    }
1134}
1135
1136/// See [`Response::union`].
1137///
1138/// To summarize the response from many widgets you can use this pattern:
1139///
1140/// ```
1141/// use egui::*;
1142/// fn draw_vec2(ui: &mut Ui, v: &mut Vec2) -> Response {
1143///     ui.add(DragValue::new(&mut v.x)) | ui.add(DragValue::new(&mut v.y))
1144/// }
1145/// ```
1146///
1147/// Now `draw_vec2(ui, foo).hovered` is true if either [`DragValue`](crate::DragValue) were hovered.
1148impl std::ops::BitOr for Response {
1149    type Output = Self;
1150
1151    fn bitor(self, rhs: Self) -> Self {
1152        self.union(rhs)
1153    }
1154}
1155
1156/// See [`Response::union`].
1157///
1158/// To summarize the response from many widgets you can use this pattern:
1159///
1160/// ```
1161/// # egui::__run_test_ui(|ui| {
1162/// # let (widget_a, widget_b, widget_c) = (egui::Label::new("a"), egui::Label::new("b"), egui::Label::new("c"));
1163/// let mut response = ui.add(widget_a);
1164/// response |= ui.add(widget_b);
1165/// response |= ui.add(widget_c);
1166/// if response.hovered() { ui.label("You hovered at least one of the widgets"); }
1167/// # });
1168/// ```
1169impl std::ops::BitOrAssign for Response {
1170    fn bitor_assign(&mut self, rhs: Self) {
1171        *self = self.union(rhs);
1172    }
1173}
1174
1175// ----------------------------------------------------------------------------
1176
1177/// Returned when we wrap some ui-code and want to return both
1178/// the results of the inner function and the ui as a whole, e.g.:
1179///
1180/// ```
1181/// # egui::__run_test_ui(|ui| {
1182/// let inner_resp = ui.horizontal(|ui| {
1183///     ui.label("Blah blah");
1184///     42
1185/// });
1186/// inner_resp.response.on_hover_text("You hovered the horizontal layout");
1187/// assert_eq!(inner_resp.inner, 42);
1188/// # });
1189/// ```
1190#[derive(Debug)]
1191pub struct InnerResponse<R> {
1192    /// What the user closure returned.
1193    pub inner: R,
1194
1195    /// The response of the area.
1196    pub response: Response,
1197}
1198
1199impl<R> InnerResponse<R> {
1200    #[inline]
1201    pub fn new(inner: R, response: Response) -> Self {
1202        Self { inner, response }
1203    }
1204}