use slint::{ComponentHandle, PhysicalSize};
use slint_mapping::camera::{pan as camera_pan, zoom_anchored as camera_zoom_anchored};
use slint_mapping::viewport::{
center_for_anchor_at_viewport_px, lonlat_to_viewport_px, viewport_px_to_lonlat,
};
use slint_mapping::MapPanel;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
fn init_backend() {
thread_local! {
static INITED: Cell<bool> = const { Cell::new(false) };
}
INITED.with(|done| {
if !done.get() {
i_slint_backend_testing::init_no_event_loop();
done.set(true);
}
});
}
fn make_panel(window_w: u32, window_h: u32) -> MapPanel {
init_backend();
let panel = MapPanel::new().expect("MapPanel::new");
panel
.window()
.set_size(PhysicalSize::new(window_w, window_h));
panel
}
#[test]
fn panel_reports_inner_embed_size_not_window_size() {
let panel = make_panel(600, 400);
let vw = panel.get_map_viewport_width() as f64;
let vh = panel.get_map_viewport_height() as f64;
assert!(
(vw - 576.0).abs() < 0.5,
"viewport width should report embed.width=576, got {vw}"
);
assert!(
(vh - 344.0).abs() < 0.5,
"viewport height should report embed.height=344, got {vh}"
);
}
#[test]
fn panel_inner_size_scales_with_window_resize() {
let panel = make_panel(480, 360);
let vw0 = panel.get_map_viewport_width() as f64;
let vh0 = panel.get_map_viewport_height() as f64;
panel.window().set_size(PhysicalSize::new(800, 500));
let vw1 = panel.get_map_viewport_width() as f64;
let vh1 = panel.get_map_viewport_height() as f64;
assert!(
vw1 > vw0,
"viewport width should grow on resize: {vw0} → {vw1}"
);
assert!(
vh1 > vh0,
"viewport height should grow on resize: {vh0} → {vh1}"
);
assert!((vw1 - 776.0).abs() < 0.5, "expected 800-24=776, got {vw1}");
assert!((vh1 - 444.0).abs() < 0.5, "expected 500-56=444, got {vh1}");
}
fn wire_controller(panel: &MapPanel) {
let camera = Rc::new(RefCell::new((
panel.get_longitude() as f64,
panel.get_latitude() as f64,
panel.get_zoom() as f64,
)));
*camera.borrow_mut() = (-0.1276, 51.5074, 12.0);
panel.set_longitude(-0.1276);
panel.set_latitude(51.5074);
panel.set_zoom(12.0);
let tile_size = 256u32;
{
let camera = Rc::clone(&camera);
let weak = panel.as_weak();
panel.on_pan(move |dx, dy| {
let Some(panel) = weak.upgrade() else { return };
let mut c = camera.borrow_mut();
let (lon, lat) = camera_pan(c.0, c.1, c.2, dx as f64, dy as f64, tile_size);
c.0 = lon;
c.1 = lat;
panel.set_longitude(lon as f32);
panel.set_latitude(lat as f32);
});
}
{
let camera = Rc::clone(&camera);
let weak = panel.as_weak();
panel.on_zoom_by(move |delta, anchor_x, anchor_y| {
let Some(panel) = weak.upgrade() else { return };
let vw = panel.get_map_viewport_width() as f64;
let vh = panel.get_map_viewport_height() as f64;
let mut c = camera.borrow_mut();
let (lon, lat, z) = camera_zoom_anchored(
c.0,
c.1,
c.2,
delta as f64,
anchor_x as f64,
anchor_y as f64,
vw,
vh,
tile_size,
0,
22,
);
c.0 = lon;
c.1 = lat;
c.2 = z;
panel.set_longitude(lon as f32);
panel.set_latitude(lat as f32);
panel.set_zoom(z as f32);
});
}
}
#[test]
fn zoom_anchored_at_embed_centre_does_not_move_camera() {
let panel = make_panel(600, 400);
wire_controller(&panel);
let lon0 = panel.get_longitude() as f64;
let lat0 = panel.get_latitude() as f64;
panel.invoke_zoom_by(1.0, 288.0, 172.0);
let lon1 = panel.get_longitude() as f64;
let lat1 = panel.get_latitude() as f64;
assert!(
(lon1 - lon0).abs() < 1e-4,
"centre-anchored zoom: lon should not move, {lon0} → {lon1}",
);
assert!(
(lat1 - lat0).abs() < 1e-4,
"centre-anchored zoom: lat should not move, {lat0} → {lat1}",
);
assert!((panel.get_zoom() as f64 - 13.0).abs() < 1e-6);
}
#[test]
fn zoom_anchored_at_embed_corner_pins_cursor_pixel() {
let panel = make_panel(600, 400);
wire_controller(&panel);
let vw = panel.get_map_viewport_width() as f64; let vh = panel.get_map_viewport_height() as f64; let cursor = (24.0_f64, 18.0_f64); let tile_size = 256_u32;
let lon_before = panel.get_longitude() as f64;
let lat_before = panel.get_latitude() as f64;
let zoom_before = panel.get_zoom() as f64;
let (anchor_lon, anchor_lat) = viewport_px_to_lonlat(
cursor.0,
cursor.1,
lon_before,
lat_before,
zoom_before,
vw,
vh,
tile_size,
);
panel.invoke_zoom_by(1.0, cursor.0 as f32, cursor.1 as f32);
let lon_after = panel.get_longitude() as f64;
let lat_after = panel.get_latitude() as f64;
let zoom_after = panel.get_zoom() as f64;
assert!((zoom_after - (zoom_before + 1.0)).abs() < 1e-6);
let (rpx, rpy) = lonlat_to_viewport_px(
anchor_lon, anchor_lat, lon_after, lat_after, zoom_after, vw, vh, tile_size,
);
assert!(
(rpx - cursor.0).abs() < 1.0,
"cursor x should pin after zoom, want {} got {rpx}",
cursor.0,
);
assert!(
(rpy - cursor.1).abs() < 1.0,
"cursor y should pin after zoom, want {} got {rpy}",
cursor.1,
);
let (expected_lon, expected_lat) = center_for_anchor_at_viewport_px(
anchor_lon, anchor_lat, cursor.0, cursor.1, zoom_after, vw, vh, tile_size,
);
assert!(
(lon_after - expected_lon).abs() < 1e-3,
"centre lon off: expected {expected_lon}, got {lon_after}",
);
assert!(
(lat_after - expected_lat).abs() < 1e-3,
"centre lat off: expected {expected_lat}, got {lat_after}",
);
}
#[test]
fn pan_through_panel_callback_moves_camera() {
let panel = make_panel(600, 400);
wire_controller(&panel);
let lon_before = panel.get_longitude() as f64;
panel.invoke_pan(100.0, 0.0);
let lon_after = panel.get_longitude() as f64;
assert!(
lon_after < lon_before,
"drag right should decrease longitude, {lon_before} → {lon_after}",
);
}