1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

Allow moving existing widgets to the top of interaction stack (#7805)

This adds `Ui::interact_opt` which is a version of `Ui::interact` that
lets you specify that if this widget already exists, it should be moved
to the top of the interaction stack.

This lets you easily more easily "read a response from the future", by
simply calling interact twice, instead of calling
`Context::read_response`.
This commit is contained in:
Emil Ernerfeldt
2025-12-22 21:34:03 +01:00
committed by GitHub
parent 24ac9e193d
commit 1fdc741ebb
7 changed files with 56 additions and 5 deletions

View File

@@ -523,6 +523,7 @@ impl Area {
enabled,
},
true,
Default::default(),
);
// Used to prevent drift

View File

@@ -958,6 +958,12 @@ fn resize_interaction(
enabled: true,
},
true,
InteractOptions {
// We call this multiple times.
// First to read the result (to avoid frame delay)
// and the second time to move it to the top, above the window contents.
move_to_top: true,
},
);
response.widget_info(|| WidgetInfo::new(crate::WidgetType::ResizeHandle));

View File

@@ -1226,7 +1226,12 @@ impl Context {
///
/// `allow_focus` should usually be true, unless you call this function multiple times with the
/// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).
pub(crate) fn create_widget(&self, w: WidgetRect, allow_focus: bool) -> Response {
pub(crate) fn create_widget(
&self,
w: WidgetRect,
allow_focus: bool,
options: crate::InteractOptions,
) -> Response {
let interested_in_focus = w.enabled
&& w.sense.is_focusable()
&& self.memory(|mem| mem.allows_interaction(w.layer_id));
@@ -1238,7 +1243,7 @@ impl Context {
// We add all widgets here, even non-interactive ones,
// because we need this list not only for checking for blocking widgets,
// but also to know when we have reached the widget we are checking for cover.
viewport.this_pass.widgets.insert(w.layer_id, w);
viewport.this_pass.widgets.insert(w.layer_id, w, options);
if allow_focus && interested_in_focus {
ctx.memory.interested_in_focus(w.id, w.layer_id);

View File

@@ -494,7 +494,7 @@ pub use self::{
ui_builder::UiBuilder,
ui_stack::*,
viewport::*,
widget_rect::{WidgetRect, WidgetRects},
widget_rect::{InteractOptions, WidgetRect, WidgetRects},
widget_text::{RichText, WidgetText},
widgets::*,
};

View File

@@ -738,6 +738,7 @@ impl Response {
enabled: self.enabled(),
},
true,
Default::default(),
)
}

View File

@@ -180,6 +180,7 @@ impl Ui {
enabled: ui.enabled,
},
true,
Default::default(),
);
if disabled {
@@ -345,6 +346,7 @@ impl Ui {
enabled: child_ui.enabled,
},
true,
Default::default(),
);
child_ui
@@ -1025,6 +1027,17 @@ impl Ui {
impl Ui {
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
self.interact_opt(rect, id, sense, Default::default())
}
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
pub fn interact_opt(
&self,
rect: Rect,
id: Id,
sense: Sense,
options: crate::InteractOptions,
) -> Response {
self.ctx().register_accesskit_parent(id, self.unique_id);
self.ctx().create_widget(
@@ -1037,6 +1050,7 @@ impl Ui {
enabled: self.enabled,
},
true,
options,
)
}
@@ -1105,6 +1119,7 @@ impl Ui {
enabled: self.enabled,
},
false,
Default::default(),
);
if self.should_close() {
response.set_close();

View File

@@ -63,6 +63,21 @@ impl WidgetRect {
}
}
/// How to handle multiple calls to [`crate::Response::interact`] and [`crate::Ui::interact_opt`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InteractOptions {
/// If we call interact on the same widget multiple times,
/// should we move it to the top on subsequent calls?
pub move_to_top: bool,
}
#[expect(clippy::derivable_impls)] // Nice to be explicit
impl Default for InteractOptions {
fn default() -> Self {
Self { move_to_top: false }
}
}
/// Stores the [`WidgetRect`]s of all widgets generated during a single egui update/frame.
///
/// All [`crate::Ui`]s have a [`WidgetRect`]. It is created in [`crate::Ui::new`] with [`Rect::NOTHING`]
@@ -140,13 +155,15 @@ impl WidgetRects {
}
/// Insert the given widget rect in the given layer.
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect) {
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect, options: InteractOptions) {
let Self {
by_layer,
by_id,
infos: _,
} = self;
let InteractOptions { move_to_top } = options;
let layer_widgets = by_layer.entry(layer_id).or_default();
match by_id.entry(widget_rect.id) {
@@ -176,7 +193,13 @@ impl WidgetRects {
existing.enabled |= widget_rect.enabled;
if existing.layer_id == widget_rect.layer_id {
layer_widgets[*idx_in_layer] = *existing;
if move_to_top {
layer_widgets.remove(*idx_in_layer);
*idx_in_layer = layer_widgets.len();
layer_widgets.push(*existing);
} else {
layer_widgets[*idx_in_layer] = *existing;
}
}
}
}