| 749 | Event::WheelScrolled { delta } => { |
| 750 | // Only respond to wheel when cursor is inside our bounds |
| 751 | let Some(cursor_position) = self.available_cursor_local_position_inside(cursor) |
| 752 | else { |
| 753 | return needs_redraw; |
| 754 | }; |
| 755 | |
| 756 | self.cursor_position = cursor_position; |
| 757 | |
| 758 | let (x, y) = match delta { |
| 759 | iced::mouse::ScrollDelta::Lines { x, y } => (x, y), |
| 760 | iced::mouse::ScrollDelta::Pixels { x, y } => (x, y), |
| 761 | }; |
| 762 | |
| 763 | match widget.controls.scroll_action(self.modifiers) { |
| 764 | Some(ScrollAction::Zoom) => { |
| 765 | // Determine zoom axes from modifiers: |
| 766 | // Shift → Y-axis only |
| 767 | // Alt → X-axis only |
| 768 | // neither → both axes |
| 769 | let zoom_x = !self.modifiers.shift(); |
| 770 | let zoom_y = !self.modifiers.alt(); |
| 771 | self.zoom_at_cursor(y, viewport, zoom_x, zoom_y); |
| 772 | needs_redraw = true; |
| 773 | } |
| 774 | Some(ScrollAction::Pan) => { |
| 775 | let world_pan_x = |
| 776 | -x as f64 * (self.camera.half_extents.x / (viewport.x / 2.0)); |
| 777 | let world_pan_y = |
| 778 | y as f64 * (self.camera.half_extents.y / (viewport.y / 2.0)); |
| 779 | self.camera.position.x += world_pan_x; |
| 780 | self.camera.position.y += world_pan_y; |
| 781 | self.update_axis_links(); |
| 782 | needs_redraw = true; |
| 783 | } |
| 784 | _ => {} |
| 785 | } |
| 786 | } |
| 787 | _ => {} |
| 788 | } |
| 789 | |
| 790 | // camera uniform is handled in renderer per frame |
| 791 | needs_redraw |