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