1 """

2 Alignment operations mixin for pyPhotoAlbum

3 """

4

5 from pyPhotoAlbum.decorators import ribbon_action

6 from pyPhotoAlbum.alignment import AlignmentManager

7 from pyPhotoAlbum.commands import AlignElementsCommand, ResizeElementsCommand

8

9

10 class AlignmentOperationsMixin:

11 """Mixin providing element alignment operations"""

12

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 []

16

17 def _execute_alignment(self, alignment_func, status_msg: str):

18 """

19 Execute an alignment operation with common boilerplate.

20

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

28

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]

35

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")

47

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")

59

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")

71

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")

83

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")

95

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")

107

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

121

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

127

128 page_size = page.layout.size

129

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)