diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d5a6aa..0846a4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ * Allow clicking on a date in the Statistics heatmap and have it open that page * Add the ability to choose the database path at startup * Add in-app bug report functionality - * Add ability to take screenshots in-app and insert them into the page # 0.3.1 diff --git a/README.md b/README.md index 5610c12..2df84cf 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ report from within the app. * All changes are version controlled, with ability to view/diff versions and revert * Text is Markdown with basic styling * Tabs are supported - right-click on a date from the calendar to open it in a new tab. - * Images are supported, as is the ability to take a screenshot and have it insert into the page automatically. + * Images are supported * Search all pages, or find text on page (Ctrl+F) * Add tags to pages, find pages by tag in the Tag Browser, and customise tag names and colours * Automatic periodic saving (or explicitly save) diff --git a/bouquin/bug_report_dialog.py b/bouquin/bug_report_dialog.py index 59c8fcc..6285c77 100644 --- a/bouquin/bug_report_dialog.py +++ b/bouquin/bug_report_dialog.py @@ -1,6 +1,8 @@ from __future__ import annotations import importlib.metadata +from pathlib import Path + import requests from PySide6.QtWidgets import ( @@ -30,6 +32,8 @@ class BugReportDialog(QDialog): super().__init__(parent) self.setWindowTitle(strings._("report_a_bug")) + self._attachment_path: Path | None = None + layout = QVBoxLayout(self) header = QLabel(strings._("bug_report_explanation")) @@ -44,7 +48,9 @@ class BugReportDialog(QDialog): # Buttons: Cancel / Send button_box = QDialogButtonBox(QDialogButtonBox.Cancel) - button_box.addButton(strings._("send"), QDialogButtonBox.AcceptRole) + self.send_button = button_box.addButton( + strings._("send"), QDialogButtonBox.AcceptRole + ) button_box.accepted.connect(self._send) button_box.rejected.connect(self.reject) layout.addWidget(button_box) diff --git a/bouquin/locales/en.json b/bouquin/locales/en.json index de72186..6cd9270 100644 --- a/bouquin/locales/en.json +++ b/bouquin/locales/en.json @@ -154,9 +154,5 @@ "bug_report_empty": "Please enter some details about the bug before sending.", "bug_report_send_failed": "Could not send bug report.", "bug_report_sent_ok": "Bug report sent. Thank you!", - "send": "Send", - "screenshot": "Take screenshot", - "screenshot_could_not_save": "Could not save screenshot", - "screenshot_get_ready": "You will have five seconds to position your screen before a preview is taken.\n\nYou will be able to click and drag to select a specific region of the preview.", - "screenshot_click_and_drag": "Click and drag to select a region or press enter to accept the whole region" + "send": "Send" } diff --git a/bouquin/main_window.py b/bouquin/main_window.py index 3221809..59581fb 100644 --- a/bouquin/main_window.py +++ b/bouquin/main_window.py @@ -978,7 +978,6 @@ class MainWindow(QMainWindow): tb.historyRequested.connect(self._open_history) tb.insertImageRequested.connect(self._on_insert_image) - tb.insertScreenshotRequested.connect(self._start_screenshot) self._toolbar_bound = True @@ -1052,21 +1051,6 @@ class MainWindow(QMainWindow): for path_str in paths: self.editor.insert_image_from_path(Path(path_str)) - # ----------- Screenshot handler ------------# - def _start_screenshot(self): - ready = QMessageBox.information( - self, - strings._("screenshot"), - strings._("screenshot_get_ready"), - QMessageBox.Ok, - ) - if ready == QMessageBox.Ok: - QGuiApplication.processEvents() - QTimer.singleShot(5000, self._do_screenshot_capture) - - def _do_screenshot_capture(self): - self.editor.take_screenshot() - # ----------- Tags handler ----------------# def _update_tag_views_for_date(self, date_iso: str): if hasattr(self, "tags"): diff --git a/bouquin/markdown_editor.py b/bouquin/markdown_editor.py index d73200c..3a33363 100644 --- a/bouquin/markdown_editor.py +++ b/bouquin/markdown_editor.py @@ -16,10 +16,9 @@ from PySide6.QtGui import ( QTextImageFormat, QDesktopServices, ) -from PySide6.QtCore import Qt, QRect, QTimer, QUrl, QStandardPaths +from PySide6.QtCore import Qt, QRect, QTimer, QUrl from PySide6.QtWidgets import QTextEdit -from .screenshot import ScreenshotMarkdownInserter from .theme import ThemeManager from .markdown_highlighter import MarkdownHighlighter @@ -1065,8 +1064,3 @@ class MarkdownEditor(QTextEdit): cursor = self.textCursor() cursor.insertImage(img_format) cursor.insertText("\n") # Add newline after image - - def take_screenshot(self): - images_dir = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation) - self._screenshot_helper = ScreenshotMarkdownInserter(self, images_dir, self) - self._screenshot_helper.capture_and_insert() diff --git a/bouquin/screenshot.py b/bouquin/screenshot.py deleted file mode 100644 index 0a71e7f..0000000 --- a/bouquin/screenshot.py +++ /dev/null @@ -1,164 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from PySide6.QtCore import Qt, QRect, QPoint, Signal, QDateTime, QObject -from PySide6.QtGui import QGuiApplication, QPainter, QColor, QCursor, QPixmap -from PySide6.QtWidgets import QWidget, QRubberBand, QMessageBox - -from . import strings - - -class ScreenRegionGrabber(QWidget): - regionCaptured = Signal(QPixmap) - - def __init__(self, screenshot_pixmap: QPixmap, parent=None): - super().__init__(parent) - - self._screen_pixmap = screenshot_pixmap - self._selection_rect = QRect() - self._origin = QPoint() - - self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool) - self.setWindowState(Qt.WindowFullScreen) - self.setAttribute(Qt.WA_TranslucentBackground, True) - self.setCursor(Qt.CrossCursor) - - self._rubber_band = QRubberBand(QRubberBand.Rectangle, self) - - def paintEvent(self, event): - painter = QPainter(self) - painter.drawPixmap(self.rect(), self._screen_pixmap) - - # Dim everything - painter.fillRect(self.rect(), QColor(0, 0, 0, 120)) - - # Punch a clear hole for the selection, if there is one - if self._selection_rect.isValid(): - painter.setCompositionMode(QPainter.CompositionMode_Clear) - painter.fillRect(self._selection_rect, Qt.transparent) - painter.setCompositionMode(QPainter.CompositionMode_SourceOver) - else: - # Placeholder text before first click - painter.setPen(QColor(255, 255, 255, 220)) - painter.drawText( - self.rect(), - Qt.AlignCenter, - strings._("screenshot_click_and_drag"), - ) - - painter.end() - - def mousePressEvent(self, event): - if event.button() != Qt.LeftButton: - return - self._origin = event.pos() - self._selection_rect = QRect(self._origin, self._origin) - self._rubber_band.setGeometry(self._selection_rect) - self._rubber_band.show() - - def mouseMoveEvent(self, event): - if not self._rubber_band.isVisible(): - return - self._selection_rect = QRect(self._origin, event.pos()).normalized() - self._rubber_band.setGeometry(self._selection_rect) - self.update() - - def mouseReleaseEvent(self, event): - if event.button() != Qt.LeftButton: - return - if not self._rubber_band.isVisible(): - return - - self._rubber_band.hide() - rect = self._selection_rect.intersected(self._screen_pixmap.rect()) - if rect.isValid(): - cropped = self._screen_pixmap.copy(rect) - self.regionCaptured.emit(cropped) - self.close() - - def keyPressEvent(self, event): - key = event.key() - - # Enter / Return → accept full screen - if key in (Qt.Key_Return, Qt.Key_Enter): - if self._screen_pixmap is not None and not self._screen_pixmap.isNull(): - self.regionCaptured.emit(self._screen_pixmap) - self.close() - return - - # Esc → cancel (no screenshot) - if key == Qt.Key_Escape: - self.close() - return - - # Fallback to default behaviour - super().keyPressEvent(event) - - -class ScreenshotMarkdownInserter(QObject): - """ - Helper that captures a region of the screen, saves it to `images_dir`, - and inserts a Markdown image reference into the MarkdownEditor. - """ - - def __init__(self, editor, images_dir: Path, parent=None): - super().__init__(parent) - self._editor = editor - self._images_dir = Path(images_dir) - self._grabber: ScreenRegionGrabber | None = None - - def capture_and_insert(self): - """ - Starts the screen-region selection overlay. When the user finishes, - the screenshot is saved and the Markdown is inserted in the editor. - """ - screen = QGuiApplication.screenAt(QCursor.pos()) - if screen is None: - screen = QGuiApplication.primaryScreen() - - pixmap = screen.grabWindow(0) - self._grabber = ScreenRegionGrabber(pixmap) - self._grabber.regionCaptured.connect(self._on_region_captured) - self._grabber.show() - - # ------------------------------------------------------------------ internals - - def _on_region_captured(self, pixmap): - if pixmap is None or pixmap.isNull(): - return - - # Ensure output directory exists - self._images_dir.mkdir(parents=True, exist_ok=True) - - timestamp = QDateTime.currentDateTime().toString("yyyyMMdd_HHmmsszzz") - filename = f"bouquin_screenshot_{timestamp}.png" - full_path = self._images_dir / filename - - if not pixmap.save(str(full_path), "PNG"): - QMessageBox.critical( - self, - strings._("screenshot"), - strings._("screenshot_could_not_save"), - ) - return - - self._insert_markdown_image(full_path) - - def _insert_markdown_image(self, path: Path): - """ - Insert image into the MarkdownEditor. - """ - if hasattr(self._editor, "insert_image_from_path"): - self._editor.insert_image_from_path(path) - return - - rel = path.name - markdown = f"![screenshot]({rel})" - - if hasattr(self._editor, "textCursor"): - cursor = self._editor.textCursor() - cursor.insertText(markdown) - self._editor.setTextCursor(cursor) - else: - self._editor.insertPlainText(markdown) diff --git a/bouquin/toolbar.py b/bouquin/toolbar.py index bcc59de..89999b8 100644 --- a/bouquin/toolbar.py +++ b/bouquin/toolbar.py @@ -18,7 +18,6 @@ class ToolBar(QToolBar): checkboxesRequested = Signal() historyRequested = Signal() insertImageRequested = Signal() - insertScreenshotRequested = Signal() def __init__(self, parent=None): super().__init__(strings._("toolbar_format"), parent) @@ -82,21 +81,16 @@ class ToolBar(QToolBar): self.actNumbers.setToolTip(strings._("toolbar_numbered_list")) self.actNumbers.setCheckable(True) self.actNumbers.triggered.connect(self.numbersRequested) - self.actCheckboxes = QAction("☑", self) + self.actCheckboxes = QAction("☐", self) self.actCheckboxes.setToolTip(strings._("toolbar_toggle_checkboxes")) self.actCheckboxes.triggered.connect(self.checkboxesRequested) # Images - self.actInsertImg = QAction("🌄", self) + self.actInsertImg = QAction(strings._("images"), self) self.actInsertImg.setToolTip(strings._("insert_images")) self.actInsertImg.setShortcut("Ctrl+Shift+I") self.actInsertImg.triggered.connect(self.insertImageRequested) - self.actScreenshot = QAction("📸", self) - self.actScreenshot.setToolTip(strings._("screenshot")) - self.actScreenshot.setShortcut("Ctrl+Shift+O") - self.actScreenshot.triggered.connect(self.insertScreenshotRequested) - # History button self.actHistory = QAction(strings._("history"), self) self.actHistory.triggered.connect(self.historyRequested) @@ -136,7 +130,6 @@ class ToolBar(QToolBar): self.actNumbers, self.actCheckboxes, self.actInsertImg, - self.actScreenshot, self.actHistory, ] )