Skip to main content

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