83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
"""
|
|
Image pan mixin for GLWidget - handles panning images within frames
|
|
"""
|
|
|
|
from typing import Optional, Tuple
|
|
from pyPhotoAlbum.models import ImageData
|
|
|
|
|
|
class ImagePanMixin:
|
|
"""
|
|
Mixin providing image panning functionality.
|
|
|
|
This mixin handles Control+drag to pan an image within its frame by
|
|
adjusting the crop_info property.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Image pan state (for panning image within frame with Control key)
|
|
self.image_pan_mode: bool = False # True when Control+dragging an ImageData element
|
|
self.image_pan_start_crop: Optional[Tuple[float, float, float, float]] = None # Starting crop_info
|
|
|
|
def _handle_image_pan_move(self, x: float, y: float, element: ImageData):
|
|
"""
|
|
Handle image panning within a frame during mouse move.
|
|
|
|
Args:
|
|
x: Current mouse X position in screen coordinates
|
|
y: Current mouse Y position in screen coordinates
|
|
element: The ImageData element being panned
|
|
"""
|
|
if not self.image_pan_mode or not isinstance(element, ImageData):
|
|
return
|
|
|
|
if not self.drag_start_pos:
|
|
return
|
|
|
|
# Calculate mouse movement in screen pixels
|
|
screen_dx = x - self.drag_start_pos[0]
|
|
screen_dy = y - self.drag_start_pos[1]
|
|
|
|
# Get element size in page-local coordinates
|
|
elem_w, elem_h = element.size
|
|
|
|
# Convert screen movement to normalized crop coordinates
|
|
# Negative because moving mouse right should pan image left (show more of right side)
|
|
# Scale by zoom level and element size
|
|
crop_dx = -screen_dx / (elem_w * self.zoom_level)
|
|
crop_dy = -screen_dy / (elem_h * self.zoom_level)
|
|
|
|
# Get starting crop info
|
|
start_crop = self.image_pan_start_crop
|
|
if not start_crop:
|
|
start_crop = (0, 0, 1, 1)
|
|
|
|
# Calculate new crop_info
|
|
crop_width = start_crop[2] - start_crop[0]
|
|
crop_height = start_crop[3] - start_crop[1]
|
|
|
|
new_x_min = start_crop[0] + crop_dx
|
|
new_y_min = start_crop[1] + crop_dy
|
|
new_x_max = new_x_min + crop_width
|
|
new_y_max = new_y_min + crop_height
|
|
|
|
# Clamp to valid range (0-1) to prevent panning beyond image boundaries
|
|
if new_x_min < 0:
|
|
new_x_min = 0
|
|
new_x_max = crop_width
|
|
if new_x_max > 1:
|
|
new_x_max = 1
|
|
new_x_min = 1 - crop_width
|
|
|
|
if new_y_min < 0:
|
|
new_y_min = 0
|
|
new_y_max = crop_height
|
|
if new_y_max > 1:
|
|
new_y_max = 1
|
|
new_y_min = 1 - crop_height
|
|
|
|
# Update element's crop_info
|
|
element.crop_info = (new_x_min, new_y_min, new_x_max, new_y_max)
|