novelWriter 2.5b1__py3-none-any.whl → 2.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/METADATA +1 -1
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/RECORD +61 -61
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/themes/cyberpunk_night.conf +1 -0
- novelwriter/assets/themes/default_dark.conf +1 -0
- novelwriter/assets/themes/default_light.conf +1 -0
- novelwriter/assets/themes/dracula.conf +1 -0
- novelwriter/assets/themes/solarized_dark.conf +1 -0
- novelwriter/assets/themes/solarized_light.conf +1 -0
- novelwriter/common.py +2 -3
- novelwriter/config.py +67 -15
- novelwriter/constants.py +8 -10
- novelwriter/core/buildsettings.py +5 -3
- novelwriter/core/coretools.py +3 -1
- novelwriter/core/docbuild.py +1 -0
- novelwriter/core/tohtml.py +69 -29
- novelwriter/core/tokenizer.py +83 -14
- novelwriter/core/toodt.py +48 -21
- novelwriter/core/toqdoc.py +25 -9
- novelwriter/dialogs/about.py +10 -15
- novelwriter/dialogs/docmerge.py +16 -16
- novelwriter/dialogs/docsplit.py +16 -16
- novelwriter/dialogs/editlabel.py +6 -8
- novelwriter/dialogs/preferences.py +94 -68
- novelwriter/dialogs/projectsettings.py +10 -10
- novelwriter/dialogs/quotes.py +9 -5
- novelwriter/dialogs/wordlist.py +6 -6
- novelwriter/enum.py +4 -5
- novelwriter/extensions/configlayout.py +23 -4
- novelwriter/extensions/modified.py +22 -3
- novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
- novelwriter/extensions/statusled.py +28 -22
- novelwriter/gui/doceditor.py +20 -11
- novelwriter/gui/dochighlight.py +30 -39
- novelwriter/gui/docviewer.py +21 -14
- novelwriter/gui/mainmenu.py +11 -11
- novelwriter/gui/outline.py +3 -3
- novelwriter/gui/projtree.py +19 -28
- novelwriter/gui/search.py +10 -1
- novelwriter/gui/statusbar.py +25 -29
- novelwriter/gui/theme.py +3 -0
- novelwriter/guimain.py +91 -84
- novelwriter/shared.py +10 -8
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +2 -8
- novelwriter/tools/lipsum.py +8 -12
- novelwriter/tools/manusbuild.py +9 -9
- novelwriter/tools/manuscript.py +10 -5
- novelwriter/tools/manussettings.py +7 -3
- novelwriter/tools/noveldetails.py +10 -10
- novelwriter/tools/welcome.py +10 -10
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +5 -2
- novelwriter/extensions/simpleprogress.py +0 -53
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/top_level.txt +0 -0
novelwriter/dialogs/quotes.py
CHANGED
@@ -28,18 +28,22 @@ import logging
|
|
28
28
|
from PyQt5.QtCore import QSize, pyqtSlot
|
29
29
|
from PyQt5.QtGui import QFontMetrics
|
30
30
|
from PyQt5.QtWidgets import (
|
31
|
-
|
31
|
+
QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
|
32
32
|
QListWidgetItem, QVBoxLayout, QWidget
|
33
33
|
)
|
34
34
|
|
35
35
|
from novelwriter import CONFIG
|
36
36
|
from novelwriter.constants import nwQuotes, trConst
|
37
|
-
from novelwriter.
|
37
|
+
from novelwriter.extensions.modified import NDialog
|
38
|
+
from novelwriter.types import (
|
39
|
+
QtAccepted, QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk,
|
40
|
+
QtUserRole
|
41
|
+
)
|
38
42
|
|
39
43
|
logger = logging.getLogger(__name__)
|
40
44
|
|
41
45
|
|
42
|
-
class GuiQuoteSelect(
|
46
|
+
class GuiQuoteSelect(NDialog):
|
43
47
|
|
44
48
|
_selected = ""
|
45
49
|
|
@@ -126,8 +130,8 @@ class GuiQuoteSelect(QDialog):
|
|
126
130
|
cls = GuiQuoteSelect(parent, current=current)
|
127
131
|
cls.exec()
|
128
132
|
quote = cls._selected
|
129
|
-
accepted = cls.result() ==
|
130
|
-
cls.
|
133
|
+
accepted = cls.result() == QtAccepted
|
134
|
+
cls.softDelete()
|
131
135
|
return quote, accepted
|
132
136
|
|
133
137
|
##
|
novelwriter/dialogs/wordlist.py
CHANGED
@@ -30,7 +30,7 @@ from pathlib import Path
|
|
30
30
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
31
31
|
from PyQt5.QtGui import QCloseEvent
|
32
32
|
from PyQt5.QtWidgets import (
|
33
|
-
QAbstractItemView, QApplication,
|
33
|
+
QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
|
34
34
|
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
|
35
35
|
)
|
36
36
|
|
@@ -38,13 +38,13 @@ from novelwriter import CONFIG, SHARED
|
|
38
38
|
from novelwriter.common import formatFileFilter
|
39
39
|
from novelwriter.core.spellcheck import UserDictionary
|
40
40
|
from novelwriter.extensions.configlayout import NColourLabel
|
41
|
-
from novelwriter.extensions.modified import NIconToolButton
|
41
|
+
from novelwriter.extensions.modified import NDialog, NIconToolButton
|
42
42
|
from novelwriter.types import QtDialogClose, QtDialogSave
|
43
43
|
|
44
44
|
logger = logging.getLogger(__name__)
|
45
45
|
|
46
46
|
|
47
|
-
class GuiWordList(
|
47
|
+
class GuiWordList(NDialog):
|
48
48
|
|
49
49
|
newWordListReady = pyqtSignal()
|
50
50
|
|
@@ -69,7 +69,7 @@ class GuiWordList(QDialog):
|
|
69
69
|
|
70
70
|
# Header
|
71
71
|
self.headLabel = NColourLabel(
|
72
|
-
self.tr("Project Word List"), SHARED.theme.helpText,
|
72
|
+
self.tr("Project Word List"), self, color=SHARED.theme.helpText,
|
73
73
|
scale=NColourLabel.HEADER_SCALE
|
74
74
|
)
|
75
75
|
|
@@ -109,7 +109,7 @@ class GuiWordList(QDialog):
|
|
109
109
|
# Buttons
|
110
110
|
self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogClose, self)
|
111
111
|
self.buttonBox.accepted.connect(self._doSave)
|
112
|
-
self.buttonBox.rejected.connect(self.
|
112
|
+
self.buttonBox.rejected.connect(self.reject)
|
113
113
|
|
114
114
|
# Assemble
|
115
115
|
self.outerBox = QVBoxLayout()
|
@@ -140,7 +140,7 @@ class GuiWordList(QDialog):
|
|
140
140
|
"""Capture the close event and perform cleanup."""
|
141
141
|
self._saveGuiSettings()
|
142
142
|
event.accept()
|
143
|
-
self.
|
143
|
+
self.softDelete()
|
144
144
|
return
|
145
145
|
|
146
146
|
##
|
novelwriter/enum.py
CHANGED
@@ -148,12 +148,11 @@ class nwView(Enum):
|
|
148
148
|
SEARCH = 4
|
149
149
|
|
150
150
|
|
151
|
-
class
|
151
|
+
class nwFocus(Enum):
|
152
152
|
|
153
|
-
TREE
|
154
|
-
|
155
|
-
|
156
|
-
OUTLINE = 4
|
153
|
+
TREE = 1
|
154
|
+
DOCUMENT = 2
|
155
|
+
OUTLINE = 3
|
157
156
|
|
158
157
|
|
159
158
|
class nwOutline(Enum):
|
@@ -203,7 +203,7 @@ class NScrollableForm(QScrollArea):
|
|
203
203
|
|
204
204
|
if helpText:
|
205
205
|
qHelp = NColourLabel(
|
206
|
-
str(helpText), color=self._helpCol,
|
206
|
+
str(helpText), self, color=self._helpCol,
|
207
207
|
scale=self._fontScale, wrap=True, indent=self._indent
|
208
208
|
)
|
209
209
|
labelBox = QVBoxLayout()
|
@@ -252,11 +252,20 @@ class NColourLabel(QLabel):
|
|
252
252
|
HELP_SCALE = DEFAULT_SCALE
|
253
253
|
HEADER_SCALE = 1.25
|
254
254
|
|
255
|
-
|
256
|
-
|
257
|
-
|
255
|
+
_state = None
|
256
|
+
|
257
|
+
def __init__(
|
258
|
+
self, text: str, parent: QWidget, *,
|
259
|
+
color: QColor | None = None, faded: QColor | None = None,
|
260
|
+
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
|
261
|
+
bold: bool = False
|
262
|
+
) -> None:
|
258
263
|
super().__init__(text, parent=parent)
|
259
264
|
|
265
|
+
default = self.palette().windowText().color()
|
266
|
+
self._color = color or default
|
267
|
+
self._faded = faded or default
|
268
|
+
|
260
269
|
font = self.font()
|
261
270
|
font.setPointSizeF(scale*font.pointSizeF())
|
262
271
|
font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
|
@@ -268,7 +277,17 @@ class NColourLabel(QLabel):
|
|
268
277
|
self.setFont(font)
|
269
278
|
self.setIndent(indent)
|
270
279
|
self.setWordWrap(wrap)
|
280
|
+
self.setColorState(True)
|
281
|
+
|
282
|
+
return
|
271
283
|
|
284
|
+
def setColorState(self, state: bool) -> None:
|
285
|
+
"""Change the colour state."""
|
286
|
+
if self._state is not state:
|
287
|
+
self._state = state
|
288
|
+
colour = self.palette()
|
289
|
+
colour.setColor(QPalette.ColorRole.WindowText, self._color if state else self._faded)
|
290
|
+
self.setPalette(colour)
|
272
291
|
return
|
273
292
|
|
274
293
|
|
@@ -30,7 +30,7 @@ from __future__ import annotations
|
|
30
30
|
from enum import Enum
|
31
31
|
from typing import TYPE_CHECKING
|
32
32
|
|
33
|
-
from PyQt5.QtCore import QSize, Qt
|
33
|
+
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
34
34
|
from PyQt5.QtGui import QWheelEvent
|
35
35
|
from PyQt5.QtWidgets import (
|
36
36
|
QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
|
@@ -43,7 +43,26 @@ if TYPE_CHECKING: # pragma: no cover
|
|
43
43
|
from novelwriter.guimain import GuiMain
|
44
44
|
|
45
45
|
|
46
|
-
class
|
46
|
+
class NDialog(QDialog):
|
47
|
+
|
48
|
+
def softDelete(self) -> None:
|
49
|
+
"""Since calling deleteLater is sometimes not safe from Python,
|
50
|
+
as the C++ object can be deleted before the Python process is
|
51
|
+
done with the object, we instead set the dialog's parent to None
|
52
|
+
so that it gets garbage collected when it runs out of scope.
|
53
|
+
"""
|
54
|
+
self.setParent(None) # type: ignore
|
55
|
+
return
|
56
|
+
|
57
|
+
@pyqtSlot()
|
58
|
+
def reject(self) -> None:
|
59
|
+
"""Overload the reject slot and also call close."""
|
60
|
+
super().reject()
|
61
|
+
self.close()
|
62
|
+
return
|
63
|
+
|
64
|
+
|
65
|
+
class NToolDialog(NDialog):
|
47
66
|
|
48
67
|
def __init__(self, parent: GuiMain) -> None:
|
49
68
|
super().__init__(parent=parent)
|
@@ -62,7 +81,7 @@ class NToolDialog(QDialog):
|
|
62
81
|
return
|
63
82
|
|
64
83
|
|
65
|
-
class NNonBlockingDialog(
|
84
|
+
class NNonBlockingDialog(NDialog):
|
66
85
|
|
67
86
|
def __init__(self, parent: QWidget | None = None) -> None:
|
68
87
|
super().__init__(parent=parent)
|
@@ -1,9 +1,10 @@
|
|
1
1
|
"""
|
2
|
-
novelWriter – Custom Widget: Progress
|
3
|
-
|
2
|
+
novelWriter – Custom Widget: Progress Bars
|
3
|
+
==========================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2023-06-07 [2.1b1]
|
6
|
+
Created: 2023-06-07 [2.1b1] NProgressCircle
|
7
|
+
Created: 2023-06-09 [2.1b1] NProgressSimple
|
7
8
|
|
8
9
|
This file is a part of novelWriter
|
9
10
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
@@ -101,3 +102,25 @@ class NProgressCircle(QProgressBar):
|
|
101
102
|
painter.setPen(self._tColor)
|
102
103
|
painter.drawText(self._cRect, QtAlignCenter, self._text or f"{progress:.1f} %")
|
103
104
|
return
|
105
|
+
|
106
|
+
|
107
|
+
class NProgressSimple(QProgressBar):
|
108
|
+
"""Extension: Simple Progress Widget
|
109
|
+
|
110
|
+
A custom widget that paints a plain bar with no other styling.
|
111
|
+
"""
|
112
|
+
|
113
|
+
def __init__(self, parent: QWidget) -> None:
|
114
|
+
super().__init__(parent=parent)
|
115
|
+
return
|
116
|
+
|
117
|
+
def paintEvent(self, event: QPaintEvent) -> None:
|
118
|
+
"""Custom painter for the progress bar."""
|
119
|
+
if (value := self.value()) > 0:
|
120
|
+
progress = ceil(self.width()*float(value)/self.maximum())
|
121
|
+
painter = QPainter(self)
|
122
|
+
painter.setRenderHint(QtPaintAnitAlias, True)
|
123
|
+
painter.setPen(self.palette().highlight().color())
|
124
|
+
painter.setBrush(self.palette().highlight())
|
125
|
+
painter.drawRect(0, 0, progress, self.height())
|
126
|
+
return
|
@@ -25,44 +25,50 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
-
from typing import Literal
|
29
|
-
|
30
28
|
from PyQt5.QtGui import QColor, QPainter, QPaintEvent
|
31
29
|
from PyQt5.QtWidgets import QAbstractButton, QWidget
|
32
30
|
|
33
|
-
from novelwriter.
|
31
|
+
from novelwriter.enum import nwTrinary
|
32
|
+
from novelwriter.types import QtBlack, QtPaintAnitAlias
|
34
33
|
|
35
34
|
logger = logging.getLogger(__name__)
|
36
35
|
|
37
36
|
|
38
37
|
class StatusLED(QAbstractButton):
|
39
38
|
|
40
|
-
|
41
|
-
S_BAD = 1
|
42
|
-
S_GOOD = 2
|
43
|
-
|
44
|
-
def __init__(self, colNone: QColor, colGood: QColor, colBad: QColor,
|
45
|
-
sW: int, sH: int, parent: QWidget | None = None) -> None:
|
39
|
+
def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
|
46
40
|
super().__init__(parent=parent)
|
47
|
-
|
48
|
-
self.
|
49
|
-
self.
|
50
|
-
self.
|
51
|
-
self.
|
52
|
-
|
41
|
+
self._neutral = QtBlack
|
42
|
+
self._postitve = QtBlack
|
43
|
+
self._negative = QtBlack
|
44
|
+
self._color = QtBlack
|
45
|
+
self._state = nwTrinary.NEUTRAL
|
53
46
|
self.setFixedWidth(sW)
|
54
47
|
self.setFixedHeight(sH)
|
48
|
+
return
|
55
49
|
|
50
|
+
@property
|
51
|
+
def state(self) -> nwTrinary:
|
52
|
+
"""The current state of the LED."""
|
53
|
+
return self._state
|
54
|
+
|
55
|
+
def setColors(self, neutral: QColor, positive: QColor, negative: QColor) -> None:
|
56
|
+
"""Set the three colours for the status values."""
|
57
|
+
self._neutral = neutral
|
58
|
+
self._postitve = positive
|
59
|
+
self._negative = negative
|
60
|
+
self.setState(self._state)
|
56
61
|
return
|
57
62
|
|
58
|
-
def setState(self, state:
|
63
|
+
def setState(self, state: nwTrinary) -> None:
|
59
64
|
"""Set the colour state."""
|
60
|
-
if state ==
|
61
|
-
self.
|
62
|
-
elif state ==
|
63
|
-
self.
|
65
|
+
if state == nwTrinary.POSITIVE:
|
66
|
+
self._color = self._postitve
|
67
|
+
elif state == nwTrinary.NEGATIVE:
|
68
|
+
self._color = self._negative
|
64
69
|
else:
|
65
|
-
self.
|
70
|
+
self._color = self._neutral
|
71
|
+
self._state = state
|
66
72
|
self.update()
|
67
73
|
return
|
68
74
|
|
@@ -71,7 +77,7 @@ class StatusLED(QAbstractButton):
|
|
71
77
|
painter = QPainter(self)
|
72
78
|
painter.setRenderHint(QtPaintAnitAlias, True)
|
73
79
|
painter.setPen(self.palette().dark().color())
|
74
|
-
painter.setBrush(self.
|
80
|
+
painter.setBrush(self._color)
|
75
81
|
painter.setOpacity(1.0)
|
76
82
|
painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
|
77
83
|
return
|
novelwriter/gui/doceditor.py
CHANGED
@@ -55,6 +55,7 @@ from novelwriter.common import minmax, transferCase
|
|
55
55
|
from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
|
56
56
|
from novelwriter.core.document import NWDocument
|
57
57
|
from novelwriter.enum import nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
|
58
|
+
from novelwriter.extensions.configlayout import NColourLabel
|
58
59
|
from novelwriter.extensions.eventfilters import WheelEventFilter
|
59
60
|
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
|
60
61
|
from novelwriter.gui.dochighlight import BLOCK_META, BLOCK_TITLE
|
@@ -64,7 +65,7 @@ from novelwriter.text.counting import standardCounter
|
|
64
65
|
from novelwriter.tools.lipsum import GuiLipsum
|
65
66
|
from novelwriter.types import (
|
66
67
|
QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop,
|
67
|
-
QtAlignRight, QtKeepAnchor, QtModCtrl,
|
68
|
+
QtAlignRight, QtKeepAnchor, QtModCtrl, QtModNone, QtModShift, QtMouseLeft,
|
68
69
|
QtMoveAnchor, QtMoveLeft, QtMoveRight
|
69
70
|
)
|
70
71
|
|
@@ -210,6 +211,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
210
211
|
# Function Mapping
|
211
212
|
self.closeSearch = self.docSearch.closeSearch
|
212
213
|
self.searchVisible = self.docSearch.isVisible
|
214
|
+
self.changeFocusState = self.docHeader.changeFocusState
|
213
215
|
|
214
216
|
# Finalise
|
215
217
|
self.updateSyntaxColours()
|
@@ -436,11 +438,6 @@ class GuiDocEditor(QPlainTextEdit):
|
|
436
438
|
|
437
439
|
return True
|
438
440
|
|
439
|
-
def updateTagHighLighting(self) -> None:
|
440
|
-
"""Rerun the syntax highlighter on all meta data lines."""
|
441
|
-
self._qDocument.syntaxHighlighter.rehighlightByType(BLOCK_META)
|
442
|
-
return
|
443
|
-
|
444
441
|
def replaceText(self, text: str) -> None:
|
445
442
|
"""Replace the text of the current document with the provided
|
446
443
|
text. This also clears undo history.
|
@@ -956,7 +953,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
956
953
|
super().keyPressEvent(event)
|
957
954
|
nPos = self.cursorRect().topLeft().y()
|
958
955
|
kMod = event.modifiers()
|
959
|
-
okMod = kMod in (
|
956
|
+
okMod = kMod in (QtModNone, QtModShift)
|
960
957
|
okKey = event.key() not in self.MOVE_KEYS
|
961
958
|
if nPos != cPos and okMod and okKey:
|
962
959
|
mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
|
@@ -1034,6 +1031,13 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1034
1031
|
self.beginSearch()
|
1035
1032
|
return
|
1036
1033
|
|
1034
|
+
@pyqtSlot(list, list)
|
1035
|
+
def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None:
|
1036
|
+
"""Tags have changed, so just in case we rehighlight them."""
|
1037
|
+
if updated or deleted:
|
1038
|
+
self._qDocument.syntaxHighlighter.rehighlightByType(BLOCK_META)
|
1039
|
+
return
|
1040
|
+
|
1037
1041
|
##
|
1038
1042
|
# Private Slots
|
1039
1043
|
##
|
@@ -1922,8 +1926,6 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1922
1926
|
).format(tag)):
|
1923
1927
|
itemClass = nwKeyWords.KEY_CLASS.get(tBits[0], nwItemClass.NO_CLASS)
|
1924
1928
|
self.requestNewNoteCreation.emit(tag, itemClass)
|
1925
|
-
QApplication.processEvents()
|
1926
|
-
self._qDocument.syntaxHighlighter.rehighlightBlock(block)
|
1927
1929
|
|
1928
1930
|
return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
|
1929
1931
|
|
@@ -2030,6 +2032,9 @@ class GuiDocEditor(QPlainTextEdit):
|
|
2030
2032
|
cursor.movePosition(QtMoveLeft, QtKeepAnchor, nDelete)
|
2031
2033
|
cursor.insertText(tInsert)
|
2032
2034
|
|
2035
|
+
# Re-highlight, since the auto-replace sometimes interferes with it
|
2036
|
+
self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block())
|
2037
|
+
|
2033
2038
|
return
|
2034
2039
|
|
2035
2040
|
@staticmethod
|
@@ -2782,8 +2787,7 @@ class GuiDocEditHeader(QWidget):
|
|
2782
2787
|
self.setAutoFillBackground(True)
|
2783
2788
|
|
2784
2789
|
# Title Label
|
2785
|
-
self.itemTitle =
|
2786
|
-
self.itemTitle.setIndent(0)
|
2790
|
+
self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
|
2787
2791
|
self.itemTitle.setMargin(0)
|
2788
2792
|
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
2789
2793
|
self.itemTitle.setAutoFillBackground(True)
|
@@ -2921,6 +2925,11 @@ class GuiDocEditHeader(QWidget):
|
|
2921
2925
|
|
2922
2926
|
return
|
2923
2927
|
|
2928
|
+
def changeFocusState(self, state: bool) -> None:
|
2929
|
+
"""Toggle focus state."""
|
2930
|
+
self.itemTitle.setColorState(state)
|
2931
|
+
return
|
2932
|
+
|
2924
2933
|
def setHandle(self, tHandle: str) -> None:
|
2925
2934
|
"""Set the document title from the handle, or alternatively, set
|
2926
2935
|
the whole document path within the project.
|
novelwriter/gui/dochighlight.py
CHANGED
@@ -39,6 +39,7 @@ from novelwriter.common import checkInt
|
|
39
39
|
from novelwriter.constants import nwHeaders, nwRegEx, nwUnicode
|
40
40
|
from novelwriter.core.index import processComment
|
41
41
|
from novelwriter.enum import nwComment
|
42
|
+
from novelwriter.text.patterns import REGEX_PATTERNS
|
42
43
|
from novelwriter.types import QRegExUnicode
|
43
44
|
|
44
45
|
logger = logging.getLogger(__name__)
|
@@ -59,8 +60,8 @@ BLOCK_TITLE = 4
|
|
59
60
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
60
61
|
|
61
62
|
__slots__ = (
|
62
|
-
"_tHandle", "
|
63
|
-
"_txtRules", "_cmnRules",
|
63
|
+
"_tHandle", "_isNovel", "_isInactive", "_spellCheck", "_spellErr",
|
64
|
+
"_hStyles", "_minRules", "_txtRules", "_cmnRules",
|
64
65
|
)
|
65
66
|
|
66
67
|
def __init__(self, document: QTextDocument) -> None:
|
@@ -69,11 +70,13 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
69
70
|
logger.debug("Create: GuiDocHighlighter")
|
70
71
|
|
71
72
|
self._tHandle = None
|
73
|
+
self._isNovel = False
|
72
74
|
self._isInactive = False
|
73
75
|
self._spellCheck = False
|
74
76
|
self._spellErr = QTextCharFormat()
|
75
77
|
|
76
78
|
self._hStyles: dict[str, QTextCharFormat] = {}
|
79
|
+
self._minRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
|
77
80
|
self._txtRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
|
78
81
|
self._cmnRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
|
79
82
|
|
@@ -137,6 +140,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
137
140
|
hlRule = {
|
138
141
|
0: self._hStyles["mspaces"],
|
139
142
|
}
|
143
|
+
self._minRules.append((rxRule, hlRule))
|
140
144
|
self._txtRules.append((rxRule, hlRule))
|
141
145
|
self._cmnRules.append((rxRule, hlRule))
|
142
146
|
|
@@ -146,106 +150,89 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
146
150
|
hlRule = {
|
147
151
|
0: self._hStyles["nobreak"],
|
148
152
|
}
|
153
|
+
self._minRules.append((rxRule, hlRule))
|
149
154
|
self._txtRules.append((rxRule, hlRule))
|
150
155
|
self._cmnRules.append((rxRule, hlRule))
|
151
156
|
|
152
157
|
# Dialogue
|
153
158
|
if CONFIG.dialogStyle > 0:
|
154
|
-
|
155
|
-
symC = ""
|
156
|
-
if CONFIG.dialogStyle in (1, 3):
|
157
|
-
symO += CONFIG.fmtSQuoteOpen
|
158
|
-
symC += CONFIG.fmtSQuoteClose
|
159
|
-
if CONFIG.dialogStyle in (2, 3):
|
160
|
-
symO += CONFIG.fmtDQuoteOpen
|
161
|
-
symC += CONFIG.fmtDQuoteClose
|
162
|
-
|
163
|
-
rxEnd = "|$" if CONFIG.allowOpenDial else ""
|
164
|
-
rxRule = QRegularExpression(f"\\B[{symO}].*?[{symC}]\\B{rxEnd}")
|
165
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
159
|
+
rxRule = REGEX_PATTERNS.dialogStyle
|
166
160
|
hlRule = {
|
167
161
|
0: self._hStyles["dialog"],
|
168
162
|
}
|
169
163
|
self._txtRules.append((rxRule, hlRule))
|
170
164
|
|
171
165
|
if CONFIG.dialogLine:
|
172
|
-
|
173
|
-
rxRule = QRegularExpression(f"^{sym}.*?$")
|
174
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
166
|
+
rxRule = REGEX_PATTERNS.dialogLine
|
175
167
|
hlRule = {
|
176
168
|
0: self._hStyles["dialog"],
|
177
169
|
}
|
178
170
|
self._txtRules.append((rxRule, hlRule))
|
179
171
|
|
180
172
|
if CONFIG.narratorBreak:
|
181
|
-
|
182
|
-
rxRule = QRegularExpression(f"({sym}\\b)(.*?)(\\b{sym})")
|
183
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
173
|
+
rxRule = REGEX_PATTERNS.narratorBreak
|
184
174
|
hlRule = {
|
185
175
|
0: self._hStyles["text"],
|
186
176
|
}
|
187
177
|
self._txtRules.append((rxRule, hlRule))
|
188
178
|
|
189
179
|
if CONFIG.altDialogOpen and CONFIG.altDialogClose:
|
190
|
-
|
191
|
-
symC = QRegularExpression.escape(CONFIG.altDialogClose)
|
192
|
-
rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B")
|
193
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
180
|
+
rxRule = REGEX_PATTERNS.altDialogStyle
|
194
181
|
hlRule = {
|
195
182
|
0: self._hStyles["altdialog"],
|
196
183
|
}
|
197
184
|
self._txtRules.append((rxRule, hlRule))
|
198
185
|
|
199
186
|
# Markdown Italic
|
200
|
-
rxRule =
|
201
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
187
|
+
rxRule = REGEX_PATTERNS.markdownItalic
|
202
188
|
hlRule = {
|
203
189
|
1: self._hStyles["markup"],
|
204
190
|
2: self._hStyles["italic"],
|
205
191
|
3: self._hStyles["markup"],
|
206
192
|
}
|
193
|
+
self._minRules.append((rxRule, hlRule))
|
207
194
|
self._txtRules.append((rxRule, hlRule))
|
208
195
|
self._cmnRules.append((rxRule, hlRule))
|
209
196
|
|
210
197
|
# Markdown Bold
|
211
|
-
rxRule =
|
212
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
198
|
+
rxRule = REGEX_PATTERNS.markdownBold
|
213
199
|
hlRule = {
|
214
200
|
1: self._hStyles["markup"],
|
215
201
|
2: self._hStyles["bold"],
|
216
202
|
3: self._hStyles["markup"],
|
217
203
|
}
|
204
|
+
self._minRules.append((rxRule, hlRule))
|
218
205
|
self._txtRules.append((rxRule, hlRule))
|
219
206
|
self._cmnRules.append((rxRule, hlRule))
|
220
207
|
|
221
208
|
# Markdown Strikethrough
|
222
|
-
rxRule =
|
223
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
209
|
+
rxRule = REGEX_PATTERNS.markdownStrike
|
224
210
|
hlRule = {
|
225
211
|
1: self._hStyles["markup"],
|
226
212
|
2: self._hStyles["strike"],
|
227
213
|
3: self._hStyles["markup"],
|
228
214
|
}
|
215
|
+
self._minRules.append((rxRule, hlRule))
|
229
216
|
self._txtRules.append((rxRule, hlRule))
|
230
217
|
self._cmnRules.append((rxRule, hlRule))
|
231
218
|
|
232
219
|
# Shortcodes
|
233
|
-
rxRule =
|
234
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
220
|
+
rxRule = REGEX_PATTERNS.shortcodePlain
|
235
221
|
hlRule = {
|
236
222
|
1: self._hStyles["code"],
|
237
223
|
}
|
224
|
+
self._minRules.append((rxRule, hlRule))
|
238
225
|
self._txtRules.append((rxRule, hlRule))
|
239
226
|
self._cmnRules.append((rxRule, hlRule))
|
240
227
|
|
241
228
|
# Shortcodes w/Value
|
242
|
-
rxRule =
|
243
|
-
rxRule.setPatternOptions(QRegExUnicode)
|
229
|
+
rxRule = REGEX_PATTERNS.shortcodeValue
|
244
230
|
hlRule = {
|
245
231
|
1: self._hStyles["code"],
|
246
232
|
2: self._hStyles["value"],
|
247
233
|
3: self._hStyles["code"],
|
248
234
|
}
|
235
|
+
self._minRules.append((rxRule, hlRule))
|
249
236
|
self._txtRules.append((rxRule, hlRule))
|
250
237
|
self._cmnRules.append((rxRule, hlRule))
|
251
238
|
|
@@ -255,6 +242,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
255
242
|
hlRule = {
|
256
243
|
1: self._hStyles["markup"],
|
257
244
|
}
|
245
|
+
self._minRules.append((rxRule, hlRule))
|
258
246
|
self._txtRules.append((rxRule, hlRule))
|
259
247
|
|
260
248
|
# Auto-Replace Tags
|
@@ -263,6 +251,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
263
251
|
hlRule = {
|
264
252
|
0: self._hStyles["replace"],
|
265
253
|
}
|
254
|
+
self._minRules.append((rxRule, hlRule))
|
266
255
|
self._txtRules.append((rxRule, hlRule))
|
267
256
|
self._cmnRules.append((rxRule, hlRule))
|
268
257
|
|
@@ -280,9 +269,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
280
269
|
def setHandle(self, tHandle: str) -> None:
|
281
270
|
"""Set the handle of the currently highlighted document."""
|
282
271
|
self._tHandle = tHandle
|
283
|
-
self.
|
284
|
-
|
285
|
-
|
272
|
+
self._isNovel = False
|
273
|
+
self._isInactive = False
|
274
|
+
if item := SHARED.project.tree[tHandle]:
|
275
|
+
self._isNovel = item.isDocumentLayout()
|
276
|
+
self._isInactive = item.isInactiveClass()
|
286
277
|
logger.debug("Syntax highlighter enabled for item '%s'", tHandle)
|
287
278
|
return
|
288
279
|
|
@@ -397,7 +388,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
397
388
|
|
398
389
|
elif text.startswith("["): # Special Command
|
399
390
|
self.setCurrentBlockState(BLOCK_TEXT)
|
400
|
-
hRules = self._txtRules
|
391
|
+
hRules = self._txtRules if self._isNovel else self._minRules
|
401
392
|
|
402
393
|
sText = text.rstrip().lower()
|
403
394
|
if sText in ("[newpage]", "[new page]", "[vspace]"):
|
@@ -414,7 +405,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
414
405
|
|
415
406
|
else: # Text Paragraph
|
416
407
|
self.setCurrentBlockState(BLOCK_TEXT)
|
417
|
-
hRules = self._txtRules
|
408
|
+
hRules = self._txtRules if self._isNovel else self._minRules
|
418
409
|
|
419
410
|
if hRules:
|
420
411
|
for rX, hRule in hRules:
|