update page selection
All checks were successful
Python CI / test (push) Successful in 1m24s
Lint / lint (push) Successful in 1m22s
Tests / test (3.10) (push) Successful in 1m2s
Tests / test (3.11) (push) Successful in 1m3s
Tests / test (3.9) (push) Successful in 59s

This commit is contained in:
Duncan Tourolle 2025-11-22 08:22:01 +01:00
parent 353b0c4aff
commit ca2b3545ee
4 changed files with 89 additions and 67 deletions

View File

@ -54,21 +54,58 @@ class ApplicationStateMixin:
return self._template_manager
# Common helper methods
def _get_most_visible_page_index(self):
"""
Determine which page is most visible in the current viewport.
Returns:
int: Index of the most visible page
"""
if not hasattr(self.gl_widget, '_page_renderers') or not self.gl_widget._page_renderers:
return self.gl_widget.current_page_index
# Get viewport dimensions
viewport_height = self.gl_widget.height()
viewport_center_y = viewport_height / 2
# Find which page's center is closest to viewport center
min_distance = float('inf')
best_page_index = self.gl_widget.current_page_index
for renderer, page in self.gl_widget._page_renderers:
# Get page center Y position in screen coordinates
page_height_mm = page.layout.size[1]
page_height_px = page_height_mm * self.project.working_dpi / 25.4
page_center_y_offset = renderer.screen_y + (page_height_px * self.gl_widget.zoom_level / 2)
# Calculate distance from viewport center
distance = abs(page_center_y_offset - viewport_center_y)
if distance < min_distance:
min_distance = distance
# Find the page index in project.pages
try:
best_page_index = self.project.pages.index(page)
except ValueError:
pass
return best_page_index
def get_current_page(self):
"""
Get currently selected page.
Get currently visible page (most visible in viewport).
Returns:
Page instance or None if no page is selected
"""
if not self.project or not self.project.pages:
return None
index = self.gl_widget.current_page_index
index = self._get_most_visible_page_index()
if 0 <= index < len(self.project.pages):
return self.project.pages[index]
return None
def get_current_page_index(self) -> int:

View File

@ -238,9 +238,12 @@ class PageOperationsMixin:
# Connect page selection change
page_combo.currentIndexChanged.connect(on_page_changed)
# Initialize with first page
on_page_changed(0)
# Initialize with most visible page
initial_page_index = self._get_most_visible_page_index()
if 0 <= initial_page_index < len(self.project.pages):
page_combo.setCurrentIndex(initial_page_index)
on_page_changed(initial_page_index if 0 <= initial_page_index < len(self.project.pages) else 0)
# Buttons
button_layout = QHBoxLayout()
@ -335,43 +338,6 @@ class PageOperationsMixin:
if set_default_checkbox.isChecked():
status_msg += " (set as default)"
self.show_status(status_msg, 2000)
def _get_most_visible_page_index(self):
"""
Determine which page is most visible in the current viewport.
Returns:
int: Index of the most visible page
"""
if not hasattr(self.gl_widget, '_page_renderers') or not self.gl_widget._page_renderers:
return self.gl_widget.current_page_index
# Get viewport dimensions
viewport_height = self.gl_widget.height()
viewport_center_y = viewport_height / 2
# Find which page's center is closest to viewport center
min_distance = float('inf')
best_page_index = self.gl_widget.current_page_index
for renderer, page in self.gl_widget._page_renderers:
# Get page center Y position in screen coordinates
page_height_mm = page.layout.size[1]
page_height_px = page_height_mm * self.project.working_dpi / 25.4
page_center_y_offset = renderer.screen_y + (page_height_px * self.gl_widget.zoom_level / 2)
# Calculate distance from viewport center
distance = abs(page_center_y_offset - viewport_center_y)
if distance < min_distance:
min_distance = distance
# Find the page index in project.pages
try:
best_page_index = self.project.pages.index(page)
except ValueError:
pass
return best_page_index
@ribbon_action(
label="Toggle Spread",
@ -427,26 +393,35 @@ class PageOperationsMixin:
@ribbon_action(
label="Remove Page",
tooltip="Remove the last page",
tooltip="Remove the currently selected page",
tab="Layout",
group="Page"
)
def remove_page(self):
"""Remove the last page"""
"""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
# Remove last page
last_page = self.project.pages[-1]
self.project.remove_page(last_page)
# 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
for i, page in enumerate(self.project.pages):
page.page_number = i + 1
# Update display
self.update_view()
print(f"Removed page, now have {len(self.project.pages)} pages")
print(f"Removed {page_name}, now have {len(self.project.pages)} pages")

View File

@ -103,12 +103,14 @@ class TestGetCurrentPage:
# Setup project with pages
project = Mock(spec=Project)
project.working_dpi = 96
page1 = Mock(spec=Page)
page2 = Mock(spec=Page)
project.pages = [page1, page2]
gl_widget = Mock()
gl_widget.current_page_index = 0
gl_widget._page_renderers = [] # No renderers, so it will use current_page_index
window._project = project
window._gl_widget = gl_widget
@ -120,12 +122,14 @@ class TestGetCurrentPage:
qtbot.addWidget(window)
project = Mock(spec=Project)
project.working_dpi = 96
page1 = Mock(spec=Page)
page2 = Mock(spec=Page)
project.pages = [page1, page2]
gl_widget = Mock()
gl_widget.current_page_index = 1
gl_widget._page_renderers = [] # No renderers, so it will use current_page_index
window._project = project
window._gl_widget = gl_widget
@ -158,10 +162,12 @@ class TestGetCurrentPage:
qtbot.addWidget(window)
project = Mock(spec=Project)
project.working_dpi = 96
project.pages = [Mock(spec=Page)]
gl_widget = Mock()
gl_widget.current_page_index = 5 # Out of range
gl_widget._page_renderers = [] # No renderers, so it will use current_page_index
window._project = project
window._gl_widget = gl_widget
@ -280,10 +286,12 @@ class TestRequirePage:
qtbot.addWidget(window)
project = Mock(spec=Project)
project.working_dpi = 96
project.pages = [Mock(spec=Page)]
gl_widget = Mock()
gl_widget.current_page_index = 0
gl_widget._page_renderers = [] # No renderers, so it will use current_page_index
window._project = project
window._gl_widget = gl_widget

View File

@ -5,28 +5,30 @@ Tests for PageOperationsMixin
import pytest
from unittest.mock import Mock, MagicMock, patch
from PyQt6.QtWidgets import QMainWindow
from pyPhotoAlbum.mixins.base import ApplicationStateMixin
from pyPhotoAlbum.mixins.operations.page_ops import PageOperationsMixin
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
class TestPageOpsWindow(PageOperationsMixin, QMainWindow):
class TestPageOpsWindow(PageOperationsMixin, ApplicationStateMixin, QMainWindow):
"""Test window with page operations mixin"""
def __init__(self):
super().__init__()
self.gl_widget = Mock()
self.gl_widget.current_page_index = 0
self.gl_widget.zoom_level = 1.0
self.gl_widget.pan_offset = [0, 0]
self.gl_widget._page_renderers = []
self.gl_widget.width = Mock(return_value=800)
self.gl_widget.height = Mock(return_value=600)
self.project = Project(name="Test")
self.project.working_dpi = 96
self.project.page_size_mm = (210, 297)
self._gl_widget = Mock()
self._gl_widget.current_page_index = 0
self._gl_widget.zoom_level = 1.0
self._gl_widget.pan_offset = [0, 0]
self._gl_widget._page_renderers = []
self._gl_widget.width = Mock(return_value=800)
self._gl_widget.height = Mock(return_value=600)
self._project = Project(name="Test")
self._project.working_dpi = 96
self._project.page_size_mm = (210, 297)
self._update_view_called = False
self._status_message = None
self._status_bar = Mock()
def update_view(self):
self._update_view_called = True