1#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
3pub struct UiCapture {
4 pub pointer: bool,
5 pub keyboard: bool,
6}
7
8impl UiCapture {
9 pub fn read(ctx: &egui::Context) -> Self {
11 Self {
12 pointer: ctx.is_using_pointer()
13 || ctx.interaction_snapshot(|i| !i.contains_pointer.is_empty()),
14 keyboard: ctx.wants_keyboard_input(),
15 }
16 }
17}
18
19#[cfg(test)]
20mod tests {
21 use super::UiCapture;
22
23 const SCREEN: egui::Vec2 = egui::vec2(800.0, 600.0);
24 const PANEL_BLANK: egui::Pos2 = egui::pos2(400.0, 8.0);
25 const FIELD: egui::Pos2 = egui::pos2(120.0, 10.0);
26 const OPEN_SCENE: egui::Pos2 = egui::pos2(200.0, 400.0);
27 const WINDOW_POS: egui::Pos2 = egui::pos2(400.0, 300.0);
28 const IN_WINDOW: egui::Pos2 = egui::pos2(430.0, 320.0);
29
30 fn build(ctx: &egui::Context, text: &mut String) {
31 egui::TopBottomPanel::top("bar").show(ctx, |ui| {
32 ui.horizontal(|ui| {
33 let _ = ui.button("File");
34 ui.add(egui::TextEdit::singleline(text).id(egui::Id::new("edit")));
35 });
36 });
37 egui::Window::new("win")
38 .fixed_pos(WINDOW_POS)
39 .fixed_size(egui::vec2(100.0, 80.0))
40 .show(ctx, |ui| {
41 let _ = ui.button("press");
42 });
43 }
44
45 struct Host {
46 ctx: egui::Context,
47 text: String,
48 }
49
50 impl Host {
51 fn new() -> Self {
52 let mut host = Self {
53 ctx: egui::Context::default(),
54 text: String::new(),
55 };
56 host.frame(vec![]);
57 host.frame(vec![]);
58 host
59 }
60
61 fn frame(&mut self, events: Vec<egui::Event>) -> UiCapture {
62 let input = egui::RawInput {
63 screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, SCREEN)),
64 events,
65 ..Default::default()
66 };
67 let mut capture = UiCapture::default();
68 let text = &mut self.text;
69 let _ = self.ctx.run(input, |ctx| {
70 capture = UiCapture::read(ctx);
71 build(ctx, text);
72 });
73 capture
74 }
75
76 fn hover(&mut self, pos: egui::Pos2) -> UiCapture {
77 self.frame(vec![egui::Event::PointerMoved(pos)])
78 }
79
80 fn press(&mut self, pos: egui::Pos2) -> UiCapture {
81 self.frame(vec![
82 egui::Event::PointerMoved(pos),
83 egui::Event::PointerButton {
84 pos,
85 button: egui::PointerButton::Primary,
86 pressed: true,
87 modifiers: egui::Modifiers::default(),
88 },
89 ])
90 }
91
92 fn click(&mut self, pos: egui::Pos2) -> UiCapture {
93 self.frame(vec![
94 egui::Event::PointerMoved(pos),
95 egui::Event::PointerButton {
96 pos,
97 button: egui::PointerButton::Primary,
98 pressed: true,
99 modifiers: egui::Modifiers::default(),
100 },
101 egui::Event::PointerButton {
102 pos,
103 button: egui::PointerButton::Primary,
104 pressed: false,
105 modifiers: egui::Modifiers::default(),
106 },
107 ])
108 }
109
110 fn key(&mut self, key: egui::Key) -> UiCapture {
111 self.frame(vec![egui::Event::Key {
112 key,
113 physical_key: None,
114 pressed: true,
115 repeat: false,
116 modifiers: egui::Modifiers::default(),
117 }])
118 }
119 }
120
121 #[test]
122 fn pointer_capture_reports_a_press_on_the_frame_the_pointer_arrives() {
123 let mut host = Host::new();
124 host.hover(OPEN_SCENE);
125 let capture = host.press(IN_WINDOW);
126 assert!(capture.pointer);
127 }
128
129 #[test]
130 fn pointer_capture_covers_a_hovered_panel_without_claiming_the_keyboard() {
131 let mut host = Host::new();
132 host.hover(OPEN_SCENE);
133 let capture = host.hover(PANEL_BLANK);
134 assert!(capture.pointer);
135 assert!(!capture.keyboard);
136 }
137
138 #[test]
139 fn pointer_capture_follows_a_drag_off_the_widget_it_started_on() {
140 let mut host = Host::new();
141 host.hover(FIELD);
142 host.press(FIELD);
143 let capture = host.hover(OPEN_SCENE);
144 assert!(capture.pointer);
145 }
146
147 #[test]
148 fn a_focused_field_claims_the_keyboard_without_claiming_the_pointer() {
149 let mut host = Host::new();
150 host.click(egui::pos2(120.0, 10.0));
151 let capture = host.hover(OPEN_SCENE);
152 assert!(capture.keyboard);
153 assert!(!capture.pointer);
154 }
155
156 #[test]
157 fn keyboard_capture_trails_the_click_that_focuses_a_field_by_one_build() {
158 let mut host = Host::new();
159 let on_click = host.click(egui::pos2(120.0, 10.0));
160 assert!(!on_click.keyboard);
161 let next = host.frame(vec![]);
162 assert!(next.keyboard);
163 }
164
165 #[test]
166 fn escape_clears_keyboard_capture_on_the_frame_it_is_pressed() {
167 let mut host = Host::new();
168 host.click(egui::pos2(120.0, 10.0));
169 host.frame(vec![]);
170 let capture = host.key(egui::Key::Escape);
171 assert!(!capture.keyboard);
172 }
173}