Restore link styling and clickability
This commit is contained in:
parent
97e723ce34
commit
22901d0e51
4 changed files with 93 additions and 10 deletions
|
|
@ -3,6 +3,7 @@
|
|||
* Make it possible to add a tag from the Tag Browser
|
||||
* Add a statistics dialog with heatmap
|
||||
* Remove export to .txt (just use .md)
|
||||
* Restore link styling and clickability
|
||||
|
||||
# 0.3
|
||||
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ class MainWindow(QMainWindow):
|
|||
return self._create_new_tab(date)
|
||||
|
||||
def _create_new_tab(self, date: QDate | None = None) -> MarkdownEditor:
|
||||
"""Create a new editor tab and return the editor instance."""
|
||||
if date is None:
|
||||
date = self.calendar.selectedDate()
|
||||
|
||||
|
|
@ -449,7 +450,6 @@ class MainWindow(QMainWindow):
|
|||
self.tab_widget.setCurrentIndex(existing)
|
||||
return self.tab_widget.widget(existing)
|
||||
|
||||
"""Create a new editor tab and return the editor instance."""
|
||||
editor = MarkdownEditor(self.themes)
|
||||
|
||||
# Set up the editor's event connections
|
||||
|
|
@ -1129,6 +1129,15 @@ class MainWindow(QMainWindow):
|
|||
self._load_selected_date()
|
||||
self._refresh_calendar_marks()
|
||||
|
||||
# ------------ Statistics handler --------------- #
|
||||
|
||||
def _open_statistics(self):
|
||||
if not getattr(self, "db", None) or self.db.conn is None:
|
||||
return
|
||||
|
||||
dlg = StatisticsDialog(self.db, self)
|
||||
dlg.exec()
|
||||
|
||||
# ------------ Window positioning --------------- #
|
||||
def _restore_window_position(self):
|
||||
geom = self.settings.value("main/geometry", None)
|
||||
|
|
@ -1434,11 +1443,3 @@ class MainWindow(QMainWindow):
|
|||
super().changeEvent(ev)
|
||||
if ev.type() == QEvent.ActivationChange and self.isActiveWindow():
|
||||
QTimer.singleShot(0, self._focus_editor_now)
|
||||
|
||||
def _open_statistics(self):
|
||||
# If the DB isn't ready for some reason, just do nothing
|
||||
if not getattr(self, "db", None) or self.db.conn is None:
|
||||
return
|
||||
|
||||
dlg = StatisticsDialog(self.db, self)
|
||||
dlg.exec()
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ from PySide6.QtGui import (
|
|||
QTextFormat,
|
||||
QTextBlockFormat,
|
||||
QTextImageFormat,
|
||||
QDesktopServices,
|
||||
)
|
||||
from PySide6.QtCore import Qt, QRect, QTimer
|
||||
from PySide6.QtCore import Qt, QRect, QTimer, QUrl
|
||||
from PySide6.QtWidgets import QTextEdit
|
||||
|
||||
from .theme import ThemeManager
|
||||
|
|
@ -32,6 +33,9 @@ class MarkdownEditor(QTextEdit):
|
|||
|
||||
self.theme_manager = theme_manager
|
||||
|
||||
# Track hyperlink under click
|
||||
self._clicked_link: str | None = None
|
||||
|
||||
# Setup tab width
|
||||
tab_w = 4 * self.fontMetrics().horizontalAdvance(" ")
|
||||
self.setTabStopDistance(tab_w)
|
||||
|
|
@ -70,6 +74,11 @@ class MarkdownEditor(QTextEdit):
|
|||
|
||||
# Enable mouse tracking for checkbox clicking
|
||||
self.viewport().setMouseTracking(True)
|
||||
# Also mark links as mouse-accessible
|
||||
flags = self.textInteractionFlags()
|
||||
self.setTextInteractionFlags(
|
||||
flags | Qt.TextInteractionFlag.LinksAccessibleByMouse
|
||||
)
|
||||
|
||||
def setDocument(self, doc):
|
||||
super().setDocument(doc)
|
||||
|
|
@ -400,6 +409,28 @@ class MarkdownEditor(QTextEdit):
|
|||
|
||||
return (None, "")
|
||||
|
||||
def _url_at_pos(self, pos) -> str | None:
|
||||
"""
|
||||
Return the URL under the given widget position, or None if there isn't one.
|
||||
"""
|
||||
cursor = self.cursorForPosition(pos)
|
||||
block = cursor.block()
|
||||
text = block.text()
|
||||
if not text:
|
||||
return None
|
||||
|
||||
# Position of the cursor inside this block
|
||||
pos_in_block = cursor.position() - block.position()
|
||||
|
||||
# Same pattern as in MarkdownHighlighter
|
||||
url_pattern = re.compile(r"(https?://[^\s<>()]+)")
|
||||
for m in url_pattern.finditer(text):
|
||||
start, end = m.span(1)
|
||||
if start <= pos_in_block < end:
|
||||
return m.group(1)
|
||||
|
||||
return None
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Handle special key events for markdown editing."""
|
||||
|
||||
|
|
@ -622,6 +653,37 @@ class MarkdownEditor(QTextEdit):
|
|||
# Default handling
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
# Change cursor when hovering a link
|
||||
url = self._url_at_pos(event.pos())
|
||||
if url:
|
||||
self.viewport().setCursor(Qt.PointingHandCursor)
|
||||
else:
|
||||
self.viewport().setCursor(Qt.IBeamCursor)
|
||||
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
# Let QTextEdit handle caret/selection first
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
if event.button() != Qt.LeftButton:
|
||||
return
|
||||
|
||||
# If the user dragged to select text, don't treat it as a click
|
||||
if self.textCursor().hasSelection():
|
||||
return
|
||||
|
||||
url_str = self._url_at_pos(event.pos())
|
||||
if not url_str:
|
||||
return
|
||||
|
||||
url = QUrl(url_str)
|
||||
if not url.scheme():
|
||||
url.setScheme("https")
|
||||
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Toggle a checkbox only when the click lands on its icon."""
|
||||
if event.button() == Qt.LeftButton:
|
||||
|
|
|
|||
|
|
@ -91,6 +91,13 @@ class MarkdownHighlighter(QSyntaxHighlighter):
|
|||
self.h3_format.setFontPointSize(14.0)
|
||||
self.h3_format.setFontWeight(QFont.Weight.Bold)
|
||||
|
||||
# Hyperlinks
|
||||
self.link_format = QTextCharFormat()
|
||||
link_color = pal.color(QPalette.Link)
|
||||
self.link_format.setForeground(link_color)
|
||||
self.link_format.setFontUnderline(True)
|
||||
self.link_format.setAnchor(True)
|
||||
|
||||
# Markdown syntax (the markers themselves) - make invisible
|
||||
self.syntax_format = QTextCharFormat()
|
||||
# Make the markers invisible by setting font size to 0.1 points
|
||||
|
|
@ -243,3 +250,15 @@ class MarkdownHighlighter(QSyntaxHighlighter):
|
|||
self.setFormat(start, 1, self.syntax_format)
|
||||
self.setFormat(end - 1, 1, self.syntax_format)
|
||||
self.setFormat(content_start, content_end - content_start, self.code_format)
|
||||
|
||||
# Hyperlinks
|
||||
url_pattern = re.compile(r"(https?://[^\s<>()]+)")
|
||||
for m in url_pattern.finditer(text):
|
||||
start, end = m.span(1)
|
||||
url = m.group(1)
|
||||
|
||||
# Clone link format so we can attach a per-link href
|
||||
fmt = QTextCharFormat(self.link_format)
|
||||
fmt.setAnchorHref(url)
|
||||
# Overlay link attributes on top of whatever formatting is already there
|
||||
self._overlay_range(start, end - start, fmt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue