novelWriter 2.2.1__py3-none-any.whl → 2.3b1__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.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
- novelwriter/__init__.py +4 -4
- novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +192 -154
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +69 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +178 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +73 -45
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -18
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +188 -61
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +60 -68
- novelwriter/guimain.py +49 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +9 -9
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/dochighlight.py
CHANGED
@@ -35,10 +35,10 @@ from PyQt5.QtGui import (
|
|
35
35
|
)
|
36
36
|
|
37
37
|
from novelwriter import CONFIG, SHARED
|
38
|
+
from novelwriter.enum import nwComment
|
38
39
|
from novelwriter.common import checkInt
|
39
40
|
from novelwriter.constants import nwRegEx, nwUnicode
|
40
41
|
from novelwriter.core.index import processComment
|
41
|
-
from novelwriter.enum import nwComment
|
42
42
|
|
43
43
|
logger = logging.getLogger(__name__)
|
44
44
|
|
@@ -48,6 +48,8 @@ SPELLRX.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
|
48
48
|
|
49
49
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
50
50
|
|
51
|
+
__slots__ = ("_tItem", "_tHandle", "_spellCheck", "_spellErr", "_hRules", "_hStyles")
|
52
|
+
|
51
53
|
BLOCK_NONE = 0
|
52
54
|
BLOCK_TEXT = 1
|
53
55
|
BLOCK_META = 2
|
@@ -58,29 +60,14 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
58
60
|
|
59
61
|
logger.debug("Create: GuiDocHighlighter")
|
60
62
|
|
61
|
-
self.
|
62
|
-
self.
|
63
|
+
self._tHandle = None
|
64
|
+
self._isInactive = False
|
63
65
|
self._spellCheck = False
|
66
|
+
self._spellErr = QTextCharFormat()
|
64
67
|
|
65
68
|
self._hRules: list[tuple[str, dict]] = []
|
66
69
|
self._hStyles: dict[str, QTextCharFormat] = {}
|
67
70
|
|
68
|
-
self._colHead = QColor(0, 0, 0)
|
69
|
-
self._colHeadH = QColor(0, 0, 0)
|
70
|
-
self._colEmph = QColor(0, 0, 0)
|
71
|
-
self._colDialN = QColor(0, 0, 0)
|
72
|
-
self._colDialD = QColor(0, 0, 0)
|
73
|
-
self._colDialS = QColor(0, 0, 0)
|
74
|
-
self._colHidden = QColor(0, 0, 0)
|
75
|
-
self._colCode = QColor(0, 0, 0)
|
76
|
-
self._colKey = QColor(0, 0, 0)
|
77
|
-
self._colVal = QColor(0, 0, 0)
|
78
|
-
self._colSpell = QColor(0, 0, 0)
|
79
|
-
self._colError = QColor(0, 0, 0)
|
80
|
-
self._colRepTag = QColor(0, 0, 0)
|
81
|
-
self._colMod = QColor(0, 0, 0)
|
82
|
-
self._colBreak = QColor(0, 0, 0)
|
83
|
-
|
84
71
|
self.initHighlighter()
|
85
72
|
|
86
73
|
logger.debug("Ready: GuiDocHighlighter")
|
@@ -93,54 +80,43 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
93
80
|
"""
|
94
81
|
logger.debug("Setting up highlighting rules")
|
95
82
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
self._colDialD = QColor(*SHARED.theme.colDialD)
|
100
|
-
self._colDialS = QColor(*SHARED.theme.colDialS)
|
101
|
-
self._colHidden = QColor(*SHARED.theme.colHidden)
|
102
|
-
self._colCode = QColor(*SHARED.theme.colCode)
|
103
|
-
self._colKey = QColor(*SHARED.theme.colKey)
|
104
|
-
self._colVal = QColor(*SHARED.theme.colVal)
|
105
|
-
self._colSpell = QColor(*SHARED.theme.colSpell)
|
106
|
-
self._colError = QColor(*SHARED.theme.colError)
|
107
|
-
self._colRepTag = QColor(*SHARED.theme.colRepTag)
|
108
|
-
self._colMod = QColor(*SHARED.theme.colMod)
|
109
|
-
self._colBreak = QColor(*SHARED.theme.colEmph)
|
110
|
-
self._colBreak.setAlpha(64)
|
111
|
-
|
112
|
-
self._colEmph = None
|
113
|
-
if CONFIG.highlightEmph:
|
114
|
-
self._colEmph = QColor(*SHARED.theme.colEmph)
|
83
|
+
colEmph = SHARED.theme.colEmph if CONFIG.highlightEmph else None
|
84
|
+
colBreak = QColor(SHARED.theme.colEmph)
|
85
|
+
colBreak.setAlpha(64)
|
115
86
|
|
87
|
+
self._hRules = []
|
116
88
|
self._hStyles = {
|
117
|
-
"header1": self._makeFormat(
|
118
|
-
"header2": self._makeFormat(
|
119
|
-
"header3": self._makeFormat(
|
120
|
-
"header4": self._makeFormat(
|
121
|
-
"header1h": self._makeFormat(
|
122
|
-
"header2h": self._makeFormat(
|
123
|
-
"header3h": self._makeFormat(
|
124
|
-
"header4h": self._makeFormat(
|
125
|
-
"bold": self._makeFormat(
|
126
|
-
"italic": self._makeFormat(
|
127
|
-
"strike": self._makeFormat(
|
128
|
-
"mspaces": self._makeFormat(
|
129
|
-
"nobreak": self._makeFormat(
|
130
|
-
"dialogue1": self._makeFormat(
|
131
|
-
"dialogue2": self._makeFormat(
|
132
|
-
"dialogue3": self._makeFormat(
|
133
|
-
"replace": self._makeFormat(
|
134
|
-
"hidden": self._makeFormat(
|
135
|
-
"code": self._makeFormat(
|
136
|
-
"keyword": self._makeFormat(
|
137
|
-
"modifier": self._makeFormat(
|
138
|
-
"value": self._makeFormat(
|
139
|
-
"
|
89
|
+
"header1": self._makeFormat(SHARED.theme.colHead, "bold", 1.8),
|
90
|
+
"header2": self._makeFormat(SHARED.theme.colHead, "bold", 1.6),
|
91
|
+
"header3": self._makeFormat(SHARED.theme.colHead, "bold", 1.4),
|
92
|
+
"header4": self._makeFormat(SHARED.theme.colHead, "bold", 1.2),
|
93
|
+
"header1h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.8),
|
94
|
+
"header2h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.6),
|
95
|
+
"header3h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.4),
|
96
|
+
"header4h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.2),
|
97
|
+
"bold": self._makeFormat(colEmph, "bold"),
|
98
|
+
"italic": self._makeFormat(colEmph, "italic"),
|
99
|
+
"strike": self._makeFormat(SHARED.theme.colHidden, "strike"),
|
100
|
+
"mspaces": self._makeFormat(SHARED.theme.colError, "errline"),
|
101
|
+
"nobreak": self._makeFormat(colBreak, "background"),
|
102
|
+
"dialogue1": self._makeFormat(SHARED.theme.colDialN),
|
103
|
+
"dialogue2": self._makeFormat(SHARED.theme.colDialD),
|
104
|
+
"dialogue3": self._makeFormat(SHARED.theme.colDialS),
|
105
|
+
"replace": self._makeFormat(SHARED.theme.colRepTag),
|
106
|
+
"hidden": self._makeFormat(SHARED.theme.colHidden),
|
107
|
+
"code": self._makeFormat(SHARED.theme.colCode),
|
108
|
+
"keyword": self._makeFormat(SHARED.theme.colKey),
|
109
|
+
"modifier": self._makeFormat(SHARED.theme.colMod),
|
110
|
+
"value": self._makeFormat(SHARED.theme.colVal),
|
111
|
+
"optional": self._makeFormat(SHARED.theme.colOpt),
|
112
|
+
"codevalue": self._makeFormat(SHARED.theme.colVal),
|
140
113
|
"codeinval": self._makeFormat(None, "errline"),
|
141
114
|
}
|
142
115
|
|
143
|
-
|
116
|
+
# Cache Spell Error Format
|
117
|
+
self._spellErr = QTextCharFormat()
|
118
|
+
self._spellErr.setUnderlineColor(SHARED.theme.colSpell)
|
119
|
+
self._spellErr.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
144
120
|
|
145
121
|
# Multiple or Trailing Spaces
|
146
122
|
if CONFIG.showMultiSpaces:
|
@@ -262,11 +238,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
262
238
|
def setHandle(self, tHandle: str) -> None:
|
263
239
|
"""Set the handle of the currently highlighted document."""
|
264
240
|
self._tHandle = tHandle
|
265
|
-
self.
|
266
|
-
|
267
|
-
"Syntax highlighter %s for item '%s'",
|
268
|
-
"enabled" if self._tItem else "disabled", tHandle
|
241
|
+
self._isInactive = (
|
242
|
+
item.isInactiveClass() if (item := SHARED.project.tree[tHandle]) else False
|
269
243
|
)
|
244
|
+
logger.debug("Syntax highlighter enabled for item '%s'", tHandle)
|
270
245
|
return
|
271
246
|
|
272
247
|
##
|
@@ -281,9 +256,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
281
256
|
nBlocks = qDoc.blockCount()
|
282
257
|
tStart = time()
|
283
258
|
for i in range(nBlocks):
|
284
|
-
|
285
|
-
if
|
286
|
-
self.rehighlightBlock(
|
259
|
+
block = qDoc.findBlockByNumber(i)
|
260
|
+
if block.userState() & cType > 0:
|
261
|
+
self.rehighlightBlock(block)
|
287
262
|
logger.debug("Document highlighted in %.3f ms" % (1000*(time() - tStart)))
|
288
263
|
return
|
289
264
|
|
@@ -303,24 +278,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
303
278
|
|
304
279
|
if text.startswith("@"): # Keywords and commands
|
305
280
|
self.setCurrentBlockState(self.BLOCK_META)
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
self.setFormat(xPos, xLen, kwFmt)
|
281
|
+
index = SHARED.project.index
|
282
|
+
isValid, bits, pos = index.scanThis(text)
|
283
|
+
isGood = index.checkThese(bits, self._tHandle)
|
284
|
+
if isValid:
|
285
|
+
for n, bit in enumerate(bits):
|
286
|
+
xPos = pos[n]
|
287
|
+
xLen = len(bit)
|
288
|
+
if n == 0 and isGood[n]:
|
289
|
+
self.setFormat(xPos, xLen, self._hStyles["keyword"])
|
290
|
+
elif isGood[n] and not self._isInactive:
|
291
|
+
one, two = index.parseValue(bit)
|
292
|
+
self.setFormat(xPos, len(one), self._hStyles["value"])
|
293
|
+
if two:
|
294
|
+
yPos = xPos + len(bit) - len(two)
|
295
|
+
self.setFormat(yPos, len(two), self._hStyles["optional"])
|
296
|
+
elif not self._isInactive:
|
297
|
+
self.setFormat(xPos, xLen, self._hStyles["codeinval"])
|
324
298
|
|
325
299
|
# We never want to run the spell checker on keyword/values,
|
326
300
|
# so we force a return here
|
@@ -402,8 +376,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
402
376
|
for xPos, xLen in data.spellCheck(text):
|
403
377
|
for x in range(xPos, xPos+xLen):
|
404
378
|
spFmt = self.format(x)
|
405
|
-
spFmt.
|
406
|
-
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
379
|
+
spFmt.merge(self._spellErr)
|
407
380
|
self.setFormat(x, 1, spFmt)
|
408
381
|
|
409
382
|
return
|
@@ -431,10 +404,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
431
404
|
if "strike" in styles:
|
432
405
|
charFormat.setFontStrikeOut(True)
|
433
406
|
if "errline" in styles:
|
434
|
-
charFormat.setUnderlineColor(
|
407
|
+
charFormat.setUnderlineColor(SHARED.theme.colError)
|
435
408
|
charFormat.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
436
|
-
if "underline" in styles:
|
437
|
-
charFormat.setFontUnderline(True)
|
438
409
|
if "background" in styles and color is not None:
|
439
410
|
charFormat.setBackground(QBrush(color, Qt.SolidPattern))
|
440
411
|
|
novelwriter/gui/docviewer.py
CHANGED
@@ -33,7 +33,7 @@ from typing import TYPE_CHECKING
|
|
33
33
|
|
34
34
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
|
35
35
|
from PyQt5.QtGui import (
|
36
|
-
|
36
|
+
QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
|
37
37
|
QTextOption
|
38
38
|
)
|
39
39
|
from PyQt5.QtWidgets import (
|
@@ -142,21 +142,21 @@ class GuiDocViewer(QTextBrowser):
|
|
142
142
|
self._makeStyleSheet()
|
143
143
|
|
144
144
|
# Set Font
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
self.setFont(
|
145
|
+
font = QFont()
|
146
|
+
font.setFamily(CONFIG.textFont)
|
147
|
+
font.setPointSize(CONFIG.textSize)
|
148
|
+
self.setFont(font)
|
149
149
|
|
150
150
|
# Set the widget colours to match syntax theme
|
151
151
|
mainPalette = self.palette()
|
152
|
-
mainPalette.setColor(QPalette.ColorRole.Window,
|
153
|
-
mainPalette.setColor(QPalette.ColorRole.Base,
|
154
|
-
mainPalette.setColor(QPalette.ColorRole.Text,
|
152
|
+
mainPalette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
153
|
+
mainPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
|
154
|
+
mainPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
155
155
|
self.setPalette(mainPalette)
|
156
156
|
|
157
157
|
docPalette = self.viewport().palette()
|
158
|
-
docPalette.setColor(QPalette.ColorRole.Base,
|
159
|
-
docPalette.setColor(QPalette.ColorRole.Text,
|
158
|
+
docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
|
159
|
+
docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
160
160
|
self.viewport().setPalette(docPalette)
|
161
161
|
|
162
162
|
self.docHeader.matchColours()
|
@@ -164,10 +164,10 @@ class GuiDocViewer(QTextBrowser):
|
|
164
164
|
|
165
165
|
# Set default text margins
|
166
166
|
self.document().setDocumentMargin(0)
|
167
|
-
|
167
|
+
options = QTextOption()
|
168
168
|
if CONFIG.doJustify:
|
169
|
-
|
170
|
-
self.document().setDefaultTextOption(
|
169
|
+
options.setAlignment(Qt.AlignmentFlag.AlignJustify)
|
170
|
+
self.document().setDefaultTextOption(options)
|
171
171
|
|
172
172
|
# Scroll bars
|
173
173
|
if CONFIG.hideVScroll:
|
@@ -229,7 +229,7 @@ class GuiDocViewer(QTextBrowser):
|
|
229
229
|
self.setDocumentTitle(tHandle)
|
230
230
|
|
231
231
|
# Replace tabs before setting the HTML, and then put them back in
|
232
|
-
self.setHtml(aDoc.
|
232
|
+
self.setHtml(aDoc.result.replace("\t", "!!tab!!"))
|
233
233
|
while self.find("!!tab!!"):
|
234
234
|
self.textCursor().insertText("\t")
|
235
235
|
|
@@ -367,9 +367,9 @@ class GuiDocViewer(QTextBrowser):
|
|
367
367
|
link = url.url()
|
368
368
|
logger.debug("Clicked link: '%s'", link)
|
369
369
|
if len(link) > 0:
|
370
|
-
|
371
|
-
if len(
|
372
|
-
self.loadDocumentTagRequest.emit(
|
370
|
+
bits = link.split("=")
|
371
|
+
if len(bits) == 2:
|
372
|
+
self.loadDocumentTagRequest.emit(bits[1], nwDocMode.VIEW)
|
373
373
|
return
|
374
374
|
|
375
375
|
@pyqtSlot("QPoint")
|
@@ -461,9 +461,16 @@ class GuiDocViewer(QTextBrowser):
|
|
461
461
|
|
462
462
|
def _makeStyleSheet(self) -> None:
|
463
463
|
"""Generate an appropriate style sheet for the document viewer,
|
464
|
-
based on the current syntax highlighter theme
|
464
|
+
based on the current syntax highlighter theme.
|
465
465
|
"""
|
466
|
-
|
466
|
+
colText = SHARED.theme.colText
|
467
|
+
colHead = SHARED.theme.colHead
|
468
|
+
colVals = SHARED.theme.colVal
|
469
|
+
colEmph = SHARED.theme.colEmph
|
470
|
+
colKeys = SHARED.theme.colKey
|
471
|
+
colHide = SHARED.theme.colHidden
|
472
|
+
colMods = SHARED.theme.colMod
|
473
|
+
colOpts = SHARED.theme.colOpt
|
467
474
|
styleSheet = (
|
468
475
|
"body {{"
|
469
476
|
" color: rgb({tColR}, {tColG}, {tColB});"
|
@@ -480,6 +487,9 @@ class GuiDocViewer(QTextBrowser):
|
|
480
487
|
".tags {{"
|
481
488
|
" color: rgb({kColR}, {kColG}, {kColB});"
|
482
489
|
"}}\n"
|
490
|
+
".optional {{"
|
491
|
+
" color: rgb({oColR}, {oColG}, {oColB});"
|
492
|
+
"}}\n"
|
483
493
|
".comment {{"
|
484
494
|
" color: rgb({cColR}, {cColG}, {cColB});"
|
485
495
|
"}}\n"
|
@@ -490,27 +500,14 @@ class GuiDocViewer(QTextBrowser):
|
|
490
500
|
" text-align: center;"
|
491
501
|
"}}\n"
|
492
502
|
).format(
|
493
|
-
tColR=
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
aColB=pTheme.colVal[2],
|
502
|
-
eColR=pTheme.colEmph[0],
|
503
|
-
eColG=pTheme.colEmph[1],
|
504
|
-
eColB=pTheme.colEmph[2],
|
505
|
-
kColR=pTheme.colKey[0],
|
506
|
-
kColG=pTheme.colKey[1],
|
507
|
-
kColB=pTheme.colKey[2],
|
508
|
-
cColR=pTheme.colHidden[0],
|
509
|
-
cColG=pTheme.colHidden[1],
|
510
|
-
cColB=pTheme.colHidden[2],
|
511
|
-
mColR=pTheme.colMod[0],
|
512
|
-
mColG=pTheme.colMod[1],
|
513
|
-
mColB=pTheme.colMod[2],
|
503
|
+
tColR=colText.red(), tColG=colText.green(), tColB=colText.blue(),
|
504
|
+
hColR=colHead.red(), hColG=colHead.green(), hColB=colHead.blue(),
|
505
|
+
aColR=colVals.red(), aColG=colVals.green(), aColB=colVals.blue(),
|
506
|
+
eColR=colEmph.red(), eColG=colEmph.green(), eColB=colEmph.blue(),
|
507
|
+
kColR=colKeys.red(), kColG=colKeys.green(), kColB=colKeys.blue(),
|
508
|
+
cColR=colHide.red(), cColG=colHide.green(), cColB=colHide.blue(),
|
509
|
+
mColR=colMods.red(), mColG=colMods.green(), mColB=colMods.blue(),
|
510
|
+
oColR=colOpts.red(), oColG=colOpts.green(), oColB=colOpts.blue(),
|
514
511
|
)
|
515
512
|
self.document().setDefaultStyleSheet(styleSheet)
|
516
513
|
|
@@ -744,10 +741,11 @@ class GuiDocViewHeader(QWidget):
|
|
744
741
|
self.refreshButton.setIcon(SHARED.theme.getIcon("refresh"))
|
745
742
|
self.closeButton.setIcon(SHARED.theme.getIcon("close"))
|
746
743
|
|
744
|
+
colText = SHARED.theme.colText
|
747
745
|
buttonStyle = (
|
748
746
|
"QToolButton {{border: none; background: transparent;}} "
|
749
|
-
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
750
|
-
).format(
|
747
|
+
"QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
|
748
|
+
).format(colText.red(), colText.green(), colText.blue())
|
751
749
|
|
752
750
|
self.backButton.setStyleSheet(buttonStyle)
|
753
751
|
self.forwardButton.setStyleSheet(buttonStyle)
|
@@ -763,9 +761,9 @@ class GuiDocViewHeader(QWidget):
|
|
763
761
|
theme rather than the main GUI.
|
764
762
|
"""
|
765
763
|
palette = QPalette()
|
766
|
-
palette.setColor(QPalette.ColorRole.Window,
|
767
|
-
palette.setColor(QPalette.ColorRole.WindowText,
|
768
|
-
palette.setColor(QPalette.ColorRole.Text,
|
764
|
+
palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
765
|
+
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
766
|
+
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
769
767
|
self.setPalette(palette)
|
770
768
|
self.docTitle.setPalette(palette)
|
771
769
|
return
|
@@ -937,11 +935,11 @@ class GuiDocViewFooter(QWidget):
|
|
937
935
|
self.showComments.setIcon(bulletIcon)
|
938
936
|
self.showSynopsis.setIcon(bulletIcon)
|
939
937
|
|
940
|
-
|
938
|
+
colText = SHARED.theme.colText
|
941
939
|
buttonStyle = (
|
942
940
|
"QToolButton {{border: none; background: transparent;}} "
|
943
|
-
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
944
|
-
).format(
|
941
|
+
"QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
|
942
|
+
).format(colText.red(), colText.green(), colText.blue())
|
945
943
|
|
946
944
|
self.showHide.setStyleSheet(buttonStyle)
|
947
945
|
self.showComments.setStyleSheet(buttonStyle)
|
@@ -956,9 +954,9 @@ class GuiDocViewFooter(QWidget):
|
|
956
954
|
theme rather than the main GUI.
|
957
955
|
"""
|
958
956
|
palette = QPalette()
|
959
|
-
palette.setColor(QPalette.ColorRole.Window,
|
960
|
-
palette.setColor(QPalette.ColorRole.WindowText,
|
961
|
-
palette.setColor(QPalette.ColorRole.Text,
|
957
|
+
palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
|
958
|
+
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
959
|
+
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
962
960
|
self.setPalette(palette)
|
963
961
|
return
|
964
962
|
|
@@ -29,8 +29,8 @@ from enum import Enum
|
|
29
29
|
|
30
30
|
from PyQt5.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
|
31
31
|
from PyQt5.QtWidgets import (
|
32
|
-
QAbstractItemView, QFrame, QHeaderView, QTabWidget,
|
33
|
-
QTreeWidgetItem, QVBoxLayout, QWidget
|
32
|
+
QAbstractItemView, QFrame, QHeaderView, QMenu, QTabWidget, QToolButton,
|
33
|
+
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
34
34
|
)
|
35
35
|
|
36
36
|
from novelwriter import CONFIG, SHARED
|
@@ -54,10 +54,24 @@ class GuiDocViewerPanel(QWidget):
|
|
54
54
|
|
55
55
|
self._lastHandle = None
|
56
56
|
|
57
|
+
iPx = int(1.0*SHARED.theme.baseIconSize)
|
58
|
+
|
57
59
|
self.tabBackRefs = _ViewPanelBackRefs(self)
|
58
60
|
|
61
|
+
self.optsMenu = QMenu(self)
|
62
|
+
|
63
|
+
self.aInactive = self.optsMenu.addAction(self.tr("Hide Inactive Tags"))
|
64
|
+
self.aInactive.setCheckable(True)
|
65
|
+
self.aInactive.toggled.connect(self._toggleHideInactive)
|
66
|
+
|
67
|
+
self.optsButton = QToolButton(self)
|
68
|
+
self.optsButton.setIconSize(QSize(iPx, iPx))
|
69
|
+
self.optsButton.setMenu(self.optsMenu)
|
70
|
+
self.optsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
71
|
+
|
59
72
|
self.mainTabs = QTabWidget(self)
|
60
73
|
self.mainTabs.addTab(self.tabBackRefs, self.tr("References"))
|
74
|
+
self.mainTabs.setCornerWidget(self.optsButton, Qt.Corner.TopLeftCorner)
|
61
75
|
|
62
76
|
self.kwTabs: dict[str, _ViewPanelKeyWords] = {}
|
63
77
|
self.idTabs: dict[str, int] = {}
|
@@ -85,20 +99,27 @@ class GuiDocViewerPanel(QWidget):
|
|
85
99
|
|
86
100
|
def updateTheme(self, updateTabs: bool = True) -> None:
|
87
101
|
"""Update theme elements."""
|
102
|
+
qPalette = self.palette()
|
103
|
+
mPx = CONFIG.pxInt(2)
|
88
104
|
vPx = CONFIG.pxInt(4)
|
89
|
-
|
90
|
-
|
91
|
-
|
105
|
+
hPx = CONFIG.pxInt(8)
|
106
|
+
hCol = qPalette.highlight().color()
|
107
|
+
fCol = qPalette.text().color()
|
108
|
+
|
109
|
+
buttonStyle = (
|
110
|
+
"QToolButton {{padding: {0}px; margin: 0 0 {1}px 0; border: none; "
|
111
|
+
"background: transparent;}} "
|
112
|
+
"QToolButton:hover {{border: none; background: rgba({2}, {3}, {4}, 0.2);}} "
|
113
|
+
"QToolButton::menu-indicator {{image: none;}} "
|
114
|
+
).format(mPx, mPx, fCol.red(), fCol.green(), fCol.blue())
|
115
|
+
self.optsButton.setIcon(SHARED.theme.getIcon("menu"))
|
116
|
+
self.optsButton.setStyleSheet(buttonStyle)
|
92
117
|
|
93
118
|
styleSheet = (
|
94
|
-
"QTabWidget::pane {border: 0;} "
|
95
|
-
"QTabWidget QTabBar::tab {"
|
96
|
-
|
97
|
-
|
98
|
-
"QTabWidget QTabBar::tab:selected {"
|
99
|
-
f"color: rgb({hCol.red()}, {hCol.green()}, {hCol.blue()});"
|
100
|
-
"} "
|
101
|
-
)
|
119
|
+
"QTabWidget::pane {{border: 0;}} "
|
120
|
+
"QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} "
|
121
|
+
"QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} "
|
122
|
+
).format(vPx, hPx, hCol.red(), hCol.green(), hCol.blue())
|
102
123
|
self.mainTabs.setStyleSheet(styleSheet)
|
103
124
|
self.updateHandle(self._lastHandle)
|
104
125
|
|
@@ -111,20 +132,22 @@ class GuiDocViewerPanel(QWidget):
|
|
111
132
|
|
112
133
|
def openProjectTasks(self) -> None:
|
113
134
|
"""Run open project tasks."""
|
114
|
-
|
115
|
-
|
116
|
-
|
135
|
+
colWidths = SHARED.project.options.getValue("GuiDocViewerPanel", "colWidths", {})
|
136
|
+
hideInactive = SHARED.project.options.getBool("GuiDocViewerPanel", "hideInactive", False)
|
137
|
+
self.aInactive.setChecked(hideInactive)
|
138
|
+
if isinstance(colWidths, dict):
|
139
|
+
for key, value in colWidths.items():
|
117
140
|
if key in self.kwTabs and isinstance(value, list):
|
118
141
|
self.kwTabs[key].setColumnWidths(value)
|
119
142
|
return
|
120
143
|
|
121
144
|
def closeProjectTasks(self) -> None:
|
122
145
|
"""Run close project tasks."""
|
123
|
-
widths = {}
|
124
|
-
for key, tab in self.kwTabs.items():
|
125
|
-
widths[key] = tab.getColumnWidths()
|
126
146
|
logger.debug("Saving State: GuiDocViewerPanel")
|
127
|
-
|
147
|
+
colWidths = {k: t.getColumnWidths() for k, t in self.kwTabs.items()}
|
148
|
+
hideInactive = self.aInactive.isChecked()
|
149
|
+
SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", colWidths)
|
150
|
+
SHARED.project.options.setValue("GuiDocViewerPanel", "hideInactive", hideInactive)
|
128
151
|
return
|
129
152
|
|
130
153
|
##
|
@@ -142,9 +165,7 @@ class GuiDocViewerPanel(QWidget):
|
|
142
165
|
@pyqtSlot()
|
143
166
|
def indexHasAppeared(self) -> None:
|
144
167
|
"""Handle event when the index has appeared."""
|
145
|
-
|
146
|
-
if tClass in self.kwTabs and iItem and hItem:
|
147
|
-
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
|
168
|
+
self._loadAllTags()
|
148
169
|
self._updateTabVisibility()
|
149
170
|
self.updateHandle(self._lastHandle)
|
150
171
|
return
|
@@ -153,10 +174,15 @@ class GuiDocViewerPanel(QWidget):
|
|
153
174
|
def projectItemChanged(self, tHandle: str) -> None:
|
154
175
|
"""Update meta data for project item."""
|
155
176
|
self.tabBackRefs.refreshDocument(tHandle)
|
177
|
+
activeOnly = self.aInactive.isChecked()
|
156
178
|
for key in SHARED.project.index.getDocumentTags(tHandle):
|
157
179
|
name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key)
|
158
180
|
if tClass in self.kwTabs and iItem and hItem:
|
159
|
-
|
181
|
+
if not activeOnly or (iItem and iItem.item.isActive):
|
182
|
+
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
|
183
|
+
else:
|
184
|
+
self.kwTabs[tClass].removeEntry(key)
|
185
|
+
self._updateTabVisibility()
|
160
186
|
return
|
161
187
|
|
162
188
|
@pyqtSlot(str)
|
@@ -182,6 +208,20 @@ class GuiDocViewerPanel(QWidget):
|
|
182
208
|
self._updateTabVisibility()
|
183
209
|
return
|
184
210
|
|
211
|
+
##
|
212
|
+
# Private Slots
|
213
|
+
##
|
214
|
+
|
215
|
+
@pyqtSlot(bool)
|
216
|
+
def _toggleHideInactive(self, state: bool) -> None:
|
217
|
+
"""Process toggling of active/inactive visibility."""
|
218
|
+
logger.debug("Setting inactive items to %s", "hidden" if state else "visible")
|
219
|
+
for cTab in self.kwTabs.values():
|
220
|
+
cTab.clearContent()
|
221
|
+
self._loadAllTags()
|
222
|
+
self._updateTabVisibility()
|
223
|
+
return
|
224
|
+
|
185
225
|
##
|
186
226
|
# Internal Functions
|
187
227
|
##
|
@@ -193,6 +233,14 @@ class GuiDocViewerPanel(QWidget):
|
|
193
233
|
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
|
194
234
|
return
|
195
235
|
|
236
|
+
def _loadAllTags(self) -> None:
|
237
|
+
"""Load all tags into the tabs."""
|
238
|
+
data = SHARED.project.index.getTagsData(activeOnly=self.aInactive.isChecked())
|
239
|
+
for key, name, tClass, iItem, hItem in data:
|
240
|
+
if tClass in self.kwTabs and iItem and hItem:
|
241
|
+
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
|
242
|
+
return
|
243
|
+
|
196
244
|
# END Class GuiDocViewerPanel
|
197
245
|
|
198
246
|
|
novelwriter/gui/itemdetails.py
CHANGED
@@ -239,9 +239,9 @@ class GuiItemDetails(QWidget):
|
|
239
239
|
# Label
|
240
240
|
# =====
|
241
241
|
|
242
|
-
|
243
|
-
if len(
|
244
|
-
|
242
|
+
label = nwItem.itemName
|
243
|
+
if len(label) > 100:
|
244
|
+
label = label[:96].rstrip()+" ..."
|
245
245
|
|
246
246
|
if nwItem.isFileType():
|
247
247
|
if nwItem.isActive:
|
@@ -251,14 +251,14 @@ class GuiItemDetails(QWidget):
|
|
251
251
|
else:
|
252
252
|
self.labelIcon.setPixmap(SHARED.theme.getPixmap("noncheckable", (iPx, iPx)))
|
253
253
|
|
254
|
-
self.labelData.setText(
|
254
|
+
self.labelData.setText(label)
|
255
255
|
|
256
256
|
# Status
|
257
257
|
# ======
|
258
258
|
|
259
|
-
|
260
|
-
self.statusIcon.setPixmap(
|
261
|
-
self.statusData.setText(
|
259
|
+
status, icon = nwItem.getImportStatus(incIcon=True)
|
260
|
+
self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
|
261
|
+
self.statusData.setText(status)
|
262
262
|
|
263
263
|
# Class
|
264
264
|
# =====
|