1use std::cell::RefCell;
23use std::rc::Rc;
24
25use enigo::{Enigo, KeyboardControllable, MouseButton, MouseControllable};
26use gdk::keys::Key;
27use gdk::keys::constants as key;
28use glib::{IsA, Object, object::Cast};
29use gtk::{prelude::*, Inhibit, ToolButton, Widget};
30use gtk_test::{self, focus, mouse_move, run_loop, wait_for_draw};
31use relm::StreamHandle;
32
33macro_rules! gtk_observer_new {
36 ($widget:expr, $signal_name:ident, |$e1:pat $(,$e:pat)*|) => {{
37 let observer = gtk_test::Observer::new();
38 let res = (*observer.get_inner()).clone();
39 $widget.$signal_name(move |$e1 $(,$e:expr)*| {
40 *res.borrow_mut() = true;
41 });
42 observer
43 }};
44 ($widget:expr, $signal_name:ident, |$e1:pat $(,$e:pat)*| $block:block) => {{
45 let observer = gtk_test::Observer::new();
46 let res = (*observer.get_inner()).clone();
47 $widget.$signal_name(move |$e1 $(,$e)*| {
48 *res.borrow_mut() = true;
49 $block
50 });
51 observer
52 }}
53}
54
55pub struct Observer<MSG> {
56 result: Rc<RefCell<Option<MSG>>>,
57}
58
59impl<MSG: Clone + 'static> Observer<MSG> {
60 pub fn new<F: Fn(&MSG) -> bool + 'static>(stream: StreamHandle<MSG>, predicate: F) -> Self {
61 let result = Rc::new(RefCell::new(None));
62 let res = result.clone();
63 stream.observe(move |msg| {
64 if predicate(msg) {
65 *res.borrow_mut() = Some(msg.clone());
66 }
67 });
68 Self {
69 result,
70 }
71 }
72
73 pub fn wait(&self) -> MSG {
74 loop {
75 if let Ok(ref result) = self.result.try_borrow() {
76 if result.is_some() {
77 break;
78 }
79 }
80 gtk_test::run_loop();
81 }
82 self.result.borrow_mut().take()
83 .expect("Message to take")
84 }
85}
86
87#[macro_export]
88macro_rules! relm_observer_new {
89 ($component:expr, $pat:pat) => {
90 $crate::Observer::new($component.stream(), |msg|
91 if let $pat = msg {
92 true
93 }
94 else {
95 false
96 }
97 );
98 };
99}
100
101#[macro_export]
102macro_rules! relm_observer_wait {
103 (let $($variant:ident)::*($name1:ident, $name2:ident $(,$rest:ident)*) = $observer:expr) => {
104 let ($name1, $name2 $(, $rest)*) = {
105 let msg = $observer.wait();
106 if let $($variant)::*($name1, $name2 $(, $rest)*) = msg {
107 ($name1, $name2 $(, $rest)*)
108 }
109 else {
110 panic!("Wrong message type.");
111 }
112 };
113 };
114 (let $($variant:ident)::*($name:ident) = $observer:expr) => {
115 let $name = {
116 let msg = $observer.wait();
117 if let $($variant)::*($name) = msg {
118 $name
119 }
120 else {
121 panic!("Wrong message type.");
122 }
123 };
124 };
125 (let $($variant:ident)::* = $observer:expr) => {
126 let () = {
127 let msg = $observer.wait();
128 if let $($variant)::* = msg {
129 ()
130 }
131 else {
132 panic!("Wrong message type.");
133 }
134 };
135 };
136}
137
138pub fn click<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt + IsA<W>>(widget: &W) {
140 wait_for_draw(widget, || {
141 let observer =
142 if let Ok(tool_button) = widget.clone().dynamic_cast::<ToolButton>() {
143 gtk_observer_new!(tool_button, connect_clicked, |_|)
144 }
145 else {
146 gtk_observer_new!(widget, connect_button_press_event, |_, _| {
147 Inhibit(false)
148 })
149 };
150 let allocation = widget.allocation();
151 mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
152 let mut enigo = Enigo::new();
153 enigo.mouse_click(MouseButton::Left);
154 observer.wait();
155
156 wait_for_relm_events();
157 });
158}
159
160pub fn mouse_move_to<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt + IsA<W>>(widget: &W) {
161 wait_for_draw(widget, || {
162 let allocation = widget.allocation();
163 mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
164
165 wait_for_relm_events();
166 });
167}
168
169pub fn double_click<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W) {
170 wait_for_draw(widget, || {
171 let observer = gtk_observer_new!(widget, connect_button_release_event, |_, _| {
172 Inhibit(false)
173 });
174 let allocation = widget.allocation();
175 mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
176 let mut enigo = Enigo::new();
177 enigo.mouse_click(MouseButton::Left);
178 observer.wait();
179
180 let observer = gtk_observer_new!(widget, connect_button_release_event, |_, _| {
181 Inhibit(false)
182 });
183 enigo.mouse_click(MouseButton::Left);
184 observer.wait();
185
186 wait_for_relm_events();
187 });
188}
189
190pub fn key_press<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
192 wait_for_draw(widget, || {
193 let observer = gtk_observer_new!(widget, connect_key_press_event, |_, _| {
194 Inhibit(false)
195 });
196 focus(widget);
197 let mut enigo = Enigo::new();
198 enigo.key_down(gdk_key_to_enigo_key(key));
199 observer.wait();
200
201 wait_for_relm_events();
202 });
203}
204
205pub fn key_release<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
206 wait_for_draw(widget, || {
207 let observer = gtk_observer_new!(widget, connect_key_release_event, |_, _| {
208 Inhibit(false)
209 });
210 focus(widget);
211 let mut enigo = Enigo::new();
212 enigo.key_up(gdk_key_to_enigo_key(key));
213 observer.wait();
214
215 wait_for_relm_events();
216 });
217}
218
219pub fn enter_key<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
220 wait_for_draw(widget, || {
221 let observer = gtk_observer_new!(widget, connect_key_release_event, |_, _| {
222 Inhibit(false)
223 });
224 focus(widget);
225 let mut enigo = Enigo::new();
226 enigo.key_click(gdk_key_to_enigo_key(key));
227 observer.wait();
228
229 wait_for_relm_events();
230 });
231}
232
233pub fn enter_keys<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, text: &str) {
234 wait_for_draw(widget, || {
235 focus(widget);
236 let mut enigo = Enigo::new();
237 for char in text.chars() {
238 let observer = gtk_observer_new!(widget, connect_key_release_event, |_, _| {
239 Inhibit(false)
240 });
241 enigo.key_sequence(&char.to_string());
242 observer.wait();
243 }
244
245 wait_for_relm_events();
246 });
247}
248
249fn wait_for_relm_events() {
250 gtk_test::wait(1);
251 while gtk::events_pending() {
252 run_loop();
253 gtk_test::wait(1);
254 }
255}
256
257fn gdk_key_to_enigo_key(key: Key) -> enigo::Key {
258 use enigo::Key::*;
259 match key {
260 key::Return => Return,
261 key::Tab => Tab,
262 key::space => Space,
263 key::BackSpace => Backspace,
264 key::Escape => Escape,
265 key::Super_L | key::Super_R => Meta,
266 key::Control_L | key::Control_R => Control,
267 key::Shift_L | key::Shift_R => Shift,
268 key::Shift_Lock => CapsLock,
269 key::Alt_L | key::Alt_R => Alt,
270 key::Option => Option,
271 key::End => End,
272 key::Home => Home,
273 key::Page_Down => PageDown,
274 key::Page_Up => PageUp,
275 key::leftarrow => LeftArrow,
276 key::rightarrow => RightArrow,
277 key::downarrow => DownArrow,
278 key::uparrow => UpArrow,
279 key::F1 => F1,
280 key::F2 => F2,
281 key::F3 => F3,
282 key::F4 => F4,
283 key::F5 => F5,
284 key::F6 => F6,
285 key::F7 => F7,
286 key::F8 => F8,
287 key::F9 => F9,
288 key::F10 => F10,
289 key::F11 => F11,
290 key::F12 => F12,
291 _ => {
292 if let Some(char) = key.to_unicode() {
293 Layout(char)
294 }
295 else {
296 Raw(*key as u16)
297 }
298 },
299 }
300}