feat: add local pan offset to eliminate one-frame panning latency in shader canvas
mandatory-regression-gate / deterministic-tests (push) Has been cancelled
mandatory-regression-gate / protected-performance-path (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-23 18:00:58 +03:00
parent 513efcdcff
commit 135ab8a628
@@ -893,6 +893,8 @@ pub struct CanvasShaderState {
pub middle_pressed: bool,
/// Distinguishes Space+left temporary pan from a physical middle-button pan.
pub space_left_pan: bool,
/// Immediate local pan offset maintained during active panning to eliminate 1-frame Elm update latency.
pub local_pan_offset: Option<Vector>,
}
/// Canvas shader program — implements `iced::widget::shader::Program`.
@@ -988,6 +990,10 @@ impl shader::Program<Message> for CanvasShaderProgram {
cursor: mouse::Cursor,
_shell: &mut Shell<'_, Message>,
) -> (iced::event::Status, Option<Message>) {
if !state.middle_pressed {
state.local_pan_offset = None;
}
match event {
shader::Event::Mouse(mouse_event) => {
match mouse_event {
@@ -1005,7 +1011,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
self.engine_w as f32,
self.engine_h as f32,
self.zoom,
self.pan_offset,
state.local_pan_offset.unwrap_or(self.pan_offset),
);
// Left mouse drag (drawing)
@@ -1024,7 +1030,8 @@ impl shader::Program<Message> for CanvasShaderProgram {
let start = state.pan_start.unwrap();
let delta = position - start;
state.pan_start = Some(position);
let mut new_pan = self.pan_offset;
let current_pan = state.local_pan_offset.unwrap_or(self.pan_offset);
let mut new_pan = current_pan;
new_pan.x += delta.x;
new_pan.y += delta.y;
@@ -1048,6 +1055,8 @@ impl shader::Program<Message> for CanvasShaderProgram {
(bounds.height - min_vis_h).max(0.0),
) - default_oy;
state.local_pan_offset = Some(new_pan);
return (
iced::event::Status::Captured,
Some(Message::CanvasPanZoom {
@@ -1079,6 +1088,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
state.middle_pressed = true;
state.space_left_pan = true;
state.pan_start = cursor.position();
state.local_pan_offset = Some(self.pan_offset);
return (iced::event::Status::Captured, None);
}
let local_pos = {
@@ -1105,6 +1115,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
if let Some(pos) = cursor.position() {
state.middle_pressed = true;
state.pan_start = Some(pos);
state.local_pan_offset = Some(self.pan_offset);
return (iced::event::Status::Captured, None);
}
} else if button == mouse::Button::Right {
@@ -1142,6 +1153,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
state.middle_pressed = false;
state.space_left_pan = false;
state.pan_start = None;
state.local_pan_offset = None;
return (iced::event::Status::Captured, None);
}
state.left_pressed = false;
@@ -1152,6 +1164,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
} else if button == mouse::Button::Middle {
state.middle_pressed = false;
state.pan_start = None;
state.local_pan_offset = None;
return (iced::event::Status::Captured, None);
}
}
@@ -1240,7 +1253,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
/// and pixel data so `prepare()` can upload only the changed sub-region.
fn draw(
&self,
_state: &Self::State,
state: &Self::State,
_cursor: mouse::Cursor,
bounds: Rectangle,
) -> Self::Primitive {