Fix disabled widgets "eating" focus (#5370)

- fixes https://github.com/emilk/egui/issues/5359

For the test I added a `Harness::press_key` function. We should
eventually add these to kittest, probably via a trait one can implement
for the `Harness` but for now this should do.
This commit is contained in:
lucasmerlin 2024-11-26 14:31:30 +01:00 committed by GitHub
parent 83a30064f4
commit 9ecc0b232c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 58 additions and 2 deletions

View File

@ -1229,6 +1229,7 @@ dependencies = [
"ahash",
"backtrace",
"document-features",
"egui_kittest",
"emath",
"epaint",
"log",

View File

@ -98,3 +98,7 @@ log = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
ron = { workspace = true, optional = true }
serde = { workspace = true, optional = true, features = ["derive", "rc"] }
[dev-dependencies]
egui_kittest = { workspace = true, features = ["wgpu", "snapshot"] }

View File

@ -1160,6 +1160,8 @@ impl Context {
/// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_widget(&self, w: WidgetRect, allow_focus: bool) -> Response {
let interested_in_focus = w.enabled && w.sense.focusable && w.layer_id.allow_interaction();
// Remember this widget
self.write(|ctx| {
let viewport = ctx.viewport();
@ -1169,12 +1171,12 @@ impl Context {
// but also to know when we have reached the widget we are checking for cover.
viewport.this_pass.widgets.insert(w.layer_id, w);
if allow_focus && w.sense.focusable {
if allow_focus && interested_in_focus {
ctx.memory.interested_in_focus(w.id);
}
});
if allow_focus && (!w.enabled || !w.sense.focusable || !w.layer_id.allow_interaction()) {
if allow_focus && !interested_in_focus {
// Not interested or allowed input:
self.memory_mut(|mem| mem.surrender_focus(w.id));
}

View File

@ -0,0 +1,30 @@
use egui::Button;
use egui_kittest::kittest::Queryable;
use egui_kittest::Harness;
#[test]
pub fn focus_should_skip_over_disabled_buttons() {
let mut harness = Harness::new_ui(|ui| {
ui.add(Button::new("Button 1"));
ui.add_enabled(false, Button::new("Button Disabled"));
ui.add(Button::new("Button 3"));
});
harness.press_key(egui::Key::Tab);
harness.run();
let button_1 = harness.get_by_name("Button 1");
assert!(button_1.is_focused());
harness.press_key(egui::Key::Tab);
harness.run();
let button_3 = harness.get_by_name("Button 3");
assert!(button_3.is_focused());
harness.press_key(egui::Key::Tab);
harness.run();
let button_1 = harness.get_by_name("Button 1");
assert!(button_1.is_focused());
}

View File

@ -249,6 +249,25 @@ impl<'a, State> Harness<'a, State> {
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
/// Press a key.
/// This will create a key down event and a key up event.
pub fn press_key(&mut self, key: egui::Key) {
self.input.events.push(egui::Event::Key {
key,
pressed: true,
modifiers: Default::default(),
repeat: false,
physical_key: None,
});
self.input.events.push(egui::Event::Key {
key,
pressed: false,
modifiers: Default::default(),
repeat: false,
physical_key: None,
});
}
}
/// Utilities for stateless harnesses.