novelWriter 2.5b1__py3-none-any.whl → 2.5.1__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 (78) hide show
  1. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/RECORD +77 -75
  3. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +3 -3
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  13. novelwriter/assets/i18n/nw_pl_PL.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_pl_PL.json +116 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +74 -74
  18. novelwriter/assets/manual.pdf +0 -0
  19. novelwriter/assets/sample.zip +0 -0
  20. novelwriter/assets/text/credits_en.htm +52 -44
  21. novelwriter/assets/themes/cyberpunk_night.conf +1 -0
  22. novelwriter/assets/themes/default_dark.conf +1 -0
  23. novelwriter/assets/themes/default_light.conf +1 -0
  24. novelwriter/assets/themes/dracula.conf +1 -0
  25. novelwriter/assets/themes/solarized_dark.conf +1 -0
  26. novelwriter/assets/themes/solarized_light.conf +1 -0
  27. novelwriter/common.py +12 -3
  28. novelwriter/config.py +67 -15
  29. novelwriter/constants.py +8 -10
  30. novelwriter/core/buildsettings.py +5 -3
  31. novelwriter/core/coretools.py +3 -1
  32. novelwriter/core/docbuild.py +1 -0
  33. novelwriter/core/project.py +15 -4
  34. novelwriter/core/status.py +4 -1
  35. novelwriter/core/storage.py +6 -1
  36. novelwriter/core/tohtml.py +69 -29
  37. novelwriter/core/tokenizer.py +83 -14
  38. novelwriter/core/toodt.py +48 -21
  39. novelwriter/core/toqdoc.py +37 -21
  40. novelwriter/dialogs/about.py +10 -15
  41. novelwriter/dialogs/docmerge.py +16 -16
  42. novelwriter/dialogs/docsplit.py +16 -16
  43. novelwriter/dialogs/editlabel.py +6 -8
  44. novelwriter/dialogs/preferences.py +106 -93
  45. novelwriter/dialogs/projectsettings.py +16 -20
  46. novelwriter/dialogs/quotes.py +9 -5
  47. novelwriter/dialogs/wordlist.py +6 -6
  48. novelwriter/enum.py +4 -5
  49. novelwriter/extensions/configlayout.py +38 -4
  50. novelwriter/extensions/modified.py +22 -3
  51. novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
  52. novelwriter/extensions/statusled.py +39 -23
  53. novelwriter/gui/doceditor.py +22 -13
  54. novelwriter/gui/dochighlight.py +30 -39
  55. novelwriter/gui/docviewer.py +24 -15
  56. novelwriter/gui/docviewerpanel.py +7 -0
  57. novelwriter/gui/mainmenu.py +11 -11
  58. novelwriter/gui/outline.py +4 -3
  59. novelwriter/gui/projtree.py +85 -77
  60. novelwriter/gui/search.py +10 -1
  61. novelwriter/gui/statusbar.py +25 -29
  62. novelwriter/gui/theme.py +3 -0
  63. novelwriter/guimain.py +139 -124
  64. novelwriter/shared.py +19 -8
  65. novelwriter/text/patterns.py +113 -0
  66. novelwriter/tools/dictionaries.py +2 -8
  67. novelwriter/tools/lipsum.py +8 -12
  68. novelwriter/tools/manusbuild.py +9 -9
  69. novelwriter/tools/manuscript.py +10 -5
  70. novelwriter/tools/manussettings.py +7 -3
  71. novelwriter/tools/noveldetails.py +10 -10
  72. novelwriter/tools/welcome.py +19 -10
  73. novelwriter/tools/writingstats.py +3 -3
  74. novelwriter/types.py +5 -2
  75. novelwriter/extensions/simpleprogress.py +0 -53
  76. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/LICENSE.md +0 -0
  77. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/entry_points.txt +0 -0
  78. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/top_level.txt +0 -0
@@ -29,7 +29,7 @@ import logging
29
29
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
30
30
  from PyQt5.QtGui import QCloseEvent, QColor
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QApplication, QColorDialog, QDialog, QDialogButtonBox,
32
+ QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
33
33
  QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
34
34
  QTreeWidgetItem, QVBoxLayout, QWidget
35
35
  )
@@ -40,7 +40,7 @@ from novelwriter.constants import nwLabels, trConst
40
40
  from novelwriter.core.status import NWStatus, StatusEntry
41
41
  from novelwriter.enum import nwStatusShape
42
42
  from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
43
- from novelwriter.extensions.modified import NComboBox, NIconToolButton
43
+ from novelwriter.extensions.modified import NComboBox, NDialog, NIconToolButton
44
44
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
45
45
  from novelwriter.extensions.switch import NSwitch
46
46
  from novelwriter.types import (
@@ -51,14 +51,14 @@ from novelwriter.types import (
51
51
  logger = logging.getLogger(__name__)
52
52
 
53
53
 
54
- class GuiProjectSettings(QDialog):
54
+ class GuiProjectSettings(NDialog):
55
55
 
56
56
  PAGE_SETTINGS = 0
57
57
  PAGE_STATUS = 1
58
58
  PAGE_IMPORT = 2
59
59
  PAGE_REPLACE = 3
60
60
 
61
- newProjectSettingsReady = pyqtSignal(bool)
61
+ newProjectSettingsReady = pyqtSignal()
62
62
 
63
63
  def __init__(self, parent: QWidget, gotoPage: int = PAGE_SETTINGS) -> None:
64
64
  super().__init__(parent=parent)
@@ -76,8 +76,8 @@ class GuiProjectSettings(QDialog):
76
76
 
77
77
  # Title
78
78
  self.titleLabel = NColourLabel(
79
- self.tr("Project Settings"), SHARED.theme.helpText,
80
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
79
+ self.tr("Project Settings"), self, color=SHARED.theme.helpText,
80
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
81
81
  )
82
82
 
83
83
  # SideBar
@@ -92,7 +92,7 @@ class GuiProjectSettings(QDialog):
92
92
  # Buttons
93
93
  self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogCancel, self)
94
94
  self.buttonBox.accepted.connect(self._doSave)
95
- self.buttonBox.rejected.connect(self.close)
95
+ self.buttonBox.rejected.connect(self.reject)
96
96
 
97
97
  # Content
98
98
  SHARED.project.countStatus()
@@ -147,7 +147,7 @@ class GuiProjectSettings(QDialog):
147
147
  """Capture the user closing the window and save settings."""
148
148
  self._saveSettings()
149
149
  event.accept()
150
- self.deleteLater()
150
+ self.softDelete()
151
151
  return
152
152
 
153
153
  ##
@@ -175,7 +175,7 @@ class GuiProjectSettings(QDialog):
175
175
  projAuthor = self.settingsPage.projAuthor.text()
176
176
  projLang = self.settingsPage.projLang.currentData()
177
177
  spellLang = self.settingsPage.spellLang.currentData()
178
- doBackup = not self.settingsPage.doBackup.isChecked()
178
+ doBackup = not self.settingsPage.noBackup.isChecked()
179
179
 
180
180
  project.data.setName(projName)
181
181
  project.data.setAuthor(projAuthor)
@@ -183,23 +183,19 @@ class GuiProjectSettings(QDialog):
183
183
  project.data.setSpellLang(spellLang)
184
184
  project.setProjectLang(projLang)
185
185
 
186
- rebuildTrees = False
187
-
188
186
  if self.statusPage.changed:
189
187
  logger.debug("Updating status labels")
190
188
  project.data.itemStatus.update(self.statusPage.getNewList())
191
- rebuildTrees = True
192
189
 
193
190
  if self.importPage.changed:
194
191
  logger.debug("Updating importance labels")
195
192
  project.data.itemImport.update(self.importPage.getNewList())
196
- rebuildTrees = True
197
193
 
198
194
  if self.replacePage.changed:
199
195
  logger.debug("Updating auto-replace settings")
200
196
  project.data.setAutoReplace(self.replacePage.getNewList())
201
197
 
202
- self.newProjectSettingsReady.emit(rebuildTrees)
198
+ self.newProjectSettingsReady.emit()
203
199
  QApplication.processEvents()
204
200
  self.close()
205
201
 
@@ -289,10 +285,10 @@ class _SettingsPage(NScrollableForm):
289
285
  self.spellLang.setCurrentIndex(idx)
290
286
 
291
287
  # Backup on Close
292
- self.doBackup = NSwitch(self)
293
- self.doBackup.setChecked(not data.doBackup)
288
+ self.noBackup = NSwitch(self)
289
+ self.noBackup.setChecked(not data.doBackup)
294
290
  self.addRow(
295
- self.tr("Disable backup on close"), self.doBackup,
291
+ self.tr("Disable backup on close"), self.noBackup,
296
292
  self.tr("Overrides main preferences.")
297
293
  )
298
294
 
@@ -345,7 +341,7 @@ class _StatusPage(NFixedPage):
345
341
 
346
342
  # Title
347
343
  self.pageTitle = NColourLabel(
348
- pageLabel, SHARED.theme.helpText, parent=self,
344
+ pageLabel, self, color=SHARED.theme.helpText,
349
345
  scale=NColourLabel.HEADER_SCALE
350
346
  )
351
347
 
@@ -637,8 +633,8 @@ class _ReplacePage(NFixedPage):
637
633
 
638
634
  # Title
639
635
  self.pageTitle = NColourLabel(
640
- self.tr("Text Auto-Replace for Preview and Build"),
641
- SHARED.theme.helpText, parent=self, scale=NColourLabel.HEADER_SCALE
636
+ self.tr("Text Auto-Replace for Preview and Build"), self,
637
+ color=SHARED.theme.helpText, scale=NColourLabel.HEADER_SCALE
642
638
  )
643
639
 
644
640
  # List Box
@@ -28,18 +28,22 @@ import logging
28
28
  from PyQt5.QtCore import QSize, pyqtSlot
29
29
  from PyQt5.QtGui import QFontMetrics
30
30
  from PyQt5.QtWidgets import (
31
- QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
31
+ QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
32
32
  QListWidgetItem, QVBoxLayout, QWidget
33
33
  )
34
34
 
35
35
  from novelwriter import CONFIG
36
36
  from novelwriter.constants import nwQuotes, trConst
37
- from novelwriter.types import QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk, QtUserRole
37
+ from novelwriter.extensions.modified import NDialog
38
+ from novelwriter.types import (
39
+ QtAccepted, QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk,
40
+ QtUserRole
41
+ )
38
42
 
39
43
  logger = logging.getLogger(__name__)
40
44
 
41
45
 
42
- class GuiQuoteSelect(QDialog):
46
+ class GuiQuoteSelect(NDialog):
43
47
 
44
48
  _selected = ""
45
49
 
@@ -126,8 +130,8 @@ class GuiQuoteSelect(QDialog):
126
130
  cls = GuiQuoteSelect(parent, current=current)
127
131
  cls.exec()
128
132
  quote = cls._selected
129
- accepted = cls.result() == QDialog.DialogCode.Accepted
130
- cls.deleteLater()
133
+ accepted = cls.result() == QtAccepted
134
+ cls.softDelete()
131
135
  return quote, accepted
132
136
 
133
137
  ##
@@ -30,7 +30,7 @@ from pathlib import Path
30
30
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
31
31
  from PyQt5.QtGui import QCloseEvent
32
32
  from PyQt5.QtWidgets import (
33
- QAbstractItemView, QApplication, QDialog, QDialogButtonBox, QFileDialog,
33
+ QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
34
34
  QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
35
35
  )
36
36
 
@@ -38,13 +38,13 @@ from novelwriter import CONFIG, SHARED
38
38
  from novelwriter.common import formatFileFilter
39
39
  from novelwriter.core.spellcheck import UserDictionary
40
40
  from novelwriter.extensions.configlayout import NColourLabel
41
- from novelwriter.extensions.modified import NIconToolButton
41
+ from novelwriter.extensions.modified import NDialog, NIconToolButton
42
42
  from novelwriter.types import QtDialogClose, QtDialogSave
43
43
 
44
44
  logger = logging.getLogger(__name__)
45
45
 
46
46
 
47
- class GuiWordList(QDialog):
47
+ class GuiWordList(NDialog):
48
48
 
49
49
  newWordListReady = pyqtSignal()
50
50
 
@@ -69,7 +69,7 @@ class GuiWordList(QDialog):
69
69
 
70
70
  # Header
71
71
  self.headLabel = NColourLabel(
72
- self.tr("Project Word List"), SHARED.theme.helpText, parent=self,
72
+ self.tr("Project Word List"), self, color=SHARED.theme.helpText,
73
73
  scale=NColourLabel.HEADER_SCALE
74
74
  )
75
75
 
@@ -109,7 +109,7 @@ class GuiWordList(QDialog):
109
109
  # Buttons
110
110
  self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogClose, self)
111
111
  self.buttonBox.accepted.connect(self._doSave)
112
- self.buttonBox.rejected.connect(self.close)
112
+ self.buttonBox.rejected.connect(self.reject)
113
113
 
114
114
  # Assemble
115
115
  self.outerBox = QVBoxLayout()
@@ -140,7 +140,7 @@ class GuiWordList(QDialog):
140
140
  """Capture the close event and perform cleanup."""
141
141
  self._saveGuiSettings()
142
142
  event.accept()
143
- self.deleteLater()
143
+ self.softDelete()
144
144
  return
145
145
 
146
146
  ##
novelwriter/enum.py CHANGED
@@ -148,12 +148,11 @@ class nwView(Enum):
148
148
  SEARCH = 4
149
149
 
150
150
 
151
- class nwWidget(Enum):
151
+ class nwFocus(Enum):
152
152
 
153
- TREE = 1
154
- EDITOR = 2
155
- VIEWER = 3
156
- OUTLINE = 4
153
+ TREE = 1
154
+ DOCUMENT = 2
155
+ OUTLINE = 3
157
156
 
158
157
 
159
158
  class nwOutline(Enum):
@@ -203,7 +203,7 @@ class NScrollableForm(QScrollArea):
203
203
 
204
204
  if helpText:
205
205
  qHelp = NColourLabel(
206
- str(helpText), color=self._helpCol, parent=self,
206
+ str(helpText), self, color=self._helpCol,
207
207
  scale=self._fontScale, wrap=True, indent=self._indent
208
208
  )
209
209
  labelBox = QVBoxLayout()
@@ -252,11 +252,20 @@ class NColourLabel(QLabel):
252
252
  HELP_SCALE = DEFAULT_SCALE
253
253
  HEADER_SCALE = 1.25
254
254
 
255
- def __init__(self, text: str, color: QColor | None = None, parent: QWidget | None = None,
256
- scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
257
- bold: bool = False) -> None:
255
+ _state = None
256
+
257
+ def __init__(
258
+ self, text: str, parent: QWidget, *,
259
+ color: QColor | None = None, faded: QColor | None = None,
260
+ scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
261
+ bold: bool = False
262
+ ) -> None:
258
263
  super().__init__(text, parent=parent)
259
264
 
265
+ default = self.palette().windowText().color()
266
+ self._color = color or default
267
+ self._faded = faded or default
268
+
260
269
  font = self.font()
261
270
  font.setPointSizeF(scale*font.pointSizeF())
262
271
  font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
@@ -268,7 +277,32 @@ class NColourLabel(QLabel):
268
277
  self.setFont(font)
269
278
  self.setIndent(indent)
270
279
  self.setWordWrap(wrap)
280
+ self.setColorState(True)
281
+
282
+ return
283
+
284
+ def setTextColors(self, *, color: QColor | None = None, faded: QColor | None = None) -> None:
285
+ """Set or update the text colours."""
286
+ self._color = color or self._color
287
+ self._faded = faded or self._faded
288
+ self._refeshTextColor()
289
+ return
290
+
291
+ def setColorState(self, state: bool) -> None:
292
+ """Change the colour state."""
293
+ if self._state is not state:
294
+ self._state = state
295
+ self._refeshTextColor()
296
+ return
271
297
 
298
+ def _refeshTextColor(self) -> None:
299
+ """Refresh the colour of the text on the label."""
300
+ palette = self.palette()
301
+ palette.setColor(
302
+ QPalette.ColorRole.WindowText,
303
+ self._color if self._state else self._faded,
304
+ )
305
+ self.setPalette(palette)
272
306
  return
273
307
 
274
308
 
@@ -30,7 +30,7 @@ from __future__ import annotations
30
30
  from enum import Enum
31
31
  from typing import TYPE_CHECKING
32
32
 
33
- from PyQt5.QtCore import QSize, Qt
33
+ from PyQt5.QtCore import QSize, Qt, pyqtSlot
34
34
  from PyQt5.QtGui import QWheelEvent
35
35
  from PyQt5.QtWidgets import (
36
36
  QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
@@ -43,7 +43,26 @@ if TYPE_CHECKING: # pragma: no cover
43
43
  from novelwriter.guimain import GuiMain
44
44
 
45
45
 
46
- class NToolDialog(QDialog):
46
+ class NDialog(QDialog):
47
+
48
+ def softDelete(self) -> None:
49
+ """Since calling deleteLater is sometimes not safe from Python,
50
+ as the C++ object can be deleted before the Python process is
51
+ done with the object, we instead set the dialog's parent to None
52
+ so that it gets garbage collected when it runs out of scope.
53
+ """
54
+ self.setParent(None) # type: ignore
55
+ return
56
+
57
+ @pyqtSlot()
58
+ def reject(self) -> None:
59
+ """Overload the reject slot and also call close."""
60
+ super().reject()
61
+ self.close()
62
+ return
63
+
64
+
65
+ class NToolDialog(NDialog):
47
66
 
48
67
  def __init__(self, parent: GuiMain) -> None:
49
68
  super().__init__(parent=parent)
@@ -62,7 +81,7 @@ class NToolDialog(QDialog):
62
81
  return
63
82
 
64
83
 
65
- class NNonBlockingDialog(QDialog):
84
+ class NNonBlockingDialog(NDialog):
66
85
 
67
86
  def __init__(self, parent: QWidget | None = None) -> None:
68
87
  super().__init__(parent=parent)
@@ -1,9 +1,10 @@
1
1
  """
2
- novelWriter – Custom Widget: Progress Circle
3
- ============================================
2
+ novelWriter – Custom Widget: Progress Bars
3
+ ==========================================
4
4
 
5
5
  File History:
6
- Created: 2023-06-07 [2.1b1]
6
+ Created: 2023-06-07 [2.1b1] NProgressCircle
7
+ Created: 2023-06-09 [2.1b1] NProgressSimple
7
8
 
8
9
  This file is a part of novelWriter
9
10
  Copyright 2018–2024, Veronica Berglyd Olsen
@@ -101,3 +102,25 @@ class NProgressCircle(QProgressBar):
101
102
  painter.setPen(self._tColor)
102
103
  painter.drawText(self._cRect, QtAlignCenter, self._text or f"{progress:.1f} %")
103
104
  return
105
+
106
+
107
+ class NProgressSimple(QProgressBar):
108
+ """Extension: Simple Progress Widget
109
+
110
+ A custom widget that paints a plain bar with no other styling.
111
+ """
112
+
113
+ def __init__(self, parent: QWidget) -> None:
114
+ super().__init__(parent=parent)
115
+ return
116
+
117
+ def paintEvent(self, event: QPaintEvent) -> None:
118
+ """Custom painter for the progress bar."""
119
+ if (value := self.value()) > 0:
120
+ progress = ceil(self.width()*float(value)/self.maximum())
121
+ painter = QPainter(self)
122
+ painter.setRenderHint(QtPaintAnitAlias, True)
123
+ painter.setPen(self.palette().highlight().color())
124
+ painter.setBrush(self.palette().highlight())
125
+ painter.drawRect(0, 0, progress, self.height())
126
+ return
@@ -25,44 +25,56 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from typing import Literal
29
-
30
28
  from PyQt5.QtGui import QColor, QPainter, QPaintEvent
31
29
  from PyQt5.QtWidgets import QAbstractButton, QWidget
32
30
 
33
- from novelwriter.types import QtPaintAnitAlias
31
+ from novelwriter import CONFIG
32
+ from novelwriter.enum import nwTrinary
33
+ from novelwriter.types import QtBlack, QtPaintAnitAlias
34
34
 
35
35
  logger = logging.getLogger(__name__)
36
36
 
37
37
 
38
38
  class StatusLED(QAbstractButton):
39
39
 
40
- S_NONE = 0
41
- S_BAD = 1
42
- S_GOOD = 2
40
+ __slots__ = (
41
+ "_neutral", "_postitve", "_negative", "_color", "_state", "_bPx"
42
+ )
43
43
 
44
- def __init__(self, colNone: QColor, colGood: QColor, colBad: QColor,
45
- sW: int, sH: int, parent: QWidget | None = None) -> None:
44
+ def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
46
45
  super().__init__(parent=parent)
47
-
48
- self._colNone = colNone
49
- self._colGood = colGood
50
- self._colBad = colBad
51
- self._theCol = colNone
52
-
46
+ self._neutral = QtBlack
47
+ self._postitve = QtBlack
48
+ self._negative = QtBlack
49
+ self._color = QtBlack
50
+ self._state = nwTrinary.NEUTRAL
51
+ self._bPx = CONFIG.pxInt(1)
53
52
  self.setFixedWidth(sW)
54
53
  self.setFixedHeight(sH)
54
+ return
55
55
 
56
+ @property
57
+ def state(self) -> nwTrinary:
58
+ """The current state of the LED."""
59
+ return self._state
60
+
61
+ def setColors(self, neutral: QColor, positive: QColor, negative: QColor) -> None:
62
+ """Set the three colours for the status values."""
63
+ self._neutral = neutral
64
+ self._postitve = positive
65
+ self._negative = negative
66
+ self.setState(self._state)
56
67
  return
57
68
 
58
- def setState(self, state: Literal[0, 1, 2]) -> None:
69
+ def setState(self, state: nwTrinary) -> None:
59
70
  """Set the colour state."""
60
- if state == self.S_GOOD:
61
- self._theCol = self._colGood
62
- elif state == self.S_BAD:
63
- self._theCol = self._colBad
71
+ if state == nwTrinary.POSITIVE:
72
+ self._color = self._postitve
73
+ elif state == nwTrinary.NEGATIVE:
74
+ self._color = self._negative
64
75
  else:
65
- self._theCol = self._colNone
76
+ self._color = self._neutral
77
+ self._state = state
66
78
  self.update()
67
79
  return
68
80
 
@@ -70,8 +82,12 @@ class StatusLED(QAbstractButton):
70
82
  """Draw the LED."""
71
83
  painter = QPainter(self)
72
84
  painter.setRenderHint(QtPaintAnitAlias, True)
73
- painter.setPen(self.palette().dark().color())
74
- painter.setBrush(self._theCol)
85
+ painter.setPen(self.palette().windowText().color())
86
+ painter.setBrush(self._color)
75
87
  painter.setOpacity(1.0)
76
- painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
88
+ painter.drawEllipse(
89
+ self._bPx, self._bPx,
90
+ self.width() - 2*self._bPx,
91
+ self.height() - 2*self._bPx
92
+ )
77
93
  return
@@ -55,6 +55,7 @@ from novelwriter.common import minmax, transferCase
55
55
  from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
56
56
  from novelwriter.core.document import NWDocument
57
57
  from novelwriter.enum import nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
58
+ from novelwriter.extensions.configlayout import NColourLabel
58
59
  from novelwriter.extensions.eventfilters import WheelEventFilter
59
60
  from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
60
61
  from novelwriter.gui.dochighlight import BLOCK_META, BLOCK_TITLE
@@ -64,7 +65,7 @@ from novelwriter.text.counting import standardCounter
64
65
  from novelwriter.tools.lipsum import GuiLipsum
65
66
  from novelwriter.types import (
66
67
  QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop,
67
- QtAlignRight, QtKeepAnchor, QtModCtrl, QtModeNone, QtModShift, QtMouseLeft,
68
+ QtAlignRight, QtKeepAnchor, QtModCtrl, QtModNone, QtModShift, QtMouseLeft,
68
69
  QtMoveAnchor, QtMoveLeft, QtMoveRight
69
70
  )
70
71
 
@@ -210,6 +211,7 @@ class GuiDocEditor(QPlainTextEdit):
210
211
  # Function Mapping
211
212
  self.closeSearch = self.docSearch.closeSearch
212
213
  self.searchVisible = self.docSearch.isVisible
214
+ self.changeFocusState = self.docHeader.changeFocusState
213
215
 
214
216
  # Finalise
215
217
  self.updateSyntaxColours()
@@ -436,11 +438,6 @@ class GuiDocEditor(QPlainTextEdit):
436
438
 
437
439
  return True
438
440
 
439
- def updateTagHighLighting(self) -> None:
440
- """Rerun the syntax highlighter on all meta data lines."""
441
- self._qDocument.syntaxHighlighter.rehighlightByType(BLOCK_META)
442
- return
443
-
444
441
  def replaceText(self, text: str) -> None:
445
442
  """Replace the text of the current document with the provided
446
443
  text. This also clears undo history.
@@ -956,7 +953,7 @@ class GuiDocEditor(QPlainTextEdit):
956
953
  super().keyPressEvent(event)
957
954
  nPos = self.cursorRect().topLeft().y()
958
955
  kMod = event.modifiers()
959
- okMod = kMod in (QtModeNone, QtModShift)
956
+ okMod = kMod in (QtModNone, QtModShift)
960
957
  okKey = event.key() not in self.MOVE_KEYS
961
958
  if nPos != cPos and okMod and okKey:
962
959
  mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
@@ -1034,6 +1031,13 @@ class GuiDocEditor(QPlainTextEdit):
1034
1031
  self.beginSearch()
1035
1032
  return
1036
1033
 
1034
+ @pyqtSlot(list, list)
1035
+ def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None:
1036
+ """Tags have changed, so just in case we rehighlight them."""
1037
+ if updated or deleted:
1038
+ self._qDocument.syntaxHighlighter.rehighlightByType(BLOCK_META)
1039
+ return
1040
+
1037
1041
  ##
1038
1042
  # Private Slots
1039
1043
  ##
@@ -1922,8 +1926,6 @@ class GuiDocEditor(QPlainTextEdit):
1922
1926
  ).format(tag)):
1923
1927
  itemClass = nwKeyWords.KEY_CLASS.get(tBits[0], nwItemClass.NO_CLASS)
1924
1928
  self.requestNewNoteCreation.emit(tag, itemClass)
1925
- QApplication.processEvents()
1926
- self._qDocument.syntaxHighlighter.rehighlightBlock(block)
1927
1929
 
1928
1930
  return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
1929
1931
 
@@ -2030,6 +2032,9 @@ class GuiDocEditor(QPlainTextEdit):
2030
2032
  cursor.movePosition(QtMoveLeft, QtKeepAnchor, nDelete)
2031
2033
  cursor.insertText(tInsert)
2032
2034
 
2035
+ # Re-highlight, since the auto-replace sometimes interferes with it
2036
+ self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block())
2037
+
2033
2038
  return
2034
2039
 
2035
2040
  @staticmethod
@@ -2782,8 +2787,7 @@ class GuiDocEditHeader(QWidget):
2782
2787
  self.setAutoFillBackground(True)
2783
2788
 
2784
2789
  # Title Label
2785
- self.itemTitle = QLabel("", self)
2786
- self.itemTitle.setIndent(0)
2790
+ self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
2787
2791
  self.itemTitle.setMargin(0)
2788
2792
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
2789
2793
  self.itemTitle.setAutoFillBackground(True)
@@ -2915,10 +2919,15 @@ class GuiDocEditHeader(QWidget):
2915
2919
  palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
2916
2920
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
2917
2921
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
2918
-
2919
2922
  self.setPalette(palette)
2920
- self.itemTitle.setPalette(palette)
2923
+ self.itemTitle.setTextColors(
2924
+ color=palette.windowText().color(), faded=SHARED.theme.fadedText
2925
+ )
2926
+ return
2921
2927
 
2928
+ def changeFocusState(self, state: bool) -> None:
2929
+ """Toggle focus state."""
2930
+ self.itemTitle.setColorState(state)
2922
2931
  return
2923
2932
 
2924
2933
  def setHandle(self, tHandle: str) -> None: