Increase line spacing between lines (except for code blocks)
This commit is contained in:
parent
8cd9538a50
commit
0773084ec5
2 changed files with 101 additions and 10 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
* Fix a few small matters identified with tests
|
* Fix a few small matters identified with tests
|
||||||
* Make locales dynamically detected from the locales dir rather than hardcoded
|
* Make locales dynamically detected from the locales dir rather than hardcoded
|
||||||
* Add version information in the navigation
|
* Add version information in the navigation
|
||||||
|
* Increase line spacing between lines (except for code blocks)
|
||||||
|
|
||||||
# 0.2.1.8
|
# 0.2.1.8
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from PySide6.QtGui import (
|
||||||
QTextCursor,
|
QTextCursor,
|
||||||
QTextDocument,
|
QTextDocument,
|
||||||
QTextFormat,
|
QTextFormat,
|
||||||
|
QTextBlockFormat,
|
||||||
QTextImageFormat,
|
QTextImageFormat,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt, QRect, QTimer
|
from PySide6.QtCore import Qt, QRect, QTimer
|
||||||
|
|
@ -43,6 +44,8 @@ class MarkdownEditor(QTextEdit):
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
self.setFont(font)
|
self.setFont(font)
|
||||||
|
|
||||||
|
self._apply_line_spacing() # 1.25× initial spacing
|
||||||
|
|
||||||
# Checkbox characters (Unicode for display, markdown for storage)
|
# Checkbox characters (Unicode for display, markdown for storage)
|
||||||
self._CHECK_UNCHECKED_DISPLAY = "☐"
|
self._CHECK_UNCHECKED_DISPLAY = "☐"
|
||||||
self._CHECK_CHECKED_DISPLAY = "☑"
|
self._CHECK_CHECKED_DISPLAY = "☑"
|
||||||
|
|
@ -73,6 +76,8 @@ class MarkdownEditor(QTextEdit):
|
||||||
# reattach the highlighter to the new document
|
# reattach the highlighter to the new document
|
||||||
if hasattr(self, "highlighter") and self.highlighter:
|
if hasattr(self, "highlighter") and self.highlighter:
|
||||||
self.highlighter.setDocument(self.document())
|
self.highlighter.setDocument(self.document())
|
||||||
|
self._apply_line_spacing()
|
||||||
|
self._apply_code_block_spacing()
|
||||||
QTimer.singleShot(0, self._update_code_block_row_backgrounds)
|
QTimer.singleShot(0, self._update_code_block_row_backgrounds)
|
||||||
|
|
||||||
def showEvent(self, e):
|
def showEvent(self, e):
|
||||||
|
|
@ -140,9 +145,10 @@ class MarkdownEditor(QTextEdit):
|
||||||
def _update_code_block_row_backgrounds(self):
|
def _update_code_block_row_backgrounds(self):
|
||||||
"""Paint a full-width background for each line that is in a fenced code block."""
|
"""Paint a full-width background for each line that is in a fenced code block."""
|
||||||
doc = self.document()
|
doc = self.document()
|
||||||
sels = []
|
if doc is None:
|
||||||
|
return
|
||||||
|
|
||||||
# Use the same bg color as the highlighter's code block
|
sels = []
|
||||||
bg_brush = self.highlighter.code_block_format.background()
|
bg_brush = self.highlighter.code_block_format.background()
|
||||||
|
|
||||||
inside = False
|
inside = False
|
||||||
|
|
@ -158,16 +164,12 @@ class MarkdownEditor(QTextEdit):
|
||||||
fmt = QTextCharFormat()
|
fmt = QTextCharFormat()
|
||||||
fmt.setBackground(bg_brush)
|
fmt.setBackground(bg_brush)
|
||||||
fmt.setProperty(QTextFormat.FullWidthSelection, True)
|
fmt.setProperty(QTextFormat.FullWidthSelection, True)
|
||||||
# mark so we can merge with other selections safely
|
|
||||||
fmt.setProperty(QTextFormat.UserProperty, "codeblock_bg")
|
fmt.setProperty(QTextFormat.UserProperty, "codeblock_bg")
|
||||||
sel.format = fmt
|
sel.format = fmt
|
||||||
|
|
||||||
cur = QTextCursor(doc)
|
cur = QTextCursor(doc)
|
||||||
cur.setPosition(
|
cur.setPosition(block.position())
|
||||||
block.position()
|
|
||||||
) # collapsed cursor = whole line when FullWidthSelection
|
|
||||||
sel.cursor = cur
|
sel.cursor = cur
|
||||||
|
|
||||||
sels.append(sel)
|
sels.append(sel)
|
||||||
|
|
||||||
if is_fence:
|
if is_fence:
|
||||||
|
|
@ -182,6 +184,60 @@ class MarkdownEditor(QTextEdit):
|
||||||
]
|
]
|
||||||
self.setExtraSelections(others + sels)
|
self.setExtraSelections(others + sels)
|
||||||
|
|
||||||
|
def _apply_line_spacing(self, height: float = 125.0):
|
||||||
|
"""Apply proportional line spacing to the whole document."""
|
||||||
|
doc = self.document()
|
||||||
|
if doc is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
cursor = QTextCursor(doc)
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
cursor.select(QTextCursor.Document)
|
||||||
|
|
||||||
|
fmt = QTextBlockFormat()
|
||||||
|
fmt.setLineHeight(
|
||||||
|
height, # 125.0 = 1.25×
|
||||||
|
QTextBlockFormat.LineHeightTypes.ProportionalHeight.value,
|
||||||
|
)
|
||||||
|
cursor.mergeBlockFormat(fmt)
|
||||||
|
cursor.endEditBlock()
|
||||||
|
|
||||||
|
def _apply_code_block_spacing(self):
|
||||||
|
"""
|
||||||
|
Make all fenced code-block lines (including ``` fences) single-spaced.
|
||||||
|
Call this AFTER _apply_line_spacing().
|
||||||
|
"""
|
||||||
|
doc = self.document()
|
||||||
|
if doc is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
cursor = QTextCursor(doc)
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
|
||||||
|
inside = False
|
||||||
|
block = doc.begin()
|
||||||
|
while block.isValid():
|
||||||
|
text = block.text()
|
||||||
|
stripped = text.strip()
|
||||||
|
is_fence = stripped.startswith("```")
|
||||||
|
is_code_line = is_fence or inside
|
||||||
|
|
||||||
|
if is_code_line:
|
||||||
|
fmt = block.blockFormat()
|
||||||
|
fmt.setLineHeight(
|
||||||
|
0.0,
|
||||||
|
QTextBlockFormat.LineHeightTypes.SingleHeight.value,
|
||||||
|
)
|
||||||
|
cursor.setPosition(block.position())
|
||||||
|
cursor.setBlockFormat(fmt)
|
||||||
|
|
||||||
|
if is_fence:
|
||||||
|
inside = not inside
|
||||||
|
|
||||||
|
block = block.next()
|
||||||
|
|
||||||
|
cursor.endEditBlock()
|
||||||
|
|
||||||
def to_markdown(self) -> str:
|
def to_markdown(self) -> str:
|
||||||
"""Export current content as markdown."""
|
"""Export current content as markdown."""
|
||||||
# First, extract any embedded images and convert to markdown
|
# First, extract any embedded images and convert to markdown
|
||||||
|
|
@ -255,6 +311,9 @@ class MarkdownEditor(QTextEdit):
|
||||||
finally:
|
finally:
|
||||||
self._updating = False
|
self._updating = False
|
||||||
|
|
||||||
|
self._apply_line_spacing()
|
||||||
|
self._apply_code_block_spacing()
|
||||||
|
|
||||||
# Render any embedded images
|
# Render any embedded images
|
||||||
self._render_images()
|
self._render_images()
|
||||||
|
|
||||||
|
|
@ -452,9 +511,33 @@ class MarkdownEditor(QTextEdit):
|
||||||
|
|
||||||
block_state = current_block.userState()
|
block_state = current_block.userState()
|
||||||
|
|
||||||
# If current line is opening code fence, or we're inside a code block
|
stripped = current_line.strip()
|
||||||
if current_line.strip().startswith("```") or block_state == 1:
|
is_fence_line = stripped.startswith("```")
|
||||||
# Just insert a regular newline - the highlighter will format it as code
|
|
||||||
|
if is_fence_line:
|
||||||
|
# Work out if this fence is closing (inside block before it)
|
||||||
|
inside_before = self._is_inside_code_block(current_block.previous())
|
||||||
|
|
||||||
|
# Insert the newline as usual
|
||||||
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
|
if inside_before:
|
||||||
|
# We were on the *closing* fence; the new line is outside the block.
|
||||||
|
# Give that new block normal 1.25× spacing.
|
||||||
|
new_block = self.textCursor().block()
|
||||||
|
fmt = new_block.blockFormat()
|
||||||
|
fmt.setLineHeight(
|
||||||
|
125.0,
|
||||||
|
QTextBlockFormat.LineHeightTypes.ProportionalHeight.value,
|
||||||
|
)
|
||||||
|
cur2 = self.textCursor()
|
||||||
|
cur2.setBlockFormat(fmt)
|
||||||
|
self.setTextCursor(cur2)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# Inside a code block (but not on a fence): newline stays code-style
|
||||||
|
if block_state == 1:
|
||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -646,6 +729,9 @@ class MarkdownEditor(QTextEdit):
|
||||||
c.insertText(f"```\n{selected.rstrip()}\n```\n")
|
c.insertText(f"```\n{selected.rstrip()}\n```\n")
|
||||||
if hasattr(self, "_update_code_block_row_backgrounds"):
|
if hasattr(self, "_update_code_block_row_backgrounds"):
|
||||||
self._update_code_block_row_backgrounds()
|
self._update_code_block_row_backgrounds()
|
||||||
|
# tighten spacing for the new code block
|
||||||
|
self._apply_code_block_spacing()
|
||||||
|
|
||||||
self.setFocus()
|
self.setFocus()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -710,6 +796,10 @@ class MarkdownEditor(QTextEdit):
|
||||||
|
|
||||||
if hasattr(self, "_update_code_block_row_backgrounds"):
|
if hasattr(self, "_update_code_block_row_backgrounds"):
|
||||||
self._update_code_block_row_backgrounds()
|
self._update_code_block_row_backgrounds()
|
||||||
|
|
||||||
|
# tighten spacing for the new code block
|
||||||
|
self._apply_code_block_spacing()
|
||||||
|
|
||||||
self.setFocus()
|
self.setFocus()
|
||||||
|
|
||||||
def apply_heading(self, size: int):
|
def apply_heading(self, size: int):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue