Ensure checkbox only can get checked on/off if it is clicked right on its block position, not any click on the whole line
This commit is contained in:
parent
dfde0d6e6c
commit
bc9fa86281
1 changed files with 66 additions and 26 deletions
|
|
@ -8,6 +8,7 @@ from PySide6.QtGui import (
|
||||||
QColor,
|
QColor,
|
||||||
QFont,
|
QFont,
|
||||||
QFontDatabase,
|
QFontDatabase,
|
||||||
|
QFontMetrics,
|
||||||
QImage,
|
QImage,
|
||||||
QPalette,
|
QPalette,
|
||||||
QGuiApplication,
|
QGuiApplication,
|
||||||
|
|
@ -17,7 +18,7 @@ from PySide6.QtGui import (
|
||||||
QSyntaxHighlighter,
|
QSyntaxHighlighter,
|
||||||
QTextImageFormat,
|
QTextImageFormat,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt, QRect
|
||||||
from PySide6.QtWidgets import QTextEdit
|
from PySide6.QtWidgets import QTextEdit
|
||||||
|
|
||||||
from .theme import ThemeManager, Theme
|
from .theme import ThemeManager, Theme
|
||||||
|
|
@ -525,34 +526,73 @@ class MarkdownEditor(QTextEdit):
|
||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
"""Handle mouse clicks - check for checkbox clicking."""
|
"""Toggle a checkbox only when the click lands on its icon."""
|
||||||
if event.button() == Qt.MouseButton.LeftButton:
|
if event.button() == Qt.LeftButton:
|
||||||
cursor = self.cursorForPosition(event.pos())
|
pt = event.pos()
|
||||||
cursor.select(QTextCursor.SelectionType.LineUnderCursor)
|
|
||||||
line = cursor.selectedText()
|
|
||||||
|
|
||||||
# Check if clicking on a checkbox line
|
# Cursor and block under the mouse
|
||||||
if (
|
cur = self.cursorForPosition(pt)
|
||||||
f"{self._CHECK_UNCHECKED_DISPLAY} " in line
|
block = cur.block()
|
||||||
or f"{self._CHECK_CHECKED_DISPLAY} " in line
|
text = block.text()
|
||||||
):
|
|
||||||
# Toggle the checkbox
|
# The display tokens, e.g. "☐ " / "☑ " (icon + trailing space)
|
||||||
if f"{self._CHECK_UNCHECKED_DISPLAY} " in line:
|
unchecked = f"{self._CHECK_UNCHECKED_DISPLAY} "
|
||||||
new_line = line.replace(
|
checked = f"{self._CHECK_CHECKED_DISPLAY} "
|
||||||
f"{self._CHECK_UNCHECKED_DISPLAY} ",
|
|
||||||
f"{self._CHECK_CHECKED_DISPLAY} ",
|
# Helper: rect for a single character at a given doc position
|
||||||
|
def char_rect_at(doc_pos, ch):
|
||||||
|
c = QTextCursor(self.document())
|
||||||
|
c.setPosition(doc_pos)
|
||||||
|
start_rect = self.cursorRect(
|
||||||
|
c
|
||||||
|
) # caret rect at char start (viewport coords)
|
||||||
|
|
||||||
|
# Use the actual font at this position for an accurate width
|
||||||
|
fmt_font = (
|
||||||
|
c.charFormat().font() if c.charFormat().isValid() else self.font()
|
||||||
)
|
)
|
||||||
|
fm = QFontMetrics(fmt_font)
|
||||||
|
w = max(1, fm.horizontalAdvance(ch))
|
||||||
|
return QRect(start_rect.x(), start_rect.y(), w, start_rect.height())
|
||||||
|
|
||||||
|
# Scan the line for any checkbox icons; toggle the one we clicked
|
||||||
|
i = 0
|
||||||
|
while i < len(text):
|
||||||
|
icon = None
|
||||||
|
if text.startswith(unchecked, i):
|
||||||
|
icon = self._CHECK_UNCHECKED_DISPLAY
|
||||||
|
elif text.startswith(checked, i):
|
||||||
|
icon = self._CHECK_CHECKED_DISPLAY
|
||||||
|
|
||||||
|
if icon:
|
||||||
|
doc_pos = (
|
||||||
|
block.position() + i
|
||||||
|
) # absolute document position of the icon
|
||||||
|
r = char_rect_at(doc_pos, icon)
|
||||||
|
|
||||||
|
if r.contains(pt):
|
||||||
|
# Build the replacement: swap ☐ <-> ☑ (keep trailing space)
|
||||||
|
new_icon = (
|
||||||
|
self._CHECK_CHECKED_DISPLAY
|
||||||
|
if icon == self._CHECK_UNCHECKED_DISPLAY
|
||||||
|
else self._CHECK_UNCHECKED_DISPLAY
|
||||||
|
)
|
||||||
|
edit = QTextCursor(self.document())
|
||||||
|
edit.beginEditBlock()
|
||||||
|
edit.setPosition(doc_pos)
|
||||||
|
edit.movePosition(
|
||||||
|
QTextCursor.Right, QTextCursor.KeepAnchor, len(icon) + 1
|
||||||
|
) # icon + space
|
||||||
|
edit.insertText(f"{new_icon} ")
|
||||||
|
edit.endEditBlock()
|
||||||
|
return # handled
|
||||||
|
|
||||||
|
# advance past this token
|
||||||
|
i += len(icon) + 1
|
||||||
else:
|
else:
|
||||||
new_line = line.replace(
|
i += 1
|
||||||
f"{self._CHECK_CHECKED_DISPLAY} ",
|
|
||||||
f"{self._CHECK_UNCHECKED_DISPLAY} ",
|
|
||||||
)
|
|
||||||
|
|
||||||
cursor.insertText(new_line)
|
# Default handling for anything else
|
||||||
# Don't call super() - we handled the click
|
|
||||||
return
|
|
||||||
|
|
||||||
# Default handling for non-checkbox clicks
|
|
||||||
super().mousePressEvent(event)
|
super().mousePressEvent(event)
|
||||||
|
|
||||||
# ------------------------ Toolbar action handlers ------------------------
|
# ------------------------ Toolbar action handlers ------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue