Coverage for pyPhotoAlbum/mixins/operations/alignment_ops.py: 72%
50 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 12:55 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 12:55 +0000
1"""
2Alignment operations mixin for pyPhotoAlbum
3"""
5from pyPhotoAlbum.decorators import ribbon_action
6from pyPhotoAlbum.alignment import AlignmentManager
7from pyPhotoAlbum.commands import AlignElementsCommand, ResizeElementsCommand
10class AlignmentOperationsMixin:
11 """Mixin providing element alignment operations"""
13 def _get_selected_elements_list(self):
14 """Get list of selected elements for alignment operations"""
15 return list(self.gl_widget.selected_elements) if self.gl_widget.selected_elements else []
17 def _execute_alignment(self, alignment_func, status_msg: str):
18 """
19 Execute an alignment operation with common boilerplate.
21 Args:
22 alignment_func: AlignmentManager method to call with elements
23 status_msg: Status message format string (will receive element count)
24 """
25 elements = self._get_selected_elements_list()
26 if not self.require_selection(min_count=2): # type: ignore[attr-defined]
27 return
29 changes = alignment_func(elements)
30 if changes:
31 cmd = AlignElementsCommand(changes)
32 self.project.history.execute(cmd) # type: ignore[attr-defined]
33 self.update_view() # type: ignore[attr-defined]
34 self.show_status(status_msg.format(len(elements)), 2000) # type: ignore[attr-defined]
36 @ribbon_action(
37 label="Align Left",
38 tooltip="Align selected elements to the left",
39 tab="Arrange",
40 group="Align",
41 requires_selection=True,
42 min_selection=2,
43 )
44 def align_left(self):
45 """Align selected elements to the left"""
46 self._execute_alignment(AlignmentManager.align_left, "Aligned {} elements to left")
48 @ribbon_action(
49 label="Align Right",
50 tooltip="Align selected elements to the right",
51 tab="Arrange",
52 group="Align",
53 requires_selection=True,
54 min_selection=2,
55 )
56 def align_right(self):
57 """Align selected elements to the right"""
58 self._execute_alignment(AlignmentManager.align_right, "Aligned {} elements to right")
60 @ribbon_action(
61 label="Align Top",
62 tooltip="Align selected elements to the top",
63 tab="Arrange",
64 group="Align",
65 requires_selection=True,
66 min_selection=2,
67 )
68 def align_top(self):
69 """Align selected elements to the top"""
70 self._execute_alignment(AlignmentManager.align_top, "Aligned {} elements to top")
72 @ribbon_action(
73 label="Align Bottom",
74 tooltip="Align selected elements to the bottom",
75 tab="Arrange",
76 group="Align",
77 requires_selection=True,
78 min_selection=2,
79 )
80 def align_bottom(self):
81 """Align selected elements to the bottom"""
82 self._execute_alignment(AlignmentManager.align_bottom, "Aligned {} elements to bottom")
84 @ribbon_action(
85 label="Align H-Center",
86 tooltip="Align selected elements to horizontal center",
87 tab="Arrange",
88 group="Align",
89 requires_selection=True,
90 min_selection=2,
91 )
92 def align_horizontal_center(self):
93 """Align selected elements to horizontal center"""
94 self._execute_alignment(AlignmentManager.align_horizontal_center, "Aligned {} elements to horizontal center")
96 @ribbon_action(
97 label="Align V-Center",
98 tooltip="Align selected elements to vertical center",
99 tab="Arrange",
100 group="Align",
101 requires_selection=True,
102 min_selection=2,
103 )
104 def align_vertical_center(self):
105 """Align selected elements to vertical center"""
106 self._execute_alignment(AlignmentManager.align_vertical_center, "Aligned {} elements to vertical center")
108 @ribbon_action(
109 label="Maximize Pattern",
110 tooltip="Maximize selected elements using crystal growth algorithm",
111 tab="Arrange",
112 group="Size",
113 requires_selection=True,
114 min_selection=1,
115 )
116 def maximize_pattern(self):
117 """Maximize selected elements until they are close to borders or each other"""
118 elements = self._get_selected_elements_list()
119 if not self.require_selection(min_count=1):
120 return
122 # Get page size from current page
123 page = self.get_current_page()
124 if not page:
125 self.show_warning("No Page", "Please create a page first.")
126 return
128 page_size = page.layout.size
130 changes = AlignmentManager.maximize_pattern(elements, page_size)
131 if changes:
132 cmd = ResizeElementsCommand(changes)
133 self.project.history.execute(cmd)
134 self.update_view()
135 self.show_status(f"Maximized {len(elements)} element(s) using pattern growth", 2000)