Fix broken mouse coordinates when there's padding on the canvas element (#4729)
* Closes https://github.com/emilk/egui/issues/4725 Also fixes touch input being wrong if the web page is scrolled.
This commit is contained in:
parent
a6937f79f3
commit
254dfc1ebc
|
@ -241,8 +241,8 @@ web-sys = { workspace = true, features = [
|
||||||
"NodeList",
|
"NodeList",
|
||||||
"Performance",
|
"Performance",
|
||||||
"ResizeObserver",
|
"ResizeObserver",
|
||||||
"ResizeObserverEntry",
|
|
||||||
"ResizeObserverBoxOptions",
|
"ResizeObserverBoxOptions",
|
||||||
|
"ResizeObserverEntry",
|
||||||
"ResizeObserverOptions",
|
"ResizeObserverOptions",
|
||||||
"ResizeObserverSize",
|
"ResizeObserverSize",
|
||||||
"Storage",
|
"Storage",
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use super::{canvas_origin, AppRunner};
|
use super::{canvas_content_rect, AppRunner};
|
||||||
|
|
||||||
pub fn pos_from_mouse_event(
|
pub fn pos_from_mouse_event(
|
||||||
canvas: &web_sys::HtmlCanvasElement,
|
canvas: &web_sys::HtmlCanvasElement,
|
||||||
event: &web_sys::MouseEvent,
|
event: &web_sys::MouseEvent,
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
) -> egui::Pos2 {
|
) -> egui::Pos2 {
|
||||||
let rect = canvas.get_bounding_client_rect();
|
let rect = canvas_content_rect(canvas);
|
||||||
let zoom_factor = ctx.zoom_factor();
|
let zoom_factor = ctx.zoom_factor();
|
||||||
egui::Pos2 {
|
egui::Pos2 {
|
||||||
x: (event.client_x() as f32 - rect.left() as f32) / zoom_factor,
|
x: (event.client_x() as f32 - rect.left()) / zoom_factor,
|
||||||
y: (event.client_y() as f32 - rect.top() as f32) / zoom_factor,
|
y: (event.client_y() as f32 - rect.top()) / zoom_factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,31 +52,31 @@ pub fn pos_from_touch_event(
|
||||||
.or_else(|| event.touches().get(0))
|
.or_else(|| event.touches().get(0))
|
||||||
.map_or(Default::default(), |touch| {
|
.map_or(Default::default(), |touch| {
|
||||||
*touch_id_for_pos = Some(egui::TouchId::from(touch.identifier()));
|
*touch_id_for_pos = Some(egui::TouchId::from(touch.identifier()));
|
||||||
pos_from_touch(canvas_origin(canvas), &touch, egui_ctx)
|
pos_from_touch(canvas_content_rect(canvas), &touch, egui_ctx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pos_from_touch(
|
fn pos_from_touch(
|
||||||
canvas_origin: egui::Pos2,
|
canvas_rect: egui::Rect,
|
||||||
touch: &web_sys::Touch,
|
touch: &web_sys::Touch,
|
||||||
egui_ctx: &egui::Context,
|
egui_ctx: &egui::Context,
|
||||||
) -> egui::Pos2 {
|
) -> egui::Pos2 {
|
||||||
let zoom_factor = egui_ctx.zoom_factor();
|
let zoom_factor = egui_ctx.zoom_factor();
|
||||||
egui::Pos2 {
|
egui::Pos2 {
|
||||||
x: (touch.page_x() as f32 - canvas_origin.x) / zoom_factor,
|
x: (touch.client_x() as f32 - canvas_rect.left()) / zoom_factor,
|
||||||
y: (touch.page_y() as f32 - canvas_origin.y) / zoom_factor,
|
y: (touch.client_y() as f32 - canvas_rect.top()) / zoom_factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_touches(runner: &mut AppRunner, phase: egui::TouchPhase, event: &web_sys::TouchEvent) {
|
pub fn push_touches(runner: &mut AppRunner, phase: egui::TouchPhase, event: &web_sys::TouchEvent) {
|
||||||
let canvas_origin = canvas_origin(runner.canvas());
|
let canvas_rect = canvas_content_rect(runner.canvas());
|
||||||
for touch_idx in 0..event.changed_touches().length() {
|
for touch_idx in 0..event.changed_touches().length() {
|
||||||
if let Some(touch) = event.changed_touches().item(touch_idx) {
|
if let Some(touch) = event.changed_touches().item(touch_idx) {
|
||||||
runner.input.raw.events.push(egui::Event::Touch {
|
runner.input.raw.events.push(egui::Event::Touch {
|
||||||
device_id: egui::TouchDeviceId(0),
|
device_id: egui::TouchDeviceId(0),
|
||||||
id: egui::TouchId::from(touch.identifier()),
|
id: egui::TouchId::from(touch.identifier()),
|
||||||
phase,
|
phase,
|
||||||
pos: pos_from_touch(canvas_origin, &touch, runner.egui_ctx()),
|
pos: pos_from_touch(canvas_rect, &touch, runner.egui_ctx()),
|
||||||
force: Some(touch.force()),
|
force: Some(touch.force()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,9 +133,31 @@ fn get_canvas_element_by_id_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElemen
|
||||||
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
|
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canvas_origin(canvas: &web_sys::HtmlCanvasElement) -> egui::Pos2 {
|
/// Returns the canvas in client coordinates.
|
||||||
let rect = canvas.get_bounding_client_rect();
|
fn canvas_content_rect(canvas: &web_sys::HtmlCanvasElement) -> egui::Rect {
|
||||||
egui::pos2(rect.left() as f32, rect.top() as f32)
|
let bounding_rect = canvas.get_bounding_client_rect();
|
||||||
|
|
||||||
|
let mut rect = egui::Rect::from_min_max(
|
||||||
|
egui::pos2(bounding_rect.left() as f32, bounding_rect.top() as f32),
|
||||||
|
egui::pos2(bounding_rect.right() as f32, bounding_rect.bottom() as f32),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We need to subtract padding and border:
|
||||||
|
if let Some(window) = web_sys::window() {
|
||||||
|
if let Ok(Some(style)) = window.get_computed_style(canvas) {
|
||||||
|
let get_property = |name: &str| -> Option<f32> {
|
||||||
|
let property = style.get_property_value(name).ok()?;
|
||||||
|
property.trim_end_matches("px").parse::<f32>().ok()
|
||||||
|
};
|
||||||
|
|
||||||
|
rect.min.x += get_property("padding-left").unwrap_or_default();
|
||||||
|
rect.min.y += get_property("padding-top").unwrap_or_default();
|
||||||
|
rect.max.x -= get_property("padding-right").unwrap_or_default();
|
||||||
|
rect.max.y -= get_property("padding-bottom").unwrap_or_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement, ctx: &egui::Context) -> egui::Vec2 {
|
fn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement, ctx: &egui::Context) -> egui::Vec2 {
|
||||||
|
|
|
@ -115,8 +115,8 @@ impl TextAgent {
|
||||||
let Some(ime) = ime else { return Ok(()) };
|
let Some(ime) = ime else { return Ok(()) };
|
||||||
|
|
||||||
let ime_pos = ime.cursor_rect.left_top();
|
let ime_pos = ime.cursor_rect.left_top();
|
||||||
let canvas_rect = canvas.get_bounding_client_rect();
|
let canvas_rect = super::canvas_content_rect(canvas);
|
||||||
let new_pos = ime_pos + egui::vec2(canvas_rect.left() as f32, canvas_rect.top() as f32);
|
let new_pos = canvas_rect.min + ime_pos.to_vec2();
|
||||||
|
|
||||||
let style = self.input.style();
|
let style = self.input.style();
|
||||||
style.set_property("top", &format!("{}px", new_pos.y))?;
|
style.set_property("top", &format!("{}px", new_pos.y))?;
|
||||||
|
|
Loading…
Reference in New Issue