novelWriter 2.3rc1__py3-none-any.whl → 2.4__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.
Files changed (125) hide show
  1. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/METADATA +5 -6
  2. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/RECORD +119 -109
  3. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/WHEEL +1 -1
  4. novelWriter-2.4.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +17 -10
  6. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  7. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  8. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  9. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  10. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  11. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  12. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  13. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  16. novelwriter/assets/i18n/project_nl_NL.json +11 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  18. novelwriter/assets/icons/none.svg +4 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
  20. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
  21. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
  22. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
  23. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
  25. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +4 -0
  28. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
  29. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
  30. novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
  31. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
  32. novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
  33. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  34. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  38. novelwriter/assets/syntax/default_dark.conf +1 -0
  39. novelwriter/assets/syntax/default_light.conf +1 -0
  40. novelwriter/assets/syntax/grey_dark.conf +1 -0
  41. novelwriter/assets/syntax/grey_light.conf +1 -0
  42. novelwriter/assets/syntax/light_owl.conf +1 -0
  43. novelwriter/assets/syntax/night_owl.conf +1 -0
  44. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  45. novelwriter/assets/syntax/solarized_light.conf +1 -0
  46. novelwriter/assets/syntax/tango.conf +23 -0
  47. novelwriter/assets/syntax/tomorrow.conf +1 -0
  48. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  49. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  50. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  51. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  52. novelwriter/assets/text/credits_en.htm +25 -23
  53. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  54. novelwriter/common.py +12 -4
  55. novelwriter/config.py +47 -16
  56. novelwriter/constants.py +5 -6
  57. novelwriter/core/buildsettings.py +64 -44
  58. novelwriter/core/coretools.py +97 -13
  59. novelwriter/core/docbuild.py +74 -7
  60. novelwriter/core/document.py +24 -3
  61. novelwriter/core/index.py +31 -112
  62. novelwriter/core/project.py +11 -15
  63. novelwriter/core/projectxml.py +3 -2
  64. novelwriter/core/sessions.py +2 -2
  65. novelwriter/core/spellcheck.py +3 -3
  66. novelwriter/core/status.py +6 -5
  67. novelwriter/core/storage.py +16 -6
  68. novelwriter/core/tohtml.py +22 -25
  69. novelwriter/core/tokenizer.py +417 -237
  70. novelwriter/core/tomd.py +17 -8
  71. novelwriter/core/toodt.py +386 -351
  72. novelwriter/core/tree.py +8 -8
  73. novelwriter/dialogs/about.py +10 -12
  74. novelwriter/dialogs/docmerge.py +17 -14
  75. novelwriter/dialogs/docsplit.py +20 -19
  76. novelwriter/dialogs/editlabel.py +5 -4
  77. novelwriter/dialogs/preferences.py +32 -40
  78. novelwriter/dialogs/projectsettings.py +31 -28
  79. novelwriter/dialogs/quotes.py +10 -9
  80. novelwriter/dialogs/wordlist.py +18 -15
  81. novelwriter/enum.py +17 -14
  82. novelwriter/error.py +14 -12
  83. novelwriter/extensions/circularprogress.py +12 -8
  84. novelwriter/extensions/configlayout.py +23 -3
  85. novelwriter/extensions/modified.py +51 -2
  86. novelwriter/extensions/pagedsidebar.py +16 -14
  87. novelwriter/extensions/simpleprogress.py +3 -1
  88. novelwriter/extensions/statusled.py +3 -1
  89. novelwriter/extensions/switch.py +10 -9
  90. novelwriter/extensions/switchbox.py +14 -13
  91. novelwriter/extensions/versioninfo.py +1 -1
  92. novelwriter/gui/doceditor.py +433 -496
  93. novelwriter/gui/dochighlight.py +54 -33
  94. novelwriter/gui/docviewer.py +162 -175
  95. novelwriter/gui/docviewerpanel.py +20 -37
  96. novelwriter/gui/editordocument.py +15 -4
  97. novelwriter/gui/itemdetails.py +51 -54
  98. novelwriter/gui/mainmenu.py +37 -17
  99. novelwriter/gui/noveltree.py +31 -37
  100. novelwriter/gui/outline.py +120 -98
  101. novelwriter/gui/projtree.py +114 -112
  102. novelwriter/gui/search.py +362 -0
  103. novelwriter/gui/sidebar.py +36 -45
  104. novelwriter/gui/statusbar.py +14 -14
  105. novelwriter/gui/theme.py +116 -34
  106. novelwriter/guimain.py +216 -207
  107. novelwriter/shared.py +31 -6
  108. novelwriter/text/counting.py +138 -0
  109. novelwriter/tools/dictionaries.py +15 -14
  110. novelwriter/tools/lipsum.py +20 -17
  111. novelwriter/tools/manusbuild.py +43 -35
  112. novelwriter/tools/manuscript.py +381 -104
  113. novelwriter/tools/manussettings.py +263 -125
  114. novelwriter/tools/noveldetails.py +21 -19
  115. novelwriter/tools/welcome.py +59 -57
  116. novelwriter/tools/writingstats.py +61 -55
  117. novelwriter/types.py +90 -0
  118. novelWriter-2.3rc1.dist-info/entry_points.txt +0 -5
  119. novelwriter/core/__init__.py +0 -3
  120. novelwriter/dialogs/__init__.py +0 -3
  121. novelwriter/extensions/__init__.py +0 -3
  122. novelwriter/gui/__init__.py +0 -3
  123. novelwriter/tools/__init__.py +0 -3
  124. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/LICENSE.md +0 -0
  125. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/top_level.txt +0 -0
@@ -29,27 +29,29 @@ 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
- from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
33
+ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, Qt, QUrl
35
34
  from PyQt5.QtGui import (
36
35
  QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
37
36
  QTextOption
38
37
  )
39
38
  from PyQt5.QtWidgets import (
40
- QAction, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser, QToolButton,
41
- QWidget, qApp
39
+ QAction, QApplication, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser,
40
+ QToolButton, QWidget
42
41
  )
43
42
 
44
43
  from novelwriter import CONFIG, SHARED
44
+ from novelwriter.common import cssCol
45
+ from novelwriter.constants import nwHeaders, nwUnicode
46
+ from novelwriter.core.tohtml import ToHtml
45
47
  from novelwriter.enum import nwItemType, nwDocAction, nwDocMode
46
48
  from novelwriter.error import logException
47
- from novelwriter.constants import nwUnicode
48
- from novelwriter.core.tohtml import ToHtml
49
49
  from novelwriter.extensions.eventfilters import WheelEventFilter
50
-
51
- if TYPE_CHECKING: # pragma: no cover
52
- from novelwriter.guimain import GuiMain
50
+ from novelwriter.extensions.modified import NIconToolButton
51
+ from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
52
+ from novelwriter.types import (
53
+ QtAlignCenterTop, QtAlignJustify, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
54
+ )
53
55
 
54
56
  logger = logging.getLogger(__name__)
55
57
 
@@ -58,17 +60,16 @@ class GuiDocViewer(QTextBrowser):
58
60
 
59
61
  documentLoaded = pyqtSignal(str)
60
62
  loadDocumentTagRequest = pyqtSignal(str, Enum)
63
+ closeDocumentRequest = pyqtSignal()
64
+ reloadDocumentRequest = pyqtSignal()
61
65
  togglePanelVisibility = pyqtSignal()
62
66
  requestProjectItemSelected = pyqtSignal(str, bool)
63
67
 
64
- def __init__(self, mainGui: GuiMain) -> None:
65
- super().__init__(parent=mainGui)
68
+ def __init__(self, parent: QWidget) -> None:
69
+ super().__init__(parent=parent)
66
70
 
67
71
  logger.debug("Create: GuiDocViewer")
68
72
 
69
- # Class Variables
70
- self.mainGui = mainGui
71
-
72
73
  # Internal Variables
73
74
  self._docHandle = None
74
75
 
@@ -128,7 +129,7 @@ class GuiDocViewer(QTextBrowser):
128
129
  self.clear()
129
130
  self.setSearchPaths([""])
130
131
  self._docHandle = None
131
- self.docHeader.setTitleFromHandle(self._docHandle)
132
+ self.docHeader.clearHeader()
132
133
  return
133
134
 
134
135
  def updateTheme(self) -> None:
@@ -166,7 +167,7 @@ class GuiDocViewer(QTextBrowser):
166
167
  self.document().setDocumentMargin(0)
167
168
  options = QTextOption()
168
169
  if CONFIG.doJustify:
169
- options.setAlignment(Qt.AlignmentFlag.AlignJustify)
170
+ options.setAlignment(QtAlignJustify)
170
171
  self.document().setDefaultTextOption(options)
171
172
 
172
173
  # Scroll bars
@@ -184,8 +185,7 @@ class GuiDocViewer(QTextBrowser):
184
185
  self.setTabStopDistance(CONFIG.getTabWidth())
185
186
 
186
187
  # If we have a document open, we should reload it in case the font changed
187
- if self._docHandle is not None:
188
- self.reloadText()
188
+ self.reloadText()
189
189
 
190
190
  return
191
191
 
@@ -197,12 +197,15 @@ class GuiDocViewer(QTextBrowser):
197
197
  return False
198
198
 
199
199
  logger.debug("Generating preview for item '%s'", tHandle)
200
- qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
200
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
201
201
 
202
202
  sPos = self.verticalScrollBar().value()
203
203
  aDoc = ToHtml(SHARED.project)
204
- aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis)
205
- aDoc.setLinkHeaders(True)
204
+ aDoc.setPreview(True)
205
+ aDoc.setKeywords(True)
206
+ aDoc.setComments(CONFIG.viewComments)
207
+ aDoc.setSynopsis(CONFIG.viewSynopsis)
208
+ aDoc.setLinkHeadings(True)
206
209
 
207
210
  # Be extra careful here to prevent crashes when first opening a
208
211
  # project as a crash here leaves no way of recovering.
@@ -216,7 +219,7 @@ class GuiDocViewer(QTextBrowser):
216
219
  logger.error("Failed to generate preview for document with handle '%s'", tHandle)
217
220
  logException()
218
221
  self.setText(self.tr("An error occurred while generating the preview."))
219
- qApp.restoreOverrideCursor()
222
+ QApplication.restoreOverrideCursor()
220
223
  return False
221
224
 
222
225
  # Refresh the tab stops
@@ -239,13 +242,17 @@ class GuiDocViewer(QTextBrowser):
239
242
 
240
243
  self._docHandle = tHandle
241
244
  SHARED.project.data.setLastHandle(tHandle, "viewer")
242
- self.docHeader.setTitleFromHandle(self._docHandle)
245
+ self.docHeader.setHandle(tHandle)
246
+ self.docHeader.setOutline({
247
+ sTitle: (hItem.title, nwHeaders.H_LEVEL.get(hItem.level, 0))
248
+ for sTitle, hItem in SHARED.project.index.iterItemHeadings(tHandle)
249
+ })
243
250
  self.updateDocMargins()
244
251
 
245
252
  # Since we change the content while it may still be rendering, we mark
246
253
  # the document dirty again to make sure it's re-rendered properly.
247
254
  self.redrawText()
248
- qApp.restoreOverrideCursor()
255
+ QApplication.restoreOverrideCursor()
249
256
  self.documentLoaded.emit(tHandle)
250
257
 
251
258
  return True
@@ -281,13 +288,6 @@ class GuiDocViewer(QTextBrowser):
281
288
  return False
282
289
  return True
283
290
 
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
291
  def clearNavHistory(self) -> None:
292
292
  """Clear the navigation history."""
293
293
  self.docHistory.clear()
@@ -340,11 +340,19 @@ class GuiDocViewer(QTextBrowser):
340
340
  @pyqtSlot(str)
341
341
  def updateDocInfo(self, tHandle: str) -> None:
342
342
  """Update the header title bar if needed."""
343
- if tHandle == self._docHandle:
344
- self.docHeader.setTitleFromHandle(self._docHandle)
343
+ if tHandle and tHandle == self._docHandle:
344
+ self.docHeader.setHandle(tHandle)
345
345
  self.updateDocMargins()
346
346
  return
347
347
 
348
+ @pyqtSlot(str)
349
+ def navigateTo(self, anchor: str) -> None:
350
+ """Go to a specific #link in the document."""
351
+ if isinstance(anchor, str) and anchor.startswith("#"):
352
+ logger.debug("Moving to anchor '%s'", anchor)
353
+ self.setSource(QUrl(anchor))
354
+ return
355
+
348
356
  ##
349
357
  # Private Slots
350
358
  ##
@@ -404,7 +412,7 @@ class GuiDocViewer(QTextBrowser):
404
412
  ctxMenu.addAction(mnuSelPara)
405
413
 
406
414
  # Open the context menu
407
- ctxMenu.exec_(self.viewport().mapToGlobal(point))
415
+ ctxMenu.exec(self.viewport().mapToGlobal(point))
408
416
  ctxMenu.deleteLater()
409
417
 
410
418
  return
@@ -446,8 +454,8 @@ class GuiDocViewer(QTextBrowser):
446
454
  posE = cursor.selectionEnd()
447
455
  selTxt = cursor.selectedText()
448
456
  if selTxt.startswith(nwUnicode.U_PSEP):
449
- cursor.setPosition(posS+1, QTextCursor.MoveMode.MoveAnchor)
450
- cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
457
+ cursor.setPosition(posS+1, QtMoveAnchor)
458
+ cursor.setPosition(posE, QtKeepAnchor)
451
459
 
452
460
  self.setTextCursor(cursor)
453
461
 
@@ -463,53 +471,25 @@ class GuiDocViewer(QTextBrowser):
463
471
  """Generate an appropriate style sheet for the document viewer,
464
472
  based on the current syntax highlighter theme.
465
473
  """
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
474
- styleSheet = (
475
- "body {{"
476
- " color: rgb({tColR}, {tColG}, {tColB});"
477
- "}}\n"
478
- "h1, h2, h3, h4 {{"
479
- " color: rgb({hColR}, {hColG}, {hColB});"
480
- "}}\n"
481
- "a {{"
482
- " color: rgb({aColR}, {aColG}, {aColB});"
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"
502
- ).format(
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(),
474
+ colText = cssCol(SHARED.theme.colText)
475
+ colHead = cssCol(SHARED.theme.colHead)
476
+ colVals = cssCol(SHARED.theme.colVal)
477
+ colMark = cssCol(SHARED.theme.colMark)
478
+ colKeys = cssCol(SHARED.theme.colKey)
479
+ colOpts = cssCol(SHARED.theme.colOpt)
480
+ colHide = cssCol(SHARED.theme.colHidden)
481
+ colMods = cssCol(SHARED.theme.colMod)
482
+ self.document().setDefaultStyleSheet(
483
+ f"body {{color: {colText};}}\n"
484
+ f"h1, h2, h3, h4 {{color: {colHead};}}\n"
485
+ f"a {{color: {colVals};}}\n"
486
+ f"mark {{background-color: {colMark};}}\n"
487
+ f".tags {{color: {colKeys};}}\n"
488
+ f".optional {{color: {colOpts};}}\n"
489
+ f".comment {{color: {colHide};}}\n"
490
+ f".synopsis {{color: {colMods};}}\n"
491
+ ".title {text-align: center;}\n"
511
492
  )
512
- self.document().setDefaultStyleSheet(styleSheet)
513
493
 
514
494
  return
515
495
 
@@ -644,84 +624,81 @@ class GuiDocViewHeader(QWidget):
644
624
  logger.debug("Create: GuiDocViewHeader")
645
625
 
646
626
  self.docViewer = docViewer
647
- self.mainGui = docViewer.mainGui
648
627
 
649
628
  # Internal Variables
650
629
  self._docHandle = None
630
+ self._docOutline: dict[str, tuple[str, int]] = {}
651
631
 
652
- fPx = int(0.9*SHARED.theme.fontPixelSize)
653
- hSp = CONFIG.pxInt(6)
632
+ iPx = SHARED.theme.baseIconHeight
633
+ iSz = SHARED.theme.baseIconSize
634
+ mPx = CONFIG.pxInt(4)
654
635
 
655
636
  # Main Widget Settings
656
637
  self.setAutoFillBackground(True)
657
638
 
658
639
  # Title Label
659
- self.docTitle = QLabel()
660
- self.docTitle.setText("")
661
- self.docTitle.setIndent(0)
662
- self.docTitle.setMargin(0)
663
- self.docTitle.setContentsMargins(0, 0, 0, 0)
664
- self.docTitle.setAutoFillBackground(True)
665
- self.docTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
666
- self.docTitle.setFixedHeight(fPx)
667
-
668
- lblFont = self.docTitle.font()
640
+ self.itemTitle = QLabel(self)
641
+ self.itemTitle.setText("")
642
+ self.itemTitle.setIndent(0)
643
+ self.itemTitle.setMargin(0)
644
+ self.itemTitle.setContentsMargins(0, 0, 0, 0)
645
+ self.itemTitle.setAutoFillBackground(True)
646
+ self.itemTitle.setAlignment(QtAlignCenterTop)
647
+ self.itemTitle.setFixedHeight(iPx)
648
+
649
+ lblFont = self.itemTitle.font()
669
650
  lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
670
- self.docTitle.setFont(lblFont)
651
+ self.itemTitle.setFont(lblFont)
652
+
653
+ # Other Widgets
654
+ self.outlineMenu = QMenu(self)
671
655
 
672
656
  # Buttons
673
- self.backButton = QToolButton(self)
674
- self.backButton.setContentsMargins(0, 0, 0, 0)
675
- self.backButton.setIconSize(QSize(fPx, fPx))
676
- self.backButton.setFixedSize(fPx, fPx)
677
- self.backButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
657
+ self.outlineButton = NIconToolButton(self, iSz)
658
+ self.outlineButton.setVisible(False)
659
+ self.outlineButton.setToolTip(self.tr("Outline"))
660
+ self.outlineButton.setMenu(self.outlineMenu)
661
+
662
+ self.backButton = NIconToolButton(self, iSz)
678
663
  self.backButton.setVisible(False)
679
664
  self.backButton.setToolTip(self.tr("Go Backward"))
680
665
  self.backButton.clicked.connect(self.docViewer.navBackward)
681
666
 
682
- self.forwardButton = QToolButton(self)
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)
667
+ self.forwardButton = NIconToolButton(self, iSz)
687
668
  self.forwardButton.setVisible(False)
688
669
  self.forwardButton.setToolTip(self.tr("Go Forward"))
689
670
  self.forwardButton.clicked.connect(self.docViewer.navForward)
690
671
 
691
- self.refreshButton = QToolButton(self)
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)
672
+ self.refreshButton = NIconToolButton(self, iSz)
696
673
  self.refreshButton.setVisible(False)
697
674
  self.refreshButton.setToolTip(self.tr("Reload"))
698
675
  self.refreshButton.clicked.connect(self._refreshDocument)
699
676
 
700
- self.closeButton = QToolButton(self)
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)
677
+ self.closeButton = NIconToolButton(self, iSz)
705
678
  self.closeButton.setVisible(False)
706
679
  self.closeButton.setToolTip(self.tr("Close"))
707
680
  self.closeButton.clicked.connect(self._closeDocument)
708
681
 
709
682
  # Assemble Layout
710
683
  self.outerBox = QHBoxLayout()
711
- self.outerBox.setSpacing(hSp)
684
+ self.outerBox.addWidget(self.outlineButton, 0)
712
685
  self.outerBox.addWidget(self.backButton, 0)
713
686
  self.outerBox.addWidget(self.forwardButton, 0)
714
- self.outerBox.addWidget(self.docTitle, 1)
687
+ self.outerBox.addSpacing(mPx)
688
+ self.outerBox.addWidget(self.itemTitle, 1)
689
+ self.outerBox.addSpacing(mPx)
690
+ self.outerBox.addSpacing(iPx)
715
691
  self.outerBox.addWidget(self.refreshButton, 0)
716
692
  self.outerBox.addWidget(self.closeButton, 0)
693
+ self.outerBox.setSpacing(0)
694
+
717
695
  self.setLayout(self.outerBox)
718
696
 
719
697
  # Fix Margins and Size
720
698
  # This is needed for high DPI systems. See issue #499.
721
- cM = CONFIG.pxInt(8)
722
699
  self.setContentsMargins(0, 0, 0, 0)
723
- self.outerBox.setContentsMargins(cM, cM, cM, cM)
724
- self.setMinimumHeight(fPx + 2*cM)
700
+ self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
701
+ self.setMinimumHeight(iPx + 2*mPx)
725
702
 
726
703
  # Fix the Colours
727
704
  self.updateTheme()
@@ -734,19 +711,50 @@ class GuiDocViewHeader(QWidget):
734
711
  # Methods
735
712
  ##
736
713
 
737
- def updateTheme(self) -> None:
738
- """Update theme elements."""
739
- self.backButton.setIcon(SHARED.theme.getIcon("backward"))
740
- self.forwardButton.setIcon(SHARED.theme.getIcon("forward"))
741
- self.refreshButton.setIcon(SHARED.theme.getIcon("refresh"))
742
- self.closeButton.setIcon(SHARED.theme.getIcon("close"))
714
+ def clearHeader(self) -> None:
715
+ """Clear the header."""
716
+ self._docHandle = None
717
+ self._docOutline = {}
743
718
 
744
- colText = SHARED.theme.colText
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())
719
+ self.itemTitle.setText("")
720
+ self.outlineMenu.clear()
721
+ self.outlineButton.setVisible(False)
722
+ self.backButton.setVisible(False)
723
+ self.forwardButton.setVisible(False)
724
+ self.closeButton.setVisible(False)
725
+ self.refreshButton.setVisible(False)
726
+ return
749
727
 
728
+ def setOutline(self, data: dict[str, tuple[str, int]]) -> None:
729
+ """Set the document outline dataset."""
730
+ tHandle = self._docHandle
731
+ if data != self._docOutline and tHandle:
732
+ self.outlineMenu.clear()
733
+ entries = []
734
+ minLevel = 5
735
+ for title, (text, level) in data.items():
736
+ if title != "T0000":
737
+ entries.append((title, text, level))
738
+ minLevel = min(minLevel, level)
739
+ for title, text, level in entries[:30]:
740
+ indent = " "*(level - minLevel)
741
+ action = self.outlineMenu.addAction(f"{indent}{text}")
742
+ action.triggered.connect(
743
+ lambda _, title=title: self.docViewer.navigateTo(f"#{tHandle}:{title}")
744
+ )
745
+ self._docOutline = data
746
+ return
747
+
748
+ def updateTheme(self) -> None:
749
+ """Update theme elements."""
750
+ self.outlineButton.setThemeIcon("list")
751
+ self.backButton.setThemeIcon("backward")
752
+ self.forwardButton.setThemeIcon("forward")
753
+ self.refreshButton.setThemeIcon("refresh")
754
+ self.closeButton.setThemeIcon("close")
755
+
756
+ buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
757
+ self.outlineButton.setStyleSheet(buttonStyle)
750
758
  self.backButton.setStyleSheet(buttonStyle)
751
759
  self.forwardButton.setStyleSheet(buttonStyle)
752
760
  self.refreshButton.setStyleSheet(buttonStyle)
@@ -765,38 +773,25 @@ class GuiDocViewHeader(QWidget):
765
773
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
766
774
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
767
775
  self.setPalette(palette)
768
- self.docTitle.setPalette(palette)
776
+ self.itemTitle.setPalette(palette)
769
777
  return
770
778
 
771
- def setTitleFromHandle(self, tHandle: str | None) -> None:
779
+ def setHandle(self, tHandle: str) -> None:
772
780
  """Sets the document title from the handle, or alternatively,
773
781
  set the whole document path.
774
782
  """
775
783
  self._docHandle = tHandle
776
- if tHandle is None:
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
784
+
785
785
  if CONFIG.showFullPath:
786
- tTitle = []
787
- tTree = pTree.getItemPath(tHandle)
788
- for aHandle in reversed(tTree):
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))
786
+ self.itemTitle.setText(f" {nwUnicode.U_RSAQUO} ".join(reversed(
787
+ [name for name in SHARED.project.tree.getItemPath(tHandle, asName=True)]
788
+ )))
794
789
  else:
795
- if nwItem := pTree[tHandle]:
796
- self.docTitle.setText(nwItem.itemName)
790
+ self.itemTitle.setText(i.itemName if (i := SHARED.project.tree[tHandle]) else "")
797
791
 
798
792
  self.backButton.setVisible(True)
799
793
  self.forwardButton.setVisible(True)
794
+ self.outlineButton.setVisible(True)
800
795
  self.closeButton.setVisible(True)
801
796
  self.refreshButton.setVisible(True)
802
797
 
@@ -815,15 +810,14 @@ class GuiDocViewHeader(QWidget):
815
810
  @pyqtSlot()
816
811
  def _closeDocument(self) -> None:
817
812
  """Trigger the close editor/viewer on the main window."""
818
- self.mainGui.closeDocViewer()
813
+ self.clearHeader()
814
+ self.docViewer.closeDocumentRequest.emit()
819
815
  return
820
816
 
821
817
  @pyqtSlot()
822
818
  def _refreshDocument(self) -> None:
823
819
  """Reload the content of the document."""
824
- if self.docViewer.docHandle == self.mainGui.docEditor.docHandle:
825
- self.mainGui.saveDocument()
826
- self.docViewer.reloadText()
820
+ self.docViewer.reloadDocumentRequest.emit()
827
821
  return
828
822
 
829
823
  ##
@@ -834,7 +828,7 @@ class GuiDocViewHeader(QWidget):
834
828
  """Capture a click on the title and ensure that the item is
835
829
  selected in the project tree.
836
830
  """
837
- if event.button() == Qt.MouseButton.LeftButton:
831
+ if event.button() == QtMouseLeft:
838
832
  self.docViewer.requestProjectItemSelected.emit(self._docHandle, True)
839
833
  return
840
834
 
@@ -854,22 +848,21 @@ class GuiDocViewFooter(QWidget):
854
848
  logger.debug("Create: GuiDocViewFooter")
855
849
 
856
850
  self.docViewer = docViewer
857
- self.mainGui = docViewer.mainGui
858
851
 
859
852
  # Internal Variables
860
853
  self._docHandle = None
861
854
 
862
- fPx = int(0.9*SHARED.theme.fontPixelSize)
855
+ iPx = SHARED.theme.baseIconHeight
856
+ iSz = SHARED.theme.baseIconSize
863
857
  hSp = CONFIG.pxInt(4)
858
+ mPx = CONFIG.pxInt(4)
864
859
 
865
860
  # Main Widget Settings
866
861
  self.setContentsMargins(0, 0, 0, 0)
867
862
  self.setAutoFillBackground(True)
868
863
 
869
864
  # Show/Hide Details
870
- self.showHide = QToolButton(self)
871
- self.showHide.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
872
- self.showHide.setIconSize(QSize(fPx, fPx))
865
+ self.showHide = NIconToolButton(self, iSz)
873
866
  self.showHide.clicked.connect(lambda: self.docViewer.togglePanelVisibility.emit())
874
867
  self.showHide.setToolTip(self.tr("Show/Hide Viewer Panel"))
875
868
 
@@ -879,7 +872,7 @@ class GuiDocViewFooter(QWidget):
879
872
  self.showComments.setCheckable(True)
880
873
  self.showComments.setChecked(CONFIG.viewComments)
881
874
  self.showComments.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
882
- self.showComments.setIconSize(QSize(fPx, fPx))
875
+ self.showComments.setIconSize(iSz)
883
876
  self.showComments.toggled.connect(self._doToggleComments)
884
877
  self.showComments.setToolTip(self.tr("Show Comments"))
885
878
 
@@ -889,7 +882,7 @@ class GuiDocViewFooter(QWidget):
889
882
  self.showSynopsis.setCheckable(True)
890
883
  self.showSynopsis.setChecked(CONFIG.viewSynopsis)
891
884
  self.showSynopsis.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
892
- self.showSynopsis.setIconSize(QSize(fPx, fPx))
885
+ self.showSynopsis.setIconSize(iSz)
893
886
  self.showSynopsis.toggled.connect(self._doToggleSynopsis)
894
887
  self.showSynopsis.setToolTip(self.tr("Show Synopsis Comments"))
895
888
 
@@ -909,10 +902,9 @@ class GuiDocViewFooter(QWidget):
909
902
 
910
903
  # Fix Margins and Size
911
904
  # This is needed for high DPI systems. See issue #499.
912
- cM = CONFIG.pxInt(8)
913
905
  self.setContentsMargins(0, 0, 0, 0)
914
- self.outerBox.setContentsMargins(cM, cM, cM, cM)
915
- self.setMinimumHeight(fPx + 2*cM)
906
+ self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
907
+ self.setMinimumHeight(iPx + 2*mPx)
916
908
 
917
909
  # Fix the Colours
918
910
  self.updateTheme()
@@ -931,16 +923,11 @@ class GuiDocViewFooter(QWidget):
931
923
  fPx = int(0.9*SHARED.theme.fontPixelSize)
932
924
  bulletIcon = SHARED.theme.getToggleIcon("bullet", (fPx, fPx))
933
925
 
934
- self.showHide.setIcon(SHARED.theme.getIcon("panel"))
926
+ self.showHide.setThemeIcon("panel")
935
927
  self.showComments.setIcon(bulletIcon)
936
928
  self.showSynopsis.setIcon(bulletIcon)
937
929
 
938
- colText = SHARED.theme.colText
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
-
930
+ buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
944
931
  self.showHide.setStyleSheet(buttonStyle)
945
932
  self.showComments.setStyleSheet(buttonStyle)
946
933
  self.showSynopsis.setStyleSheet(buttonStyle)