novelWriter 2.7.4__py3-none-any.whl → 2.8b1__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 (196) hide show
  1. novelwriter/__init__.py +8 -7
  2. novelwriter/assets/icons/font_awesome.icons +22 -4
  3. novelwriter/assets/icons/material_filled_normal.icons +20 -2
  4. novelwriter/assets/icons/material_filled_thin.icons +20 -2
  5. novelwriter/assets/icons/material_rounded_normal.icons +20 -2
  6. novelwriter/assets/icons/material_rounded_thin.icons +20 -2
  7. novelwriter/assets/icons/material_sharp_normal.icons +20 -2
  8. novelwriter/assets/icons/material_sharp_thin.icons +20 -2
  9. novelwriter/assets/icons/remix_filled.icons +20 -2
  10. novelwriter/assets/icons/remix_outline.icons +20 -2
  11. novelwriter/assets/images/welcome.webp +0 -0
  12. novelwriter/assets/manual.pdf +0 -0
  13. novelwriter/assets/manual_fr.pdf +0 -0
  14. novelwriter/assets/sample.zip +0 -0
  15. novelwriter/assets/text/credits_en.htm +61 -11
  16. novelwriter/assets/themes/aura.conf +97 -0
  17. novelwriter/assets/themes/aura_bright.conf +95 -0
  18. novelwriter/assets/themes/aura_soft.conf +97 -0
  19. novelwriter/assets/themes/b2t_garden_dark.conf +97 -0
  20. novelwriter/assets/themes/b2t_garden_light.conf +97 -0
  21. novelwriter/assets/themes/b2t_suburb_dark.conf +97 -0
  22. novelwriter/assets/themes/b2t_suburb_light.conf +97 -0
  23. novelwriter/assets/themes/b4t_classic_o_dark.conf +97 -0
  24. novelwriter/assets/themes/b4t_classic_o_light.conf +97 -0
  25. novelwriter/assets/themes/b4t_modern_c_dark.conf +97 -0
  26. novelwriter/assets/themes/b4t_modern_c_light.conf +97 -0
  27. novelwriter/assets/themes/blue_streak_dark.conf +97 -0
  28. novelwriter/assets/themes/blue_streak_light.conf +97 -0
  29. novelwriter/assets/themes/castle_day.conf +95 -0
  30. novelwriter/assets/themes/castle_night.conf +95 -0
  31. novelwriter/assets/themes/catppuccin_latte.conf +97 -0
  32. novelwriter/assets/themes/catppuccin_mocha.conf +97 -0
  33. novelwriter/assets/themes/chalky_soil.conf +95 -0
  34. novelwriter/assets/themes/chernozem.conf +95 -0
  35. novelwriter/assets/themes/cyberpunk_night.conf +88 -40
  36. novelwriter/assets/themes/default_dark.conf +89 -41
  37. novelwriter/assets/themes/default_light.conf +89 -41
  38. novelwriter/assets/themes/dracula.conf +91 -42
  39. novelwriter/assets/themes/espresso.conf +97 -0
  40. novelwriter/assets/themes/everforest_dark.conf +97 -0
  41. novelwriter/assets/themes/everforest_light.conf +97 -0
  42. novelwriter/assets/themes/floral_daydream.conf +95 -0
  43. novelwriter/assets/themes/floral_midnight.conf +95 -0
  44. novelwriter/assets/themes/full_moon.conf +95 -0
  45. novelwriter/assets/themes/grey_dark.conf +97 -0
  46. novelwriter/assets/themes/grey_light.conf +97 -0
  47. novelwriter/assets/themes/horizon_dark.conf +97 -0
  48. novelwriter/assets/themes/horizon_light.conf +97 -0
  49. novelwriter/assets/themes/jewel_case_dark.conf +95 -0
  50. novelwriter/assets/themes/jewel_case_light.conf +95 -0
  51. novelwriter/assets/themes/lcars.conf +97 -0
  52. novelwriter/assets/themes/light_owl.conf +117 -0
  53. novelwriter/assets/themes/new_moon.conf +97 -0
  54. novelwriter/assets/themes/night_owl.conf +117 -0
  55. novelwriter/assets/themes/noctis.conf +129 -0
  56. novelwriter/assets/themes/noctis_lux.conf +129 -0
  57. novelwriter/assets/themes/nord.conf +97 -0
  58. novelwriter/assets/themes/nordlicht.conf +95 -0
  59. novelwriter/assets/themes/otium_dark.conf +95 -0
  60. novelwriter/assets/themes/otium_light.conf +95 -0
  61. novelwriter/assets/themes/paragon.conf +96 -0
  62. novelwriter/assets/themes/primer_light.conf +97 -0
  63. novelwriter/assets/themes/primer_night.conf +97 -0
  64. novelwriter/assets/themes/rose_pine.conf +97 -0
  65. novelwriter/assets/themes/rose_pine_dawn.conf +97 -0
  66. novelwriter/assets/themes/ruby_day.conf +95 -0
  67. novelwriter/assets/themes/ruby_night.conf +95 -0
  68. novelwriter/assets/themes/selenium_dark.conf +95 -0
  69. novelwriter/assets/themes/selenium_light.conf +95 -0
  70. novelwriter/assets/themes/sepia_dark.conf +95 -0
  71. novelwriter/assets/themes/sepia_light.conf +95 -0
  72. novelwriter/assets/themes/snazzy.conf +102 -40
  73. novelwriter/assets/themes/solarized_dark.conf +108 -40
  74. novelwriter/assets/themes/solarized_light.conf +108 -40
  75. novelwriter/assets/themes/sultana_light.conf +95 -0
  76. novelwriter/assets/themes/sultana_night.conf +95 -0
  77. novelwriter/assets/themes/tango_dark.conf +111 -0
  78. novelwriter/assets/themes/tango_light.conf +111 -0
  79. novelwriter/assets/themes/tomorrow.conf +117 -0
  80. novelwriter/assets/themes/tomorrow_night.conf +117 -0
  81. novelwriter/assets/themes/tomorrow_night_blue.conf +117 -0
  82. novelwriter/assets/themes/tomorrow_night_bright.conf +117 -0
  83. novelwriter/assets/themes/tomorrow_night_eighties.conf +117 -0
  84. novelwriter/assets/themes/vivid_black_green.conf +97 -0
  85. novelwriter/assets/themes/vivid_black_red.conf +97 -0
  86. novelwriter/assets/themes/vivid_white_green.conf +97 -0
  87. novelwriter/assets/themes/vivid_white_red.conf +97 -0
  88. novelwriter/assets/themes/warpgate.conf +96 -0
  89. novelwriter/assets/themes/waterlily_dark.conf +95 -0
  90. novelwriter/assets/themes/waterlily_light.conf +95 -0
  91. novelwriter/common.py +47 -17
  92. novelwriter/config.py +57 -62
  93. novelwriter/constants.py +32 -6
  94. novelwriter/core/buildsettings.py +3 -23
  95. novelwriter/core/coretools.py +21 -25
  96. novelwriter/core/docbuild.py +4 -9
  97. novelwriter/core/document.py +2 -6
  98. novelwriter/core/index.py +33 -53
  99. novelwriter/core/indexdata.py +17 -22
  100. novelwriter/core/item.py +11 -35
  101. novelwriter/core/itemmodel.py +5 -21
  102. novelwriter/core/novelmodel.py +3 -7
  103. novelwriter/core/options.py +3 -4
  104. novelwriter/core/project.py +31 -21
  105. novelwriter/core/projectdata.py +2 -21
  106. novelwriter/core/projectxml.py +13 -21
  107. novelwriter/core/sessions.py +2 -4
  108. novelwriter/core/spellcheck.py +12 -13
  109. novelwriter/core/status.py +27 -20
  110. novelwriter/core/storage.py +5 -10
  111. novelwriter/core/tree.py +6 -15
  112. novelwriter/dialogs/about.py +9 -10
  113. novelwriter/dialogs/docmerge.py +17 -14
  114. novelwriter/dialogs/docsplit.py +18 -14
  115. novelwriter/dialogs/editlabel.py +15 -9
  116. novelwriter/dialogs/preferences.py +69 -68
  117. novelwriter/dialogs/projectsettings.py +88 -67
  118. novelwriter/dialogs/quotes.py +15 -10
  119. novelwriter/dialogs/wordlist.py +18 -21
  120. novelwriter/enum.py +75 -30
  121. novelwriter/error.py +6 -11
  122. novelwriter/extensions/configlayout.py +8 -34
  123. novelwriter/extensions/eventfilters.py +3 -3
  124. novelwriter/extensions/modified.py +87 -32
  125. novelwriter/extensions/novelselector.py +13 -12
  126. novelwriter/extensions/pagedsidebar.py +10 -18
  127. novelwriter/extensions/progressbars.py +5 -11
  128. novelwriter/extensions/statusled.py +3 -6
  129. novelwriter/extensions/switch.py +8 -11
  130. novelwriter/extensions/switchbox.py +2 -11
  131. novelwriter/extensions/versioninfo.py +6 -7
  132. novelwriter/formats/shared.py +10 -2
  133. novelwriter/formats/todocx.py +15 -37
  134. novelwriter/formats/tohtml.py +52 -61
  135. novelwriter/formats/tokenizer.py +33 -64
  136. novelwriter/formats/tomarkdown.py +4 -11
  137. novelwriter/formats/toodt.py +12 -71
  138. novelwriter/formats/toqdoc.py +11 -21
  139. novelwriter/formats/toraw.py +2 -6
  140. novelwriter/gui/doceditor.py +207 -245
  141. novelwriter/gui/dochighlight.py +142 -101
  142. novelwriter/gui/docviewer.py +53 -84
  143. novelwriter/gui/docviewerpanel.py +18 -41
  144. novelwriter/gui/editordocument.py +12 -17
  145. novelwriter/gui/itemdetails.py +5 -14
  146. novelwriter/gui/mainmenu.py +24 -32
  147. novelwriter/gui/noveltree.py +13 -51
  148. novelwriter/gui/outline.py +20 -61
  149. novelwriter/gui/projtree.py +40 -96
  150. novelwriter/gui/search.py +9 -24
  151. novelwriter/gui/sidebar.py +54 -22
  152. novelwriter/gui/statusbar.py +7 -22
  153. novelwriter/gui/theme.py +482 -368
  154. novelwriter/guimain.py +87 -101
  155. novelwriter/shared.py +79 -48
  156. novelwriter/splash.py +9 -5
  157. novelwriter/text/comments.py +1 -1
  158. novelwriter/text/counting.py +9 -5
  159. novelwriter/text/patterns.py +20 -15
  160. novelwriter/tools/dictionaries.py +18 -16
  161. novelwriter/tools/lipsum.py +15 -17
  162. novelwriter/tools/manusbuild.py +25 -45
  163. novelwriter/tools/manuscript.py +94 -95
  164. novelwriter/tools/manussettings.py +149 -104
  165. novelwriter/tools/noveldetails.py +10 -24
  166. novelwriter/tools/welcome.py +24 -72
  167. novelwriter/tools/writingstats.py +17 -26
  168. novelwriter/types.py +25 -13
  169. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/METADATA +7 -7
  170. novelwriter-2.8b1.dist-info/RECORD +212 -0
  171. novelwriter/assets/images/welcome-dark.jpg +0 -0
  172. novelwriter/assets/images/welcome-light.jpg +0 -0
  173. novelwriter/assets/syntax/cyberpunk_night.conf +0 -28
  174. novelwriter/assets/syntax/default_dark.conf +0 -42
  175. novelwriter/assets/syntax/default_light.conf +0 -42
  176. novelwriter/assets/syntax/dracula.conf +0 -44
  177. novelwriter/assets/syntax/grey_dark.conf +0 -29
  178. novelwriter/assets/syntax/grey_light.conf +0 -29
  179. novelwriter/assets/syntax/light_owl.conf +0 -49
  180. novelwriter/assets/syntax/night_owl.conf +0 -49
  181. novelwriter/assets/syntax/snazzy.conf +0 -42
  182. novelwriter/assets/syntax/solarized_dark.conf +0 -29
  183. novelwriter/assets/syntax/solarized_light.conf +0 -29
  184. novelwriter/assets/syntax/tango.conf +0 -39
  185. novelwriter/assets/syntax/tomorrow.conf +0 -49
  186. novelwriter/assets/syntax/tomorrow_night.conf +0 -49
  187. novelwriter/assets/syntax/tomorrow_night_blue.conf +0 -49
  188. novelwriter/assets/syntax/tomorrow_night_bright.conf +0 -49
  189. novelwriter/assets/syntax/tomorrow_night_eighties.conf +0 -49
  190. novelwriter/assets/themes/default.conf +0 -3
  191. novelwriter-2.7.4.dist-info/RECORD +0 -163
  192. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
  193. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
  194. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
  195. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
  196. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
@@ -24,15 +24,15 @@ General Public License for more details.
24
24
 
25
25
  You should have received a copy of the GNU General Public License
26
26
  along with this program. If not, see <https://www.gnu.org/licenses/>.
27
- """
27
+ """ # noqa
28
28
  from __future__ import annotations
29
29
 
30
30
  from typing import TYPE_CHECKING
31
31
 
32
32
  from PyQt6.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
33
33
  from PyQt6.QtWidgets import (
34
- QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
35
- QToolButton, QTreeView, QWidget
34
+ QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QPushButton,
35
+ QSpinBox, QToolButton, QTreeView, QWidget
36
36
  )
37
37
 
38
38
  from novelwriter import CONFIG, SHARED
@@ -47,6 +47,7 @@ if TYPE_CHECKING:
47
47
 
48
48
 
49
49
  class NDialog(QDialog):
50
+ """Custom: Modified QDialog."""
50
51
 
51
52
  def softDelete(self) -> None:
52
53
  """Since calling deleteLater is sometimes not safe from Python,
@@ -55,53 +56,54 @@ class NDialog(QDialog):
55
56
  so that it gets garbage collected when it runs out of scope.
56
57
  """
57
58
  self.setParent(None) # type: ignore
58
- return
59
59
 
60
60
  @pyqtSlot()
61
61
  def reject(self) -> None:
62
62
  """Overload the reject slot and also call close."""
63
63
  super().reject()
64
64
  self.close()
65
- return
66
65
 
67
66
 
68
67
  class NToolDialog(NDialog):
68
+ """Custom: Modified QDialog for Tools."""
69
69
 
70
70
  def __init__(self, parent: GuiMain) -> None:
71
71
  super().__init__(parent=parent)
72
72
  self.setModal(False)
73
73
  if CONFIG.osDarwin:
74
74
  self.setWindowFlag(Qt.WindowType.Tool)
75
- return
76
75
 
77
76
  def activateDialog(self) -> None:
78
- """Helper function to activate dialog on various systems."""
77
+ """Activate dialog on various operating systems."""
79
78
  self.show()
80
79
  if CONFIG.osWindows:
81
80
  self.activateWindow()
82
81
  self.raise_()
83
82
  QApplication.processEvents()
84
- return
85
83
 
86
84
 
87
85
  class NNonBlockingDialog(NDialog):
86
+ """Custom: Modified Non-Blocking QDialog."""
88
87
 
89
88
  def __init__(self, parent: QWidget | None = None) -> None:
90
89
  super().__init__(parent=parent)
91
90
  self.setModal(True)
92
- return
93
91
 
94
92
  def activateDialog(self) -> None:
95
- """Helper function to activate dialog on various systems."""
93
+ """Activate dialog on various operating systems."""
96
94
  self.show()
97
95
  if CONFIG.osWindows:
98
96
  self.activateWindow()
99
97
  self.raise_()
100
98
  QApplication.processEvents()
101
- return
102
99
 
103
100
 
104
101
  class NTreeView(QTreeView):
102
+ """Custom: Modified QTreeView.
103
+
104
+ The main purpose is to provide the middleClicked signal that matches
105
+ clicked and doubleCLicked.
106
+ """
105
107
 
106
108
  middleClicked = pyqtSignal(QModelIndex)
107
109
 
@@ -116,42 +118,65 @@ class NTreeView(QTreeView):
116
118
 
117
119
 
118
120
  class NComboBox(QComboBox):
121
+ """Custom: Modified QComboBox.
119
122
 
120
- def __init__(self, parent: QWidget | None = None) -> None:
123
+ The main purpose is to provide a combo box that doesn't scroll when
124
+ the mousewheel is active on it while scrolling through a scrollable
125
+ window of many widgets.
126
+ """
127
+
128
+ def __init__(self, parent: QWidget | None = None, maxItems: int = 15) -> None:
121
129
  super().__init__(parent=parent)
122
130
  self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
123
- return
131
+ self.setMaxVisibleItems(maxItems)
132
+ self.updateStyle()
124
133
 
125
134
  def wheelEvent(self, event: QWheelEvent) -> None:
135
+ """Only capture the mouse wheel if the widget has focus."""
126
136
  if self.hasFocus():
127
137
  super().wheelEvent(event)
128
138
  else:
129
139
  event.ignore()
130
- return
131
140
 
132
141
  def setCurrentData(self, data: str | int | Enum, default: str | int | Enum) -> None:
133
142
  """Set the current index from data, with a fallback."""
134
143
  idx = self.findData(data)
135
144
  self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
136
- return
145
+
146
+ def updateStyle(self) -> None:
147
+ """Update the style sheet."""
148
+ # The style sheet disables Fusion style pop-up mode on some
149
+ # platforms and allows for scrolling of long lists of items
150
+ self.setStyleSheet("QComboBox {combobox-popup: 0;}")
137
151
 
138
152
 
139
153
  class NSpinBox(QSpinBox):
154
+ """Custom: Modified QSpinBox.
155
+
156
+ The main purpose is to provide a spin box that doesn't scroll when
157
+ the mousewheel is active on it while scrolling through a scrollable
158
+ window of many widgets.
159
+ """
140
160
 
141
161
  def __init__(self, parent: QWidget | None = None) -> None:
142
162
  super().__init__(parent=parent)
143
163
  self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
144
- return
145
164
 
146
165
  def wheelEvent(self, event: QWheelEvent) -> None:
166
+ """Only capture the mouse wheel if the widget has focus."""
147
167
  if self.hasFocus():
148
168
  super().wheelEvent(event)
149
169
  else:
150
170
  event.ignore()
151
- return
152
171
 
153
172
 
154
173
  class NDoubleSpinBox(QDoubleSpinBox):
174
+ """Custom: Modified QDoubleSpinBox.
175
+
176
+ The main purpose is to provide a float spin box that doesn't scroll
177
+ when the mousewheel is active on it while scrolling through a
178
+ scrollable window of many widgets.
179
+ """
155
180
 
156
181
  def __init__(
157
182
  self,
@@ -168,17 +193,43 @@ class NDoubleSpinBox(QDoubleSpinBox):
168
193
  self.setMaximum(maxVal)
169
194
  self.setSingleStep(step)
170
195
  self.setDecimals(prec)
171
- return
172
196
 
173
197
  def wheelEvent(self, event: QWheelEvent) -> None:
198
+ """Only capture the mouse wheel if the widget has focus."""
174
199
  if self.hasFocus():
175
200
  super().wheelEvent(event)
176
201
  else:
177
202
  event.ignore()
178
- return
203
+
204
+
205
+ class NPushButton(QPushButton):
206
+ """Custom: Modified QPushButton.
207
+
208
+ A quicker way to create a push button using the app theme.
209
+ """
210
+
211
+ def __init__(
212
+ self, parent: QWidget, text: str, iconSize: QSize,
213
+ icon: str | None = None, color: str | None = None
214
+ ) -> None:
215
+ super().__init__(parent=parent)
216
+ self._icon = icon
217
+ self._color = color
218
+ self.setText(text)
219
+ self.setIconSize(iconSize)
220
+ self.updateIcon()
221
+
222
+ def updateIcon(self) -> None:
223
+ """Update the theme icon."""
224
+ if self._icon and self._color:
225
+ self.setIcon(SHARED.theme.getIcon(self._icon, self._color))
179
226
 
180
227
 
181
228
  class NIconToolButton(QToolButton):
229
+ """Custom: Modified QToolButton.
230
+
231
+ A quicker way to create a tool button using the app theme.
232
+ """
182
233
 
183
234
  def __init__(
184
235
  self, parent: QWidget, iconSize: QSize,
@@ -188,37 +239,41 @@ class NIconToolButton(QToolButton):
188
239
  self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
189
240
  self.setIconSize(iconSize)
190
241
  self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
191
- if icon:
242
+ if icon and color:
192
243
  self.setThemeIcon(icon, color)
193
- return
194
244
 
195
- def setThemeIcon(self, iconKey: str, color: str | None = None) -> None:
245
+ def setThemeIcon(self, icon: str, color: str) -> None:
196
246
  """Set an icon from the current theme."""
197
- self.setIcon(SHARED.theme.getIcon(iconKey, color))
198
- return
247
+ self.setIcon(SHARED.theme.getIcon(icon, color))
199
248
 
200
249
 
201
250
  class NIconToggleButton(QToolButton):
251
+ """Custom: Modified QToolButton.
202
252
 
203
- def __init__(self, parent: QWidget, iconSize: QSize, icon: str | None = None) -> None:
253
+ A quicker way to create a toggle button using the app theme.
254
+ """
255
+
256
+ def __init__(
257
+ self, parent: QWidget, iconSize: QSize,
258
+ icon: str | None = None, color: str | None = None
259
+ ) -> None:
204
260
  super().__init__(parent=parent)
205
261
  self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
206
262
  self.setIconSize(iconSize)
207
263
  self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
208
264
  self.setCheckable(True)
209
265
  self.setStyleSheet("border: none; background: transparent;")
210
- if icon:
211
- self.setThemeIcon(icon)
212
- return
266
+ if icon and color:
267
+ self.setThemeIcon(icon, color)
213
268
 
214
- def setThemeIcon(self, iconKey: str) -> None:
269
+ def setThemeIcon(self, icon: str, color: str) -> None:
215
270
  """Set an icon from the current theme."""
216
- iconSize = self.iconSize()
217
- self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height())))
218
- return
271
+ size = self.iconSize()
272
+ self.setIcon(SHARED.theme.getToggleIcon(icon, (size.width(), size.height()), color))
219
273
 
220
274
 
221
275
  class NClickableLabel(QLabel):
276
+ """Custom: Clickable QLabel."""
222
277
 
223
278
  mouseClicked = pyqtSignal()
224
279
 
@@ -20,23 +20,30 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
+ from typing import TYPE_CHECKING
29
+
28
30
  from PyQt6.QtCore import pyqtSignal, pyqtSlot
29
31
  from PyQt6.QtGui import QPalette
30
- from PyQt6.QtWidgets import QComboBox, QWidget
32
+ from PyQt6.QtWidgets import QComboBox
31
33
 
32
34
  from novelwriter import SHARED
33
35
  from novelwriter.constants import nwLabels
34
36
  from novelwriter.enum import nwItemClass
37
+ from novelwriter.types import QtColDisabled
38
+
39
+ if TYPE_CHECKING:
40
+ from PyQt6.QtWidgets import QWidget
35
41
 
36
42
  logger = logging.getLogger(__name__)
37
43
 
38
44
 
39
45
  class NovelSelector(QComboBox):
46
+ """Custom: Novel Root Folder Selector."""
40
47
 
41
48
  novelSelectionChanged = pyqtSignal(str)
42
49
 
@@ -48,7 +55,6 @@ class NovelSelector(QComboBox):
48
55
  self._listFormat = None
49
56
  self.currentIndexChanged.connect(self._indexChanged)
50
57
  self.updateTheme()
51
- return
52
58
 
53
59
  ##
54
60
  # Properties
@@ -75,26 +81,24 @@ class NovelSelector(QComboBox):
75
81
  self._blockSignal = blockSignal
76
82
  self.setCurrentIndex(index)
77
83
  self._blockSignal = False
78
- return
79
84
 
80
85
  def setIncludeAll(self, value: bool) -> None:
81
86
  """Set flag to add an "All Novel Folders" option."""
82
87
  self._includeAll = value
83
- return
84
88
 
85
89
  def setListFormat(self, value: str | None) -> None:
86
90
  """Set a format string for the list entries."""
87
91
  if value is None or "{0}" in value:
88
92
  self._listFormat = value
89
- return
90
93
 
91
94
  def updateTheme(self) -> None:
92
95
  """Update theme colours."""
93
96
  palette = self.palette()
94
- palette.setBrush(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, palette.text())
97
+ palette.setBrush(QtColDisabled, QPalette.ColorRole.Text, palette.text())
98
+ palette.setBrush(QtColDisabled, QPalette.ColorRole.WindowText, palette.windowText())
99
+ palette.setBrush(QtColDisabled, QPalette.ColorRole.ButtonText, palette.buttonText())
95
100
  self.setPalette(palette)
96
101
  self.refreshNovelList()
97
- return
98
102
 
99
103
  ##
100
104
  # Public Slots
@@ -109,7 +113,7 @@ class NovelSelector(QComboBox):
109
113
  self._firstHandle = None
110
114
  self.clear()
111
115
 
112
- icon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL], "blue")
116
+ icon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL], "root")
113
117
  for tHandle, nwItem in SHARED.project.tree.iterRoots(nwItemClass.NOVEL):
114
118
  if self._listFormat:
115
119
  name = self._listFormat.format(nwItem.itemName)
@@ -128,8 +132,6 @@ class NovelSelector(QComboBox):
128
132
  self.setEnabled(self.count() > 1)
129
133
  self._blockSignal = False
130
134
 
131
- return
132
-
133
135
  ##
134
136
  # Private Slots
135
137
  ##
@@ -139,4 +141,3 @@ class NovelSelector(QComboBox):
139
141
  """Re-emit the change of selection signal, unless blocked."""
140
142
  if not self._blockSignal:
141
143
  self.novelSelectionChanged.emit(self.currentData())
142
- return
@@ -22,7 +22,7 @@ General Public License for more details.
22
22
 
23
23
  You should have received a copy of the GNU General Public License
24
24
  along with this program. If not, see <https://www.gnu.org/licenses/>.
25
- """
25
+ """ # noqa
26
26
  from __future__ import annotations
27
27
 
28
28
  from PyQt6.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
@@ -39,7 +39,7 @@ from novelwriter.types import (
39
39
 
40
40
 
41
41
  class NPagedSideBar(QToolBar):
42
- """Extensions: Paged Side Bar
42
+ """Extensions: Paged Side Bar.
43
43
 
44
44
  A side bar widget that holds buttons that mimic tabs. It is designed
45
45
  to be used in combination with a QStackedWidget for options panels.
@@ -65,8 +65,6 @@ class NPagedSideBar(QToolBar):
65
65
  stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
66
66
  self._stretchAction = self.addWidget(stretch)
67
67
 
68
- return
69
-
70
68
  def button(self, buttonId: int) -> _PagedToolButton:
71
69
  """Return a specific button."""
72
70
  return self._buttons[buttonId]
@@ -74,14 +72,15 @@ class NPagedSideBar(QToolBar):
74
72
  def setLabelColor(self, color: QColor) -> None:
75
73
  """Set the text color for the labels."""
76
74
  self._labelCol = color
77
- return
75
+ for widget in self.children():
76
+ if isinstance(widget, _NPagedToolLabel):
77
+ widget.setTextColor(color)
78
78
 
79
79
  def addLabel(self, text: str) -> None:
80
80
  """Add a new label to the toolbar."""
81
81
  label = _NPagedToolLabel(self, self._labelCol)
82
82
  label.setText(text)
83
83
  self.insertWidget(self._stretchAction, label)
84
- return
85
84
 
86
85
  def addButton(self, text: str, buttonId: int = -1) -> None:
87
86
  """Add a new button to the toolbar."""
@@ -90,13 +89,11 @@ class NPagedSideBar(QToolBar):
90
89
  self.insertWidget(self._stretchAction, button)
91
90
  self._group.addButton(button, id=buttonId)
92
91
  self._buttons[buttonId] = button
93
- return
94
92
 
95
93
  def setSelected(self, buttonId: int) -> None:
96
94
  """Set the selected button."""
97
95
  if button := self._group.button(buttonId):
98
96
  button.setChecked(True)
99
- return
100
97
 
101
98
  ##
102
99
  # Private Slots
@@ -104,11 +101,10 @@ class NPagedSideBar(QToolBar):
104
101
 
105
102
  @pyqtSlot("QAbstractButton*")
106
103
  def _buttonClicked(self, button: QAbstractButton) -> None:
107
- """A button was clicked in the group, emit its id."""
104
+ """Handle a button click in the group and emit its id."""
108
105
  buttonId = self._group.id(button)
109
106
  if buttonId != -1:
110
107
  self.buttonClicked.emit(buttonId)
111
- return
112
108
 
113
109
 
114
110
  class _PagedToolButton(QToolButton):
@@ -127,8 +123,6 @@ class _PagedToolButton(QToolButton):
127
123
  self._aH = 2*fH//7
128
124
  self.setFixedHeight(self._bH)
129
125
 
130
- return
131
-
132
126
  def sizeHint(self) -> QSize:
133
127
  """Return a size hint that includes the arrow."""
134
128
  return super().sizeHint() + QSize(4*self._aH, 0)
@@ -156,7 +150,7 @@ class _PagedToolButton(QToolButton):
156
150
 
157
151
  if self.isChecked():
158
152
  painter.setBrush(palette.highlight())
159
- painter.setOpacity(0.35)
153
+ painter.setOpacity(0.8)
160
154
  painter.drawRoundedRect(0, 0, width, height, 4, 4)
161
155
  textCol = palette.highlightedText().color()
162
156
  else:
@@ -180,8 +174,6 @@ class _PagedToolButton(QToolButton):
180
174
  ]))
181
175
  painter.end()
182
176
 
183
- return
184
-
185
177
 
186
178
  class _NPagedToolLabel(QLabel):
187
179
 
@@ -199,7 +191,9 @@ class _NPagedToolLabel(QLabel):
199
191
 
200
192
  self._textCol = textColor or self.palette().text().color()
201
193
 
202
- return
194
+ def setTextColor(self, textColor: QColor | None = None) -> None:
195
+ """Set a new text colour."""
196
+ self._textCol = textColor or self.palette().text().color()
203
197
 
204
198
  def paintEvent(self, event: QPaintEvent) -> None:
205
199
  """Overload the paint event to draw a simple, left aligned text
@@ -215,5 +209,3 @@ class _NPagedToolLabel(QLabel):
215
209
  painter.setOpacity(1.0)
216
210
  painter.drawText(QRectF(4, self._tM, tW, tH), QtAlignLeft, self.text())
217
211
  painter.end()
218
-
219
- return
@@ -21,7 +21,7 @@ General Public License for more details.
21
21
 
22
22
  You should have received a copy of the GNU General Public License
23
23
  along with this program. If not, see <https://www.gnu.org/licenses/>.
24
- """
24
+ """ # noqa
25
25
  from __future__ import annotations
26
26
 
27
27
  from math import ceil
@@ -37,7 +37,7 @@ from novelwriter.types import (
37
37
 
38
38
 
39
39
  class NProgressCircle(QProgressBar):
40
- """Extension: Circular Progress Widget
40
+ """Extension: Circular Progress Widget.
41
41
 
42
42
  A custom widget that paints a circular progress indicator instead of
43
43
  a straight bar. It is also possible to set custom text for iṫ.
@@ -64,7 +64,6 @@ class NProgressCircle(QProgressBar):
64
64
  self.setSizePolicy(QtSizeFixed, QtSizeFixed)
65
65
  self.setFixedWidth(size)
66
66
  self.setFixedHeight(size)
67
- return
68
67
 
69
68
  def setColors(
70
69
  self, back: QColor | None = None, track: QColor | None = None,
@@ -80,16 +79,14 @@ class NProgressCircle(QProgressBar):
80
79
  self._bPen = QPen(QBrush(track), self._point, QtSolidLine, QtRoundCap)
81
80
  if isinstance(text, QColor):
82
81
  self._tColor = text
83
- return
84
82
 
85
83
  def setCentreText(self, text: str | None) -> None:
86
84
  """Replace the progress text with a custom string."""
87
85
  self._text = text
88
86
  self.setValue(self.value()) # Triggers a redraw
89
- return
90
87
 
91
88
  def paintEvent(self, event: QPaintEvent) -> None:
92
- """Custom painter for the progress bar."""
89
+ """Paint the progress bar."""
93
90
  progress = 100.0*self.value()/self.maximum()
94
91
  angle = ceil(16*3.6*progress)
95
92
  painter = QPainter(self)
@@ -103,21 +100,19 @@ class NProgressCircle(QProgressBar):
103
100
  painter.drawArc(self._cRect, 90*16, -angle)
104
101
  painter.setPen(self._tColor)
105
102
  painter.drawText(self._cRect, QtAlignCenter, self._text or f"{progress:.1f} %")
106
- return
107
103
 
108
104
 
109
105
  class NProgressSimple(QProgressBar):
110
- """Extension: Simple Progress Widget
106
+ """Extension: Simple Progress Widget.
111
107
 
112
108
  A custom widget that paints a plain bar with no other styling.
113
109
  """
114
110
 
115
111
  def __init__(self, parent: QWidget) -> None:
116
112
  super().__init__(parent=parent)
117
- return
118
113
 
119
114
  def paintEvent(self, event: QPaintEvent) -> None:
120
- """Custom painter for the progress bar."""
115
+ """Paint the progress bar."""
121
116
  if (value := self.value()) > 0:
122
117
  progress = ceil(self.width()*float(value)/self.maximum())
123
118
  painter = QPainter(self)
@@ -125,4 +120,3 @@ class NProgressSimple(QProgressBar):
125
120
  painter.setPen(self.palette().highlight().color())
126
121
  painter.setBrush(self.palette().highlight())
127
122
  painter.drawRect(0, 0, progress, self.height())
128
- return
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import logging
@@ -34,6 +34,7 @@ logger = logging.getLogger(__name__)
34
34
 
35
35
 
36
36
  class StatusLED(QAbstractButton):
37
+ """Custom: LED Style Indicator."""
37
38
 
38
39
  __slots__ = ("_color", "_negative", "_neutral", "_postitve", "_state")
39
40
 
@@ -46,7 +47,6 @@ class StatusLED(QAbstractButton):
46
47
  self._state = None
47
48
  self.setFixedWidth(sW)
48
49
  self.setFixedHeight(sH)
49
- return
50
50
 
51
51
  @property
52
52
  def state(self) -> bool | None:
@@ -59,7 +59,6 @@ class StatusLED(QAbstractButton):
59
59
  self._postitve = positive
60
60
  self._negative = negative
61
61
  self.setState(self._state)
62
- return
63
62
 
64
63
  def setState(self, state: bool | None) -> None:
65
64
  """Set the colour state."""
@@ -71,15 +70,13 @@ class StatusLED(QAbstractButton):
71
70
  self._color = self._neutral
72
71
  self._state = state
73
72
  self.update()
74
- return
75
73
 
76
74
  def paintEvent(self, event: QPaintEvent) -> None:
77
75
  """Draw the LED."""
78
76
  painter = QPainter(self)
79
77
  painter.setRenderHint(QtPaintAntiAlias, True)
80
- painter.setPen(self.palette().text().color())
78
+ painter.setPen(self.palette().light().color())
81
79
  painter.setBrush(self._color)
82
80
  painter.setOpacity(1.0)
83
81
  painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
84
82
  painter.end()
85
- return
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty, pyqtSlot # pyright: ignore
@@ -32,6 +32,7 @@ from novelwriter.types import QtNoPen, QtPaintAntiAlias, QtSizeFixed
32
32
 
33
33
 
34
34
  class NSwitch(QAbstractButton):
35
+ """Custom: Toggle Switch."""
35
36
 
36
37
  __slots__ = ("_offset", "_rH", "_rR", "_xH", "_xR", "_xW")
37
38
 
@@ -48,12 +49,11 @@ class NSwitch(QAbstractButton):
48
49
  self.setSizePolicy(QtSizeFixed, QtSizeFixed)
49
50
  self.setFixedWidth(self._xW)
50
51
  self.setFixedHeight(self._xH)
52
+ self.setUpdatesEnabled(True)
51
53
  self._offset = self._xR
52
54
 
53
55
  self.clicked.connect(self._onClick)
54
56
 
55
- return
56
-
57
57
  ##
58
58
  # Properties
59
59
  ##
@@ -66,7 +66,6 @@ class NSwitch(QAbstractButton):
66
66
  def offset(self, offset: int) -> None:
67
67
  self._offset = offset
68
68
  self.update()
69
- return
70
69
 
71
70
  ##
72
71
  # Getters and Setters
@@ -76,7 +75,6 @@ class NSwitch(QAbstractButton):
76
75
  """Overload setChecked to also alter the offset."""
77
76
  super().setChecked(checked)
78
77
  self._offset = (self._xW - self._xR) if checked else self._xR
79
- return
80
78
 
81
79
  ##
82
80
  # Events
@@ -86,7 +84,6 @@ class NSwitch(QAbstractButton):
86
84
  """Overload resize to ensure correct offset."""
87
85
  super().resizeEvent(event)
88
86
  self._offset = (self._xW - self._xR) if self.isChecked() else self._xR
89
- return
90
87
 
91
88
  def paintEvent(self, event: QPaintEvent) -> None:
92
89
  """Drawing the switch itself."""
@@ -97,7 +94,7 @@ class NSwitch(QAbstractButton):
97
94
  painter.setOpacity(1.0 if self.isEnabled() else 0.5)
98
95
 
99
96
  painter.setPen(palette.highlight().color() if self.hasFocus() else palette.mid().color())
100
- painter.setBrush(palette.highlight() if self.isChecked() else palette.alternateBase())
97
+ painter.setBrush(SHARED.theme.accentCol if self.isChecked() else palette.alternateBase())
101
98
  painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
102
99
 
103
100
  painter.setPen(QtNoPen)
@@ -106,13 +103,14 @@ class NSwitch(QAbstractButton):
106
103
 
107
104
  painter.end()
108
105
 
109
- return
110
-
111
106
  def enterEvent(self, event: QEnterEvent) -> None:
112
107
  """Change the cursor when hovering the button."""
113
108
  self.setCursor(Qt.CursorShape.PointingHandCursor)
114
109
  super().enterEvent(event)
115
- return
110
+
111
+ ##
112
+ # Internal Functions
113
+ ##
116
114
 
117
115
  @pyqtSlot(bool)
118
116
  def _onClick(self, checked: bool) -> None:
@@ -122,4 +120,3 @@ class NSwitch(QAbstractButton):
122
120
  anim.setStartValue(self._offset)
123
121
  anim.setEndValue((self._xW - self._xR) if checked else self._xR)
124
122
  anim.start()
125
- return