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
@@ -0,0 +1,113 @@
1
+ """
2
+ novelWriter – Text Pattern Functions
3
+ ====================================
4
+
5
+ File History:
6
+ Created: 2024-06-01 [2.5ec1]
7
+
8
+ This file is a part of novelWriter
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
10
+
11
+ This program is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful, but
17
+ WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
+ General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ from PyQt5.QtCore import QRegularExpression
27
+
28
+ from novelwriter import CONFIG
29
+ from novelwriter.constants import nwRegEx
30
+ from novelwriter.types import QRegExUnicode
31
+
32
+
33
+ class RegExPatterns:
34
+
35
+ @property
36
+ def markdownItalic(self) -> QRegularExpression:
37
+ """Markdown italic style."""
38
+ rxRule = QRegularExpression(nwRegEx.FMT_EI)
39
+ rxRule.setPatternOptions(QRegExUnicode)
40
+ return rxRule
41
+
42
+ @property
43
+ def markdownBold(self) -> QRegularExpression:
44
+ """Markdown bold style."""
45
+ rxRule = QRegularExpression(nwRegEx.FMT_EB)
46
+ rxRule.setPatternOptions(QRegExUnicode)
47
+ return rxRule
48
+
49
+ @property
50
+ def markdownStrike(self) -> QRegularExpression:
51
+ """Markdown strikethrough style."""
52
+ rxRule = QRegularExpression(nwRegEx.FMT_ST)
53
+ rxRule.setPatternOptions(QRegExUnicode)
54
+ return rxRule
55
+
56
+ @property
57
+ def shortcodePlain(self) -> QRegularExpression:
58
+ """Plain shortcode style."""
59
+ rxRule = QRegularExpression(nwRegEx.FMT_SC)
60
+ rxRule.setPatternOptions(QRegExUnicode)
61
+ return rxRule
62
+
63
+ @property
64
+ def shortcodeValue(self) -> QRegularExpression:
65
+ """Plain shortcode style."""
66
+ rxRule = QRegularExpression(nwRegEx.FMT_SV)
67
+ rxRule.setPatternOptions(QRegExUnicode)
68
+ return rxRule
69
+
70
+ @property
71
+ def dialogStyle(self) -> QRegularExpression:
72
+ """Dialogue detection rule based on user settings."""
73
+ symO = ""
74
+ symC = ""
75
+ if CONFIG.dialogStyle in (1, 3):
76
+ symO += CONFIG.fmtSQuoteOpen
77
+ symC += CONFIG.fmtSQuoteClose
78
+ if CONFIG.dialogStyle in (2, 3):
79
+ symO += CONFIG.fmtDQuoteOpen
80
+ symC += CONFIG.fmtDQuoteClose
81
+
82
+ rxEnd = "|$" if CONFIG.allowOpenDial else ""
83
+ rxRule = QRegularExpression(f"\\B[{symO}].*?(?:[{symC}]\\B{rxEnd})")
84
+ rxRule.setPatternOptions(QRegExUnicode)
85
+ return rxRule
86
+
87
+ @property
88
+ def dialogLine(self) -> QRegularExpression:
89
+ """Dialogue line rule based on user settings."""
90
+ sym = QRegularExpression.escape(CONFIG.dialogLine)
91
+ rxRule = QRegularExpression(f"^{sym}.*?$")
92
+ rxRule.setPatternOptions(QRegExUnicode)
93
+ return rxRule
94
+
95
+ @property
96
+ def narratorBreak(self) -> QRegularExpression:
97
+ """Dialogue narrator break rule based on user settings."""
98
+ sym = QRegularExpression.escape(CONFIG.narratorBreak)
99
+ rxRule = QRegularExpression(f"\\B{sym}\\S.*?\\S{sym}\\B")
100
+ rxRule.setPatternOptions(QRegExUnicode)
101
+ return rxRule
102
+
103
+ @property
104
+ def altDialogStyle(self) -> QRegularExpression:
105
+ """Dialogue alternative rule based on user settings."""
106
+ symO = QRegularExpression.escape(CONFIG.altDialogOpen)
107
+ symC = QRegularExpression.escape(CONFIG.altDialogClose)
108
+ rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B")
109
+ rxRule.setPatternOptions(QRegExUnicode)
110
+ return rxRule
111
+
112
+
113
+ REGEX_PATTERNS = RegExPatterns()
@@ -109,7 +109,7 @@ class GuiDictionaries(NNonBlockingDialog):
109
109
 
110
110
  # Buttons
111
111
  self.buttonBox = QDialogButtonBox(QtDialogClose, self)
112
- self.buttonBox.rejected.connect(self._doClose)
112
+ self.buttonBox.rejected.connect(self.reject)
113
113
 
114
114
  # Assemble
115
115
  self.innerBox = QVBoxLayout()
@@ -172,7 +172,7 @@ class GuiDictionaries(NNonBlockingDialog):
172
172
  def closeEvent(self, event: QCloseEvent) -> None:
173
173
  """Capture the user closing the window."""
174
174
  event.accept()
175
- self.deleteLater()
175
+ self.softDelete()
176
176
  return
177
177
 
178
178
  ##
@@ -220,12 +220,6 @@ class GuiDictionaries(NNonBlockingDialog):
220
220
  SHARED.error("Path not found.")
221
221
  return
222
222
 
223
- @pyqtSlot()
224
- def _doClose(self) -> None:
225
- """Close the dialog."""
226
- self.close()
227
- return
228
-
229
223
  ##
230
224
  # Internal Functions
231
225
  ##
@@ -26,34 +26,32 @@ from __future__ import annotations
26
26
  import logging
27
27
  import random
28
28
 
29
- from PyQt5.QtCore import Qt, pyqtSlot
29
+ from PyQt5.QtCore import pyqtSlot
30
30
  from PyQt5.QtWidgets import (
31
- QDialog, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox,
32
- QVBoxLayout, QWidget
31
+ QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox, QVBoxLayout,
32
+ QWidget
33
33
  )
34
34
 
35
35
  from novelwriter import CONFIG, SHARED
36
36
  from novelwriter.common import readTextFile
37
+ from novelwriter.extensions.modified import NDialog
37
38
  from novelwriter.extensions.switch import NSwitch
38
39
  from novelwriter.types import QtAlignLeft, QtAlignRight, QtDialogClose, QtRoleAction
39
40
 
40
41
  logger = logging.getLogger(__name__)
41
42
 
42
43
 
43
- class GuiLipsum(QDialog):
44
+ class GuiLipsum(NDialog):
44
45
 
45
46
  def __init__(self, parent: QWidget) -> None:
46
47
  super().__init__(parent=parent)
47
48
 
48
49
  logger.debug("Create: GuiLipsum")
49
50
  self.setObjectName("GuiLipsum")
50
- if CONFIG.osDarwin:
51
- self.setWindowFlag(Qt.WindowType.Tool)
51
+ self.setWindowTitle(self.tr("Insert Placeholder Text"))
52
52
 
53
53
  self._lipsumText = ""
54
54
 
55
- self.setWindowTitle(self.tr("Insert Placeholder Text"))
56
-
57
55
  vSp = CONFIG.pxInt(4)
58
56
  nPx = CONFIG.pxInt(64)
59
57
 
@@ -95,7 +93,7 @@ class GuiLipsum(QDialog):
95
93
 
96
94
  # Buttons
97
95
  self.buttonBox = QDialogButtonBox(self)
98
- self.buttonBox.rejected.connect(self.close)
96
+ self.buttonBox.rejected.connect(self.reject)
99
97
 
100
98
  self.btnClose = self.buttonBox.addButton(QtDialogClose)
101
99
  self.btnClose.setAutoDefault(False)
@@ -104,8 +102,6 @@ class GuiLipsum(QDialog):
104
102
  self.btnInsert.clicked.connect(self._doInsert)
105
103
  self.btnInsert.setAutoDefault(False)
106
104
 
107
- self.rejected.connect(self.close)
108
-
109
105
  # Assemble
110
106
  self.outerBox = QVBoxLayout()
111
107
  self.outerBox.addLayout(self.innerBox)
@@ -132,7 +128,7 @@ class GuiLipsum(QDialog):
132
128
  cls = GuiLipsum(parent)
133
129
  cls.exec()
134
130
  text = cls.lipsumText
135
- cls.deleteLater()
131
+ cls.softDelete()
136
132
  return text
137
133
 
138
134
  ##
@@ -30,7 +30,7 @@ from pathlib import Path
30
30
  from PyQt5.QtCore import QTimer, pyqtSlot
31
31
  from PyQt5.QtGui import QCloseEvent
32
32
  from PyQt5.QtWidgets import (
33
- QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog,
33
+ QAbstractButton, QAbstractItemView, QDialogButtonBox, QFileDialog,
34
34
  QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
35
35
  QPushButton, QSplitter, QVBoxLayout, QWidget
36
36
  )
@@ -42,14 +42,14 @@ from novelwriter.core.buildsettings import BuildSettings
42
42
  from novelwriter.core.docbuild import NWBuildDocument
43
43
  from novelwriter.core.item import NWItem
44
44
  from novelwriter.enum import nwBuildFmt
45
- from novelwriter.extensions.modified import NIconToolButton
46
- from novelwriter.extensions.simpleprogress import NProgressSimple
45
+ from novelwriter.extensions.modified import NDialog, NIconToolButton
46
+ from novelwriter.extensions.progressbars import NProgressSimple
47
47
  from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
48
48
 
49
49
  logger = logging.getLogger(__name__)
50
50
 
51
51
 
52
- class GuiManuscriptBuild(QDialog):
52
+ class GuiManuscriptBuild(NDialog):
53
53
  """GUI Tools: Manuscript Build Dialog
54
54
 
55
55
  This is the tool for running the build itself. It can be accessed
@@ -220,7 +220,7 @@ class GuiManuscriptBuild(QDialog):
220
220
 
221
221
  self.btnBuild.setFocus()
222
222
  self._populateContentList()
223
- self.buildPath.setText(str(self._build.lastPath))
223
+ self.buildPath.setText(str(self._build.lastBuildPath))
224
224
  if self._build.lastBuildName:
225
225
  self.buildName.setText(self._build.lastBuildName)
226
226
  else:
@@ -250,7 +250,7 @@ class GuiManuscriptBuild(QDialog):
250
250
  """
251
251
  self._saveSettings()
252
252
  event.accept()
253
- self.deleteLater()
253
+ self.softDelete()
254
254
  return
255
255
 
256
256
  ##
@@ -274,7 +274,7 @@ class GuiManuscriptBuild(QDialog):
274
274
  def _doSelectPath(self) -> None:
275
275
  """Select a folder for output."""
276
276
  bPath = Path(self.buildPath.text())
277
- bPath = bPath if bPath.is_dir() else self._build.lastPath
277
+ bPath = bPath if bPath.is_dir() else self._build.lastBuildPath
278
278
  savePath = QFileDialog.getExistingDirectory(
279
279
  self, self.tr("Select Folder"), str(bPath)
280
280
  )
@@ -327,7 +327,7 @@ class GuiManuscriptBuild(QDialog):
327
327
  return False
328
328
 
329
329
  # Make sure editor content is saved before we start
330
- SHARED.saveDocument()
330
+ SHARED.saveEditor()
331
331
 
332
332
  docBuild = NWBuildDocument(SHARED.project, self._build)
333
333
  docBuild.queueAll()
@@ -336,7 +336,7 @@ class GuiManuscriptBuild(QDialog):
336
336
  for i, _ in docBuild.iterBuild(buildPath, bFormat):
337
337
  self.buildProgress.setValue(i+1)
338
338
 
339
- self._build.setLastPath(bPath)
339
+ self._build.setLastBuildPath(bPath)
340
340
  self._build.setLastBuildName(bName)
341
341
  self._build.setLastFormat(bFormat)
342
342
 
@@ -44,8 +44,8 @@ from novelwriter.core.buildsettings import BuildCollection, BuildSettings
44
44
  from novelwriter.core.docbuild import NWBuildDocument
45
45
  from novelwriter.core.tokenizer import HeadingFormatter
46
46
  from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
47
- from novelwriter.extensions.circularprogress import NProgressCircle
48
47
  from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
48
+ from novelwriter.extensions.progressbars import NProgressCircle
49
49
  from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
50
50
  from novelwriter.tools.manusbuild import GuiManuscriptBuild
51
51
  from novelwriter.tools.manussettings import GuiBuildSettings
@@ -265,7 +265,7 @@ class GuiManuscript(NToolDialog):
265
265
  if isinstance(obj, GuiBuildSettings) and obj.isVisible():
266
266
  obj.close()
267
267
  event.accept()
268
- self.deleteLater()
268
+ self.softDelete()
269
269
  return
270
270
 
271
271
  ##
@@ -322,8 +322,10 @@ class GuiManuscript(NToolDialog):
322
322
  if not (build := self._getSelectedBuild()):
323
323
  return
324
324
 
325
+ start = time()
326
+
325
327
  # Make sure editor content is saved before we start
326
- SHARED.saveDocument()
328
+ SHARED.saveEditor()
327
329
 
328
330
  docBuild = NWBuildDocument(SHARED.project, build)
329
331
  docBuild.queueAll()
@@ -339,6 +341,8 @@ class GuiManuscript(NToolDialog):
339
341
  theme.keyword = QColor(245, 135, 31)
340
342
  theme.tag = QColor(66, 113, 174)
341
343
  theme.optional = QColor(66, 113, 174)
344
+ theme.dialog = QColor(66, 113, 174)
345
+ theme.altdialog = QColor(129, 55, 9)
342
346
 
343
347
  self.docPreview.beginNewBuild(len(docBuild))
344
348
  for step, _ in docBuild.iterBuildPreview(theme):
@@ -358,6 +362,8 @@ class GuiManuscript(NToolDialog):
358
362
  self.docStats.updateStats(buildObj.textStats)
359
363
  self.buildOutline.updateOutline(buildObj.textOutline)
360
364
 
365
+ logger.debug("Build completed in %.3f ms", 1000*(time()-start))
366
+
361
367
  return
362
368
 
363
369
  @pyqtSlot()
@@ -891,9 +897,8 @@ class _PreviewWidget(QTextBrowser):
891
897
  document within the viewport.
892
898
  """
893
899
  vBar = self.verticalScrollBar()
894
- sW = vBar.width() if vBar.isVisible() else 0
895
900
  tB = self.frameWidth()
896
- vW = self.width() - 2*tB - sW
901
+ vW = self.width() - 2*tB - vBar.width()
897
902
  vH = self.height() - 2*tB
898
903
  tH = self.ageLabel.height()
899
904
  pS = self.buildProgress.width()
@@ -98,8 +98,8 @@ class GuiBuildSettings(NToolDialog):
98
98
 
99
99
  # Title
100
100
  self.titleLabel = NColourLabel(
101
- self.tr("Manuscript Build Settings"), SHARED.theme.helpText,
102
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
101
+ self.tr("Manuscript Build Settings"), self, color=SHARED.theme.helpText,
102
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
103
103
  )
104
104
 
105
105
  # Settings Name
@@ -200,7 +200,7 @@ class GuiBuildSettings(NToolDialog):
200
200
  self._askToSaveBuild()
201
201
  self._saveSettings()
202
202
  event.accept()
203
- self.deleteLater()
203
+ self.softDelete()
204
204
  return
205
205
 
206
206
  ##
@@ -1093,11 +1093,13 @@ class _FormatTab(NScrollableForm):
1093
1093
  self.stripUnicode = NSwitch(self, height=iPx)
1094
1094
  self.replaceTabs = NSwitch(self, height=iPx)
1095
1095
  self.keepBreaks = NSwitch(self, height=iPx)
1096
+ self.showDialogue = NSwitch(self, height=iPx)
1096
1097
 
1097
1098
  self.addRow(self._build.getLabel("format.justifyText"), self.justifyText)
1098
1099
  self.addRow(self._build.getLabel("format.stripUnicode"), self.stripUnicode)
1099
1100
  self.addRow(self._build.getLabel("format.replaceTabs"), self.replaceTabs)
1100
1101
  self.addRow(self._build.getLabel("format.keepBreaks"), self.keepBreaks)
1102
+ self.addRow(self._build.getLabel("format.showDialogue"), self.showDialogue)
1101
1103
 
1102
1104
  # First Line Indent
1103
1105
  # =================
@@ -1180,6 +1182,7 @@ class _FormatTab(NScrollableForm):
1180
1182
  self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode"))
1181
1183
  self.replaceTabs.setChecked(self._build.getBool("format.replaceTabs"))
1182
1184
  self.keepBreaks.setChecked(self._build.getBool("format.keepBreaks"))
1185
+ self.showDialogue.setChecked(self._build.getBool("format.showDialogue"))
1183
1186
 
1184
1187
  self.firstIndent.setChecked(self._build.getBool("format.firstLineIndent"))
1185
1188
  self.indentWidth.setValue(self._build.getFloat("format.firstIndentWidth"))
@@ -1219,6 +1222,7 @@ class _FormatTab(NScrollableForm):
1219
1222
  self._build.setValue("format.stripUnicode", self.stripUnicode.isChecked())
1220
1223
  self._build.setValue("format.replaceTabs", self.replaceTabs.isChecked())
1221
1224
  self._build.setValue("format.keepBreaks", self.keepBreaks.isChecked())
1225
+ self._build.setValue("format.showDialogue", self.showDialogue.isChecked())
1222
1226
 
1223
1227
  self._build.setValue("format.firstLineIndent", self.firstIndent.isChecked())
1224
1228
  self._build.setValue("format.firstIndentWidth", self.indentWidth.value())
@@ -68,8 +68,8 @@ class GuiNovelDetails(NNonBlockingDialog):
68
68
 
69
69
  # Title
70
70
  self.titleLabel = NColourLabel(
71
- self.tr("Novel Details"), SHARED.theme.helpText,
72
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
71
+ self.tr("Novel Details"), self, color=SHARED.theme.helpText,
72
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
73
73
  )
74
74
 
75
75
  # Novel Selector
@@ -97,7 +97,7 @@ class GuiNovelDetails(NNonBlockingDialog):
97
97
 
98
98
  # Buttons
99
99
  self.buttonBox = QDialogButtonBox(QtDialogClose, self)
100
- self.buttonBox.rejected.connect(self.close)
100
+ self.buttonBox.rejected.connect(self.reject)
101
101
 
102
102
  # Assemble
103
103
  self.topBox = QHBoxLayout()
@@ -150,7 +150,7 @@ class GuiNovelDetails(NNonBlockingDialog):
150
150
  """Capture the user closing the window and save settings."""
151
151
  self._saveSettings()
152
152
  event.accept()
153
- self.deleteLater()
153
+ self.softDelete()
154
154
  return
155
155
 
156
156
  ##
@@ -199,8 +199,8 @@ class _OverviewPage(NScrollablePage):
199
199
 
200
200
  # Project Info
201
201
  self.projLabel = NColourLabel(
202
- self.tr("Project"), SHARED.theme.helpText,
203
- parent=self, scale=NColourLabel.HEADER_SCALE
202
+ self.tr("Project"), self, color=SHARED.theme.helpText,
203
+ scale=NColourLabel.HEADER_SCALE
204
204
  )
205
205
 
206
206
  self.projName = QLabel("", self)
@@ -223,8 +223,8 @@ class _OverviewPage(NScrollablePage):
223
223
 
224
224
  # Novel Info
225
225
  self.novelLabel = NColourLabel(
226
- self.tr("Selected Novel"), SHARED.theme.helpText,
227
- parent=self, scale=NColourLabel.HEADER_SCALE
226
+ self.tr("Selected Novel"), self, color=SHARED.theme.helpText,
227
+ scale=NColourLabel.HEADER_SCALE
228
228
  )
229
229
 
230
230
  self.novelName = QLabel("", self)
@@ -315,8 +315,8 @@ class _ContentsPage(NFixedPage):
315
315
 
316
316
  # Title
317
317
  self.contentLabel = NColourLabel(
318
- self.tr("Table of Contents"), SHARED.theme.helpText,
319
- parent=self, scale=NColourLabel.HEADER_SCALE
318
+ self.tr("Table of Contents"), self, color=SHARED.theme.helpText,
319
+ scale=NColourLabel.HEADER_SCALE
320
320
  )
321
321
 
322
322
  # Contents Tree
@@ -34,8 +34,8 @@ from PyQt5.QtCore import (
34
34
  )
35
35
  from PyQt5.QtGui import QCloseEvent, QColor, QFont, QPainter, QPaintEvent, QPen
36
36
  from PyQt5.QtWidgets import (
37
- QAction, QApplication, QDialog, QFileDialog, QFormLayout, QHBoxLayout,
38
- QLabel, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
37
+ QAction, QApplication, QFileDialog, QFormLayout, QHBoxLayout, QLabel,
38
+ QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
39
39
  QStackedWidget, QStyledItemDelegate, QStyleOptionViewItem, QVBoxLayout,
40
40
  QWidget
41
41
  )
@@ -46,7 +46,7 @@ from novelwriter.constants import nwFiles
46
46
  from novelwriter.core.coretools import ProjectBuilder
47
47
  from novelwriter.enum import nwItemClass
48
48
  from novelwriter.extensions.configlayout import NWrappedWidgetBox
49
- from novelwriter.extensions.modified import NIconToolButton, NSpinBox
49
+ from novelwriter.extensions.modified import NDialog, NIconToolButton, NSpinBox
50
50
  from novelwriter.extensions.switch import NSwitch
51
51
  from novelwriter.extensions.versioninfo import VersionInfoWidget
52
52
  from novelwriter.types import QtAlignLeft, QtAlignRightTop, QtSelected
@@ -56,7 +56,7 @@ logger = logging.getLogger(__name__)
56
56
  PANEL_ALPHA = 178
57
57
 
58
58
 
59
- class GuiWelcome(QDialog):
59
+ class GuiWelcome(NDialog):
60
60
 
61
61
  openProjectRequest = pyqtSignal(Path)
62
62
 
@@ -196,7 +196,7 @@ class GuiWelcome(QDialog):
196
196
  """Capture the user closing the window and save settings."""
197
197
  self._saveSettings()
198
198
  event.accept()
199
- self.deleteLater()
199
+ self.softDelete()
200
200
  return
201
201
 
202
202
  ##
@@ -208,6 +208,7 @@ class GuiWelcome(QDialog):
208
208
  """Show the create new project page."""
209
209
  self.mainStack.setCurrentWidget(self.tabNew)
210
210
  self._setButtonVisibility()
211
+ self.tabNew.enterForm()
211
212
  return
212
213
 
213
214
  @pyqtSlot()
@@ -220,8 +221,7 @@ class GuiWelcome(QDialog):
220
221
  @pyqtSlot()
221
222
  def _browseForProject(self) -> None:
222
223
  """Browse for a project to open."""
223
- if path := SHARED.getProjectPath(self, path=CONFIG.lastPath(), allowZip=False):
224
- CONFIG.setLastPath(path)
224
+ if path := SHARED.getProjectPath(self, path=CONFIG.homePath(), allowZip=False):
225
225
  self._openProjectPath(path)
226
226
  return
227
227
 
@@ -504,6 +504,8 @@ class _NewProjectPage(QWidget):
504
504
  self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
505
505
  self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
506
506
 
507
+ self.enterForm = self.projectForm.enterForm
508
+
507
509
  # Assemble
508
510
  # ========
509
511
 
@@ -550,7 +552,7 @@ class _NewProjectForm(QWidget):
550
552
  def __init__(self, parent: QWidget) -> None:
551
553
  super().__init__(parent=parent)
552
554
 
553
- self._basePath = CONFIG.homePath()
555
+ self._basePath = CONFIG.lastPath("project")
554
556
  self._fillMode = self.FILL_BLANK
555
557
  self._copyPath = None
556
558
 
@@ -697,6 +699,12 @@ class _NewProjectForm(QWidget):
697
699
 
698
700
  return
699
701
 
702
+ def enterForm(self) -> None:
703
+ """Focus the project name field when entering the form."""
704
+ self.projName.setFocus()
705
+ self.projName.selectAll()
706
+ return
707
+
700
708
  def getProjectData(self) -> dict:
701
709
  """Collect form data and return it as a dictionary."""
702
710
  roots = []
@@ -726,12 +734,13 @@ class _NewProjectForm(QWidget):
726
734
  @pyqtSlot()
727
735
  def _doBrowse(self) -> None:
728
736
  """Select a project folder."""
729
- if projDir := QFileDialog.getExistingDirectory(
737
+ if path := QFileDialog.getExistingDirectory(
730
738
  self, self.tr("Select Project Folder"),
731
739
  str(self._basePath), options=QFileDialog.Option.ShowDirsOnly
732
740
  ):
733
- self._basePath = Path(projDir)
741
+ self._basePath = Path(path)
734
742
  self._updateProjPath()
743
+ CONFIG.setLastPath("project", path)
735
744
  return
736
745
 
737
746
  @pyqtSlot()
@@ -318,7 +318,7 @@ class GuiWritingStats(NToolDialog):
318
318
  def closeEvent(self, event: QCloseEvent) -> None:
319
319
  """Capture the user closing the window."""
320
320
  event.accept()
321
- self.deleteLater()
321
+ self.softDelete()
322
322
  return
323
323
 
324
324
  ##
@@ -384,14 +384,14 @@ class GuiWritingStats(NToolDialog):
384
384
  return False
385
385
 
386
386
  # Generate the file name
387
- savePath = CONFIG.lastPath() / f"sessionStats.{fileExt}"
387
+ savePath = CONFIG.lastPath("stats") / f"sessionStats.{fileExt}"
388
388
  savePath, _ = QFileDialog.getSaveFileName(
389
389
  self, self.tr("Save Data As"), str(savePath), f"{textFmt} (*.{fileExt})"
390
390
  )
391
391
  if not savePath:
392
392
  return False
393
393
 
394
- CONFIG.setLastPath(savePath)
394
+ CONFIG.setLastPath("stats", savePath)
395
395
 
396
396
  # Do the actual writing
397
397
  wSuccess = False
novelwriter/types.py CHANGED
@@ -25,7 +25,7 @@ from __future__ import annotations
25
25
 
26
26
  from PyQt5.QtCore import QRegularExpression, Qt
27
27
  from PyQt5.QtGui import QColor, QFont, QPainter, QTextCharFormat, QTextCursor, QTextFormat
28
- from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle
28
+ from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy, QStyle
29
29
 
30
30
  # Qt Alignment Flags
31
31
 
@@ -73,13 +73,16 @@ QtUserRole = Qt.ItemDataRole.UserRole
73
73
  # Keyboard and Mouse Buttons
74
74
 
75
75
  QtModCtrl = Qt.KeyboardModifier.ControlModifier
76
- QtModeNone = Qt.KeyboardModifier.NoModifier
76
+ QtModNone = Qt.KeyboardModifier.NoModifier
77
77
  QtModShift = Qt.KeyboardModifier.ShiftModifier
78
78
  QtMouseLeft = Qt.MouseButton.LeftButton
79
79
  QtMouseMiddle = Qt.MouseButton.MiddleButton
80
80
 
81
81
  # Dialog Button Box Types
82
82
 
83
+ QtAccepted = QDialog.DialogCode.Accepted
84
+ QtRejected = QDialog.DialogCode.Rejected
85
+
83
86
  QtDialogApply = QDialogButtonBox.StandardButton.Apply
84
87
  QtDialogCancel = QDialogButtonBox.StandardButton.Cancel
85
88
  QtDialogClose = QDialogButtonBox.StandardButton.Close
@@ -1,53 +0,0 @@
1
- """
2
- novelWriter – Custom Widget: Progress Simple
3
- ============================================
4
-
5
- File History:
6
- Created: 2023-06-09 [2.1b1]
7
-
8
- This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
10
-
11
- This program is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- This program is distributed in the hope that it will be useful, but
17
- WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
- General Public License for more details.
20
-
21
- You should have received a copy of the GNU General Public License
22
- along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
24
- from __future__ import annotations
25
-
26
- from math import ceil
27
-
28
- from PyQt5.QtGui import QPainter, QPaintEvent
29
- from PyQt5.QtWidgets import QProgressBar, QWidget
30
-
31
- from novelwriter.types import QtPaintAnitAlias
32
-
33
-
34
- class NProgressSimple(QProgressBar):
35
- """Extension: Simple Progress Widget
36
-
37
- A custom widget that paints a plain bar with no other styling.
38
- """
39
-
40
- def __init__(self, parent: QWidget) -> None:
41
- super().__init__(parent=parent)
42
- return
43
-
44
- def paintEvent(self, event: QPaintEvent) -> None:
45
- """Custom painter for the progress bar."""
46
- if (value := self.value()) > 0:
47
- progress = ceil(self.width()*float(value)/self.maximum())
48
- painter = QPainter(self)
49
- painter.setRenderHint(QtPaintAnitAlias, True)
50
- painter.setPen(self.palette().highlight().color())
51
- painter.setBrush(self.palette().highlight())
52
- painter.drawRect(0, 0, progress, self.height())
53
- return