use enigo::{self, Enigo, KeyboardControllable, MouseButton, MouseControllable};
use gtk::gdk::keys::constants as key;
use gtk::gdk::keys::Key;
use gtk::glib::{Cast, ControlFlow, IsA, Object, Propagation, StaticType};
use gtk::prelude::{BinExt, ButtonExt, ContainerExt, EditableExt, ToolButtonExt, WidgetExt};
use gtk::{Bin, Button, Container, Entry, ToolButton, Widget, Window};
use crate::observer_new;
pub fn click<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt + IsA<W>>(widget: &W) {
wait_for_draw(widget, || {
let observer = if let Ok(tool_button) = widget.clone().dynamic_cast::<ToolButton>() {
observer_new!(tool_button, connect_clicked, |_|)
} else if let Ok(tool_button) = widget.clone().dynamic_cast::<Button>() {
observer_new!(tool_button, connect_clicked, |_|)
} else {
observer_new!(widget, connect_button_release_event, |_, _| {
Propagation::Stop
})
};
let allocation = widget.allocation();
mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
let mut enigo = Enigo::new();
enigo.mouse_click(MouseButton::Left);
observer.wait();
});
}
pub fn double_click<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W) {
wait_for_draw(widget, || {
let observer = observer_new!(widget, connect_button_release_event, |_, _| {
Propagation::Stop
});
let allocation = widget.allocation();
mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
let mut enigo = Enigo::new();
enigo.mouse_click(MouseButton::Left);
run_loop();
enigo.mouse_click(MouseButton::Left);
observer.wait();
});
}
pub fn mouse_move<W: IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, x: i32, y: i32) {
wait_for_draw(widget, || {
let toplevel_window = widget.toplevel().and_then(|toplevel| toplevel.window());
if let (Some(toplevel), Some(toplevel_window)) = (widget.toplevel(), toplevel_window) {
let (_, window_x, window_y) = toplevel_window.origin();
if let Some((x, y)) = widget.translate_coordinates(&toplevel, x, y) {
let x = window_x + x;
let y = window_y + y;
let mut enigo = Enigo::new();
enigo.mouse_move_to(x, y);
run_loop();
}
}
});
}
pub fn mouse_press<W: IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W) {
wait_for_draw(widget, || {
let allocation = widget.allocation();
mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
let mut enigo = Enigo::new();
enigo.mouse_down(MouseButton::Left);
run_loop();
});
}
pub fn mouse_release<W: IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W) {
wait_for_draw(widget, || {
let allocation = widget.allocation();
mouse_move(widget, allocation.width() / 2, allocation.height() / 2);
let mut enigo = Enigo::new();
enigo.mouse_up(MouseButton::Left);
run_loop();
});
}
pub fn enter_key<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
wait_for_draw(widget, || {
let observer = observer_new!(widget, connect_key_release_event, |_, _| {
Propagation::Stop
});
focus(widget);
let mut enigo = Enigo::new();
enigo.key_click(gdk_key_to_enigo_key(key));
observer.wait();
});
}
pub fn enter_keys<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, text: &str) {
wait_for_draw(widget, || {
focus(widget);
let mut enigo = Enigo::new();
for char in text.chars() {
let observer = observer_new!(widget, connect_key_release_event, |_, _| {
Propagation::Stop
});
enigo.key_sequence(&char.to_string());
observer.wait();
}
});
}
pub fn find_child_by_name<C: IsA<Widget>, W: Clone + IsA<Object> + IsA<Widget>>(
parent: &W,
name: &str,
) -> Option<C> {
find_widget_by_name(parent, name).and_then(|widget| widget.downcast().ok())
}
pub fn find_widget_by_name<W: Clone + IsA<Object> + IsA<Widget>>(
parent: &W,
name: &str,
) -> Option<Widget> {
if let Ok(container) = parent.clone().dynamic_cast::<Container>() {
for child in container.children() {
if child.widget_name() == name {
return Some(child);
}
if let Some(widget) = find_widget_by_name(&child, name) {
return Some(widget);
}
}
} else if let Ok(bin) = parent.clone().dynamic_cast::<Bin>() {
if let Some(child) = bin.child() {
if child.widget_name() == name {
return Some(child);
}
if let Some(widget) = find_widget_by_name(&child, name) {
return Some(widget);
}
}
}
None
}
pub fn focus<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W) {
wait_for_draw(widget, || {
if !widget.has_focus() {
widget.grab_focus();
if let Ok(entry) = widget.clone().dynamic_cast::<Entry>() {
entry.set_position(-1);
}
}
});
}
pub fn key_press<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
wait_for_draw(widget, || {
let observer = observer_new!(widget, connect_key_press_event, |_, _| {
Propagation::Stop
});
focus(widget);
let mut enigo = Enigo::new();
enigo.key_down(gdk_key_to_enigo_key(key));
observer.wait();
});
}
pub fn key_release<W: Clone + IsA<Object> + IsA<Widget> + WidgetExt>(widget: &W, key: Key) {
wait_for_draw(widget, || {
let observer = observer_new!(widget, connect_key_release_event, |_, _| {
Propagation::Stop
});
focus(widget);
let mut enigo = Enigo::new();
enigo.key_up(gdk_key_to_enigo_key(key));
observer.wait();
});
}
pub fn wait(ms: u32) {
gtk::glib::timeout_add(std::time::Duration::from_millis(ms as u64), || {
gtk::main_quit();
ControlFlow::Break
});
gtk::main();
}
pub fn run_loop() {
while gtk::events_pending() {
gtk::main_iteration();
}
}
pub fn wait_until_done<F: FnMut() -> bool>(mut f: F) {
while !f() {
run_loop();
}
}
pub fn wait_for_draw<W, F: FnOnce()>(widget: &W, callback: F)
where
W: IsA<Object> + IsA<Widget> + WidgetExt,
{
if widget.ancestor(Window::static_type()).is_none() {
return;
}
gtk::test_widget_wait_for_draw(widget);
callback();
}
fn gdk_key_to_enigo_key(key: Key) -> enigo::Key {
use enigo::Key::*;
match key {
key::Return => Return,
key::Tab => Tab,
key::space => Space,
key::BackSpace => Backspace,
key::Escape => Escape,
key::Super_L | key::Super_R => Meta,
key::Control_L | key::Control_R => Control,
key::Shift_L | key::Shift_R => Shift,
key::Shift_Lock => CapsLock,
key::Alt_L | key::Alt_R => Alt,
key::End => End,
key::Option => Option,
key::Home => Home,
key::Page_Down => PageDown,
key::Page_Up => PageUp,
key::leftarrow => LeftArrow,
key::rightarrow => RightArrow,
key::downarrow => DownArrow,
key::uparrow => UpArrow,
key::F1 => F1,
key::F2 => F2,
key::F3 => F3,
key::F4 => F4,
key::F5 => F5,
key::F6 => F6,
key::F7 => F7,
key::F8 => F8,
key::F9 => F9,
key::F10 => F10,
key::F11 => F11,
key::F12 => F12,
_ => {
if let Some(char) = key.to_unicode() {
Layout(char)
} else {
Raw(*key as u16)
}
}
}
}