novelWriter 2.3.1__py3-none-any.whl → 2.4b1__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.3.1.dist-info → novelWriter-2.4b1.dist-info}/METADATA +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/RECORD +81 -70
- novelwriter/__init__.py +5 -5
- novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
- novelwriter/assets/icons/typicons_light/icons.conf +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
- 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 +25 -23
- novelwriter/common.py +1 -1
- novelwriter/config.py +35 -12
- novelwriter/constants.py +5 -6
- novelwriter/core/buildsettings.py +60 -40
- novelwriter/core/coretools.py +98 -13
- novelwriter/core/docbuild.py +74 -7
- novelwriter/core/document.py +24 -3
- novelwriter/core/index.py +31 -112
- novelwriter/core/project.py +10 -15
- novelwriter/core/sessions.py +2 -2
- novelwriter/core/status.py +4 -4
- novelwriter/core/storage.py +8 -2
- novelwriter/core/tohtml.py +22 -25
- novelwriter/core/tokenizer.py +416 -232
- novelwriter/core/tomd.py +17 -8
- novelwriter/core/toodt.py +65 -7
- novelwriter/core/tree.py +8 -8
- novelwriter/dialogs/docsplit.py +7 -8
- novelwriter/dialogs/preferences.py +3 -6
- novelwriter/enum.py +17 -14
- novelwriter/extensions/modified.py +20 -2
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/gui/doceditor.py +257 -279
- novelwriter/gui/dochighlight.py +29 -25
- novelwriter/gui/docviewer.py +139 -148
- novelwriter/gui/docviewerpanel.py +4 -24
- novelwriter/gui/editordocument.py +12 -1
- novelwriter/gui/itemdetails.py +6 -6
- novelwriter/gui/mainmenu.py +37 -16
- novelwriter/gui/noveltree.py +11 -19
- novelwriter/gui/outline.py +43 -20
- novelwriter/gui/projtree.py +35 -43
- novelwriter/gui/search.py +316 -0
- novelwriter/gui/sidebar.py +25 -30
- novelwriter/gui/theme.py +59 -6
- novelwriter/guimain.py +176 -173
- novelwriter/shared.py +26 -1
- novelwriter/text/__init__.py +3 -0
- novelwriter/text/counting.py +137 -0
- novelwriter/tools/manuscript.py +344 -55
- novelwriter/tools/manussettings.py +213 -71
- novelwriter/tools/welcome.py +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/dochighlight.py
CHANGED
@@ -45,16 +45,16 @@ logger = logging.getLogger(__name__)
|
|
45
45
|
SPELLRX = QRegularExpression(r"\b[^\s\-\+\/–—\[\]:]+\b")
|
46
46
|
SPELLRX.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
47
47
|
|
48
|
+
BLOCK_NONE = 0
|
49
|
+
BLOCK_TEXT = 1
|
50
|
+
BLOCK_META = 2
|
51
|
+
BLOCK_TITLE = 4
|
52
|
+
|
48
53
|
|
49
54
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
50
55
|
|
51
56
|
__slots__ = ("_tItem", "_tHandle", "_spellCheck", "_spellErr", "_hRules", "_hStyles")
|
52
57
|
|
53
|
-
BLOCK_NONE = 0
|
54
|
-
BLOCK_TEXT = 1
|
55
|
-
BLOCK_META = 2
|
56
|
-
BLOCK_TITLE = 4
|
57
|
-
|
58
58
|
def __init__(self, document: QTextDocument) -> None:
|
59
59
|
super().__init__(document)
|
60
60
|
|
@@ -90,10 +90,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
90
90
|
"header2": self._makeFormat(SHARED.theme.colHead, "bold", 1.6),
|
91
91
|
"header3": self._makeFormat(SHARED.theme.colHead, "bold", 1.4),
|
92
92
|
"header4": self._makeFormat(SHARED.theme.colHead, "bold", 1.2),
|
93
|
-
"
|
94
|
-
"
|
95
|
-
"
|
96
|
-
"
|
93
|
+
"head1h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.8),
|
94
|
+
"head2h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.6),
|
95
|
+
"head3h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.4),
|
96
|
+
"head4h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.2),
|
97
97
|
"bold": self._makeFormat(colEmph, "bold"),
|
98
98
|
"italic": self._makeFormat(colEmph, "italic"),
|
99
99
|
"strike": self._makeFormat(SHARED.theme.colHidden, "strike"),
|
@@ -272,12 +272,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
272
272
|
is significantly faster than running the regex checks used for
|
273
273
|
text paragraphs.
|
274
274
|
"""
|
275
|
-
self.setCurrentBlockState(
|
275
|
+
self.setCurrentBlockState(BLOCK_NONE)
|
276
276
|
if self._tHandle is None or not text:
|
277
277
|
return
|
278
278
|
|
279
279
|
if text.startswith("@"): # Keywords and commands
|
280
|
-
self.setCurrentBlockState(
|
280
|
+
self.setCurrentBlockState(BLOCK_META)
|
281
281
|
index = SHARED.project.index
|
282
282
|
isValid, bits, pos = index.scanThis(text)
|
283
283
|
isGood = index.checkThese(bits, self._tHandle)
|
@@ -300,35 +300,39 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
300
300
|
# so we force a return here
|
301
301
|
return
|
302
302
|
|
303
|
-
elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "#### ")):
|
304
|
-
self.setCurrentBlockState(
|
303
|
+
elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "###! ", "#### ")):
|
304
|
+
self.setCurrentBlockState(BLOCK_TITLE)
|
305
305
|
|
306
|
-
if text.startswith("# "): #
|
307
|
-
self.setFormat(0, 1, self._hStyles["
|
306
|
+
if text.startswith("# "): # Heading 1
|
307
|
+
self.setFormat(0, 1, self._hStyles["head1h"])
|
308
308
|
self.setFormat(1, len(text), self._hStyles["header1"])
|
309
309
|
|
310
|
-
elif text.startswith("## "): #
|
311
|
-
self.setFormat(0, 2, self._hStyles["
|
310
|
+
elif text.startswith("## "): # Heading 2
|
311
|
+
self.setFormat(0, 2, self._hStyles["head2h"])
|
312
312
|
self.setFormat(2, len(text), self._hStyles["header2"])
|
313
313
|
|
314
|
-
elif text.startswith("### "): #
|
315
|
-
self.setFormat(0, 3, self._hStyles["
|
314
|
+
elif text.startswith("### "): # Heading 3
|
315
|
+
self.setFormat(0, 3, self._hStyles["head3h"])
|
316
316
|
self.setFormat(3, len(text), self._hStyles["header3"])
|
317
317
|
|
318
|
-
elif text.startswith("#### "): #
|
319
|
-
self.setFormat(0, 4, self._hStyles["
|
318
|
+
elif text.startswith("#### "): # Heading 4
|
319
|
+
self.setFormat(0, 4, self._hStyles["head4h"])
|
320
320
|
self.setFormat(4, len(text), self._hStyles["header4"])
|
321
321
|
|
322
322
|
elif text.startswith("#! "): # Title
|
323
|
-
self.setFormat(0, 2, self._hStyles["
|
323
|
+
self.setFormat(0, 2, self._hStyles["head1h"])
|
324
324
|
self.setFormat(2, len(text), self._hStyles["header1"])
|
325
325
|
|
326
326
|
elif text.startswith("##! "): # Unnumbered
|
327
|
-
self.setFormat(0, 3, self._hStyles["
|
327
|
+
self.setFormat(0, 3, self._hStyles["head2h"])
|
328
328
|
self.setFormat(3, len(text), self._hStyles["header2"])
|
329
329
|
|
330
|
+
elif text.startswith("###! "): # Hard Scene
|
331
|
+
self.setFormat(0, 4, self._hStyles["head3h"])
|
332
|
+
self.setFormat(4, len(text), self._hStyles["header3"])
|
333
|
+
|
330
334
|
elif text.startswith("%"): # Comments
|
331
|
-
self.setCurrentBlockState(
|
335
|
+
self.setCurrentBlockState(BLOCK_TEXT)
|
332
336
|
cStyle, _, cPos = processComment(text)
|
333
337
|
if cStyle == nwComment.PLAIN:
|
334
338
|
self.setFormat(0, len(text), self._hStyles["hidden"])
|
@@ -353,7 +357,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
353
357
|
return
|
354
358
|
|
355
359
|
# Regular Text
|
356
|
-
self.setCurrentBlockState(
|
360
|
+
self.setCurrentBlockState(BLOCK_TEXT)
|
357
361
|
for rX, xFmt in self.rxRules:
|
358
362
|
rxItt = rX.globalMatch(text, 0)
|
359
363
|
while rxItt.hasNext():
|
novelwriter/gui/docviewer.py
CHANGED
@@ -29,7 +29,6 @@ from __future__ import annotations
|
|
29
29
|
import logging
|
30
30
|
|
31
31
|
from enum import Enum
|
32
|
-
from typing import TYPE_CHECKING
|
33
32
|
|
34
33
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
|
35
34
|
from PyQt5.QtGui import (
|
@@ -42,14 +41,13 @@ from PyQt5.QtWidgets import (
|
|
42
41
|
)
|
43
42
|
|
44
43
|
from novelwriter import CONFIG, SHARED
|
44
|
+
from novelwriter.constants import nwHeaders, nwUnicode
|
45
|
+
from novelwriter.core.tohtml import ToHtml
|
45
46
|
from novelwriter.enum import nwItemType, nwDocAction, nwDocMode
|
46
47
|
from novelwriter.error import logException
|
47
|
-
from novelwriter.constants import nwUnicode
|
48
|
-
from novelwriter.core.tohtml import ToHtml
|
49
48
|
from novelwriter.extensions.eventfilters import WheelEventFilter
|
50
|
-
|
51
|
-
|
52
|
-
from novelwriter.guimain import GuiMain
|
49
|
+
from novelwriter.extensions.modified import NIconToolButton
|
50
|
+
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
53
51
|
|
54
52
|
logger = logging.getLogger(__name__)
|
55
53
|
|
@@ -58,17 +56,16 @@ class GuiDocViewer(QTextBrowser):
|
|
58
56
|
|
59
57
|
documentLoaded = pyqtSignal(str)
|
60
58
|
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
59
|
+
closeDocumentRequest = pyqtSignal()
|
60
|
+
reloadDocumentRequest = pyqtSignal()
|
61
61
|
togglePanelVisibility = pyqtSignal()
|
62
62
|
requestProjectItemSelected = pyqtSignal(str, bool)
|
63
63
|
|
64
|
-
def __init__(self,
|
65
|
-
super().__init__(parent=
|
64
|
+
def __init__(self, parent: QWidget) -> None:
|
65
|
+
super().__init__(parent=parent)
|
66
66
|
|
67
67
|
logger.debug("Create: GuiDocViewer")
|
68
68
|
|
69
|
-
# Class Variables
|
70
|
-
self.mainGui = mainGui
|
71
|
-
|
72
69
|
# Internal Variables
|
73
70
|
self._docHandle = None
|
74
71
|
|
@@ -128,7 +125,7 @@ class GuiDocViewer(QTextBrowser):
|
|
128
125
|
self.clear()
|
129
126
|
self.setSearchPaths([""])
|
130
127
|
self._docHandle = None
|
131
|
-
self.docHeader.
|
128
|
+
self.docHeader.clearHeader()
|
132
129
|
return
|
133
130
|
|
134
131
|
def updateTheme(self) -> None:
|
@@ -184,8 +181,7 @@ class GuiDocViewer(QTextBrowser):
|
|
184
181
|
self.setTabStopDistance(CONFIG.getTabWidth())
|
185
182
|
|
186
183
|
# If we have a document open, we should reload it in case the font changed
|
187
|
-
|
188
|
-
self.reloadText()
|
184
|
+
self.reloadText()
|
189
185
|
|
190
186
|
return
|
191
187
|
|
@@ -201,8 +197,11 @@ class GuiDocViewer(QTextBrowser):
|
|
201
197
|
|
202
198
|
sPos = self.verticalScrollBar().value()
|
203
199
|
aDoc = ToHtml(SHARED.project)
|
204
|
-
aDoc.setPreview(
|
205
|
-
aDoc.
|
200
|
+
aDoc.setPreview(True)
|
201
|
+
aDoc.setKeywords(True)
|
202
|
+
aDoc.setComments(CONFIG.viewComments)
|
203
|
+
aDoc.setSynopsis(CONFIG.viewSynopsis)
|
204
|
+
aDoc.setLinkHeadings(True)
|
206
205
|
|
207
206
|
# Be extra careful here to prevent crashes when first opening a
|
208
207
|
# project as a crash here leaves no way of recovering.
|
@@ -239,7 +238,11 @@ class GuiDocViewer(QTextBrowser):
|
|
239
238
|
|
240
239
|
self._docHandle = tHandle
|
241
240
|
SHARED.project.data.setLastHandle(tHandle, "viewer")
|
242
|
-
self.docHeader.
|
241
|
+
self.docHeader.setHandle(tHandle)
|
242
|
+
self.docHeader.setOutline({
|
243
|
+
sTitle: (hItem.title, nwHeaders.H_LEVEL.get(hItem.level, 0))
|
244
|
+
for sTitle, hItem in SHARED.project.index.iterItemHeadings(tHandle)
|
245
|
+
})
|
243
246
|
self.updateDocMargins()
|
244
247
|
|
245
248
|
# Since we change the content while it may still be rendering, we mark
|
@@ -281,13 +284,6 @@ class GuiDocViewer(QTextBrowser):
|
|
281
284
|
return False
|
282
285
|
return True
|
283
286
|
|
284
|
-
def navigateTo(self, tAnchor: str) -> None:
|
285
|
-
"""Go to a specific #link in the document."""
|
286
|
-
if isinstance(tAnchor, str) and tAnchor.startswith("#"):
|
287
|
-
logger.debug("Moving to anchor '%s'", tAnchor)
|
288
|
-
self.setSource(QUrl(tAnchor))
|
289
|
-
return
|
290
|
-
|
291
287
|
def clearNavHistory(self) -> None:
|
292
288
|
"""Clear the navigation history."""
|
293
289
|
self.docHistory.clear()
|
@@ -340,11 +336,19 @@ class GuiDocViewer(QTextBrowser):
|
|
340
336
|
@pyqtSlot(str)
|
341
337
|
def updateDocInfo(self, tHandle: str) -> None:
|
342
338
|
"""Update the header title bar if needed."""
|
343
|
-
if tHandle == self._docHandle:
|
344
|
-
self.docHeader.
|
339
|
+
if tHandle and tHandle == self._docHandle:
|
340
|
+
self.docHeader.setHandle(tHandle)
|
345
341
|
self.updateDocMargins()
|
346
342
|
return
|
347
343
|
|
344
|
+
@pyqtSlot(str)
|
345
|
+
def navigateTo(self, anchor: str) -> None:
|
346
|
+
"""Go to a specific #link in the document."""
|
347
|
+
if isinstance(anchor, str) and anchor.startswith("#"):
|
348
|
+
logger.debug("Moving to anchor '%s'", anchor)
|
349
|
+
self.setSource(QUrl(anchor))
|
350
|
+
return
|
351
|
+
|
348
352
|
##
|
349
353
|
# Private Slots
|
350
354
|
##
|
@@ -466,48 +470,30 @@ class GuiDocViewer(QTextBrowser):
|
|
466
470
|
colText = SHARED.theme.colText
|
467
471
|
colHead = SHARED.theme.colHead
|
468
472
|
colVals = SHARED.theme.colVal
|
469
|
-
|
473
|
+
colMark = SHARED.theme.colMark
|
470
474
|
colKeys = SHARED.theme.colKey
|
471
475
|
colHide = SHARED.theme.colHidden
|
472
476
|
colMods = SHARED.theme.colMod
|
473
477
|
colOpts = SHARED.theme.colOpt
|
474
478
|
styleSheet = (
|
475
|
-
"body {{"
|
476
|
-
"
|
477
|
-
"}}\n"
|
478
|
-
"
|
479
|
-
"
|
480
|
-
"}}\n"
|
481
|
-
"
|
482
|
-
"
|
483
|
-
"}}\n"
|
484
|
-
"mark {{"
|
485
|
-
" color: rgb({eColR}, {eColG}, {eColB});"
|
486
|
-
"}}\n"
|
487
|
-
".tags {{"
|
488
|
-
" color: rgb({kColR}, {kColG}, {kColB});"
|
489
|
-
"}}\n"
|
490
|
-
".optional {{"
|
491
|
-
" color: rgb({oColR}, {oColG}, {oColB});"
|
492
|
-
"}}\n"
|
493
|
-
".comment {{"
|
494
|
-
" color: rgb({cColR}, {cColG}, {cColB});"
|
495
|
-
"}}\n"
|
496
|
-
".synopsis {{"
|
497
|
-
" color: rgb({mColR}, {mColG}, {mColB});"
|
498
|
-
"}}\n"
|
499
|
-
".title {{"
|
500
|
-
" text-align: center;"
|
501
|
-
"}}\n"
|
479
|
+
"body {{color: rgb({rT}, {gT}, {bT});}}\n"
|
480
|
+
"h1, h2, h3, h4 {{color: rgb({rH}, {gH}, {bH});}}\n"
|
481
|
+
"a {{color: rgb({rA}, {gA}, {bA});}}\n"
|
482
|
+
"mark {{background-color: rgba({rE}, {gE}, {bE}, {aE});}}\n"
|
483
|
+
".tags {{color: rgb({rK}, {gK}, {bK});}}\n"
|
484
|
+
".optional {{color: rgb({rO}, {gO}, {bO});}}\n"
|
485
|
+
".comment {{color: rgb({rC}, {gC}, {bC});}}\n"
|
486
|
+
".synopsis {{color: rgb({rM}, {gM}, {bM});}}\n"
|
487
|
+
".title {{text-align: center;}}\n"
|
502
488
|
).format(
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
489
|
+
rT=colText.red(), gT=colText.green(), bT=colText.blue(),
|
490
|
+
rH=colHead.red(), gH=colHead.green(), bH=colHead.blue(),
|
491
|
+
rA=colVals.red(), gA=colVals.green(), bA=colVals.blue(),
|
492
|
+
rE=colMark.red(), gE=colMark.green(), bE=colMark.blue(), aE=colMark.alpha(),
|
493
|
+
rK=colKeys.red(), gK=colKeys.green(), bK=colKeys.blue(),
|
494
|
+
rC=colHide.red(), gC=colHide.green(), bC=colHide.blue(),
|
495
|
+
rM=colMods.red(), gM=colMods.green(), bM=colMods.blue(),
|
496
|
+
rO=colOpts.red(), gO=colOpts.green(), bO=colOpts.blue(),
|
511
497
|
)
|
512
498
|
self.document().setDefaultStyleSheet(styleSheet)
|
513
499
|
|
@@ -644,84 +630,80 @@ class GuiDocViewHeader(QWidget):
|
|
644
630
|
logger.debug("Create: GuiDocViewHeader")
|
645
631
|
|
646
632
|
self.docViewer = docViewer
|
647
|
-
self.mainGui = docViewer.mainGui
|
648
633
|
|
649
634
|
# Internal Variables
|
650
635
|
self._docHandle = None
|
636
|
+
self._docOutline: dict[str, tuple[str, int]] = {}
|
651
637
|
|
652
|
-
|
653
|
-
|
638
|
+
iPx = SHARED.theme.baseIconSize
|
639
|
+
mPx = CONFIG.pxInt(4)
|
654
640
|
|
655
641
|
# Main Widget Settings
|
656
642
|
self.setAutoFillBackground(True)
|
657
643
|
|
658
644
|
# Title Label
|
659
|
-
self.
|
660
|
-
self.
|
661
|
-
self.
|
662
|
-
self.
|
663
|
-
self.
|
664
|
-
self.
|
665
|
-
self.
|
666
|
-
self.
|
667
|
-
|
668
|
-
lblFont = self.
|
645
|
+
self.itemTitle = QLabel()
|
646
|
+
self.itemTitle.setText("")
|
647
|
+
self.itemTitle.setIndent(0)
|
648
|
+
self.itemTitle.setMargin(0)
|
649
|
+
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
650
|
+
self.itemTitle.setAutoFillBackground(True)
|
651
|
+
self.itemTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
|
652
|
+
self.itemTitle.setFixedHeight(iPx)
|
653
|
+
|
654
|
+
lblFont = self.itemTitle.font()
|
669
655
|
lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
|
670
|
-
self.
|
656
|
+
self.itemTitle.setFont(lblFont)
|
657
|
+
|
658
|
+
# Other Widgets
|
659
|
+
self.outlineMenu = QMenu(self)
|
671
660
|
|
672
661
|
# Buttons
|
673
|
-
self.
|
674
|
-
self.
|
675
|
-
self.
|
676
|
-
self.
|
677
|
-
|
662
|
+
self.outlineButton = NIconToolButton(self, iPx)
|
663
|
+
self.outlineButton.setVisible(False)
|
664
|
+
self.outlineButton.setToolTip(self.tr("Outline"))
|
665
|
+
self.outlineButton.setMenu(self.outlineMenu)
|
666
|
+
|
667
|
+
self.backButton = NIconToolButton(self, iPx)
|
678
668
|
self.backButton.setVisible(False)
|
679
669
|
self.backButton.setToolTip(self.tr("Go Backward"))
|
680
670
|
self.backButton.clicked.connect(self.docViewer.navBackward)
|
681
671
|
|
682
|
-
self.forwardButton =
|
683
|
-
self.forwardButton.setContentsMargins(0, 0, 0, 0)
|
684
|
-
self.forwardButton.setIconSize(QSize(fPx, fPx))
|
685
|
-
self.forwardButton.setFixedSize(fPx, fPx)
|
686
|
-
self.forwardButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
672
|
+
self.forwardButton = NIconToolButton(self, iPx)
|
687
673
|
self.forwardButton.setVisible(False)
|
688
674
|
self.forwardButton.setToolTip(self.tr("Go Forward"))
|
689
675
|
self.forwardButton.clicked.connect(self.docViewer.navForward)
|
690
676
|
|
691
|
-
self.refreshButton =
|
692
|
-
self.refreshButton.setContentsMargins(0, 0, 0, 0)
|
693
|
-
self.refreshButton.setIconSize(QSize(fPx, fPx))
|
694
|
-
self.refreshButton.setFixedSize(fPx, fPx)
|
695
|
-
self.refreshButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
677
|
+
self.refreshButton = NIconToolButton(self, iPx)
|
696
678
|
self.refreshButton.setVisible(False)
|
697
679
|
self.refreshButton.setToolTip(self.tr("Reload"))
|
698
680
|
self.refreshButton.clicked.connect(self._refreshDocument)
|
699
681
|
|
700
|
-
self.closeButton =
|
701
|
-
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
702
|
-
self.closeButton.setIconSize(QSize(fPx, fPx))
|
703
|
-
self.closeButton.setFixedSize(fPx, fPx)
|
704
|
-
self.closeButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
682
|
+
self.closeButton = NIconToolButton(self, iPx)
|
705
683
|
self.closeButton.setVisible(False)
|
706
684
|
self.closeButton.setToolTip(self.tr("Close"))
|
707
685
|
self.closeButton.clicked.connect(self._closeDocument)
|
708
686
|
|
709
687
|
# Assemble Layout
|
710
688
|
self.outerBox = QHBoxLayout()
|
711
|
-
self.outerBox.
|
689
|
+
self.outerBox.addWidget(self.outlineButton, 0)
|
712
690
|
self.outerBox.addWidget(self.backButton, 0)
|
713
691
|
self.outerBox.addWidget(self.forwardButton, 0)
|
714
|
-
self.outerBox.
|
692
|
+
self.outerBox.addSpacing(mPx)
|
693
|
+
self.outerBox.addWidget(self.itemTitle, 1)
|
694
|
+
self.outerBox.addSpacing(mPx)
|
695
|
+
self.outerBox.addSpacing(iPx)
|
715
696
|
self.outerBox.addWidget(self.refreshButton, 0)
|
716
697
|
self.outerBox.addWidget(self.closeButton, 0)
|
698
|
+
self.outerBox.setSpacing(0)
|
699
|
+
|
717
700
|
self.setLayout(self.outerBox)
|
718
701
|
|
719
702
|
# Fix Margins and Size
|
720
703
|
# This is needed for high DPI systems. See issue #499.
|
721
|
-
cM = CONFIG.pxInt(8)
|
722
704
|
self.setContentsMargins(0, 0, 0, 0)
|
723
|
-
self.outerBox.setContentsMargins(
|
724
|
-
self.setMinimumHeight(
|
705
|
+
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
|
706
|
+
self.setMinimumHeight(iPx + 2*mPx)
|
725
707
|
|
726
708
|
# Fix the Colours
|
727
709
|
self.updateTheme()
|
@@ -734,19 +716,50 @@ class GuiDocViewHeader(QWidget):
|
|
734
716
|
# Methods
|
735
717
|
##
|
736
718
|
|
719
|
+
def clearHeader(self) -> None:
|
720
|
+
"""Clear the header."""
|
721
|
+
self._docHandle = None
|
722
|
+
self._docOutline = {}
|
723
|
+
|
724
|
+
self.itemTitle.setText("")
|
725
|
+
self.outlineMenu.clear()
|
726
|
+
self.outlineButton.setVisible(False)
|
727
|
+
self.backButton.setVisible(False)
|
728
|
+
self.forwardButton.setVisible(False)
|
729
|
+
self.closeButton.setVisible(False)
|
730
|
+
self.refreshButton.setVisible(False)
|
731
|
+
return
|
732
|
+
|
733
|
+
def setOutline(self, data: dict[str, tuple[str, int]]) -> None:
|
734
|
+
"""Set the document outline dataset."""
|
735
|
+
tHandle = self._docHandle
|
736
|
+
if data != self._docOutline and tHandle:
|
737
|
+
self.outlineMenu.clear()
|
738
|
+
entries = []
|
739
|
+
minLevel = 5
|
740
|
+
for title, (text, level) in data.items():
|
741
|
+
if title != "T0000":
|
742
|
+
entries.append((title, text, level))
|
743
|
+
minLevel = min(minLevel, level)
|
744
|
+
for title, text, level in entries[:30]:
|
745
|
+
indent = " "*(level - minLevel)
|
746
|
+
action = self.outlineMenu.addAction(f"{indent}{text}")
|
747
|
+
action.triggered.connect(
|
748
|
+
lambda _, title=title: self.docViewer.navigateTo(f"#{tHandle}:{title}")
|
749
|
+
)
|
750
|
+
self._docOutline = data
|
751
|
+
return
|
752
|
+
|
737
753
|
def updateTheme(self) -> None:
|
738
754
|
"""Update theme elements."""
|
755
|
+
self.outlineButton.setIcon(SHARED.theme.getIcon("list"))
|
739
756
|
self.backButton.setIcon(SHARED.theme.getIcon("backward"))
|
740
757
|
self.forwardButton.setIcon(SHARED.theme.getIcon("forward"))
|
741
758
|
self.refreshButton.setIcon(SHARED.theme.getIcon("refresh"))
|
742
759
|
self.closeButton.setIcon(SHARED.theme.getIcon("close"))
|
743
760
|
|
744
|
-
|
745
|
-
buttonStyle
|
746
|
-
"QToolButton {{border: none; background: transparent;}} "
|
747
|
-
"QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
|
748
|
-
).format(colText.red(), colText.green(), colText.blue())
|
749
|
-
|
761
|
+
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
762
|
+
self.outlineButton.setStyleSheet(buttonStyle)
|
750
763
|
self.backButton.setStyleSheet(buttonStyle)
|
751
764
|
self.forwardButton.setStyleSheet(buttonStyle)
|
752
765
|
self.refreshButton.setStyleSheet(buttonStyle)
|
@@ -765,38 +778,25 @@ class GuiDocViewHeader(QWidget):
|
|
765
778
|
palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
|
766
779
|
palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
|
767
780
|
self.setPalette(palette)
|
768
|
-
self.
|
781
|
+
self.itemTitle.setPalette(palette)
|
769
782
|
return
|
770
783
|
|
771
|
-
def
|
784
|
+
def setHandle(self, tHandle: str) -> None:
|
772
785
|
"""Sets the document title from the handle, or alternatively,
|
773
786
|
set the whole document path.
|
774
787
|
"""
|
775
788
|
self._docHandle = tHandle
|
776
|
-
|
777
|
-
self.docTitle.setText("")
|
778
|
-
self.backButton.setVisible(False)
|
779
|
-
self.forwardButton.setVisible(False)
|
780
|
-
self.closeButton.setVisible(False)
|
781
|
-
self.refreshButton.setVisible(False)
|
782
|
-
return
|
783
|
-
|
784
|
-
pTree = SHARED.project.tree
|
789
|
+
|
785
790
|
if CONFIG.showFullPath:
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
nwItem = pTree[aHandle]
|
790
|
-
if nwItem is not None:
|
791
|
-
tTitle.append(nwItem.itemName)
|
792
|
-
sSep = " %s " % nwUnicode.U_RSAQUO
|
793
|
-
self.docTitle.setText(sSep.join(tTitle))
|
791
|
+
self.itemTitle.setText(f" {nwUnicode.U_RSAQUO} ".join(reversed(
|
792
|
+
[name for name in SHARED.project.tree.getItemPath(tHandle, asName=True)]
|
793
|
+
)))
|
794
794
|
else:
|
795
|
-
if
|
796
|
-
self.docTitle.setText(nwItem.itemName)
|
795
|
+
self.itemTitle.setText(i.itemName if (i := SHARED.project.tree[tHandle]) else "")
|
797
796
|
|
798
797
|
self.backButton.setVisible(True)
|
799
798
|
self.forwardButton.setVisible(True)
|
799
|
+
self.outlineButton.setVisible(True)
|
800
800
|
self.closeButton.setVisible(True)
|
801
801
|
self.refreshButton.setVisible(True)
|
802
802
|
|
@@ -815,15 +815,14 @@ class GuiDocViewHeader(QWidget):
|
|
815
815
|
@pyqtSlot()
|
816
816
|
def _closeDocument(self) -> None:
|
817
817
|
"""Trigger the close editor/viewer on the main window."""
|
818
|
-
self.
|
818
|
+
self.clearHeader()
|
819
|
+
self.docViewer.closeDocumentRequest.emit()
|
819
820
|
return
|
820
821
|
|
821
822
|
@pyqtSlot()
|
822
823
|
def _refreshDocument(self) -> None:
|
823
824
|
"""Reload the content of the document."""
|
824
|
-
|
825
|
-
self.mainGui.saveDocument()
|
826
|
-
self.docViewer.reloadText()
|
825
|
+
self.docViewer.reloadDocumentRequest.emit()
|
827
826
|
return
|
828
827
|
|
829
828
|
##
|
@@ -854,22 +853,20 @@ class GuiDocViewFooter(QWidget):
|
|
854
853
|
logger.debug("Create: GuiDocViewFooter")
|
855
854
|
|
856
855
|
self.docViewer = docViewer
|
857
|
-
self.mainGui = docViewer.mainGui
|
858
856
|
|
859
857
|
# Internal Variables
|
860
858
|
self._docHandle = None
|
861
859
|
|
862
|
-
|
860
|
+
iPx = SHARED.theme.baseIconSize
|
863
861
|
hSp = CONFIG.pxInt(4)
|
862
|
+
mPx = CONFIG.pxInt(4)
|
864
863
|
|
865
864
|
# Main Widget Settings
|
866
865
|
self.setContentsMargins(0, 0, 0, 0)
|
867
866
|
self.setAutoFillBackground(True)
|
868
867
|
|
869
868
|
# Show/Hide Details
|
870
|
-
self.showHide =
|
871
|
-
self.showHide.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
872
|
-
self.showHide.setIconSize(QSize(fPx, fPx))
|
869
|
+
self.showHide = NIconToolButton(self, iPx)
|
873
870
|
self.showHide.clicked.connect(lambda: self.docViewer.togglePanelVisibility.emit())
|
874
871
|
self.showHide.setToolTip(self.tr("Show/Hide Viewer Panel"))
|
875
872
|
|
@@ -879,7 +876,7 @@ class GuiDocViewFooter(QWidget):
|
|
879
876
|
self.showComments.setCheckable(True)
|
880
877
|
self.showComments.setChecked(CONFIG.viewComments)
|
881
878
|
self.showComments.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
882
|
-
self.showComments.setIconSize(QSize(
|
879
|
+
self.showComments.setIconSize(QSize(iPx, iPx))
|
883
880
|
self.showComments.toggled.connect(self._doToggleComments)
|
884
881
|
self.showComments.setToolTip(self.tr("Show Comments"))
|
885
882
|
|
@@ -889,7 +886,7 @@ class GuiDocViewFooter(QWidget):
|
|
889
886
|
self.showSynopsis.setCheckable(True)
|
890
887
|
self.showSynopsis.setChecked(CONFIG.viewSynopsis)
|
891
888
|
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
892
|
-
self.showSynopsis.setIconSize(QSize(
|
889
|
+
self.showSynopsis.setIconSize(QSize(iPx, iPx))
|
893
890
|
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
|
894
891
|
self.showSynopsis.setToolTip(self.tr("Show Synopsis Comments"))
|
895
892
|
|
@@ -909,10 +906,9 @@ class GuiDocViewFooter(QWidget):
|
|
909
906
|
|
910
907
|
# Fix Margins and Size
|
911
908
|
# This is needed for high DPI systems. See issue #499.
|
912
|
-
cM = CONFIG.pxInt(8)
|
913
909
|
self.setContentsMargins(0, 0, 0, 0)
|
914
|
-
self.outerBox.setContentsMargins(
|
915
|
-
self.setMinimumHeight(
|
910
|
+
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
|
911
|
+
self.setMinimumHeight(iPx + 2*mPx)
|
916
912
|
|
917
913
|
# Fix the Colours
|
918
914
|
self.updateTheme()
|
@@ -935,12 +931,7 @@ class GuiDocViewFooter(QWidget):
|
|
935
931
|
self.showComments.setIcon(bulletIcon)
|
936
932
|
self.showSynopsis.setIcon(bulletIcon)
|
937
933
|
|
938
|
-
|
939
|
-
buttonStyle = (
|
940
|
-
"QToolButton {{border: none; background: transparent;}} "
|
941
|
-
"QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
|
942
|
-
).format(colText.red(), colText.green(), colText.blue())
|
943
|
-
|
934
|
+
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
944
935
|
self.showHide.setStyleSheet(buttonStyle)
|
945
936
|
self.showComments.setStyleSheet(buttonStyle)
|
946
937
|
self.showSynopsis.setStyleSheet(buttonStyle)
|
@@ -34,10 +34,11 @@ from PyQt5.QtWidgets import (
|
|
34
34
|
)
|
35
35
|
|
36
36
|
from novelwriter import CONFIG, SHARED
|
37
|
-
from novelwriter.enum import nwDocMode, nwItemClass
|
38
37
|
from novelwriter.common import checkInt
|
39
38
|
from novelwriter.constants import nwHeaders, nwLabels, nwLists, trConst
|
40
39
|
from novelwriter.core.index import IndexHeading, IndexItem
|
40
|
+
from novelwriter.enum import nwDocMode, nwItemClass
|
41
|
+
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
|
41
42
|
|
42
43
|
logger = logging.getLogger(__name__)
|
43
44
|
|
@@ -99,35 +100,14 @@ class GuiDocViewerPanel(QWidget):
|
|
99
100
|
|
100
101
|
def updateTheme(self, updateTabs: bool = True) -> None:
|
101
102
|
"""Update theme elements."""
|
102
|
-
qPalette = self.palette()
|
103
|
-
mPx = CONFIG.pxInt(2)
|
104
|
-
vPx = CONFIG.pxInt(4)
|
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
103
|
self.optsButton.setIcon(SHARED.theme.getIcon("menu"))
|
116
|
-
self.optsButton.setStyleSheet(
|
117
|
-
|
118
|
-
styleSheet = (
|
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())
|
123
|
-
self.mainTabs.setStyleSheet(styleSheet)
|
104
|
+
self.optsButton.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON))
|
105
|
+
self.mainTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
|
124
106
|
self.updateHandle(self._lastHandle)
|
125
|
-
|
126
107
|
if updateTabs:
|
127
108
|
self.tabBackRefs.updateTheme()
|
128
109
|
for tab in self.kwTabs.values():
|
129
110
|
tab.updateTheme()
|
130
|
-
|
131
111
|
return
|
132
112
|
|
133
113
|
def openProjectTasks(self) -> None:
|