mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 14:50:03 -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:
@@ -28,6 +28,7 @@ pub use builder::*;
|
||||
pub use node::*;
|
||||
pub use renderer::*;
|
||||
|
||||
use egui::style::ScrollAnimation;
|
||||
use egui::{Key, Modifiers, Pos2, Rect, RepaintCause, Vec2, ViewportId};
|
||||
use kittest::Queryable;
|
||||
|
||||
@@ -55,6 +56,10 @@ impl Display for ExceededMaxStepsError {
|
||||
/// The [Harness] has a optional generic state that can be used to pass data to the app / ui closure.
|
||||
/// In _most cases_ it should be fine to just store the state in the closure itself.
|
||||
/// The state functions are useful if you need to access the state after the harness has been created.
|
||||
///
|
||||
/// Some egui style options are changed from the defaults:
|
||||
/// - The cursor blinking is disabled
|
||||
/// - The scroll animation is disabled
|
||||
pub struct Harness<'a, State = ()> {
|
||||
pub ctx: egui::Context,
|
||||
input: egui::RawInput,
|
||||
@@ -96,8 +101,12 @@ impl<'a, State> Harness<'a, State> {
|
||||
let ctx = ctx.unwrap_or_default();
|
||||
ctx.set_theme(theme);
|
||||
ctx.enable_accesskit();
|
||||
// Disable cursor blinking so it doesn't interfere with snapshots
|
||||
ctx.all_styles_mut(|style| style.visuals.text_cursor.blink = false);
|
||||
ctx.all_styles_mut(|style| {
|
||||
// Disable cursor blinking so it doesn't interfere with snapshots
|
||||
style.visuals.text_cursor.blink = false;
|
||||
style.scroll_animation = ScrollAnimation::none();
|
||||
style.animation_time = 0.0;
|
||||
});
|
||||
let mut input = egui::RawInput {
|
||||
screen_rect: Some(screen_rect),
|
||||
..Default::default()
|
||||
@@ -564,7 +573,8 @@ impl<'a, State> Harness<'a, State> {
|
||||
.expect("Missing root viewport")
|
||||
}
|
||||
|
||||
fn root(&self) -> Node<'_> {
|
||||
/// The root node of the test harness.
|
||||
pub fn root(&self) -> Node<'_> {
|
||||
Node {
|
||||
accesskit_node: self.kittest.root(),
|
||||
queue: &self.queued_events,
|
||||
|
||||
@@ -159,4 +159,49 @@ impl Node<'_> {
|
||||
pub fn is_focused(&self) -> bool {
|
||||
self.accesskit_node.is_focused()
|
||||
}
|
||||
|
||||
/// Scroll the node into view.
|
||||
pub fn scroll_to_me(&self) {
|
||||
self.event(egui::Event::AccessKitActionRequest(ActionRequest {
|
||||
action: accesskit::Action::ScrollIntoView,
|
||||
target: self.accesskit_node.id(),
|
||||
data: None,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Scroll the [`egui::ScrollArea`] containing this node down (100px).
|
||||
pub fn scroll_down(&self) {
|
||||
self.event(egui::Event::AccessKitActionRequest(ActionRequest {
|
||||
action: accesskit::Action::ScrollDown,
|
||||
target: self.accesskit_node.id(),
|
||||
data: None,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Scroll the [`egui::ScrollArea`] containing this node up (100px).
|
||||
pub fn scroll_up(&self) {
|
||||
self.event(egui::Event::AccessKitActionRequest(ActionRequest {
|
||||
action: accesskit::Action::ScrollUp,
|
||||
target: self.accesskit_node.id(),
|
||||
data: None,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Scroll the [`egui::ScrollArea`] containing this node left (100px).
|
||||
pub fn scroll_left(&self) {
|
||||
self.event(egui::Event::AccessKitActionRequest(ActionRequest {
|
||||
action: accesskit::Action::ScrollLeft,
|
||||
target: self.accesskit_node.id(),
|
||||
data: None,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Scroll the [`egui::ScrollArea`] containing this node right (100px).
|
||||
pub fn scroll_right(&self) {
|
||||
self.event(egui::Event::AccessKitActionRequest(ActionRequest {
|
||||
action: accesskit::Action::ScrollRight,
|
||||
target: self.accesskit_node.id(),
|
||||
data: None,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:70d76e55327de17163bc9c7e128c28153f95db3229dec919352a024eb80544f1
|
||||
size 7399
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b4b7b3145401b7cf9815a652a0914b230892ffda3b5e23fea530dafee9c0c3d3
|
||||
size 8110
|
||||
@@ -1,5 +1,5 @@
|
||||
use egui::{Modifiers, Vec2, include_image};
|
||||
use egui_kittest::Harness;
|
||||
use egui::{Modifiers, ScrollArea, Vec2, include_image};
|
||||
use egui_kittest::{Harness, SnapshotResults};
|
||||
use kittest::Queryable as _;
|
||||
|
||||
#[test]
|
||||
@@ -81,3 +81,60 @@ fn should_wait_for_images() {
|
||||
|
||||
harness.snapshot("should_wait_for_images");
|
||||
}
|
||||
|
||||
fn test_scroll_harness() -> Harness<'static, bool> {
|
||||
Harness::builder()
|
||||
.with_size(Vec2::new(100.0, 200.0))
|
||||
.build_ui_state(
|
||||
|ui, state| {
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
for i in 0..20 {
|
||||
ui.label(format!("Item {i}"));
|
||||
}
|
||||
if ui.button("Hidden Button").clicked() {
|
||||
*state = true;
|
||||
};
|
||||
});
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_to_me() {
|
||||
let mut harness = test_scroll_harness();
|
||||
let mut results = SnapshotResults::new();
|
||||
|
||||
results.add(harness.try_snapshot("test_scroll_initial"));
|
||||
|
||||
harness.get_by_label("Hidden Button").scroll_to_me();
|
||||
|
||||
harness.run();
|
||||
results.add(harness.try_snapshot("test_scroll_scrolled"));
|
||||
|
||||
harness.get_by_label("Hidden Button").click();
|
||||
harness.run();
|
||||
|
||||
assert!(
|
||||
harness.state(),
|
||||
"The button was not clicked after scrolling."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_down() {
|
||||
let mut harness = test_scroll_harness();
|
||||
|
||||
let button = harness.get_by_label("Hidden Button");
|
||||
button.scroll_down();
|
||||
button.scroll_down();
|
||||
harness.run();
|
||||
|
||||
harness.get_by_label("Hidden Button").click();
|
||||
harness.run();
|
||||
|
||||
assert!(
|
||||
harness.state(),
|
||||
"The button was not clicked after scrolling down. (Probably not scrolled enough / at all)"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user