egui/
response.rs

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