1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

//! Functions useful for testing
#![warn(missing_docs)]
#![allow(unsafe_code)]

use crate::input::{key_codes::Key, MouseEvent};
use crate::platform::WindowEvent;
use crate::Coord;

/// Slint animations do not use real time, but use a mocked time.
/// Normally, the event loop update the time of the animation using
/// real time, but in tests, it is more convenient to use the fake time.
/// This function will add some milliseconds to the fake time
#[no_mangle]
pub extern "C" fn slint_mock_elapsed_time(time_in_ms: u64) {
    let tick = crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
        let mut tick = driver.current_tick();
        tick += core::time::Duration::from_millis(time_in_ms);
        driver.update_animations(tick);
        tick
    });
    crate::timers::TimerList::maybe_activate_timers(tick);
}

/// Simulate a click on a position within the component.
#[no_mangle]
pub extern "C" fn slint_send_mouse_click(
    component: &crate::component::ComponentRc,
    x: Coord,
    y: Coord,
    window_adapter: &crate::window::WindowAdapterRc,
) {
    let mut state = crate::input::MouseInputState::default();
    let position = euclid::point2(x, y);

    state = crate::input::process_mouse_input(
        component.clone(),
        MouseEvent::Moved { position },
        window_adapter,
        state,
    );
    state = crate::input::process_mouse_input(
        component.clone(),
        MouseEvent::Pressed {
            position,
            button: crate::items::PointerEventButton::Left,
            click_count: 0,
        },
        window_adapter,
        state,
    );
    slint_mock_elapsed_time(50);
    crate::input::process_mouse_input(
        component.clone(),
        MouseEvent::Released {
            position,
            button: crate::items::PointerEventButton::Left,
            click_count: 0,
        },
        window_adapter,
        state,
    );
}

/// Simulate a character input event (pressed or released).
#[no_mangle]
pub extern "C" fn slint_send_keyboard_char(
    string: &crate::SharedString,
    pressed: bool,
    window_adapter: &crate::window::WindowAdapterRc,
) {
    for ch in string.chars() {
        window_adapter.window().dispatch_event(if pressed {
            WindowEvent::KeyPressed { text: ch.into() }
        } else {
            WindowEvent::KeyReleased { text: ch.into() }
        })
    }
}

/// Simulate a character input event.
#[no_mangle]
pub extern "C" fn send_keyboard_string_sequence(
    sequence: &crate::SharedString,
    window_adapter: &crate::window::WindowAdapterRc,
) {
    for ch in sequence.chars() {
        if ch.is_ascii_uppercase() {
            window_adapter
                .window()
                .dispatch_event(WindowEvent::KeyPressed { text: Key::Shift.into() });
        }

        let text: crate::SharedString = ch.into();
        window_adapter.window().dispatch_event(WindowEvent::KeyPressed { text: text.clone() });
        window_adapter.window().dispatch_event(WindowEvent::KeyReleased { text });

        if ch.is_ascii_uppercase() {
            window_adapter
                .window()
                .dispatch_event(WindowEvent::KeyReleased { text: Key::Shift.into() });
        }
    }
}

/// implementation details for debug_log()
#[doc(hidden)]
pub fn debug_log_impl(args: core::fmt::Arguments) {
    crate::platform::PLATFORM_INSTANCE.with(|p| match p.get() {
        Some(platform) => platform.debug_log(args),
        None => default_debug_log(args),
    });
}

#[doc(hidden)]
pub fn default_debug_log(_arguments: core::fmt::Arguments) {
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "wasm32")] {
            use wasm_bindgen::prelude::*;

            #[wasm_bindgen]
            extern "C" {
                #[wasm_bindgen(js_namespace = console)]
                pub fn log(s: &str);
            }

            log(&_arguments.to_string());
        } else if #[cfg(feature = "std")] {
            eprintln!("{}", _arguments);
        }
    }
}

#[macro_export]
/// This macro allows producing debug output that will appear on stderr in regular builds
/// and in the console log for wasm builds.
macro_rules! debug_log {
    ($($t:tt)*) => ($crate::tests::debug_log_impl(format_args!($($t)*)))
}