Skip to main content

egui/
response.rs

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