novelWriter 2.7.5__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 +160 -225
  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 +23 -13
  169. {novelwriter-2.7.5.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.5.dist-info/RECORD +0 -163
  192. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
  193. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
  194. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
  195. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
  196. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
@@ -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
@@ -38,12 +38,13 @@ from PyQt6.QtWidgets import (
38
38
  from novelwriter import SHARED
39
39
  from novelwriter.common import formatTime, numberToRoman
40
40
  from novelwriter.constants import nwUnicode
41
+ from novelwriter.enum import nwStandardButton
41
42
  from novelwriter.extensions.configlayout import NColorLabel, NFixedPage, NScrollablePage
42
43
  from novelwriter.extensions.modified import NNonBlockingDialog
43
44
  from novelwriter.extensions.novelselector import NovelSelector
44
45
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
45
46
  from novelwriter.extensions.switch import NSwitch
46
- from novelwriter.types import QtAlignRight, QtDecoration, QtDialogClose
47
+ from novelwriter.types import QtAlignRight, QtDecoration, QtRoleDestruct
47
48
 
48
49
  if TYPE_CHECKING:
49
50
  from PyQt6.QtGui import QCloseEvent
@@ -52,6 +53,7 @@ logger = logging.getLogger(__name__)
52
53
 
53
54
 
54
55
  class GuiNovelDetails(NNonBlockingDialog):
56
+ """GUI: Novel Details Tool."""
55
57
 
56
58
  PAGE_OVERVIEW = 1
57
59
  PAGE_CONTENTS = 2
@@ -101,8 +103,11 @@ class GuiNovelDetails(NNonBlockingDialog):
101
103
  self.mainStack.addWidget(self.contentsPage)
102
104
 
103
105
  # Buttons
104
- self.buttonBox = QDialogButtonBox(QtDialogClose, self)
105
- self.buttonBox.rejected.connect(self.reject)
106
+ self.btnClose = SHARED.theme.getStandardButton(nwStandardButton.CLOSE, self)
107
+ self.btnClose.clicked.connect(self.reject)
108
+
109
+ self.btnBox = QDialogButtonBox(self)
110
+ self.btnBox.addButton(self.btnClose, QtRoleDestruct)
106
111
 
107
112
  # Assemble
108
113
  self.topBox = QHBoxLayout()
@@ -118,7 +123,7 @@ class GuiNovelDetails(NNonBlockingDialog):
118
123
  self.outerBox = QVBoxLayout()
119
124
  self.outerBox.addLayout(self.topBox)
120
125
  self.outerBox.addLayout(self.mainBox)
121
- self.outerBox.addWidget(self.buttonBox)
126
+ self.outerBox.addWidget(self.btnBox)
122
127
  self.outerBox.setSpacing(8)
123
128
 
124
129
  self.setLayout(self.outerBox)
@@ -130,11 +135,8 @@ class GuiNovelDetails(NNonBlockingDialog):
130
135
 
131
136
  logger.debug("Ready: GuiNovelDetails")
132
137
 
133
- return
134
-
135
138
  def __del__(self) -> None: # pragma: no cover
136
139
  logger.debug("Delete: GuiNovelDetails")
137
- return
138
140
 
139
141
  ##
140
142
  # Methods
@@ -146,7 +148,6 @@ class GuiNovelDetails(NNonBlockingDialog):
146
148
  self.overviewPage.updateProjectData()
147
149
  self.overviewPage.novelValueChanged(handle)
148
150
  self.contentsPage.novelValueChanged(handle)
149
- return
150
151
 
151
152
  ##
152
153
  # Events
@@ -157,7 +158,6 @@ class GuiNovelDetails(NNonBlockingDialog):
157
158
  self._saveSettings()
158
159
  event.accept()
159
160
  self.softDelete()
160
- return
161
161
 
162
162
  ##
163
163
  # Private Slots
@@ -170,7 +170,6 @@ class GuiNovelDetails(NNonBlockingDialog):
170
170
  self.mainStack.setCurrentWidget(self.overviewPage)
171
171
  elif pageId == self.PAGE_CONTENTS:
172
172
  self.mainStack.setCurrentWidget(self.contentsPage)
173
- return
174
173
 
175
174
  ##
176
175
  # Internal Functions
@@ -185,7 +184,6 @@ class GuiNovelDetails(NNonBlockingDialog):
185
184
  options.setValue("GuiNovelDetails", "winHeight", self.height())
186
185
  options.setValue("GuiNovelDetails", "novelRoot", novelRoot)
187
186
  self.contentsPage.saveSettings()
188
- return
189
187
 
190
188
 
191
189
  class _OverviewPage(NScrollablePage):
@@ -248,8 +246,6 @@ class _OverviewPage(NScrollablePage):
248
246
 
249
247
  self.setCentralLayout(self.outerBox)
250
248
 
251
- return
252
-
253
249
  ##
254
250
  # Methods
255
251
  ##
@@ -266,7 +262,6 @@ class _OverviewPage(NScrollablePage):
266
262
  self.projWords.setText(f"{wcNovel + wcNotes:n}")
267
263
  self.projNovels.setText(f"{wcNovel:n}")
268
264
  self.projNotes.setText(f"{wcNotes:n}")
269
- return
270
265
 
271
266
  ##
272
267
  # Public Slots
@@ -286,8 +281,6 @@ class _OverviewPage(NScrollablePage):
286
281
  self.novelChapters.setText(f"{hCounts[2]:n}")
287
282
  self.novelScenes.setText(f"{hCounts[3]:n}")
288
283
 
289
- return
290
-
291
284
 
292
285
  class _ContentsPage(NFixedPage):
293
286
 
@@ -400,8 +393,6 @@ class _ContentsPage(NFixedPage):
400
393
 
401
394
  self.setCentralLayout(self.outerBox)
402
395
 
403
- return
404
-
405
396
  def saveSettings(self) -> None:
406
397
  """Save the user GUI settings."""
407
398
  options = SHARED.project.options
@@ -413,7 +404,6 @@ class _ContentsPage(NFixedPage):
413
404
  options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value())
414
405
  options.setValue("GuiNovelDetails", "countFrom", self.poValue.value())
415
406
  options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked())
416
- return
417
407
 
418
408
  ##
419
409
  # Public Slots
@@ -426,7 +416,6 @@ class _ContentsPage(NFixedPage):
426
416
  self._prepareData(tHandle)
427
417
  self._populateTree()
428
418
  self._currentRoot = tHandle
429
- return
430
419
 
431
420
  ##
432
421
  # Private Slots
@@ -496,8 +485,6 @@ class _ContentsPage(NFixedPage):
496
485
 
497
486
  self.tocTree.addTopLevelItem(newItem)
498
487
 
499
- return
500
-
501
488
  ##
502
489
  # Internal Functions
503
490
  ##
@@ -507,4 +494,3 @@ class _ContentsPage(NFixedPage):
507
494
  logger.debug("Populating ToC from handle '%s'", rootHandle)
508
495
  self._data = SHARED.project.index.getTableOfContents(rootHandle, 2)
509
496
  self._data.append(("", 0, self.tr("END"), 0))
510
- 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
@@ -32,18 +32,18 @@ from PyQt6.QtCore import (
32
32
  QAbstractListModel, QModelIndex, QObject, QPoint, QSize, Qt, pyqtSignal,
33
33
  pyqtSlot
34
34
  )
35
- from PyQt6.QtGui import QAction, QCloseEvent, QColor, QFont, QPainter, QPaintEvent, QPen, QShortcut
35
+ from PyQt6.QtGui import QAction, QCloseEvent, QFont, QPainter, QPaintEvent, QPen, QShortcut
36
36
  from PyQt6.QtWidgets import (
37
37
  QApplication, QFileDialog, QFormLayout, QHBoxLayout, QLabel, QLineEdit,
38
- QListView, QMenu, QPushButton, QScrollArea, QStackedWidget,
39
- QStyledItemDelegate, QStyleOptionViewItem, QVBoxLayout, QWidget
38
+ QListView, QMenu, QScrollArea, QStackedWidget, QStyledItemDelegate,
39
+ QStyleOptionViewItem, QVBoxLayout, QWidget
40
40
  )
41
41
 
42
42
  from novelwriter import CONFIG, SHARED
43
43
  from novelwriter.common import formatInt, makeFileNameSafe, qtAddAction, qtLambda
44
44
  from novelwriter.constants import nwFiles
45
45
  from novelwriter.core.coretools import ProjectBuilder
46
- from novelwriter.enum import nwItemClass
46
+ from novelwriter.enum import nwItemClass, nwStandardButton
47
47
  from novelwriter.extensions.configlayout import NWrappedWidgetBox
48
48
  from novelwriter.extensions.modified import NDialog, NIconToolButton, NSpinBox
49
49
  from novelwriter.extensions.switch import NSwitch
@@ -56,6 +56,11 @@ PANEL_ALPHA = 178
56
56
 
57
57
 
58
58
  class GuiWelcome(NDialog):
59
+ """GUI: Welcome Dialog.
60
+
61
+ This is the main dialog shown when novelWriter launches or when the
62
+ user wants to create or open another project.
63
+ """
59
64
 
60
65
  openProjectRequest = pyqtSignal(Path)
61
66
 
@@ -70,14 +75,11 @@ class GuiWelcome(NDialog):
70
75
  self.setMinimumHeight(450)
71
76
  self.resize(*CONFIG.welcomeWinSize)
72
77
 
73
- btnIconSize = SHARED.theme.buttonIconSize
74
-
75
78
  # Elements
76
79
  # ========
77
80
 
78
81
  self.bgImage = SHARED.theme.getDecoration("welcome")
79
82
  self.nwImage = SHARED.theme.getDecoration("nw-text", h=36)
80
- self.bgColor = QColor(54, 54, 54) if SHARED.theme.isDarkTheme else QColor(255, 255, 255)
81
83
 
82
84
  self.nwLogo = QLabel(self)
83
85
  self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (128, 128)))
@@ -100,34 +102,22 @@ class GuiWelcome(NDialog):
100
102
  # Buttons
101
103
  # =======
102
104
 
103
- self.btnList = QPushButton(self.tr("List"), self)
104
- self.btnList.setIcon(SHARED.theme.getIcon("list", "blue"))
105
- self.btnList.setIconSize(btnIconSize)
105
+ self.btnList = SHARED.theme.getStandardButton(nwStandardButton.LIST, self)
106
106
  self.btnList.clicked.connect(self._showOpenProjectPage)
107
107
 
108
- self.btnNew = QPushButton(self.tr("New"), self)
109
- self.btnNew.setIcon(SHARED.theme.getIcon("add", "green"))
110
- self.btnNew.setIconSize(btnIconSize)
108
+ self.btnNew = SHARED.theme.getStandardButton(nwStandardButton.NEW, self)
111
109
  self.btnNew.clicked.connect(self._showNewProjectPage)
112
110
 
113
- self.btnBrowse = QPushButton(self.tr("Browse"), self)
114
- self.btnBrowse.setIcon(SHARED.theme.getIcon("browse", "yellow"))
115
- self.btnBrowse.setIconSize(btnIconSize)
111
+ self.btnBrowse = SHARED.theme.getStandardButton(nwStandardButton.BROWSE, self)
116
112
  self.btnBrowse.clicked.connect(self._browseForProject)
117
113
 
118
- self.btnCancel = QPushButton(self.tr("Cancel"), self)
119
- self.btnCancel.setIcon(SHARED.theme.getIcon("cancel", "red"))
120
- self.btnCancel.setIconSize(btnIconSize)
114
+ self.btnCancel = SHARED.theme.getStandardButton(nwStandardButton.CANCEL, self)
121
115
  self.btnCancel.clicked.connect(qtLambda(self.close))
122
116
 
123
- self.btnCreate = QPushButton(self.tr("Create"), self)
124
- self.btnCreate.setIcon(SHARED.theme.getIcon("star", "yellow"))
125
- self.btnCreate.setIconSize(btnIconSize)
117
+ self.btnCreate = SHARED.theme.getStandardButton(nwStandardButton.CREATE, self)
126
118
  self.btnCreate.clicked.connect(self.tabNew.createNewProject)
127
119
 
128
- self.btnOpen = QPushButton(self.tr("Open"), self)
129
- self.btnOpen.setIcon(SHARED.theme.getIcon("open", "blue"))
130
- self.btnOpen.setIconSize(btnIconSize)
120
+ self.btnOpen = SHARED.theme.getStandardButton(nwStandardButton.OPEN, self)
131
121
  self.btnOpen.clicked.connect(self._openSelectedItem)
132
122
 
133
123
  self.btnBox = QHBoxLayout()
@@ -162,11 +152,8 @@ class GuiWelcome(NDialog):
162
152
 
163
153
  logger.debug("Ready: GuiWelcome")
164
154
 
165
- return
166
-
167
155
  def __del__(self) -> None: # pragma: no cover
168
156
  logger.debug("Delete: GuiWelcome")
169
- return
170
157
 
171
158
  ##
172
159
  # Events
@@ -178,17 +165,15 @@ class GuiWelcome(NDialog):
178
165
  hPix = min(hWin, 600)
179
166
  tMode = Qt.TransformationMode.SmoothTransformation
180
167
  painter = QPainter(self)
181
- painter.fillRect(self.rect(), self.bgColor)
182
168
  painter.drawPixmap(0, hWin - hPix, self.bgImage.scaledToHeight(hPix, tMode))
169
+ painter.end()
183
170
  super().paintEvent(event)
184
- return
185
171
 
186
172
  def closeEvent(self, event: QCloseEvent) -> None:
187
173
  """Capture the user closing the window and save settings."""
188
174
  self._saveSettings()
189
175
  event.accept()
190
176
  self.softDelete()
191
- return
192
177
 
193
178
  ##
194
179
  # Private Slots
@@ -200,28 +185,24 @@ class GuiWelcome(NDialog):
200
185
  self.mainStack.setCurrentWidget(self.tabNew)
201
186
  self._setButtonVisibility()
202
187
  self.tabNew.enterForm()
203
- return
204
188
 
205
189
  @pyqtSlot()
206
190
  def _showOpenProjectPage(self) -> None:
207
191
  """Show the open exiting project page."""
208
192
  self.mainStack.setCurrentWidget(self.tabOpen)
209
193
  self._setButtonVisibility()
210
- return
211
194
 
212
195
  @pyqtSlot()
213
196
  def _browseForProject(self) -> None:
214
197
  """Browse for a project to open."""
215
198
  if path := SHARED.getProjectPath(self, path=CONFIG.homePath(), allowZip=False):
216
199
  self._openProjectPath(path)
217
- return
218
200
 
219
201
  @pyqtSlot()
220
202
  def _openSelectedItem(self) -> None:
221
203
  """Open the currently selected project item."""
222
204
  if self.mainStack.currentWidget() == self.tabOpen:
223
205
  self.tabOpen.openSelectedItem()
224
- return
225
206
 
226
207
  @pyqtSlot(Path)
227
208
  def _openProjectPath(self, path: Path) -> None:
@@ -232,7 +213,6 @@ class GuiWelcome(NDialog):
232
213
  self.hide()
233
214
  self.openProjectRequest.emit(path)
234
215
  self.close()
235
- return
236
216
 
237
217
  ##
238
218
  # Internal Functions
@@ -242,7 +222,6 @@ class GuiWelcome(NDialog):
242
222
  """Save the user GUI settings."""
243
223
  logger.debug("Saving State: GuiWelcome")
244
224
  CONFIG.setWelcomeWinSize(self.width(), self.height())
245
- return
246
225
 
247
226
  def _setButtonVisibility(self) -> None:
248
227
  """Change the visibility of the dialog buttons."""
@@ -256,7 +235,6 @@ class GuiWelcome(NDialog):
256
235
  self.btnOpen.setFocus()
257
236
  else:
258
237
  self.btnCreate.setFocus()
259
- return
260
238
 
261
239
 
262
240
  class _OpenProjectPage(QWidget):
@@ -280,7 +258,7 @@ class _OpenProjectPage(QWidget):
280
258
 
281
259
  # Info / Tool
282
260
  self.aMissing = QAction(self)
283
- self.aMissing.setIcon(SHARED.theme.getIcon("alert_warn", "orange"))
261
+ self.aMissing.setIcon(SHARED.theme.getIcon("alert_warn", "warning"))
284
262
  self.aMissing.setToolTip(self.tr("The project path is not reachable."))
285
263
 
286
264
  self.selectedPath = QLineEdit(self)
@@ -309,8 +287,6 @@ class _OpenProjectPage(QWidget):
309
287
  f"QLineEdit {{border: none; background: {baseCol}; padding: 4px;}} "
310
288
  )
311
289
 
312
- return
313
-
314
290
  ##
315
291
  # Public Slots
316
292
  ##
@@ -320,7 +296,6 @@ class _OpenProjectPage(QWidget):
320
296
  """Open the currently selected project item."""
321
297
  if (selection := self.listWidget.selectedIndexes()) and (index := selection[0]).isValid():
322
298
  self.openProjectRequest.emit(Path(str(index.data()[1])))
323
- return
324
299
 
325
300
  ##
326
301
  # Private Slots
@@ -336,14 +311,12 @@ class _OpenProjectPage(QWidget):
336
311
  self.selectedPath.setToolTip(text)
337
312
  self.selectedPath.setCursorPosition(0)
338
313
  self.aMissing.setVisible(not (Path(value) / nwFiles.PROJ_FILE).is_file())
339
- return
340
314
 
341
315
  @pyqtSlot(QModelIndex)
342
316
  def _projectDoubleClicked(self, index: QModelIndex) -> None:
343
317
  """Process double click on project item."""
344
318
  if index.isValid():
345
319
  self.openProjectRequest.emit(Path(str(index.data()[1])))
346
- return
347
320
 
348
321
  @pyqtSlot()
349
322
  def _deleteSelectedItem(self) -> None:
@@ -356,7 +329,6 @@ class _OpenProjectPage(QWidget):
356
329
  if SHARED.question(text):
357
330
  self.listModel.removeEntry(index)
358
331
  self._selectFirstItem()
359
- return
360
332
 
361
333
  @pyqtSlot("QPoint")
362
334
  def _openContextMenu(self, pos: QPoint) -> None:
@@ -369,7 +341,6 @@ class _OpenProjectPage(QWidget):
369
341
  action.triggered.connect(self._deleteSelectedItem)
370
342
  ctxMenu.exec(self.mapToGlobal(pos))
371
343
  ctxMenu.setParent(None)
372
- return
373
344
 
374
345
  ##
375
346
  # Internal Functions
@@ -380,7 +351,6 @@ class _OpenProjectPage(QWidget):
380
351
  index = self.listModel.index(0)
381
352
  self.listWidget.setCurrentIndex(index)
382
353
  self._projectClicked(index)
383
- return
384
354
 
385
355
 
386
356
  class _ProjectListItem(QStyledItemDelegate):
@@ -408,8 +378,6 @@ class _ProjectListItem(QStyledItemDelegate):
408
378
 
409
379
  self._icon = SHARED.theme.getPixmap("proj_nwx", (iPx, iPx))
410
380
 
411
- return
412
-
413
381
  def paint(self, painter: QPainter, opt: QStyleOptionViewItem, index: QModelIndex) -> None:
414
382
  """Paint a project entry on the canvas."""
415
383
  rect = opt.rect
@@ -420,7 +388,7 @@ class _ProjectListItem(QStyledItemDelegate):
420
388
  painter.save()
421
389
  if opt.state & QtSelected == QtSelected:
422
390
  painter.setOpacity(0.25)
423
- painter.fillRect(rect, QApplication.palette().highlight())
391
+ painter.fillRect(rect, QApplication.palette().text())
424
392
  painter.setOpacity(1.0)
425
393
 
426
394
  painter.drawPixmap(2, rect.top() + 6, self._icon)
@@ -431,8 +399,6 @@ class _ProjectListItem(QStyledItemDelegate):
431
399
  painter.drawText(rect.adjusted(x, y, 0, 0), tFlag, details)
432
400
  painter.restore()
433
401
 
434
- return
435
-
436
402
  def sizeHint(self, opt: QStyleOptionViewItem, index: QModelIndex) -> QSize:
437
403
  """Set the size hint to fixed height."""
438
404
  return QSize(opt.rect.width(), self._hPx)
@@ -450,7 +416,6 @@ class _ProjectListModel(QAbstractListModel):
450
416
  when = CONFIG.localDate(datetime.fromtimestamp(time))
451
417
  data.append((title, path, f"{opened}: {when}, {words}: {formatInt(count)}"))
452
418
  self._data = data
453
- return
454
419
 
455
420
  def rowCount(self, parent: QModelIndex | None = None) -> int:
456
421
  """Return the size of the model."""
@@ -517,8 +482,6 @@ class _NewProjectPage(QWidget):
517
482
  f"_NewProjectForm {{border: none; background: {baseCol};}} "
518
483
  )
519
484
 
520
- return
521
-
522
485
  ##
523
486
  # Public Slots
524
487
  ##
@@ -571,7 +534,7 @@ class _NewProjectForm(QWidget):
571
534
  self.projPath = QLineEdit(self)
572
535
  self.projPath.setReadOnly(True)
573
536
 
574
- self.browsePath = NIconToolButton(self, iSz, "browse")
537
+ self.browsePath = NIconToolButton(self, iSz, "browse", "systemio")
575
538
  self.browsePath.clicked.connect(self._doBrowse)
576
539
 
577
540
  self.pathBox = QHBoxLayout()
@@ -582,20 +545,20 @@ class _NewProjectForm(QWidget):
582
545
  self.projFill = QLineEdit(self)
583
546
  self.projFill.setReadOnly(True)
584
547
 
585
- self.browseFill = NIconToolButton(self, iSz, "document_add", "blue")
548
+ self.browseFill = NIconToolButton(self, iSz, "document_add", "add")
586
549
 
587
550
  self.fillMenu = QMenu(self.browseFill)
588
551
 
589
552
  self.fillBlank = qtAddAction(self.fillMenu, self.tr("Create a fresh project"))
590
- self.fillBlank.setIcon(SHARED.theme.getIcon("document"))
553
+ self.fillBlank.setIcon(SHARED.theme.getIcon("document", "file"))
591
554
  self.fillBlank.triggered.connect(self._setFillBlank)
592
555
 
593
556
  self.fillSample = qtAddAction(self.fillMenu, self.tr("Create an example project"))
594
- self.fillSample.setIcon(SHARED.theme.getIcon("document_add", "blue"))
557
+ self.fillSample.setIcon(SHARED.theme.getIcon("document_add", "add"))
595
558
  self.fillSample.triggered.connect(self._setFillSample)
596
559
 
597
560
  self.fillCopy = qtAddAction(self.fillMenu, self.tr("Copy an existing project"))
598
- self.fillCopy.setIcon(SHARED.theme.getIcon("project_copy", "green"))
561
+ self.fillCopy.setIcon(SHARED.theme.getIcon("project_copy", "action"))
599
562
  self.fillCopy.triggered.connect(self._setFillCopy)
600
563
 
601
564
  self.browseFill.setMenu(self.fillMenu)
@@ -690,13 +653,10 @@ class _NewProjectForm(QWidget):
690
653
  self._updateProjPath()
691
654
  self._updateFillInfo()
692
655
 
693
- return
694
-
695
656
  def enterForm(self) -> None:
696
657
  """Focus the project name field when entering the form."""
697
658
  self.projName.setFocus()
698
659
  self.projName.selectAll()
699
- return
700
660
 
701
661
  def getProjectData(self) -> dict:
702
662
  """Collect form data and return it as a dictionary."""
@@ -734,14 +694,12 @@ class _NewProjectForm(QWidget):
734
694
  self._basePath = Path(path)
735
695
  self._updateProjPath()
736
696
  CONFIG.setLastPath("project", path)
737
- return
738
697
 
739
698
  @pyqtSlot()
740
699
  def _updateProjPath(self) -> None:
741
700
  """Update the path box to show the full project path."""
742
701
  projName = makeFileNameSafe(self.projName.text().strip())
743
702
  self.projPath.setText(str(self._basePath / projName))
744
- return
745
703
 
746
704
  @pyqtSlot()
747
705
  def _syncSwitches(self) -> None:
@@ -751,21 +709,18 @@ class _NewProjectForm(QWidget):
751
709
  addWorld = self.addWorld.isChecked()
752
710
  if not (addPlot or addChar or addWorld):
753
711
  self.addNotes.setChecked(False)
754
- return
755
712
 
756
713
  @pyqtSlot()
757
714
  def _setFillBlank(self) -> None:
758
715
  """Set fill mode to blank project."""
759
716
  self._fillMode = self.FILL_BLANK
760
717
  self._updateFillInfo()
761
- return
762
718
 
763
719
  @pyqtSlot()
764
720
  def _setFillSample(self) -> None:
765
721
  """Set fill mode to sample project."""
766
722
  self._fillMode = self.FILL_SAMPLE
767
723
  self._updateFillInfo()
768
- return
769
724
 
770
725
  @pyqtSlot()
771
726
  def _setFillCopy(self) -> None:
@@ -774,7 +729,6 @@ class _NewProjectForm(QWidget):
774
729
  self._fillMode = self.FILL_COPY
775
730
  self._copyPath = copyPath
776
731
  self._updateFillInfo()
777
- return
778
732
 
779
733
  ##
780
734
  # Internal Functions
@@ -794,5 +748,3 @@ class _NewProjectForm(QWidget):
794
748
  self.projFill.setToolTip(text)
795
749
  self.projFill.setCursorPosition(0)
796
750
  self.extraWidget.setVisible(self._fillMode == self.FILL_BLANK)
797
-
798
- 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 json
@@ -39,12 +39,13 @@ from PyQt6.QtWidgets import (
39
39
  from novelwriter import CONFIG, SHARED
40
40
  from novelwriter.common import checkInt, checkIntTuple, formatTime, minmax, qtLambda
41
41
  from novelwriter.constants import nwConst
42
+ from novelwriter.enum import nwStandardButton
42
43
  from novelwriter.error import formatException
43
- from novelwriter.extensions.modified import NToolDialog
44
+ from novelwriter.extensions.modified import NPushButton, NToolDialog
44
45
  from novelwriter.extensions.switch import NSwitch
45
46
  from novelwriter.types import (
46
47
  QtAlignLeftMiddle, QtAlignRight, QtAlignRightMiddle, QtDecoration,
47
- QtDialogClose, QtRoleAction
48
+ QtRoleAction, QtRoleDestruct
48
49
  )
49
50
 
50
51
  if TYPE_CHECKING:
@@ -54,7 +55,7 @@ logger = logging.getLogger(__name__)
54
55
 
55
56
 
56
57
  class GuiWritingStats(NToolDialog):
57
- """GUI Tools: Writing Statistics
58
+ """GUI Tools: Writing Statistics.
58
59
 
59
60
  Displays data from the NWSessionLog object.
60
61
  """
@@ -182,6 +183,7 @@ class GuiWritingStats(NToolDialog):
182
183
 
183
184
  # Filter Options
184
185
  iPx = SHARED.theme.baseIconHeight
186
+ bSz = SHARED.theme.toolButtonIconSize
185
187
 
186
188
  self.filterForm = QGridLayout(self)
187
189
  self.filterForm.setRowStretch(6, 1)
@@ -276,6 +278,10 @@ class GuiWritingStats(NToolDialog):
276
278
  self.optsBox.addWidget(self.histMax, 0)
277
279
 
278
280
  # Buttons
281
+ self.btnClose = SHARED.theme.getStandardButton(nwStandardButton.CLOSE, self)
282
+ self.btnClose.clicked.connect(self._doClose)
283
+ self.btnClose.setAutoDefault(False)
284
+
279
285
  self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
280
286
  self.saveJSON.triggered.connect(qtLambda(self._saveData, self.FMT_JSON))
281
287
 
@@ -286,17 +292,13 @@ class GuiWritingStats(NToolDialog):
286
292
  self.saveMenu.addAction(self.saveJSON)
287
293
  self.saveMenu.addAction(self.saveCSV)
288
294
 
289
- self.buttonBox = QDialogButtonBox(self)
290
- self.buttonBox.rejected.connect(self._doClose)
295
+ self.btnSave = NPushButton(self, self.tr("Save As"), bSz, "btn_save", "action")
296
+ self.btnSave.setAutoDefault(False)
297
+ self.btnSave.setMenu(self.saveMenu)
291
298
 
292
- self.btnClose = self.buttonBox.addButton(QtDialogClose)
293
- if self.btnClose:
294
- self.btnClose.setAutoDefault(False)
295
-
296
- self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QtRoleAction)
297
- if self.btnSave:
298
- self.btnSave.setAutoDefault(False)
299
- self.btnSave.setMenu(self.saveMenu)
299
+ self.btnBox = QDialogButtonBox(self)
300
+ self.btnBox.addButton(self.btnSave, QtRoleAction)
301
+ self.btnBox.addButton(self.btnClose, QtRoleDestruct)
300
302
 
301
303
  # Assemble
302
304
  self.outerBox = QGridLayout()
@@ -304,18 +306,15 @@ class GuiWritingStats(NToolDialog):
304
306
  self.outerBox.addLayout(self.optsBox, 1, 0, 1, 2)
305
307
  self.outerBox.addWidget(self.infoBox, 2, 0)
306
308
  self.outerBox.addWidget(self.filterBox, 2, 1)
307
- self.outerBox.addWidget(self.buttonBox, 3, 0, 1, 2)
309
+ self.outerBox.addWidget(self.btnBox, 3, 0, 1, 2)
308
310
  self.outerBox.setRowStretch(0, 1)
309
311
 
310
312
  self.setLayout(self.outerBox)
311
313
 
312
314
  logger.debug("Ready: GuiWritingStats")
313
315
 
314
- return
315
-
316
316
  def __del__(self) -> None: # pragma: no cover
317
317
  logger.debug("Delete: GuiWritingStats")
318
- return
319
318
 
320
319
  def populateGUI(self) -> None:
321
320
  """Populate list box with data from the log file."""
@@ -323,7 +322,6 @@ class GuiWritingStats(NToolDialog):
323
322
  self._loadLogFile()
324
323
  self._updateListBox()
325
324
  QApplication.restoreOverrideCursor()
326
- return
327
325
 
328
326
  ##
329
327
  # Events
@@ -333,7 +331,6 @@ class GuiWritingStats(NToolDialog):
333
331
  """Capture the user closing the window."""
334
332
  event.accept()
335
333
  self.softDelete()
336
- return
337
334
 
338
335
  ##
339
336
  # Private Slots
@@ -377,8 +374,6 @@ class GuiWritingStats(NToolDialog):
377
374
 
378
375
  self.close()
379
376
 
380
- return
381
-
382
377
  def _saveData(self, dataFmt: int) -> bool:
383
378
  """Save the content of the list box to a file."""
384
379
  fileExt = ""
@@ -498,8 +493,6 @@ class GuiWritingStats(NToolDialog):
498
493
  self.notesWords.setText(f"{ttNotes:n}")
499
494
  self.totalWords.setText(f"{ttWords:n}")
500
495
 
501
- return
502
-
503
496
  ##
504
497
  # Private Slots
505
498
  ##
@@ -622,5 +615,3 @@ class GuiWritingStats(NToolDialog):
622
615
  self.timeFilter += sDiff
623
616
 
624
617
  self.labelFilter.setText(formatTime(round(self.timeFilter)))
625
-
626
- return