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