egui/
response.rs

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