use blitz_dom::{Document, DocumentConfig};
use blitz_script::ScriptDocument;
use blitz_traits::{
events::{
BlitzPointerEvent, BlitzPointerId, MouseEventButton, MouseEventButtons, Point,
PointerCoords, PointerDetails, UiEvent,
},
shell::{ColorScheme, Viewport},
};
const SLIDER: &str = r#"
<html><head><style>
body { margin: 0; }
#track { position: absolute; left: 0; top: 0; width: 200px; height: 20px; }
#away { position: absolute; left: 0; top: 100px; width: 200px; height: 50px; }
</style></head>
<body>
<div id="track"></div>
<div id="away"></div>
<div id="out"></div>
<script>
const track = document.getElementById("track");
const out = document.getElementById("out");
let moves = 0;
track.addEventListener("pointerdown", (event) => {
track.setPointerCapture(event.pointerId);
out.textContent = "down";
});
track.addEventListener("pointermove", (event) => {
moves += 1;
out.textContent = `moves:${moves}:${event.clientX}`;
});
track.addEventListener("pointerup", (event) => {
track.releasePointerCapture(event.pointerId);
out.textContent += "|up";
});
</script>
</body></html>
"#;
fn make_doc(html: &str) -> ScriptDocument {
let mut doc = ScriptDocument::from_html(
html,
DocumentConfig {
viewport: Some(Viewport::new(400, 300, 1.0, ColorScheme::Light)),
..Default::default()
},
);
doc.execute_scripts();
doc.inner_mut().resolve(0.0);
doc
}
fn text_of_selector(doc: &ScriptDocument, selector: &str) -> String {
let inner = doc.inner();
let node_id = inner
.query_selector(selector)
.unwrap()
.unwrap_or_else(|| panic!("no node matching {selector}"));
inner.get_node(node_id).unwrap().text_content()
}
fn pointer_event(x: f32, y: f32, buttons: MouseEventButtons) -> BlitzPointerEvent {
BlitzPointerEvent {
id: BlitzPointerId::Mouse,
is_primary: true,
coords: PointerCoords {
page_x: x,
page_y: y,
screen_x: x,
screen_y: y,
client_x: x,
client_y: y,
},
button: MouseEventButton::Main,
buttons,
mods: Default::default(),
details: PointerDetails::default(),
element: Point::default(),
active_pointers: Default::default(),
}
}
fn down(doc: &mut ScriptDocument, x: f32, y: f32) {
let held = MouseEventButtons::from(MouseEventButton::Main);
doc.handle_ui_event(UiEvent::PointerDown(pointer_event(x, y, held)));
}
fn move_to(doc: &mut ScriptDocument, x: f32, y: f32) {
let held = MouseEventButtons::from(MouseEventButton::Main);
doc.handle_ui_event(UiEvent::PointerMove(pointer_event(x, y, held)));
}
fn up(doc: &mut ScriptDocument, x: f32, y: f32) {
doc.handle_ui_event(UiEvent::PointerUp(pointer_event(
x,
y,
MouseEventButtons::None,
)));
}
#[test]
fn a_drag_inside_the_element_reports_moves() {
let mut doc = make_doc(SLIDER);
down(&mut doc, 10.0, 10.0);
move_to(&mut doc, 60.0, 10.0);
assert_eq!(text_of_selector(&doc, "#out"), "moves:1:60");
}
#[test]
fn a_captured_drag_keeps_reporting_after_leaving_the_element() {
let mut doc = make_doc(SLIDER);
down(&mut doc, 10.0, 10.0);
move_to(&mut doc, 60.0, 120.0);
move_to(&mut doc, 120.0, 260.0);
assert_eq!(
text_of_selector(&doc, "#out"),
"moves:2:120",
"a captured pointer must keep delivering moves to the capturing element"
);
}
#[test]
fn releasing_capture_stops_the_drag() {
let mut doc = make_doc(SLIDER);
down(&mut doc, 10.0, 10.0);
move_to(&mut doc, 60.0, 120.0);
up(&mut doc, 60.0, 120.0);
move_to(&mut doc, 90.0, 120.0);
assert_eq!(
text_of_selector(&doc, "#out"),
"moves:1:60|up",
"after the release the track should hear nothing more"
);
}