diff --git a/CHANGELOG.md b/CHANGELOG.md index cd52a22..5966eb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 0.1.10 * Improve search results window and highlight in calendar when there are matches. + * Fix styling issue with text that comes after a URL, so it doesn't appear as part of the URL. # 0.1.9 diff --git a/bouquin/editor.py b/bouquin/editor.py index 07ef6d3..b7fc341 100644 --- a/bouquin/editor.py +++ b/bouquin/editor.py @@ -349,7 +349,7 @@ class Editor(QTextEdit): if source.hasImage(): img = self._to_qimage(source.imageData()) if img is not None: - self._insert_qimage_at_cursor(self, img, autoscale=True) + self._insert_qimage_at_cursor(img, autoscale=True) return # 2) File URLs (drag/drop or paste) @@ -496,12 +496,21 @@ class Editor(QTextEdit): cur_fmt = self.textCursor().charFormat() # Do nothing unless either side indicates we're in/propagating an anchor - if not (ins_fmt.isAnchor() or cur_fmt.isAnchor()): + if not ( + ins_fmt.isAnchor() + or cur_fmt.isAnchor() + or ins_fmt.fontUnderline() + or ins_fmt.foreground().style() != Qt.NoBrush + ): return nf = QTextCharFormat(ins_fmt) + # stop the link itself nf.setAnchor(False) nf.setAnchorHref("") + # also stop the link *styling* + nf.setFontUnderline(False) + nf.clearForeground() self.setCurrentCharFormat(nf) diff --git a/tests/test_editor.py b/tests/test_editor.py index cd5855d..6935143 100644 --- a/tests/test_editor.py +++ b/tests/test_editor.py @@ -4,6 +4,8 @@ from PySide6.QtTest import QTest from bouquin.editor import Editor +import re + def _move_cursor_to_first_image(editor: Editor) -> QTextImageFormat | None: c = editor.textCursor() @@ -21,6 +23,57 @@ def _move_cursor_to_first_image(editor: Editor) -> QTextImageFormat | None: return None +def _fmt_at(editor: Editor, pos: int): + c = editor.textCursor() + c.setPosition(pos) + c.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, 1) + return c.charFormat() + + +def test_space_breaks_link_anchor_and_styling(qtbot): + e = Editor() + e.resize(600, 300) + e.show() + qtbot.waitExposed(e) + + # Type a URL, which should be linkified (anchor + underline + blue) + url = "https://mig5.net" + QTest.keyClicks(e, url) + qtbot.waitUntil(lambda: e.toPlainText() == url) + + # Sanity: characters within the URL are anchors + for i in range(len(url)): + assert _fmt_at(e, i).isAnchor() + + # Hit Space – Editor.keyPressEvent() should call _break_anchor_for_next_char() + QTest.keyClick(e, Qt.Key_Space) + + # Type some normal text; it must not inherit the link formatting + tail = "this is a test" + QTest.keyClicks(e, tail) + qtbot.waitUntil(lambda: e.toPlainText().endswith(tail)) + + txt = e.toPlainText() + # Find where our 'tail' starts + start = txt.index(tail) + end = start + len(tail) + + # None of the trailing characters should be part of an anchor or visually underlined + for i in range(start, end): + fmt = _fmt_at(e, i) + assert not fmt.isAnchor(), f"char {i} unexpectedly still has an anchor" + assert not fmt.fontUnderline(), f"char {i} unexpectedly still underlined" + + # Optional: ensure the HTML only wraps the URL in , not the trailing text + html = e.document().toHtml() + assert re.search( + r']*href="https?://mig5\.net"[^>]*>(?:]*>)?https?://mig5\.net(?:)?\s+this is a test', + html, + re.S, + ), html + assert "this is a test" not in html + + def test_embed_qimage_saved_as_data_url(qtbot): e = Editor() e.resize(600, 400)