1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-02 06:40:06 -04:00

Add support for scrolling via accesskit / kittest (#7286)

I need to scroll in a snapshot test in my app, and kittest had no
utilities for this. Event::MouseWheel is error prone. This adds support
for some accesskit scroll actions, and uses this in kittest to add
helpers to scroll to a node / scroll the scroll area surrounding a node.

The accesskit code says down/up/left/right `Scrolls by approximately one
screen in a specific direction.`. Unfortunately it's difficult to get
the size of a "screen" (I guess that would be the size of the containing
scroll area)where I implemented the scrolling, so for now I've hardcoded
it to 100px. I think scrolling a fixed amount is still better than not
scrolling at all.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Lucas Meurer
2025-07-03 12:02:05 +02:00
committed by GitHub
parent 378e22e6ec
commit 6d312cc4c7
7 changed files with 186 additions and 6 deletions

View File

@@ -1149,7 +1149,7 @@ impl Context {
ID clashes happens when things like Windows or CollapsingHeaders share names,\n\
or when things like Plot and Grid:s aren't given unique id_salt:s.\n\n\
Sometimes the solution is to use ui.push_id.",
if below { "above" } else { "below" })
if below { "above" } else { "below" }),
);
}
}
@@ -1216,6 +1216,51 @@ impl Context {
self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
}
#[cfg(feature = "accesskit")]
self.write(|ctx| {
use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
let viewport = ctx.viewport_for(ctx.viewport_id());
viewport
.input
.consume_accesskit_action_requests(res.id, |request| {
// TODO(lucasmerlin): Correctly handle the scroll unit:
// https://github.com/AccessKit/accesskit/blob/e639c0e0d8ccbfd9dff302d972fa06f9766d608e/common/src/lib.rs#L2621
const DISTANCE: f32 = 100.0;
match &request.action {
accesskit::Action::ScrollIntoView => {
viewport.this_pass.scroll_target = [
Some(ScrollTarget::new(
res.rect.x_range(),
Some(Align::Center),
ScrollAnimation::none(),
)),
Some(ScrollTarget::new(
res.rect.y_range(),
Some(Align::Center),
ScrollAnimation::none(),
)),
];
}
accesskit::Action::ScrollDown => {
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
}
accesskit::Action::ScrollUp => {
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
}
accesskit::Action::ScrollLeft => {
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
}
accesskit::Action::ScrollRight => {
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
}
_ => return false,
};
true
});
});
res
}

View File

@@ -824,6 +824,23 @@ impl InputState {
})
}
#[cfg(feature = "accesskit")]
pub fn consume_accesskit_action_requests(
&mut self,
id: crate::Id,
mut consume: impl FnMut(&accesskit::ActionRequest) -> bool,
) {
let accesskit_id = id.accesskit_id();
self.events.retain(|event| {
if let Event::AccessKitActionRequest(request) = event {
if request.target == accesskit_id {
return !consume(request);
}
}
true
});
}
#[cfg(feature = "accesskit")]
pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {
self.accesskit_action_requests(id, action).next().is_some()