Duncan Tourolle 7f32858baf
All checks were successful
Python CI / test (push) Successful in 1m7s
Lint / lint (push) Successful in 1m11s
Tests / test (3.10) (push) Successful in 50s
Tests / test (3.11) (push) Successful in 51s
Tests / test (3.9) (push) Successful in 47s
big refactor to use mixin architecture
2025-11-11 10:35:24 +01:00

135 lines
5.0 KiB
Python

"""
Asset drop mixin for GLWidget - handles drag-and-drop file operations
"""
from pyPhotoAlbum.models import ImageData, PlaceholderData
from pyPhotoAlbum.commands import AddElementCommand
class AssetDropMixin:
"""
Mixin providing drag-and-drop asset functionality.
This mixin handles dragging image files into the widget and creating
or updating ImageData elements.
"""
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp']
def dragEnterEvent(self, event):
"""Handle drag enter events"""
if event.mimeData().hasUrls():
urls = event.mimeData().urls()
for url in urls:
file_path = url.toLocalFile()
if any(file_path.lower().endswith(ext) for ext in self.IMAGE_EXTENSIONS):
event.acceptProposedAction()
return
event.ignore()
def dragMoveEvent(self, event):
"""Handle drag move events"""
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
"""Handle drop events"""
if not event.mimeData().hasUrls():
event.ignore()
return
image_path = None
for url in event.mimeData().urls():
file_path = url.toLocalFile()
if any(file_path.lower().endswith(ext) for ext in self.IMAGE_EXTENSIONS):
image_path = file_path
break
if not image_path:
event.ignore()
return
x, y = event.position().x(), event.position().y()
target_element = self._get_element_at(x, y)
if target_element and isinstance(target_element, (ImageData, PlaceholderData)):
if isinstance(target_element, PlaceholderData):
new_image = ImageData(
image_path=image_path,
x=target_element.position[0],
y=target_element.position[1],
width=target_element.size[0],
height=target_element.size[1],
z_index=target_element.z_index
)
main_window = self.window()
if hasattr(main_window, 'project') and main_window.project and main_window.project.pages:
for page in main_window.project.pages:
if target_element in page.layout.elements:
page.layout.elements.remove(target_element)
page.layout.add_element(new_image)
break
else:
target_element.image_path = image_path
print(f"Updated element with image: {image_path}")
else:
try:
from PIL import Image
img = Image.open(image_path)
img_width, img_height = img.size
max_size = 300
if img_width > max_size or img_height > max_size:
scale = min(max_size / img_width, max_size / img_height)
img_width = int(img_width * scale)
img_height = int(img_height * scale)
except Exception as e:
print(f"Error loading image dimensions: {e}")
img_width, img_height = 200, 150
main_window = self.window()
if hasattr(main_window, 'project') and main_window.project and main_window.project.pages:
# Detect which page the drop occurred on
target_page, page_index, page_renderer = self._get_page_at(x, y)
if target_page and page_renderer:
# Update current_page_index
if page_index >= 0:
self.current_page_index = page_index
# Convert screen coordinates to page-local coordinates
page_local_x, page_local_y = page_renderer.screen_to_page(x, y)
try:
asset_path = main_window.project.asset_manager.import_asset(image_path)
new_image = ImageData(
image_path=asset_path,
x=page_local_x,
y=page_local_y,
width=img_width,
height=img_height
)
cmd = AddElementCommand(
target_page.layout,
new_image,
asset_manager=main_window.project.asset_manager
)
main_window.project.history.execute(cmd)
print(f"Added new image to page {page_index + 1} at ({page_local_x:.1f}, {page_local_y:.1f}): {asset_path}")
except Exception as e:
print(f"Error adding dropped image: {e}")
else:
print("Drop location not on any page")
event.acceptProposedAction()
self.update()