Add current line highlight (#2405)

This commit is contained in:
Veronica Berglyd Olsen
2025-06-14 17:36:39 +02:00
committed by GitHub
32 changed files with 79 additions and 16 deletions
+2 -1
View File
@@ -107,6 +107,7 @@ A colour theme ``.conf`` file consists of the following settings:
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = green headertext = green
headertag = green:L135 headertag = green:L135
@@ -175,7 +176,7 @@ There are several ways to enter colour values:
.. versionadded:: 2.8 .. versionadded:: 2.8
The ``[Syntax]`` section was moved into the main theme file. Previously, these settings were in The ``[Syntax]`` section was moved into the main theme file. Previously, these settings were in
their own file. The ``[Icons]`` section was renamed to ``[Base]``. their own file. The ``[Icons]`` section was renamed to ``[Base]``. Added the ``line`` setting.
Icon Themes Icon Themes
@@ -56,6 +56,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = blue:48
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
@@ -56,6 +56,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = blue:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
@@ -56,6 +56,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = #969696 text = #969696
line = #282828
link = blue link = blue
headertext = #ffffff headertext = #ffffff
headertag = purple headertag = purple
@@ -57,6 +57,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = green headertext = green
headertag = green:D150 headertag = green:D150
@@ -57,6 +57,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = green headertext = green
headertag = green:L135 headertag = green:L135
+1
View File
@@ -73,6 +73,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = #f8f8f2 text = #f8f8f2
line = base:L150
link = #ff79c6 link = #ff79c6
headertext = purple headertext = purple
headertag = purple:D150 headertag = purple:D150
+1
View File
@@ -56,6 +56,7 @@ errortext = red
[Syntax] [Syntax]
background = #363636 background = #363636
text = default text = default
line = default:32
link = default link = default
headertext = default:L115 headertext = default:L115
headertag = default:D125 headertag = default:D125
@@ -56,6 +56,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = default link = default
headertext = default:D200 headertext = default:D200
headertag = default:L400 headertag = default:L400
+1
View File
@@ -57,6 +57,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = yellow:48
link = purple link = purple
headertext = orange headertext = orange
headertag = red headertag = red
+1
View File
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:160 headertag = blue:160
+1
View File
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:48
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:160 headertag = blue:160
@@ -56,6 +56,7 @@ errortext = #f85149
[Syntax] [Syntax]
background = base background = base
text = faded text = faded
line = default:32
link = blue link = blue
headertext = default headertext = default
headertag = faded headertag = faded
@@ -56,6 +56,7 @@ errortext = #cf222e
[Syntax] [Syntax]
background = base background = base
text = faded text = faded
line = default:32
link = blue link = blue
headertext = default headertext = default
headertag = faded:128 headertag = faded:128
+1
View File
@@ -69,6 +69,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = green headertext = green
headertag = green:D125 headertag = green:D125
@@ -75,6 +75,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = default:D125 headertext = default:D125
headertag = cyan headertag = cyan
@@ -75,6 +75,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = default:L125 headertext = default:L125
headertag = cyan headertag = cyan
@@ -66,6 +66,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:160 headertag = blue:160
@@ -66,6 +66,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:160 headertag = blue:160
+1
View File
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = #4d4d4c text = #4d4d4c
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:L135 headertag = blue:L135
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:48
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:48
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
@@ -76,6 +76,7 @@ errortext = red
[Syntax] [Syntax]
background = base background = base
text = default text = default
line = default:32
link = blue link = blue
headertext = blue headertext = blue
headertag = blue:D150 headertag = blue:D150
+13 -9
View File
@@ -77,15 +77,16 @@ class Config:
"fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "hasEnchant", "hideFocusFooter", "fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "hasEnchant", "hideFocusFooter",
"hideHScroll", "hideVScroll", "highlightEmph", "hostName", "iconColDocs", "iconColTree", "hideHScroll", "hideVScroll", "highlightEmph", "hostName", "iconColDocs", "iconColTree",
"iconTheme", "incNotesWCount", "isDebug", "kernelVer", "lastNotes", "lightTheme", "iconTheme", "incNotesWCount", "isDebug", "kernelVer", "lastNotes", "lightTheme",
"mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog", "nativeFont", "lineHighlight", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak",
"osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos", "narratorDialog", "nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows",
"prefsWinSize", "scrollPastEnd", "searchCase", "searchLoop", "searchMatchCap", "outlinePanePos", "prefsWinSize", "scrollPastEnd", "searchCase", "searchLoop",
"searchNextFile", "searchProjCase", "searchProjRegEx", "searchProjWord", "searchRegEx", "searchMatchCap", "searchNextFile", "searchProjCase", "searchProjRegEx", "searchProjWord",
"searchWord", "showEditToolBar", "showFullPath", "showLineEndings", "showMultiSpaces", "searchRegEx", "searchWord", "showEditToolBar", "showFullPath", "showLineEndings",
"showSessionTime", "showTabsNSpaces", "showViewerPanel", "spellLanguage", "stopWhenIdle", "showMultiSpaces", "showSessionTime", "showTabsNSpaces", "showViewerPanel",
"tabWidth", "textFont", "textMargin", "textWidth", "themeMode", "useCharCount", "spellLanguage", "stopWhenIdle", "tabWidth", "textFont", "textMargin", "textWidth",
"userIdleTime", "verPyQtString", "verPyQtValue", "verPyString", "verQtString", "themeMode", "useCharCount", "userIdleTime", "verPyQtString", "verPyQtValue",
"verQtValue", "viewComments", "viewPanePos", "viewSynopsis", "welcomeWinSize", "verPyString", "verQtString", "verQtValue", "viewComments", "viewPanePos", "viewSynopsis",
"welcomeWinSize",
) )
LANG_NW = 1 LANG_NW = 1
@@ -191,6 +192,7 @@ class Config:
self.textMargin = 40 # Editor/viewer text margin self.textMargin = 40 # Editor/viewer text margin
self.tabWidth = 40 # Editor tabulator width self.tabWidth = 40 # Editor tabulator width
self.cursorWidth = 1 # Editor cursor width self.cursorWidth = 1 # Editor cursor width
self.lineHighlight = False # Highlight current line in editor
self.focusWidth = 800 # Focus Mode text width self.focusWidth = 800 # Focus Mode text width
self.hideFocusFooter = False # Hide document footer in Focus Mode self.hideFocusFooter = False # Hide document footer in Focus Mode
@@ -667,6 +669,7 @@ class Config:
self.textMargin = conf.rdInt(sec, "margin", self.textMargin) self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth) self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
self.cursorWidth = conf.rdInt(sec, "cursorwidth", self.cursorWidth) self.cursorWidth = conf.rdInt(sec, "cursorwidth", self.cursorWidth)
self.lineHighlight = conf.rdBool(sec, "linehighlight", self.lineHighlight)
self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth) self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth)
self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter) self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter)
self.doJustify = conf.rdBool(sec, "justify", self.doJustify) self.doJustify = conf.rdBool(sec, "justify", self.doJustify)
@@ -793,6 +796,7 @@ class Config:
"margin": str(self.textMargin), "margin": str(self.textMargin),
"tabwidth": str(self.tabWidth), "tabwidth": str(self.tabWidth),
"cursorwidth": str(self.cursorWidth), "cursorwidth": str(self.cursorWidth),
"lineHighlight": str(self.lineHighlight),
"focuswidth": str(self.focusWidth), "focuswidth": str(self.focusWidth),
"hidefocusfooter": str(self.hideFocusFooter), "hidefocusfooter": str(self.hideFocusFooter),
"justify": str(self.doJustify), "justify": str(self.doJustify),
+12
View File
@@ -544,6 +544,13 @@ class GuiPreferences(NDialog):
unit=self.tr("px") unit=self.tr("px")
) )
# Highlight Current Line
self.lineHighlight = NSwitch(self)
self.lineHighlight.setChecked(CONFIG.lineHighlight)
self.mainForm.addRow(
self.tr("Highlight current line"), self.lineHighlight
)
# Show Tabs and Spaces # Show Tabs and Spaces
self.showTabsNSpaces = NSwitch(self) self.showTabsNSpaces = NSwitch(self)
self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces) self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces)
@@ -1032,9 +1039,14 @@ class GuiPreferences(NDialog):
CONFIG.tabWidth = self.tabWidth.value() CONFIG.tabWidth = self.tabWidth.value()
# Text Editing # Text Editing
lineHighlight = self.lineHighlight.isChecked()
updateSyntax |= CONFIG.lineHighlight != lineHighlight
CONFIG.spellLanguage = self.spellLanguage.currentData() CONFIG.spellLanguage = self.spellLanguage.currentData()
CONFIG.autoSelect = self.autoSelect.isChecked() CONFIG.autoSelect = self.autoSelect.isChecked()
CONFIG.cursorWidth = self.cursorWidth.value() CONFIG.cursorWidth = self.cursorWidth.value()
CONFIG.lineHighlight = lineHighlight
CONFIG.showTabsNSpaces = self.showTabsNSpaces.isChecked() CONFIG.showTabsNSpaces = self.showTabsNSpaces.isChecked()
CONFIG.showLineEndings = self.showLineEndings.isChecked() CONFIG.showLineEndings = self.showLineEndings.isChecked()
+16 -3
View File
@@ -45,11 +45,11 @@ from PyQt6.QtCore import (
from PyQt6.QtGui import ( from PyQt6.QtGui import (
QAction, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeyEvent, QAction, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeyEvent,
QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent, QShortcut, QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent, QShortcut,
QTextBlock, QTextCursor, QTextDocument, QTextOption QTextBlock, QTextCursor, QTextDocument, QTextFormat, QTextOption
) )
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QApplication, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu, QApplication, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
QPlainTextEdit, QToolBar, QVBoxLayout, QWidget QPlainTextEdit, QTextEdit, QToolBar, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -75,7 +75,8 @@ from novelwriter.tools.lipsum import GuiLipsum
from novelwriter.types import ( from novelwriter.types import (
QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop, QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop,
QtAlignRight, QtKeepAnchor, QtModCtrl, QtModNone, QtModShift, QtMouseLeft, QtAlignRight, QtKeepAnchor, QtModCtrl, QtModNone, QtModShift, QtMouseLeft,
QtMoveAnchor, QtMoveLeft, QtMoveRight, QtScrollAlwaysOff, QtScrollAsNeeded QtMoveAnchor, QtMoveLeft, QtMoveRight, QtScrollAlwaysOff, QtScrollAsNeeded,
QtTransparent
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -145,6 +146,8 @@ class GuiDocEditor(QPlainTextEdit):
self._lastActive = 0.0 # Timestamp of last activity self._lastActive = 0.0 # Timestamp of last activity
self._lastFind = None # Position of the last found search word self._lastFind = None # Position of the last found search word
self._doReplace = False # Switch to temporarily disable auto-replace self._doReplace = False # Switch to temporarily disable auto-replace
self._lineColor = QtTransparent
self._selection = QTextEdit.ExtraSelection()
# Auto-Replace # Auto-Replace
self._autoReplace = TextAutoReplace() self._autoReplace = TextAutoReplace()
@@ -315,6 +318,10 @@ class GuiDocEditor(QPlainTextEdit):
self.docHeader.matchColors() self.docHeader.matchColors()
self.docFooter.matchColors() self.docFooter.matchColors()
self._lineColor = syntax.line
self._selection.format.setBackground(self._lineColor)
self._selection.format.setProperty(QTextFormat.Property.FullWidthSelection, True)
return return
def initEditor(self) -> None: def initEditor(self) -> None:
@@ -372,6 +379,8 @@ class GuiDocEditor(QPlainTextEdit):
# Refresh sizes # Refresh sizes
self.setTabStopDistance(CONFIG.tabWidth) self.setTabStopDistance(CONFIG.tabWidth)
self.setCursorWidth(CONFIG.cursorWidth) self.setCursorWidth(CONFIG.cursorWidth)
self.setExtraSelections([])
self._cursorMoved()
# If we have a document open, we should refresh it in case the # If we have a document open, we should refresh it in case the
# font changed, otherwise we just clear the editor entirely, # font changed, otherwise we just clear the editor entirely,
@@ -1110,6 +1119,10 @@ class GuiDocEditor(QPlainTextEdit):
def _cursorMoved(self) -> None: def _cursorMoved(self) -> None:
"""Triggered when the cursor moved in the editor.""" """Triggered when the cursor moved in the editor."""
self.docFooter.updateLineCount(self.textCursor()) self.docFooter.updateLineCount(self.textCursor())
if CONFIG.lineHighlight:
self._selection.cursor = self.textCursor()
self._selection.cursor.clearSelection()
self.setExtraSelections([self._selection])
return return
@pyqtSlot(int, int, str) @pyqtSlot(int, int, str)
+2
View File
@@ -80,6 +80,7 @@ class SyntaxColors:
back: QColor = QColor(255, 255, 255) back: QColor = QColor(255, 255, 255)
text: QColor = QColor(0, 0, 0) text: QColor = QColor(0, 0, 0)
line: QColor = QColor(0, 0, 0)
link: QColor = QColor(0, 0, 0) link: QColor = QColor(0, 0, 0)
head: QColor = QColor(0, 0, 0) head: QColor = QColor(0, 0, 0)
headH: QColor = QColor(0, 0, 0) headH: QColor = QColor(0, 0, 0)
@@ -364,6 +365,7 @@ class GuiTheme:
if parser.has_section(sec): if parser.has_section(sec):
self.syntaxTheme.back = self._readColor(parser, sec, "background") self.syntaxTheme.back = self._readColor(parser, sec, "background")
self.syntaxTheme.text = self._readColor(parser, sec, "text") self.syntaxTheme.text = self._readColor(parser, sec, "text")
self.syntaxTheme.line = self._readColor(parser, sec, "line")
self.syntaxTheme.link = self._readColor(parser, sec, "link") self.syntaxTheme.link = self._readColor(parser, sec, "link")
self.syntaxTheme.head = self._readColor(parser, sec, "headertext") self.syntaxTheme.head = self._readColor(parser, sec, "headertext")
self.syntaxTheme.headH = self._readColor(parser, sec, "headertag") self.syntaxTheme.headH = self._readColor(parser, sec, "headertag")
+5 -1
View File
@@ -65,7 +65,11 @@ def resetConfigVars():
CONFIG.darkTheme = DEF_GUI_DARK CONFIG.darkTheme = DEF_GUI_DARK
CONFIG.lightTheme = DEF_GUI_LIGHT CONFIG.lightTheme = DEF_GUI_LIGHT
CONFIG.themeMode = nwTheme.LIGHT CONFIG.themeMode = nwTheme.LIGHT
CONFIG.emphLabels = True # Ensures better coverage, off by default
# Enable a few settings to ensure better coverage
CONFIG.emphLabels = True
CONFIG.lineHighlight = True
return return
+2 -1
View File
@@ -1,5 +1,5 @@
[Meta] [Meta]
timestamp = 2025-06-10 23:18:12 timestamp = 2025-06-14 17:03:01
[Main] [Main]
font = font =
@@ -40,6 +40,7 @@ width = 700
margin = 40 margin = 40
tabwidth = 40 tabwidth = 40
cursorwidth = 1 cursorwidth = 1
linehighlight = False
focuswidth = 800 focuswidth = 800
hidefocusfooter = False hidefocusfooter = False
justify = False justify = False
@@ -250,12 +250,14 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
prefs.spellLanguage.setCurrentIndex(prefs.spellLanguage.findData("de")) prefs.spellLanguage.setCurrentIndex(prefs.spellLanguage.findData("de"))
prefs.autoSelect.setChecked(False) prefs.autoSelect.setChecked(False)
prefs.cursorWidth.setValue(5) prefs.cursorWidth.setValue(5)
prefs.lineHighlight.setChecked(False)
prefs.showTabsNSpaces.setChecked(True) prefs.showTabsNSpaces.setChecked(True)
prefs.showLineEndings.setChecked(True) prefs.showLineEndings.setChecked(True)
assert CONFIG.spellLanguage != "de" assert CONFIG.spellLanguage != "de"
assert CONFIG.autoSelect is True assert CONFIG.autoSelect is True
assert CONFIG.cursorWidth == 1 assert CONFIG.cursorWidth == 1
assert CONFIG.lineHighlight is True
assert CONFIG.showTabsNSpaces is False assert CONFIG.showTabsNSpaces is False
assert CONFIG.showLineEndings is False assert CONFIG.showLineEndings is False
@@ -390,6 +392,7 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
assert CONFIG.spellLanguage == "de" assert CONFIG.spellLanguage == "de"
assert CONFIG.autoSelect is False assert CONFIG.autoSelect is False
assert CONFIG.cursorWidth == 5 assert CONFIG.cursorWidth == 5
assert CONFIG.lineHighlight is False
assert CONFIG.showTabsNSpaces is True assert CONFIG.showTabsNSpaces is True
assert CONFIG.showLineEndings is True assert CONFIG.showLineEndings is True
+1 -1
View File
@@ -632,7 +632,7 @@ def testGuiTheme_CheckTheme(theme):
"helptext", "fadedtext", "errortext", "helptext", "fadedtext", "errortext",
], ],
"Syntax": [ "Syntax": [
"background", "text", "link", "headertext", "headertag", "background", "text", "line", "link", "headertext", "headertag",
"emphasis", "dialog", "altdialog", "hidden", "note", "shortcode", "emphasis", "dialog", "altdialog", "hidden", "note", "shortcode",
"keyword", "tag", "value", "optional", "spellcheckline", "keyword", "tag", "value", "optional", "spellcheckline",
"errorline", "replacetag", "modifier", "texthighlight", "errorline", "replacetag", "modifier", "texthighlight",