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

Remove last_accesskit_update and add on_accesskit_update hook

This commit is contained in:
lucasmerlin
2026-04-24 11:52:11 +02:00
parent 5373b1ce41
commit 7a105a1843
3 changed files with 21 additions and 17 deletions

View File

@@ -89,7 +89,6 @@ pub struct Harness<'a, State: 'static = ()> {
plugins: Vec<Box<dyn Plugin<State>>>,
entry_location: Option<&'static std::panic::Location<'static>>,
consumed_event_locations: Vec<&'static std::panic::Location<'static>>,
last_accesskit_update: Option<egui::accesskit::TreeUpdate>,
#[cfg(feature = "snapshot")]
default_snapshot_options: SnapshotOptions,
@@ -168,7 +167,7 @@ impl<'a, State: 'static> Harness<'a, State> {
app,
ctx,
input,
kittest: kittest::State::new(initial_accesskit.clone()),
kittest: kittest::State::new(initial_accesskit),
output,
response,
state,
@@ -181,7 +180,6 @@ impl<'a, State: 'static> Harness<'a, State> {
plugins,
entry_location: None,
consumed_event_locations: Vec::new(),
last_accesskit_update: Some(initial_accesskit),
#[cfg(feature = "snapshot")]
default_snapshot_options,
@@ -247,12 +245,6 @@ impl<'a, State: 'static> Harness<'a, State> {
self._step_inner(false);
}
/// The most recent AccessKit tree update, if any. Useful for plugins that mirror
/// the accessibility tree to an external debugger.
pub fn accesskit_tree_update(&self) -> Option<&egui::accesskit::TreeUpdate> {
self.last_accesskit_update.as_ref()
}
/// [`std::panic::Location`] of the most recent public `#[track_caller]` entry point
/// (e.g. the caller of `step()` / `run()`), or `None` if no such call has been made yet.
pub fn entry_location(&self) -> Option<&'static std::panic::Location<'static>> {
@@ -378,7 +370,7 @@ impl<'a, State: 'static> Harness<'a, State> {
.accesskit_update
.take()
.expect("AccessKit was disabled");
self.last_accesskit_update = Some(accesskit_update.clone());
self.dispatch(|p, h| p.on_accesskit_update(h, &accesskit_update));
self.kittest.update(accesskit_update);
self.renderer.handle_delta(&output.textures_delta);
self.output = output;

View File

@@ -36,7 +36,7 @@ use crate::{ExceededMaxStepsError, Harness};
/// Plugin hooks receive `&mut Harness`. Calling [`Harness::step`] / [`Harness::run`] /
/// etc. from inside a hook will recurse infinitely through your own `after_step`. If
/// a plugin needs to advance the harness from inside a hook — e.g. an inspector that
/// blocks on user input — use [`Harness::advance_frame`] instead.
/// blocks on user input — use [`Harness::step_no_side_effects`] instead.
#[expect(unused_variables, reason = "default no-op impls")]
pub trait Plugin<State = ()>: Send + Any {
/// Called once at the start of every `run()` / `try_run()` / `try_run_realtime()` /
@@ -58,6 +58,18 @@ pub trait Plugin<State = ()>: Send + Any {
/// Called immediately after each single-frame step.
fn after_step(&mut self, harness: &mut Harness<'_, State>) {}
/// Called after each single-frame step with the AccessKit tree update egui produced
/// for that frame, before it's applied to the internal kittest state.
///
/// Plugins that need the tree (e.g. to stream it to an external debugger) should
/// clone it here — the harness no longer retains it after this hook returns.
fn on_accesskit_update(
&mut self,
harness: &mut Harness<'_, State>,
tree: &egui::accesskit::TreeUpdate,
) {
}
/// Called after a queued event has been pushed into the harness input, before the
/// frame runs that consumes it.
fn on_event(&mut self, harness: &mut Harness<'_, State>, event: &egui::Event) {}

View File

@@ -119,20 +119,20 @@ fn on_event_fires_per_event() {
assert_eq!(events, 2, "expected 2 on_event calls, got log: {log:?}");
}
/// `advance_frame` does NOT fire `before_step`/`after_step`.
/// `step_no_side_effects` does NOT fire `before_step`/`after_step`.
#[test]
fn advance_frame_skips_hooks() {
fn step_no_side_effects_skips_hooks() {
struct DrivingPlugin {
log: Log,
drove: bool,
}
impl<S> Plugin<S> for DrivingPlugin {
impl<S: 'static> Plugin<S> for DrivingPlugin {
fn after_step(&mut self, h: &mut Harness<'_, S>) {
self.log.lock().unwrap().push("after_step".into());
if !self.drove {
self.drove = true;
// Call advance_frame from inside a hook — must not recurse.
h.advance_frame();
// Call step_no_side_effects from inside a hook — must not recurse.
h.step_no_side_effects();
}
}
}
@@ -152,7 +152,7 @@ fn advance_frame_skips_hooks() {
let log = log.lock().unwrap();
// Exactly one after_step from the user's step(), plus any from construction-time run_ok
// (cleared above). advance_frame must NOT have produced another after_step.
// (cleared above). step_no_side_effects must NOT have produced another after_step.
assert_eq!(log.iter().filter(|s| s == &"after_step").count(), 1);
}