diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/canvas/shader_canvas.rs b/hcie-iced-app/crates/hcie-iced-gui/src/canvas/shader_canvas.rs index 495495f..2509871 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/canvas/shader_canvas.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/canvas/shader_canvas.rs @@ -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, } /// Canvas shader program — implements `iced::widget::shader::Program`. @@ -988,6 +990,10 @@ impl shader::Program for CanvasShaderProgram { cursor: mouse::Cursor, _shell: &mut Shell<'_, Message>, ) -> (iced::event::Status, Option) { + 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 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 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 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 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 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 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 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 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 {