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:
@@ -523,6 +523,7 @@ impl Area {
|
||||
enabled,
|
||||
},
|
||||
true,
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
// Used to prevent drift
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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::*,
|
||||
};
|
||||
|
||||
@@ -738,6 +738,7 @@ impl Response {
|
||||
enabled: self.enabled(),
|
||||
},
|
||||
true,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user