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, pub middle_pressed: bool,
/// Distinguishes Space+left temporary pan from a physical middle-button pan. /// Distinguishes Space+left temporary pan from a physical middle-button pan.
pub space_left_pan: bool, 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`. /// Canvas shader program — implements `iced::widget::shader::Program`.
@@ -988,6 +990,10 @@ impl shader::Program<Message> for CanvasShaderProgram {
cursor: mouse::Cursor, cursor: mouse::Cursor,
_shell: &mut Shell<'_, Message>, _shell: &mut Shell<'_, Message>,
) -> (iced::event::Status, Option<Message>) { ) -> (iced::event::Status, Option<Message>) {
if !state.middle_pressed {
state.local_pan_offset = None;
}
match event { match event {
shader::Event::Mouse(mouse_event) => { shader::Event::Mouse(mouse_event) => {
match mouse_event { match mouse_event {
@@ -1005,7 +1011,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
self.engine_w as f32, self.engine_w as f32,
self.engine_h as f32, self.engine_h as f32,
self.zoom, self.zoom,
self.pan_offset, state.local_pan_offset.unwrap_or(self.pan_offset),
); );
// Left mouse drag (drawing) // Left mouse drag (drawing)
@@ -1024,7 +1030,8 @@ impl shader::Program<Message> for CanvasShaderProgram {
let start = state.pan_start.unwrap(); let start = state.pan_start.unwrap();
let delta = position - start; let delta = position - start;
state.pan_start = Some(position); 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.x += delta.x;
new_pan.y += delta.y; new_pan.y += delta.y;
@@ -1048,6 +1055,8 @@ impl shader::Program<Message> for CanvasShaderProgram {
(bounds.height - min_vis_h).max(0.0), (bounds.height - min_vis_h).max(0.0),
) - default_oy; ) - default_oy;
state.local_pan_offset = Some(new_pan);
return ( return (
iced::event::Status::Captured, iced::event::Status::Captured,
Some(Message::CanvasPanZoom { Some(Message::CanvasPanZoom {
@@ -1079,6 +1088,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
state.middle_pressed = true; state.middle_pressed = true;
state.space_left_pan = true; state.space_left_pan = true;
state.pan_start = cursor.position(); state.pan_start = cursor.position();
state.local_pan_offset = Some(self.pan_offset);
return (iced::event::Status::Captured, None); return (iced::event::Status::Captured, None);
} }
let local_pos = { let local_pos = {
@@ -1105,6 +1115,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
if let Some(pos) = cursor.position() { if let Some(pos) = cursor.position() {
state.middle_pressed = true; state.middle_pressed = true;
state.pan_start = Some(pos); state.pan_start = Some(pos);
state.local_pan_offset = Some(self.pan_offset);
return (iced::event::Status::Captured, None); return (iced::event::Status::Captured, None);
} }
} else if button == mouse::Button::Right { } else if button == mouse::Button::Right {
@@ -1142,6 +1153,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
state.middle_pressed = false; state.middle_pressed = false;
state.space_left_pan = false; state.space_left_pan = false;
state.pan_start = None; state.pan_start = None;
state.local_pan_offset = None;
return (iced::event::Status::Captured, None); return (iced::event::Status::Captured, None);
} }
state.left_pressed = false; state.left_pressed = false;
@@ -1152,6 +1164,7 @@ impl shader::Program<Message> for CanvasShaderProgram {
} else if button == mouse::Button::Middle { } else if button == mouse::Button::Middle {
state.middle_pressed = false; state.middle_pressed = false;
state.pan_start = None; state.pan_start = None;
state.local_pan_offset = None;
return (iced::event::Status::Captured, 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. /// and pixel data so `prepare()` can upload only the changed sub-region.
fn draw( fn draw(
&self, &self,
_state: &Self::State, state: &Self::State,
_cursor: mouse::Cursor, _cursor: mouse::Cursor,
bounds: Rectangle, bounds: Rectangle,
) -> Self::Primitive { ) -> Self::Primitive {