2026-01-01 17:47:58 +00:00

249 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Page operations mixin for pyPhotoAlbum
"""
from pyPhotoAlbum.decorators import ribbon_action, dialog_action
from pyPhotoAlbum.dialogs import PageSetupDialog
from pyPhotoAlbum.project import Page
from pyPhotoAlbum.page_layout import PageLayout
class PageOperationsMixin:
"""Mixin providing page management operations"""
# Note: Previous/Next page navigation removed - now using scrollable multi-page view
# User can scroll through all pages vertically
@ribbon_action(label="Add Page", tooltip="Add a new page to the project", tab="Layout", group="Page")
def add_page(self):
"""Add a new page to the project after the current page"""
# Get the most visible page in viewport to determine insertion point
current_page_index = self._get_most_visible_page_index()
# Ensure index is valid, default to end if not
if current_page_index < 0 or current_page_index >= len(self.project.pages):
insert_index = len(self.project.pages)
else:
# Insert after the current page
insert_index = current_page_index + 1
# Create layout with project default size
width_mm, height_mm = self.project.page_size_mm
new_layout = PageLayout(width=width_mm, height=height_mm)
# Calculate proper page number for the new page
# The page_number represents the logical page number in the book
if insert_index == 0:
# Inserting at the beginning
new_page_number = 1
elif insert_index >= len(self.project.pages):
# Inserting at the end - calculate based on last page
if self.project.pages:
last_page = self.project.pages[-1]
# Add the count of pages the last page represents
new_page_number = last_page.page_number + last_page.get_page_count()
else:
new_page_number = 1
else:
# Inserting in the middle - take the page number of the page that will come after
new_page_number = self.project.pages[insert_index].page_number
new_page = Page(layout=new_layout, page_number=new_page_number)
# New pages are not manually sized - they use project defaults
new_page.manually_sized = False
# Insert the page at the calculated position
self.project.add_page(new_page, index=insert_index)
# Renumber all pages to ensure consistent numbering
# Page numbers represent logical page numbers in the book
current_page_num = 1
for page in self.project.pages:
page.page_number = current_page_num
current_page_num += page.get_page_count()
self.update_view()
# Get display name for status message
new_page_name = self.project.get_page_display_name(new_page)
print(f"Added {new_page_name} at position {insert_index + 1} with size {width_mm}×{height_mm} mm")
@ribbon_action(label="Page Setup", tooltip="Configure page size and settings", tab="Layout", group="Page")
@dialog_action(dialog_class=PageSetupDialog, requires_pages=True)
def page_setup(self, values):
"""
Apply page setup configuration.
This method contains only business logic. UI presentation
is handled by PageSetupDialog and the dialog_action decorator.
Args:
values: Dictionary of values from the dialog
"""
selected_page = values["selected_page"]
selected_index = values["selected_index"]
# Update project cover settings
self.project.paper_thickness_mm = values["paper_thickness_mm"]
self.project.cover_bleed_mm = values["cover_bleed_mm"]
# Handle cover designation (only for first page)
if selected_index == 0:
was_cover = selected_page.is_cover
is_cover = values["is_cover"]
if was_cover != is_cover:
selected_page.is_cover = is_cover
self.project.has_cover = is_cover
if is_cover:
# Calculate and set cover dimensions
self.project.update_cover_dimensions()
print(f"Page 1 designated as cover")
else:
# Restore normal page size
selected_page.layout.size = self.project.page_size_mm
print(f"Cover removed from page 1")
# Get new values
width_mm = values["width_mm"]
height_mm = values["height_mm"]
# Don't allow manual size changes for covers
if not selected_page.is_cover:
# Check if size actually changed
# For double spreads, compare with base width
if selected_page.is_double_spread:
old_base_width = (
selected_page.layout.base_width
if hasattr(selected_page.layout, "base_width")
else selected_page.layout.size[0] / 2
)
old_height = selected_page.layout.size[1]
size_changed = old_base_width != width_mm or old_height != height_mm
if size_changed:
# Update double spread
selected_page.layout.base_width = width_mm
selected_page.layout.size = (width_mm * 2, height_mm)
selected_page.manually_sized = True
print(
f"{self.project.get_page_display_name(selected_page)} "
f"(double spread) updated to {width_mm}×{height_mm} mm per page"
)
else:
old_size = selected_page.layout.size
size_changed = old_size != (width_mm, height_mm)
if size_changed:
# Update single page
selected_page.layout.size = (width_mm, height_mm)
selected_page.layout.base_width = width_mm
selected_page.manually_sized = True
print(
f"{self.project.get_page_display_name(selected_page)} " f"updated to {width_mm}×{height_mm} mm"
)
# Update DPI settings
self.project.working_dpi = values["working_dpi"]
self.project.export_dpi = values["export_dpi"]
# Set as default if checkbox is checked
if values["set_as_default"]:
self.project.page_size_mm = (width_mm, height_mm)
print(f"Project default page size set to {width_mm}×{height_mm} mm")
self.update_view()
# Build status message
page_name = self.project.get_page_display_name(selected_page)
if selected_page.is_cover:
status_msg = f"{page_name} updated"
else:
status_msg = f"{page_name} size: {width_mm}×{height_mm} mm"
if values["set_as_default"]:
status_msg += " (set as default)"
self.show_status(status_msg, 2000)
@ribbon_action(
label="Toggle Spread", tooltip="Toggle double page spread for current page", tab="Layout", group="Page"
)
def toggle_double_spread(self):
"""Toggle double spread for the current page"""
if not self.project.pages:
return
# Try to get the most visible page in viewport, fallback to current_page_index
page_index = self._get_most_visible_page_index()
# Ensure index is valid
if page_index < 0 or page_index >= len(self.project.pages):
page_index = 0
current_page = self.project.pages[page_index]
# Toggle the state
is_double = not current_page.is_double_spread
current_page.is_double_spread = is_double
# Mark as manually sized when toggling spread
current_page.manually_sized = True
# Update the page layout width
current_width = current_page.layout.size[0]
current_height = current_page.layout.size[1]
# Get base width (might already be doubled)
if hasattr(current_page.layout, "base_width"):
base_width = current_page.layout.base_width
else:
# Assume current width is single if not marked as facing
base_width = current_width / 2 if current_page.layout.is_facing_page else current_width
# Set new width based on double spread state
new_width = base_width * 2 if is_double else base_width
current_page.layout.base_width = base_width
current_page.layout.is_facing_page = is_double
current_page.layout.size = (new_width, current_height)
# Update display
self.update_view()
status = "enabled" if is_double else "disabled"
page_name = self.project.get_page_display_name(current_page)
self.show_status(f"{page_name}: Double spread {status}, width = {new_width:.0f}mm", 2000)
print(f"{page_name}: Double spread {status}, width = {new_width}mm")
@ribbon_action(label="Remove Page", tooltip="Remove the currently selected page", tab="Layout", group="Page")
def remove_page(self):
"""Remove the currently selected page"""
if len(self.project.pages) <= 1:
self.show_warning("Cannot Remove", "Must have at least one page")
print("Cannot remove page - must have at least one page")
return
# Get the most visible page in viewport
page_index = self._get_most_visible_page_index()
# Ensure index is valid
if page_index < 0 or page_index >= len(self.project.pages):
page_index = len(self.project.pages) - 1
page_to_remove = self.project.pages[page_index]
page_name = self.project.get_page_display_name(page_to_remove)
# Remove the selected page
self.project.remove_page(page_to_remove)
# Renumber remaining pages to ensure consistent numbering
# Page numbers represent logical page numbers in the book
current_page_num = 1
for page in self.project.pages:
page.page_number = current_page_num
current_page_num += page.get_page_count()
# Update display
self.update_view()
print(f"Removed {page_name}, now have {len(self.project.pages)} pages")