novelWriter 2.4rc1__py3-none-any.whl → 2.4.2__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.4rc1.dist-info → novelWriter-2.4.2.dist-info}/METADATA +1 -1
- {novelWriter-2.4rc1.dist-info → novelWriter-2.4.2.dist-info}/RECORD +48 -48
- novelwriter/__init__.py +13 -6
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/common.py +5 -2
- novelwriter/config.py +4 -0
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/docbuild.py +2 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/spellcheck.py +3 -3
- novelwriter/core/tokenizer.py +3 -3
- novelwriter/core/toodt.py +1 -1
- novelwriter/dialogs/preferences.py +1 -1
- novelwriter/dialogs/projectsettings.py +2 -2
- novelwriter/dialogs/wordlist.py +2 -2
- novelwriter/error.py +1 -1
- novelwriter/gui/doceditor.py +30 -27
- novelwriter/gui/dochighlight.py +22 -5
- novelwriter/gui/docviewer.py +3 -3
- novelwriter/gui/mainmenu.py +2 -2
- novelwriter/gui/noveltree.py +1 -1
- novelwriter/gui/outline.py +9 -9
- novelwriter/gui/projtree.py +1 -1
- novelwriter/gui/search.py +2 -2
- novelwriter/gui/theme.py +15 -5
- novelwriter/guimain.py +2 -2
- novelwriter/shared.py +16 -4
- novelwriter/text/counting.py +1 -0
- novelwriter/tools/dictionaries.py +2 -2
- novelwriter/tools/manusbuild.py +12 -11
- novelwriter/tools/manuscript.py +49 -44
- novelwriter/tools/manussettings.py +27 -29
- novelwriter/tools/welcome.py +4 -5
- {novelWriter-2.4rc1.dist-info → novelWriter-2.4.2.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4rc1.dist-info → novelWriter-2.4.2.dist-info}/WHEEL +0 -0
- {novelWriter-2.4rc1.dist-info → novelWriter-2.4.2.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4rc1.dist-info → novelWriter-2.4.2.dist-info}/top_level.txt +0 -0
novelwriter/gui/doceditor.py
CHANGED
@@ -39,8 +39,8 @@ from time import time
|
|
39
39
|
from typing import TYPE_CHECKING
|
40
40
|
|
41
41
|
from PyQt5.QtCore import (
|
42
|
-
|
43
|
-
|
42
|
+
QObject, QPoint, QRegExp, QRegularExpression, QRunnable, Qt, QTimer,
|
43
|
+
pyqtSignal, pyqtSlot
|
44
44
|
)
|
45
45
|
from PyQt5.QtGui import (
|
46
46
|
QColor, QCursor, QFont, QKeyEvent, QKeySequence, QMouseEvent, QPalette,
|
@@ -65,7 +65,7 @@ from novelwriter.text.counting import standardCounter
|
|
65
65
|
from novelwriter.tools.lipsum import GuiLipsum
|
66
66
|
from novelwriter.types import (
|
67
67
|
QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop,
|
68
|
-
QtAlignRight, QtKeepAnchor, QtModCtrl,
|
68
|
+
QtAlignRight, QtKeepAnchor, QtModCtrl, QtModeNone, QtModShift, QtMouseLeft,
|
69
69
|
QtMoveAnchor, QtMoveLeft, QtMoveRight
|
70
70
|
)
|
71
71
|
|
@@ -301,8 +301,6 @@ class GuiDocEditor(QPlainTextEdit):
|
|
301
301
|
self.docHeader.matchColours()
|
302
302
|
self.docFooter.matchColours()
|
303
303
|
|
304
|
-
self._qDocument.syntaxHighlighter.initHighlighter()
|
305
|
-
|
306
304
|
return
|
307
305
|
|
308
306
|
def initEditor(self) -> None:
|
@@ -334,7 +332,10 @@ class GuiDocEditor(QPlainTextEdit):
|
|
334
332
|
font = QFont()
|
335
333
|
font.setFamily(CONFIG.textFont)
|
336
334
|
font.setPointSize(CONFIG.textSize)
|
337
|
-
self.
|
335
|
+
self._qDocument.setDefaultFont(font)
|
336
|
+
|
337
|
+
# Update highlighter settings
|
338
|
+
self._qDocument.syntaxHighlighter.initHighlighter()
|
338
339
|
|
339
340
|
# Set default text margins
|
340
341
|
# Due to cursor visibility, a part of the margin must be
|
@@ -381,7 +382,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
381
382
|
|
382
383
|
return
|
383
384
|
|
384
|
-
def loadText(self, tHandle: str, tLine=None) -> bool:
|
385
|
+
def loadText(self, tHandle: str, tLine: int | None = None) -> bool:
|
385
386
|
"""Load text from a document into the editor. If we have an I/O
|
386
387
|
error, we must handle this and clear the editor so that we don't
|
387
388
|
risk overwriting the file if it exists. This can for instance
|
@@ -497,6 +498,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
497
498
|
return False
|
498
499
|
|
499
500
|
self.setDocumentChanged(False)
|
501
|
+
self.docTextChanged.emit(self._docHandle, self._lastEdit)
|
500
502
|
|
501
503
|
oldHeader = self._nwItem.mainHeading
|
502
504
|
oldCount = SHARED.project.index.getHandleHeaderCount(tHandle)
|
@@ -1080,7 +1082,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1080
1082
|
return
|
1081
1083
|
|
1082
1084
|
@pyqtSlot()
|
1083
|
-
def _cursorMoved(self):
|
1085
|
+
def _cursorMoved(self) -> None:
|
1084
1086
|
"""Triggered when the cursor moved in the editor."""
|
1085
1087
|
self.docFooter.updateLineCount(self.textCursor())
|
1086
1088
|
return
|
@@ -1470,7 +1472,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1470
1472
|
# Internal Functions : Text Manipulation
|
1471
1473
|
##
|
1472
1474
|
|
1473
|
-
def _toggleFormat(self, fLen: int, fChar: str) ->
|
1475
|
+
def _toggleFormat(self, fLen: int, fChar: str) -> None:
|
1474
1476
|
"""Toggle the formatting of a specific type for a piece of text.
|
1475
1477
|
If more than one block is selected, the formatting is applied to
|
1476
1478
|
the first block.
|
@@ -1488,12 +1490,12 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1488
1490
|
|
1489
1491
|
posS = cursor.selectionStart()
|
1490
1492
|
posE = cursor.selectionEnd()
|
1491
|
-
if self._qDocument.characterAt(posO - 1) == fChar:
|
1493
|
+
if posS == posE and self._qDocument.characterAt(posO - 1) == fChar:
|
1492
1494
|
logger.warning("Format repetition, cancelling action")
|
1493
1495
|
cursor.clearSelection()
|
1494
1496
|
cursor.setPosition(posO)
|
1495
1497
|
self.setTextCursor(cursor)
|
1496
|
-
return
|
1498
|
+
return
|
1497
1499
|
|
1498
1500
|
blockS = self._qDocument.findBlock(posS)
|
1499
1501
|
blockE = self._qDocument.findBlock(posE)
|
@@ -1519,7 +1521,6 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1519
1521
|
break
|
1520
1522
|
|
1521
1523
|
if fLen == min(numA, numB):
|
1522
|
-
cursor.clearSelection()
|
1523
1524
|
cursor.beginEditBlock()
|
1524
1525
|
cursor.setPosition(posS)
|
1525
1526
|
for i in range(fLen):
|
@@ -1528,17 +1529,19 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1528
1529
|
for i in range(fLen):
|
1529
1530
|
cursor.deletePreviousChar()
|
1530
1531
|
cursor.endEditBlock()
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1532
|
+
|
1533
|
+
if select != _SelectAction.KEEP_SELECTION:
|
1534
|
+
cursor.clearSelection()
|
1535
|
+
cursor.setPosition(posO - fLen)
|
1536
|
+
self.setTextCursor(cursor)
|
1534
1537
|
|
1535
1538
|
else:
|
1536
1539
|
self._wrapSelection(fChar*fLen, pos=posO, select=select)
|
1537
1540
|
|
1538
|
-
return
|
1541
|
+
return
|
1539
1542
|
|
1540
1543
|
def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
|
1541
|
-
select: _SelectAction = _SelectAction.NO_DECISION) ->
|
1544
|
+
select: _SelectAction = _SelectAction.NO_DECISION) -> None:
|
1542
1545
|
"""Wrap the selected text in whatever is in tBefore and tAfter.
|
1543
1546
|
If there is no selection, the autoSelect setting decides the
|
1544
1547
|
action. AutoSelect will select the word under the cursor before
|
@@ -1578,21 +1581,21 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1578
1581
|
if select == _SelectAction.MOVE_AFTER:
|
1579
1582
|
cursor.setPosition(posE + len(before + after))
|
1580
1583
|
elif select == _SelectAction.KEEP_SELECTION:
|
1581
|
-
cursor.setPosition(
|
1582
|
-
cursor.setPosition(
|
1584
|
+
cursor.setPosition(posS + len(before), QtMoveAnchor)
|
1585
|
+
cursor.setPosition(posE + len(before), QtKeepAnchor)
|
1583
1586
|
elif select == _SelectAction.KEEP_POSITION:
|
1584
1587
|
cursor.setPosition(posO + len(before))
|
1585
1588
|
|
1586
1589
|
self.setTextCursor(cursor)
|
1587
1590
|
|
1588
|
-
return
|
1591
|
+
return
|
1589
1592
|
|
1590
|
-
def _replaceQuotes(self, sQuote: str, oQuote: str, cQuote: str) ->
|
1593
|
+
def _replaceQuotes(self, sQuote: str, oQuote: str, cQuote: str) -> None:
|
1591
1594
|
"""Replace all straight quotes in the selected text."""
|
1592
1595
|
cursor = self.textCursor()
|
1593
1596
|
if not cursor.hasSelection():
|
1594
1597
|
SHARED.error(self.tr("Please select some text before calling replace quotes."))
|
1595
|
-
return
|
1598
|
+
return
|
1596
1599
|
|
1597
1600
|
posS = cursor.selectionStart()
|
1598
1601
|
posE = cursor.selectionEnd()
|
@@ -1632,7 +1635,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|
1632
1635
|
|
1633
1636
|
self._allowAutoReplace(True)
|
1634
1637
|
|
1635
|
-
return
|
1638
|
+
return
|
1636
1639
|
|
1637
1640
|
def _processBlockFormat(
|
1638
1641
|
self, action: nwDocAction, text: str, toggle: bool = True
|
@@ -2185,7 +2188,7 @@ class MetaCompleter(QMenu):
|
|
2185
2188
|
# Internal Functions
|
2186
2189
|
##
|
2187
2190
|
|
2188
|
-
def _emitComplete(self, pos: int, length: int, value: str):
|
2191
|
+
def _emitComplete(self, pos: int, length: int, value: str) -> None:
|
2189
2192
|
"""Emit the signal to indicate a selection has been made."""
|
2190
2193
|
self.complete.emit(pos, length, value)
|
2191
2194
|
return
|
@@ -2408,12 +2411,12 @@ class GuiDocEditSearch(QFrame):
|
|
2408
2411
|
|
2409
2412
|
self.searchBox = QLineEdit(self)
|
2410
2413
|
self.searchBox.setFont(self.boxFont)
|
2411
|
-
self.searchBox.setPlaceholderText(self.tr("Search"))
|
2414
|
+
self.searchBox.setPlaceholderText(self.tr("Search for"))
|
2412
2415
|
self.searchBox.returnPressed.connect(self._doSearch)
|
2413
2416
|
|
2414
2417
|
self.replaceBox = QLineEdit(self)
|
2415
2418
|
self.replaceBox.setFont(self.boxFont)
|
2416
|
-
self.replaceBox.setPlaceholderText(self.tr("Replace"))
|
2419
|
+
self.replaceBox.setPlaceholderText(self.tr("Replace with"))
|
2417
2420
|
self.replaceBox.returnPressed.connect(self._doReplace)
|
2418
2421
|
|
2419
2422
|
self.searchOpt = QToolBar(self)
|
@@ -2966,7 +2969,7 @@ class GuiDocEditHeader(QWidget):
|
|
2966
2969
|
# Events
|
2967
2970
|
##
|
2968
2971
|
|
2969
|
-
def mousePressEvent(self, event: QMouseEvent):
|
2972
|
+
def mousePressEvent(self, event: QMouseEvent) -> None:
|
2970
2973
|
"""Capture a click on the title and ensure that the item is
|
2971
2974
|
selected in the project tree.
|
2972
2975
|
"""
|
novelwriter/gui/dochighlight.py
CHANGED
@@ -44,6 +44,10 @@ logger = logging.getLogger(__name__)
|
|
44
44
|
|
45
45
|
SPELLRX = QRegularExpression(r"\b[^\s\-\+\/–—\[\]:]+\b")
|
46
46
|
SPELLRX.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
47
|
+
SPELLSC = QRegularExpression(nwRegEx.FMT_SC)
|
48
|
+
SPELLSC.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
49
|
+
SPELLSV = QRegularExpression(nwRegEx.FMT_SV)
|
50
|
+
SPELLSV.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
47
51
|
|
48
52
|
BLOCK_NONE = 0
|
49
53
|
BLOCK_TEXT = 1
|
@@ -53,7 +57,8 @@ BLOCK_TITLE = 4
|
|
53
57
|
|
54
58
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
55
59
|
|
56
|
-
__slots__ = ("
|
60
|
+
__slots__ = ("_tHandle", "_isInactive", "_spellCheck", "_spellErr",
|
61
|
+
"_hRules", "_hStyles", "_rxRules")
|
57
62
|
|
58
63
|
def __init__(self, document: QTextDocument) -> None:
|
59
64
|
super().__init__(document)
|
@@ -67,6 +72,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
67
72
|
|
68
73
|
self._hRules: list[tuple[str, dict]] = []
|
69
74
|
self._hStyles: dict[str, QTextCharFormat] = {}
|
75
|
+
self._rxRules: list[tuple[QRegularExpression, dict[str, QTextCharFormat]]] = []
|
70
76
|
|
71
77
|
self.initHighlighter()
|
72
78
|
|
@@ -218,11 +224,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
218
224
|
))
|
219
225
|
|
220
226
|
# Build a QRegExp for each highlight pattern
|
221
|
-
self.
|
227
|
+
self._rxRules = []
|
222
228
|
for regEx, regRules in self._hRules:
|
223
229
|
hReg = QRegularExpression(regEx)
|
224
230
|
hReg.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
225
|
-
self.
|
231
|
+
self._rxRules.append((hReg, regRules))
|
226
232
|
|
227
233
|
return
|
228
234
|
|
@@ -327,7 +333,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
327
333
|
self.setFormat(0, 3, self._hStyles["head2h"])
|
328
334
|
self.setFormat(3, len(text), self._hStyles["header2"])
|
329
335
|
|
330
|
-
elif text.startswith("###! "): #
|
336
|
+
elif text.startswith("###! "): # Alternative Scene
|
331
337
|
self.setFormat(0, 4, self._hStyles["head3h"])
|
332
338
|
self.setFormat(4, len(text), self._hStyles["header3"])
|
333
339
|
|
@@ -358,7 +364,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
358
364
|
|
359
365
|
# Regular Text
|
360
366
|
self.setCurrentBlockState(BLOCK_TEXT)
|
361
|
-
for rX, xFmt in self.
|
367
|
+
for rX, xFmt in self._rxRules:
|
362
368
|
rxItt = rX.globalMatch(text, 0)
|
363
369
|
while rxItt.hasNext():
|
364
370
|
rxMatch = rxItt.next()
|
@@ -439,6 +445,17 @@ class TextBlockData(QTextBlockUserData):
|
|
439
445
|
"""Run the spell checker and cache the result, and return the
|
440
446
|
list of spell check errors.
|
441
447
|
"""
|
448
|
+
if "[" in text:
|
449
|
+
# Strip shortcodes
|
450
|
+
for rX in [SPELLSC, SPELLSV]:
|
451
|
+
rxItt = rX.globalMatch(text, 0)
|
452
|
+
while rxItt.hasNext():
|
453
|
+
rxMatch = rxItt.next()
|
454
|
+
xPos = rxMatch.capturedStart(0)
|
455
|
+
xLen = rxMatch.capturedLength(0)
|
456
|
+
xEnd = rxMatch.capturedEnd(0)
|
457
|
+
text = text[:xPos] + " "*xLen + text[xEnd:]
|
458
|
+
|
442
459
|
self._spellErrors = []
|
443
460
|
rxSpell = SPELLRX.globalMatch(text.replace("_", " "), 0)
|
444
461
|
while rxSpell.hasNext():
|
novelwriter/gui/docviewer.py
CHANGED
@@ -30,7 +30,7 @@ import logging
|
|
30
30
|
|
31
31
|
from enum import Enum
|
32
32
|
|
33
|
-
from PyQt5.QtCore import
|
33
|
+
from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
|
34
34
|
from PyQt5.QtGui import (
|
35
35
|
QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
|
36
36
|
QTextOption
|
@@ -44,7 +44,7 @@ from novelwriter import CONFIG, SHARED
|
|
44
44
|
from novelwriter.common import cssCol
|
45
45
|
from novelwriter.constants import nwHeaders, nwUnicode
|
46
46
|
from novelwriter.core.tohtml import ToHtml
|
47
|
-
from novelwriter.enum import
|
47
|
+
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
|
48
48
|
from novelwriter.error import logException
|
49
49
|
from novelwriter.extensions.eventfilters import WheelEventFilter
|
50
50
|
from novelwriter.extensions.modified import NIconToolButton
|
@@ -146,7 +146,7 @@ class GuiDocViewer(QTextBrowser):
|
|
146
146
|
font = QFont()
|
147
147
|
font.setFamily(CONFIG.textFont)
|
148
148
|
font.setPointSize(CONFIG.textSize)
|
149
|
-
self.
|
149
|
+
self.document().setDefaultFont(font)
|
150
150
|
|
151
151
|
# Set the widget colours to match syntax theme
|
152
152
|
mainPalette = self.palette()
|
novelwriter/gui/mainmenu.py
CHANGED
@@ -736,8 +736,8 @@ class GuiMainMenu(QMenuBar):
|
|
736
736
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
|
737
737
|
)
|
738
738
|
|
739
|
-
# Format >
|
740
|
-
self.aFmtHardSc = self.fmtMenu.addAction(self.tr("
|
739
|
+
# Format > Alternative Scene
|
740
|
+
self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Alternative Scene"))
|
741
741
|
self.aFmtHardSc.triggered.connect(
|
742
742
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
|
743
743
|
)
|
novelwriter/gui/noveltree.py
CHANGED
@@ -344,7 +344,7 @@ class GuiNovelToolBar(QWidget):
|
|
344
344
|
# Internal Functions
|
345
345
|
##
|
346
346
|
|
347
|
-
def _addLastColAction(self, colType, actionLabel) -> None:
|
347
|
+
def _addLastColAction(self, colType: NovelTreeColumn, actionLabel: str) -> None:
|
348
348
|
"""Add a column selection entry to the last column menu."""
|
349
349
|
aLast = self.mLastCol.addAction(actionLabel)
|
350
350
|
aLast.setCheckable(True)
|
novelwriter/gui/outline.py
CHANGED
@@ -47,6 +47,7 @@ from novelwriter.enum import (
|
|
47
47
|
from novelwriter.error import logException
|
48
48
|
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
|
49
49
|
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
|
50
|
+
from novelwriter.extensions.configlayout import NColourLabel
|
50
51
|
from novelwriter.extensions.novelselector import NovelSelector
|
51
52
|
from novelwriter.types import (
|
52
53
|
QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration, QtUserRole
|
@@ -190,7 +191,7 @@ class GuiOutlineView(QWidget):
|
|
190
191
|
return
|
191
192
|
|
192
193
|
@pyqtSlot(str)
|
193
|
-
def _rootItemChanged(self, tHandle) -> None:
|
194
|
+
def _rootItemChanged(self, tHandle: str) -> None:
|
194
195
|
"""Handle root novel changed or needs to be refreshed."""
|
195
196
|
self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True)
|
196
197
|
return
|
@@ -209,19 +210,18 @@ class GuiOutlineToolBar(QToolBar):
|
|
209
210
|
|
210
211
|
logger.debug("Create: GuiOutlineToolBar")
|
211
212
|
|
212
|
-
iSz = SHARED.theme.baseIconSize
|
213
|
-
mPx = CONFIG.pxInt(12)
|
214
|
-
|
215
213
|
self.setMovable(False)
|
216
|
-
self.setIconSize(
|
214
|
+
self.setIconSize(1.4*SHARED.theme.baseIconSize)
|
217
215
|
self.setContentsMargins(0, 0, 0, 0)
|
218
216
|
|
219
217
|
stretch = QWidget(self)
|
220
218
|
stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
221
219
|
|
222
220
|
# Novel Selector
|
223
|
-
self.novelLabel =
|
224
|
-
|
221
|
+
self.novelLabel = NColourLabel(
|
222
|
+
self.tr("Outline of"), parent=self, scale=NColourLabel.HEADER_SCALE, bold=True
|
223
|
+
)
|
224
|
+
self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
|
225
225
|
|
226
226
|
self.novelValue = NovelSelector(self)
|
227
227
|
self.novelValue.setIncludeAll(True)
|
@@ -428,7 +428,7 @@ class GuiOutlineTree(QTreeWidget):
|
|
428
428
|
##
|
429
429
|
|
430
430
|
@property
|
431
|
-
def hiddenColumns(self):
|
431
|
+
def hiddenColumns(self) -> dict[nwOutline, bool]:
|
432
432
|
return self._colHidden
|
433
433
|
|
434
434
|
##
|
@@ -586,7 +586,7 @@ class GuiOutlineTree(QTreeWidget):
|
|
586
586
|
# Internal Functions
|
587
587
|
##
|
588
588
|
|
589
|
-
def _loadHeaderState(self):
|
589
|
+
def _loadHeaderState(self) -> None:
|
590
590
|
"""Load the state of the main tree header, that is, column order
|
591
591
|
and column width.
|
592
592
|
"""
|
novelwriter/gui/projtree.py
CHANGED
@@ -453,7 +453,7 @@ class GuiProjectToolBar(QWidget):
|
|
453
453
|
|
454
454
|
def _buildRootMenu(self) -> None:
|
455
455
|
"""Build the rood folder menu."""
|
456
|
-
def addClass(itemClass):
|
456
|
+
def addClass(itemClass: nwItemClass) -> None:
|
457
457
|
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
|
458
458
|
aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass]))
|
459
459
|
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
|
novelwriter/gui/search.py
CHANGED
@@ -98,7 +98,7 @@ class GuiProjectSearch(QWidget):
|
|
98
98
|
|
99
99
|
# Search Box
|
100
100
|
self.searchText = QLineEdit(self)
|
101
|
-
self.searchText.setPlaceholderText(self.tr("Search"))
|
101
|
+
self.searchText.setPlaceholderText(self.tr("Search for"))
|
102
102
|
self.searchText.setClearButtonEnabled(True)
|
103
103
|
|
104
104
|
self.searchAction = self.searchText.addAction(
|
@@ -161,7 +161,7 @@ class GuiProjectSearch(QWidget):
|
|
161
161
|
colBase = cssCol(qPalette.base().color())
|
162
162
|
colFocus = cssCol(qPalette.highlight().color())
|
163
163
|
|
164
|
-
self.headerWidget.setStyleSheet(f"background: {colBase};")
|
164
|
+
self.headerWidget.setStyleSheet(f"QWidget {{background: {colBase};}}")
|
165
165
|
self.headerWidget.setAutoFillBackground(True)
|
166
166
|
|
167
167
|
self.setStyleSheet(
|
novelwriter/gui/theme.py
CHANGED
@@ -74,9 +74,9 @@ class GuiTheme:
|
|
74
74
|
self.isLightTheme = True
|
75
75
|
|
76
76
|
# GUI
|
77
|
-
self.statNone = QColor(
|
78
|
-
self.statUnsaved = QColor(
|
79
|
-
self.statSaved = QColor(
|
77
|
+
self.statNone = QColor(0, 0, 0)
|
78
|
+
self.statUnsaved = QColor(0, 0, 0)
|
79
|
+
self.statSaved = QColor(0, 0, 0)
|
80
80
|
self.helpText = QColor(0, 0, 0)
|
81
81
|
|
82
82
|
# Loaded Syntax Settings
|
@@ -227,6 +227,10 @@ class GuiTheme:
|
|
227
227
|
logException()
|
228
228
|
return False
|
229
229
|
|
230
|
+
# Reset Palette
|
231
|
+
self._guiPalette = QApplication.style().standardPalette()
|
232
|
+
self._resetGuiColors()
|
233
|
+
|
230
234
|
# Main
|
231
235
|
sec = "Main"
|
232
236
|
if parser.has_section(sec):
|
@@ -256,8 +260,6 @@ class GuiTheme:
|
|
256
260
|
self._setPalette(parser, sec, "highlightedtext", QPalette.ColorRole.HighlightedText)
|
257
261
|
self._setPalette(parser, sec, "link", QPalette.ColorRole.Link)
|
258
262
|
self._setPalette(parser, sec, "linkvisited", QPalette.ColorRole.LinkVisited)
|
259
|
-
else:
|
260
|
-
self._guiPalette = QApplication.style().standardPalette()
|
261
263
|
|
262
264
|
# GUI
|
263
265
|
sec = "GUI"
|
@@ -393,6 +395,14 @@ class GuiTheme:
|
|
393
395
|
# Internal Functions
|
394
396
|
##
|
395
397
|
|
398
|
+
def _resetGuiColors(self) -> None:
|
399
|
+
"""Reset GUI colours to default values."""
|
400
|
+
self.statNone = QColor(120, 120, 120)
|
401
|
+
self.statUnsaved = QColor(200, 15, 39)
|
402
|
+
self.statSaved = QColor(2, 133, 37)
|
403
|
+
self.helpText = QColor(0, 0, 0)
|
404
|
+
return
|
405
|
+
|
396
406
|
def _setGuiFont(self) -> None:
|
397
407
|
"""Update the GUI's font style from settings."""
|
398
408
|
font = QFont()
|
novelwriter/guimain.py
CHANGED
@@ -944,7 +944,7 @@ class GuiMain(QMainWindow):
|
|
944
944
|
# Events
|
945
945
|
##
|
946
946
|
|
947
|
-
def closeEvent(self, event: QCloseEvent):
|
947
|
+
def closeEvent(self, event: QCloseEvent) -> None:
|
948
948
|
"""Capture the closing event of the GUI and call the close
|
949
949
|
function to handle all the close process steps.
|
950
950
|
"""
|
@@ -1201,7 +1201,7 @@ class GuiMain(QMainWindow):
|
|
1201
1201
|
return
|
1202
1202
|
|
1203
1203
|
@pyqtSlot()
|
1204
|
-
def _toggleViewerPanelVisibility(self):
|
1204
|
+
def _toggleViewerPanelVisibility(self) -> None:
|
1205
1205
|
"""Toggle the visibility of the document viewer panel."""
|
1206
1206
|
CONFIG.showViewerPanel = not CONFIG.showViewerPanel
|
1207
1207
|
self.docViewerPanel.setVisible(CONFIG.showViewerPanel)
|
novelwriter/shared.py
CHANGED
@@ -26,21 +26,21 @@ from __future__ import annotations
|
|
26
26
|
|
27
27
|
import logging
|
28
28
|
|
29
|
+
from pathlib import Path
|
29
30
|
from time import time
|
30
31
|
from typing import TYPE_CHECKING, TypeVar
|
31
|
-
from pathlib import Path
|
32
32
|
|
33
33
|
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
|
34
34
|
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
|
35
|
-
from novelwriter.common import formatFileFilter
|
36
35
|
|
36
|
+
from novelwriter.common import formatFileFilter
|
37
37
|
from novelwriter.constants import nwFiles
|
38
38
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
39
39
|
|
40
40
|
if TYPE_CHECKING: # pragma: no cover
|
41
|
-
from novelwriter.guimain import GuiMain
|
42
|
-
from novelwriter.gui.theme import GuiTheme
|
43
41
|
from novelwriter.core.project import NWProject
|
42
|
+
from novelwriter.gui.theme import GuiTheme
|
43
|
+
from novelwriter.guimain import GuiMain
|
44
44
|
|
45
45
|
logger = logging.getLogger(__name__)
|
46
46
|
|
@@ -198,6 +198,7 @@ class SharedData(QObject):
|
|
198
198
|
|
199
199
|
def closeProject(self) -> None:
|
200
200
|
"""Close the current project."""
|
201
|
+
self._closeDialogs()
|
201
202
|
self.project.closeProject(self._idleTime)
|
202
203
|
self._resetProject()
|
203
204
|
self._resetIdleTimer()
|
@@ -356,6 +357,17 @@ class SharedData(QObject):
|
|
356
357
|
self._idleTime = 0.0
|
357
358
|
return
|
358
359
|
|
360
|
+
def _closeDialogs(self) -> None:
|
361
|
+
"""Close non-modal dialogs."""
|
362
|
+
from novelwriter.tools.manuscript import GuiManuscript
|
363
|
+
from novelwriter.tools.writingstats import GuiWritingStats
|
364
|
+
|
365
|
+
for widget in self.mainGui.children():
|
366
|
+
if isinstance(widget, (GuiManuscript, GuiWritingStats)):
|
367
|
+
widget.close()
|
368
|
+
|
369
|
+
return
|
370
|
+
|
359
371
|
# END Class SharedData
|
360
372
|
|
361
373
|
|
novelwriter/text/counting.py
CHANGED
@@ -57,6 +57,7 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
|
|
57
57
|
continue
|
58
58
|
if line[0] == ">":
|
59
59
|
line = line.lstrip(">").lstrip(" ")
|
60
|
+
if line: # Above block can return empty line (Issue #1816)
|
60
61
|
if line[-1] == "<":
|
61
62
|
line = line.rstrip("<").rstrip(" ")
|
62
63
|
if "[" in line:
|
@@ -179,7 +179,7 @@ class GuiDictionaries(QDialog):
|
|
179
179
|
##
|
180
180
|
|
181
181
|
@pyqtSlot()
|
182
|
-
def _doBrowseHunspell(self):
|
182
|
+
def _doBrowseHunspell(self) -> None:
|
183
183
|
"""Browse for a Free/Libre Office dictionary."""
|
184
184
|
ffilter = formatFileFilter([
|
185
185
|
(self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*"
|
@@ -193,7 +193,7 @@ class GuiDictionaries(QDialog):
|
|
193
193
|
return
|
194
194
|
|
195
195
|
@pyqtSlot()
|
196
|
-
def _doImportHunspell(self):
|
196
|
+
def _doImportHunspell(self) -> None:
|
197
197
|
"""Import a hunspell dictionary from .sox or .oxt file."""
|
198
198
|
procErr = self.tr("Could not process dictionary file")
|
199
199
|
if self._installPath:
|
novelwriter/tools/manusbuild.py
CHANGED
@@ -44,9 +44,7 @@ from novelwriter.core.item import NWItem
|
|
44
44
|
from novelwriter.enum import nwBuildFmt
|
45
45
|
from novelwriter.extensions.modified import NIconToolButton
|
46
46
|
from novelwriter.extensions.simpleprogress import NProgressSimple
|
47
|
-
from novelwriter.types import
|
48
|
-
QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
|
49
|
-
)
|
47
|
+
from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
|
50
48
|
|
51
49
|
logger = logging.getLogger(__name__)
|
52
50
|
|
@@ -60,7 +58,7 @@ class GuiManuscriptBuild(QDialog):
|
|
60
58
|
|
61
59
|
D_KEY = QtUserRole
|
62
60
|
|
63
|
-
def __init__(self, parent: QWidget, build: BuildSettings):
|
61
|
+
def __init__(self, parent: QWidget, build: BuildSettings) -> None:
|
64
62
|
super().__init__(parent=parent)
|
65
63
|
|
66
64
|
logger.debug("Create: GuiManuscriptBuild")
|
@@ -260,7 +258,7 @@ class GuiManuscriptBuild(QDialog):
|
|
260
258
|
##
|
261
259
|
|
262
260
|
@pyqtSlot("QAbstractButton*")
|
263
|
-
def _dialogButtonClicked(self, button: QAbstractButton):
|
261
|
+
def _dialogButtonClicked(self, button: QAbstractButton) -> None:
|
264
262
|
"""Handle button clicks from the dialog button box."""
|
265
263
|
role = self.dlgButtons.buttonRole(button)
|
266
264
|
if role == QtRoleAction:
|
@@ -273,7 +271,7 @@ class GuiManuscriptBuild(QDialog):
|
|
273
271
|
return
|
274
272
|
|
275
273
|
@pyqtSlot()
|
276
|
-
def _doSelectPath(self):
|
274
|
+
def _doSelectPath(self) -> None:
|
277
275
|
"""Select a folder for output."""
|
278
276
|
bPath = Path(self.buildPath.text())
|
279
277
|
bPath = bPath if bPath.is_dir() else self._build.lastPath
|
@@ -285,7 +283,7 @@ class GuiManuscriptBuild(QDialog):
|
|
285
283
|
return
|
286
284
|
|
287
285
|
@pyqtSlot()
|
288
|
-
def _doResetBuildName(self):
|
286
|
+
def _doResetBuildName(self) -> None:
|
289
287
|
"""Generate a default build name."""
|
290
288
|
bName = f"{SHARED.project.data.name} - {self._build.name}"
|
291
289
|
self.buildName.setText(bName)
|
@@ -293,7 +291,7 @@ class GuiManuscriptBuild(QDialog):
|
|
293
291
|
return
|
294
292
|
|
295
293
|
@pyqtSlot()
|
296
|
-
def _resetProgress(self):
|
294
|
+
def _resetProgress(self) -> None:
|
297
295
|
"""Set the progress bar back to 0."""
|
298
296
|
self.buildProgress.setValue(0)
|
299
297
|
return
|
@@ -328,6 +326,9 @@ class GuiManuscriptBuild(QDialog):
|
|
328
326
|
):
|
329
327
|
return False
|
330
328
|
|
329
|
+
# Make sure editor content is saved before we start
|
330
|
+
SHARED.mainGui.saveDocument()
|
331
|
+
|
331
332
|
docBuild = NWBuildDocument(SHARED.project, self._build)
|
332
333
|
docBuild.queueAll()
|
333
334
|
|
@@ -350,7 +351,7 @@ class GuiManuscriptBuild(QDialog):
|
|
350
351
|
return items[0].data(self.D_KEY)
|
351
352
|
return None
|
352
353
|
|
353
|
-
def _saveSettings(self):
|
354
|
+
def _saveSettings(self) -> None:
|
354
355
|
"""Save the user GUI settings."""
|
355
356
|
winWidth = CONFIG.rpxInt(self.width())
|
356
357
|
winHeight = CONFIG.rpxInt(self.height())
|
@@ -369,7 +370,7 @@ class GuiManuscriptBuild(QDialog):
|
|
369
370
|
|
370
371
|
return
|
371
372
|
|
372
|
-
def _populateContentList(self):
|
373
|
+
def _populateContentList(self) -> None:
|
373
374
|
"""Build the content list."""
|
374
375
|
rootMap = {}
|
375
376
|
filtered = self._build.buildItemFilter(SHARED.project)
|
@@ -398,7 +399,7 @@ class GuiManuscriptBuild(QDialog):
|
|
398
399
|
|
399
400
|
return
|
400
401
|
|
401
|
-
def _openOutputFolder(self):
|
402
|
+
def _openOutputFolder(self) -> None:
|
402
403
|
"""Open the build folder in the system's file explorer."""
|
403
404
|
openExternalPath(Path(self.buildPath.text()))
|
404
405
|
return
|