1 """

2 View operations mixin for pyPhotoAlbum

3 """

4

5 from pyPhotoAlbum.decorators import ribbon_action

6 from pyPhotoAlbum.dialogs import PrintSettingsDialog

7

8

9 class ViewOperationsMixin:

10 """Mixin providing view-related operations"""

11

12 @ribbon_action(label="Zoom In", tooltip="Zoom in", tab="View", group="Zoom", shortcut="Ctrl++")

13 def zoom_in(self):

14 """Zoom in"""

15 self.gl_widget.zoom_level *= 1.2

16 if self.gl_widget.zoom_level > 5.0:

17 self.gl_widget.zoom_level = 5.0

18 self.update_view()

19 self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)

20

21 @ribbon_action(label="Zoom Out", tooltip="Zoom out", tab="View", group="Zoom", shortcut="Ctrl+-")

22 def zoom_out(self):

23 """Zoom out"""

24 self.gl_widget.zoom_level /= 1.2

25 if self.gl_widget.zoom_level < 0.1:

26 self.gl_widget.zoom_level = 0.1

27 self.update_view()

28 self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)

29

30 @ribbon_action(label="Fit to Window", tooltip="Fit page to window", tab="View", group="Zoom", shortcut="Ctrl+0")

31 def zoom_fit(self):

32 """Fit page to window"""

33 if not self.project.pages:

34 return

35

36 current_page = self.project.pages[self.gl_widget.current_page_index]

37 page_width_mm = current_page.layout.size[0]

38 page_height_mm = current_page.layout.size[1]

39

40 # Convert to pixels

41 dpi = self.project.working_dpi

42 page_width_px = page_width_mm * dpi / 25.4

43 page_height_px = page_height_mm * dpi / 25.4

44

45 # Get widget size

46 widget_width = self.gl_widget.width() - 100 # Margins

47 widget_height = self.gl_widget.height() - 100

48

49 # Calculate zoom to fit

50 zoom_w = widget_width / page_width_px

51 zoom_h = widget_height / page_height_px

52

53 self.gl_widget.zoom_level = min(zoom_w, zoom_h)

54 self.gl_widget.zoom_level = max(0.1, min(5.0, self.gl_widget.zoom_level))

55 self.update_view()

56 self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)

57

58 @ribbon_action(

59 label="Grid Snap",

60 tooltip="Enable/disable snapping to grid (Ctrl+G)",

61 tab="Insert",

62 group="Snapping",

63 shortcut="Ctrl+G",

64 )

65 def toggle_grid_snap(self):

66 """Toggle grid snapping"""

67 if not self.project:

68 return

69

70 self.project.snap_to_grid = not self.project.snap_to_grid

71

72 status = "enabled" if self.project.snap_to_grid else "disabled"

73 self.update_view()

74 self.show_status(f"Grid snapping {status}", 2000)

75 print(f"Grid snapping {status}")

76

77 @ribbon_action(

78 label="Edge Snap",

79 tooltip="Enable/disable snapping to page edges (Ctrl+E)",

80 tab="Insert",

81 group="Snapping",

82 shortcut="Ctrl+E",

83 )

84 def toggle_edge_snap(self):

85 """Toggle edge snapping"""

86 if not self.project:

87 return

88

89 self.project.snap_to_edges = not self.project.snap_to_edges

90

91 status = "enabled" if self.project.snap_to_edges else "disabled"

92 self.update_view()

93 self.show_status(f"Edge snapping {status}", 2000)

94 print(f"Edge snapping {status}")

95

96 @ribbon_action(label="Guide Snap", tooltip="Enable/disable snapping to guides", tab="Insert", group="Snapping")

97 def toggle_guide_snap(self):

98 """Toggle guide snapping"""

99 if not self.project:

100 return

101

102 self.project.snap_to_guides = not self.project.snap_to_guides

103

104 status = "enabled" if self.project.snap_to_guides else "disabled"

105 self.update_view()

106 self.show_status(f"Guide snapping {status}", 2000)

107 print(f"Guide snapping {status}")

108

109 @ribbon_action(label="Show Grid", tooltip="Toggle visibility of grid lines", tab="Insert", group="Snapping")

110 def toggle_show_grid(self):

111 """Toggle grid visibility"""

112 if not self.project:

113 return

114

115 self.project.show_grid = not self.project.show_grid

116

117 status = "visible" if self.project.show_grid else "hidden"

118 self.update_view()

119 self.show_status(f"Grid {status}", 2000)

120 print(f"Grid {status}")

121

122 @ribbon_action(

123 label="Print Settings...", tooltip="Configure bleed and safe area for all pages", tab="View", group="Guides"

124 )

125 def open_print_settings(self):

126 """Open the print settings dialog (bleed and safe area)"""

127 if not self.project:

128 return

129

130 dialog = PrintSettingsDialog(self, self.project)

131 if dialog.exec():

132 values = dialog.get_values()

133 self.project.page_bleed_mm = values["page_bleed_mm"]

134 self.project.page_safe_area_mm = values["page_safe_area_mm"]

135 self.update_view()

136 self.show_status(

137 f"Bleed: {values['page_bleed_mm']:.1f}mm, Safe area: {values['page_safe_area_mm']:.1f}mm", 2000

138 )

139

140 @ribbon_action(

141 label="Print Guides", tooltip="Toggle bleed/cut/safe-area guide lines in the editor", tab="View", group="Guides"

142 )

143 def toggle_print_guides(self):

144 """Toggle print guide lines (bleed/cut/safe area)"""

145 if not self.project:

146 return

147

148 self.project.show_print_guides = not self.project.show_print_guides

149

150 status = "visible" if self.project.show_print_guides else "hidden"

151 self.update_view()

152 self.show_status(f"Print guides {status}", 2000)

153

154 @ribbon_action(label="Show Guides", tooltip="Toggle visibility of guide lines", tab="Insert", group="Snapping")

155 def toggle_snap_lines(self):

156 """Toggle guide lines visibility"""

157 if not self.project:

158 return

159

160 self.project.show_snap_lines = not self.project.show_snap_lines

161

162 status = "visible" if self.project.show_snap_lines else "hidden"

163 self.update_view()

164 self.show_status(f"Guides {status}", 2000)

165 print(f"Guides {status}")

166

167 @ribbon_action(label="Add H Guide", tooltip="Add horizontal guide at page center", tab="View", group="Guides")

168 def add_horizontal_guide(self):

169 """Add a horizontal guide at page center"""

170 current_page = self.get_current_page()

171 if not current_page:

172 return

173

174 # Add guide at vertical center (in mm)

175 center_y = current_page.layout.size[1] / 2.0

176 current_page.layout.snapping_system.add_guide(center_y, "horizontal")

177

178 self.update_view()

179 self.show_status(f"Added horizontal guide at {center_y:.1f} mm", 2000)

180 print(f"Added horizontal guide at {center_y:.1f} mm")

181

182 @ribbon_action(label="Add V Guide", tooltip="Add vertical guide at page center", tab="View", group="Guides")

183 def add_vertical_guide(self):

184 """Add a vertical guide at page center"""

185 current_page = self.get_current_page()

186 if not current_page:

187 return

188

189 # Add guide at horizontal center (in mm)

190 center_x = current_page.layout.size[0] / 2.0

191 current_page.layout.snapping_system.add_guide(center_x, "vertical")

192

193 self.update_view()

194 self.show_status(f"Added vertical guide at {center_x:.1f} mm", 2000)

195 print(f"Added vertical guide at {center_x:.1f} mm")

196

197 @ribbon_action(label="Clear Guides", tooltip="Clear all guides from current page", tab="View", group="Guides")

198 def clear_guides(self):

199 """Clear all guides from current page"""

200 current_page = self.get_current_page()

201 if not current_page:

202 return

203

204 guide_count = len(current_page.layout.snapping_system.guides)

205 current_page.layout.snapping_system.clear_guides()

206

207 self.update_view()

208 self.show_status(f"Cleared {guide_count} guides", 2000)

209 print(f"Cleared {guide_count} guides")

210

211 @ribbon_action(

212 label="Image Browser",

213 tooltip="Show/hide the image browser panel",

214 tab="View",

215 group="Panels",

216 shortcut="Ctrl+B",

217 )

218 def toggle_image_browser(self):

219 """Toggle the thumbnail browser visibility"""

220 if hasattr(self, "_thumbnail_browser"):

221 if self._thumbnail_browser.isVisible():

222 self._thumbnail_browser.hide()

223 self.show_status("Image browser hidden", 2000)

224 else:

225 self._thumbnail_browser.show()

226 self.show_status("Image browser shown", 2000)

227

228 @ribbon_action(

229 label="Grid Settings...", tooltip="Configure grid size and snap threshold", tab="Insert", group="Snapping"

230 )

231 def set_grid_size(self):

232 """Open dialog to set grid size"""

233 from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QPushButton

234

235 if not self.project:

236 return

237

238 # Create dialog

239 dialog = QDialog(self)

240 dialog.setWindowTitle("Grid Settings")

241 dialog.setMinimumWidth(300)

242

243 layout = QVBoxLayout()

244

245 # Grid size setting

246 size_layout = QHBoxLayout()

247 size_layout.addWidget(QLabel("Grid Size:"))

248

249 size_spinbox = QDoubleSpinBox()

250 size_spinbox.setRange(1.0, 100.0)

251 size_spinbox.setValue(self.project.grid_size_mm)

252 size_spinbox.setSuffix(" mm")

253 size_spinbox.setDecimals(1)

254 size_spinbox.setSingleStep(1.0)

255 size_layout.addWidget(size_spinbox)

256

257 layout.addLayout(size_layout)

258

259 # Snap threshold setting

260 threshold_layout = QHBoxLayout()

261 threshold_layout.addWidget(QLabel("Snap Threshold:"))

262

263 threshold_spinbox = QDoubleSpinBox()

264 threshold_spinbox.setRange(0.5, 20.0)

265 threshold_spinbox.setValue(self.project.snap_threshold_mm)

266 threshold_spinbox.setSuffix(" mm")

267 threshold_spinbox.setDecimals(1)

268 threshold_spinbox.setSingleStep(0.5)

269 threshold_layout.addWidget(threshold_spinbox)

270

271 layout.addLayout(threshold_layout)

272

273 # Buttons

274 button_layout = QHBoxLayout()

275 cancel_btn = QPushButton("Cancel")

276 cancel_btn.clicked.connect(dialog.reject)

277 ok_btn = QPushButton("OK")

278 ok_btn.clicked.connect(dialog.accept)

279 ok_btn.setDefault(True)

280

281 button_layout.addStretch()

282 button_layout.addWidget(cancel_btn)

283 button_layout.addWidget(ok_btn)

284 layout.addLayout(button_layout)

285

286 dialog.setLayout(layout)

287

288 # Show dialog and apply if accepted

289 if dialog.exec() == QDialog.DialogCode.Accepted:

290 new_grid_size = size_spinbox.value()

291 new_threshold = threshold_spinbox.value()

292

293 self.project.grid_size_mm = new_grid_size

294 self.project.snap_threshold_mm = new_threshold

295

296 self.update_view()

297 self.show_status(f"Grid size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm", 2000)

298 print(f"Updated grid settings - Size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm")