this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: correct draw coords for CSS zoom in webkit2gtk

In webkit2gtk, element_coordinates() (offsetX/Y) are in zoomed visual
pixels while offsetWidth/Height are in logical pixels. Dividing by zoom
before normalizing corrects the mismatch. Previous attempts using
clientX+BCR or offsetWidth alone both failed due to webkit2gtk's
inconsistent coordinate spaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+38 -45
+38 -45
crates/tala/src/main.rs
··· 162 162 let mut drag_current = use_signal(|| Option::<(f64, f64)>::None); 163 163 // Blue error boxes: drawn boxes that didn't map to insertable text. 164 164 let mut drawn_boxes = use_signal(|| Vec::<[f64; 4]>::new()); 165 - // CSS pixel dimensions of the draw-capture div (updated on mousedown). 166 - let cap_size = use_signal(|| (1.0f64, 1.0f64)); 165 + // Draw-capture div dims + zoom: [offsetWidth, offsetHeight, cssZoom]. 166 + let cap_dims = use_signal(|| [1.0f64, 1.0, 1.0]); 167 167 // Insertion error shown near the toolbar. 168 168 let mut insert_error = use_signal(|| Option::<String>::None); 169 169 ··· 269 269 div { 270 270 class: "draw-capture", 271 271 onmousedown: move |e| { 272 + // element_coordinates() = offsetX/Y (element-local px). 273 + // In webkit2gtk with CSS zoom on <html>, offsetX/Y are 274 + // scaled by zoom while offsetWidth/Height are not. 275 + // We read the current zoom and divide it out. 272 276 let ox = e.data().element_coordinates().x; 273 277 let oy = e.data().element_coordinates().y; 274 - // Read cap_size via JS on each drag start. 275 - // Use offsetWidth/offsetHeight (element-space px, zoom-invariant) 276 - // NOT getBoundingClientRect (which is scaled by CSS zoom). 277 278 let mut ds = drag_start; 278 279 let mut dc = drag_current; 279 - let mut cs = cap_size; 280 + let mut cd = cap_dims; 280 281 spawn(async move { 281 282 let mut eval = document::eval(r#" 282 283 var el = document.querySelector('.draw-capture'); 283 - dioxus.send([el ? el.offsetWidth : 1, el ? el.offsetHeight : 1]); 284 + var z = parseFloat(document.documentElement.style.zoom || '1'); 285 + dioxus.send([el ? el.offsetWidth : 1, el ? el.offsetHeight : 1, z]); 284 286 "#); 285 - if let Ok(val) = eval.recv::<[f64; 2]>().await { 286 - let w = val[0].max(1.0); 287 - let h = val[1].max(1.0); 288 - cs.set((w, h)); 289 - let (nx, ny) = normalize_draw_coords(ox, oy, w, h); 287 + if let Ok(val) = eval.recv::<[f64; 3]>().await { 288 + cd.set(val); 289 + let (nx, ny) = normalize_draw_coords(ox, oy, val); 290 290 ds.set(Some((nx, ny))); 291 291 dc.set(Some((nx, ny))); 292 292 } ··· 294 294 }, 295 295 onmousemove: move |e| { 296 296 if drag_start.read().is_some() { 297 - let (w, h) = *cap_size.read(); 298 297 let (nx, ny) = normalize_draw_coords( 299 298 e.data().element_coordinates().x, 300 299 e.data().element_coordinates().y, 301 - w, h, 300 + *cap_dims.read(), 302 301 ); 303 302 drag_current.set(Some((nx, ny))); 304 303 } ··· 309 308 }, 310 309 onmouseup: move |e| { 311 310 let Some((sx, sy)) = *drag_start.read() else { return; }; 312 - let (w, h) = *cap_size.read(); 313 311 let (nx, ny) = normalize_draw_coords( 314 312 e.data().element_coordinates().x, 315 313 e.data().element_coordinates().y, 316 - w, h, 314 + *cap_dims.read(), 317 315 ); 318 316 let rx = sx.min(nx); 319 317 let ry = sy.min(ny); ··· 391 389 } 392 390 } 393 391 394 - /// Convert element-space (offsetX, offsetY) mouse coordinates to normalized [0,1]. 392 + /// Convert element-local `offsetX/offsetY` to normalized [0,1] coords. 395 393 /// 396 - /// IMPORTANT: `container_w` and `container_h` must be **offsetWidth / offsetHeight**, 397 - /// not `getBoundingClientRect().width / height`. With CSS `zoom` applied to 398 - /// `<html>`, BCR dimensions are multiplied by the zoom factor while offsetX/Y 399 - /// are not, so using BCR gives coords that are off by 1/zoom. 400 - fn normalize_draw_coords(offset_x: f64, offset_y: f64, container_w: f64, container_h: f64) -> (f64, f64) { 394 + /// `dims` is `[offsetWidth, offsetHeight, cssZoom]`. 395 + /// In webkit2gtk with CSS `zoom` on `<html>`, `offsetX/Y` are in zoomed (visual) 396 + /// pixels while `offsetWidth/Height` are in logical pixels. Dividing out the 397 + /// zoom factor before normalizing corrects this. 398 + fn normalize_draw_coords(offset_x: f64, offset_y: f64, dims: [f64; 3]) -> (f64, f64) { 399 + let [w, h, zoom] = dims; 400 + let z = zoom.max(0.1); 401 401 ( 402 - (offset_x / container_w).clamp(0.0, 1.0), 403 - (offset_y / container_h).clamp(0.0, 1.0), 402 + (offset_x / z / w.max(1.0)).clamp(0.0, 1.0), 403 + (offset_y / z / h.max(1.0)).clamp(0.0, 1.0), 404 404 ) 405 405 } 406 406 ··· 518 518 519 519 // --- normalize_draw_coords --- 520 520 521 + // [offsetWidth, offsetHeight, zoom] 522 + const DIMS_Z1: [f64; 3] = [400.0, 200.0, 1.0]; 523 + 521 524 #[test] 522 525 fn normalize_coords_center() { 523 - let (nx, ny) = normalize_draw_coords(200.0, 100.0, 400.0, 200.0); 526 + let (nx, ny) = normalize_draw_coords(200.0, 100.0, DIMS_Z1); 524 527 assert_eq!(nx, 0.5); 525 528 assert_eq!(ny, 0.5); 526 529 } 527 530 528 - /// CSS `zoom` on `<html>` scales `getBoundingClientRect` dims by the zoom 529 - /// factor but leaves `offsetX`/`offsetY` (element coordinates) unchanged. 530 - /// Dividing by BCR dims instead of offset dims gives nx / zoom -- wrong. 531 - /// This test documents the invariant that normalize_draw_coords must receive 532 - /// offsetWidth/offsetHeight (element space), not BCR width/height. 531 + /// In webkit2gtk, offsetX/Y are in zoomed px, offsetWidth/Height in logical px. 532 + /// At zoom Z, offsetX for the visual center = (offsetWidth/2) * Z. 533 + /// normalize_draw_coords divides out Z to correct this. 533 534 #[test] 534 535 fn normalize_coords_zoom_invariant() { 535 - let offset_x = 200.0_f64; 536 - let el_w = 400.0_f64; // offsetWidth — same at any zoom level 537 - let zoom = 1.5_f64; 538 - let bcr_w = el_w * zoom; // getBoundingClientRect().width at zoom=1.5 539 - 540 - // Correct: element coords / offsetWidth 541 - let (nx_correct, _) = normalize_draw_coords(offset_x, 0.0, el_w, 1.0); 542 - assert_eq!(nx_correct, 0.5); 543 - 544 - // Buggy: element coords / BCR width — off by zoom factor 545 - let (nx_buggy, _) = normalize_draw_coords(offset_x, 0.0, bcr_w, 1.0); 546 - assert!((nx_buggy - 0.5 / zoom).abs() < 1e-10, "nx_buggy={nx_buggy}"); 547 - // Verify the buggy result is meaningfully wrong for zoom=1.5 548 - assert!((nx_buggy - nx_correct).abs() > 0.1); 536 + let zoom = 1.3_f64; 537 + // offsetX at visual center = (400/2)*1.3 = 260 (zoomed px) 538 + let offset_x_zoomed = 200.0 * zoom; 539 + let dims = [400.0, 200.0, zoom]; 540 + let (nx, _) = normalize_draw_coords(offset_x_zoomed, 0.0, dims); 541 + assert!((nx - 0.5).abs() < 1e-10, "nx={nx}"); 549 542 } 550 543 551 544 #[test] 552 545 fn normalize_coords_clamps_to_unit() { 553 - let (nx, ny) = normalize_draw_coords(500.0, -10.0, 400.0, 200.0); 546 + let (nx, ny) = normalize_draw_coords(500.0, -10.0, DIMS_Z1); 554 547 assert_eq!(nx, 1.0); 555 548 assert_eq!(ny, 0.0); 556 549 }