novelWriter 2.5b1__py3-none-any.whl → 2.5rc1__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 (62) hide show
  1. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/RECORD +61 -61
  3. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +3 -3
  5. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  6. novelwriter/assets/i18n/project_pt_BR.json +74 -74
  7. novelwriter/assets/manual.pdf +0 -0
  8. novelwriter/assets/sample.zip +0 -0
  9. novelwriter/assets/themes/cyberpunk_night.conf +1 -0
  10. novelwriter/assets/themes/default_dark.conf +1 -0
  11. novelwriter/assets/themes/default_light.conf +1 -0
  12. novelwriter/assets/themes/dracula.conf +1 -0
  13. novelwriter/assets/themes/solarized_dark.conf +1 -0
  14. novelwriter/assets/themes/solarized_light.conf +1 -0
  15. novelwriter/common.py +2 -3
  16. novelwriter/config.py +67 -15
  17. novelwriter/constants.py +8 -10
  18. novelwriter/core/buildsettings.py +5 -3
  19. novelwriter/core/coretools.py +3 -1
  20. novelwriter/core/docbuild.py +1 -0
  21. novelwriter/core/tohtml.py +69 -29
  22. novelwriter/core/tokenizer.py +83 -14
  23. novelwriter/core/toodt.py +48 -21
  24. novelwriter/core/toqdoc.py +25 -9
  25. novelwriter/dialogs/about.py +10 -15
  26. novelwriter/dialogs/docmerge.py +16 -16
  27. novelwriter/dialogs/docsplit.py +16 -16
  28. novelwriter/dialogs/editlabel.py +6 -8
  29. novelwriter/dialogs/preferences.py +94 -68
  30. novelwriter/dialogs/projectsettings.py +10 -10
  31. novelwriter/dialogs/quotes.py +9 -5
  32. novelwriter/dialogs/wordlist.py +6 -6
  33. novelwriter/enum.py +4 -5
  34. novelwriter/extensions/configlayout.py +23 -4
  35. novelwriter/extensions/modified.py +22 -3
  36. novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
  37. novelwriter/extensions/statusled.py +28 -22
  38. novelwriter/gui/doceditor.py +20 -11
  39. novelwriter/gui/dochighlight.py +30 -39
  40. novelwriter/gui/docviewer.py +21 -14
  41. novelwriter/gui/mainmenu.py +11 -11
  42. novelwriter/gui/outline.py +3 -3
  43. novelwriter/gui/projtree.py +19 -28
  44. novelwriter/gui/search.py +10 -1
  45. novelwriter/gui/statusbar.py +25 -29
  46. novelwriter/gui/theme.py +3 -0
  47. novelwriter/guimain.py +91 -84
  48. novelwriter/shared.py +10 -8
  49. novelwriter/text/patterns.py +113 -0
  50. novelwriter/tools/dictionaries.py +2 -8
  51. novelwriter/tools/lipsum.py +8 -12
  52. novelwriter/tools/manusbuild.py +9 -9
  53. novelwriter/tools/manuscript.py +10 -5
  54. novelwriter/tools/manussettings.py +7 -3
  55. novelwriter/tools/noveldetails.py +10 -10
  56. novelwriter/tools/welcome.py +10 -10
  57. novelwriter/tools/writingstats.py +3 -3
  58. novelwriter/types.py +5 -2
  59. novelwriter/extensions/simpleprogress.py +0 -53
  60. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/LICENSE.md +0 -0
  61. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/entry_points.txt +0 -0
  62. {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/top_level.txt +0 -0
@@ -45,16 +45,18 @@ T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
45
45
 
46
46
 
47
47
  class TextDocumentTheme:
48
- text: QColor = QtBlack
48
+ text: QColor = QtBlack
49
49
  highlight: QColor = QtTransparent
50
- head: QColor = QtBlack
51
- comment: QColor = QtBlack
52
- note: QColor = QtBlack
53
- code: QColor = QtBlack
54
- modifier: QColor = QtBlack
55
- keyword: QColor = QtBlack
56
- tag: QColor = QtBlack
57
- optional: QColor = QtBlack
50
+ head: QColor = QtBlack
51
+ comment: QColor = QtBlack
52
+ note: QColor = QtBlack
53
+ code: QColor = QtBlack
54
+ modifier: QColor = QtBlack
55
+ keyword: QColor = QtBlack
56
+ tag: QColor = QtBlack
57
+ optional: QColor = QtBlack
58
+ dialog: QColor = QtBlack
59
+ altdialog: QColor = QtBlack
58
60
 
59
61
 
60
62
  def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None:
@@ -125,6 +127,7 @@ class ToQTextDocument(Tokenizer):
125
127
  self._mSep = (mScale * self._marginSep[0], mScale * self._marginSep[1])
126
128
 
127
129
  self._mIndent = mScale * 2.0
130
+ self._tIndent = mScale * self._firstWidth
128
131
 
129
132
  # Block Format
130
133
  # ============
@@ -133,6 +136,9 @@ class ToQTextDocument(Tokenizer):
133
136
  self._blockFmt.setTopMargin(self._mText[0])
134
137
  self._blockFmt.setBottomMargin(self._mText[1])
135
138
  self._blockFmt.setAlignment(QtAlignJustify if self._doJustify else QtAlignAbsolute)
139
+ self._blockFmt.setLineHeight(
140
+ 100*self._lineHeight, QTextBlockFormat.LineHeightTypes.ProportionalHeight
141
+ )
136
142
 
137
143
  # Character Formats
138
144
  # =================
@@ -224,6 +230,8 @@ class ToQTextDocument(Tokenizer):
224
230
  bFmt.setLeftMargin(self._mIndent)
225
231
  if tStyle & self.A_IND_R:
226
232
  bFmt.setRightMargin(self._mIndent)
233
+ if tStyle & self.A_IND_T:
234
+ bFmt.setTextIndent(self._tIndent)
227
235
 
228
236
  if tType == self.T_TEXT:
229
237
  newBlock(cursor, bFmt)
@@ -337,6 +345,14 @@ class ToQTextDocument(Tokenizer):
337
345
  cFmt.setVerticalAlignment(QtVAlignSub)
338
346
  elif fmt == self.FMT_SUB_E:
339
347
  cFmt.setVerticalAlignment(QtVAlignNormal)
348
+ elif fmt == self.FMT_DL_B:
349
+ cFmt.setForeground(self._theme.dialog)
350
+ elif fmt == self.FMT_DL_E:
351
+ cFmt.setForeground(self._theme.text)
352
+ elif fmt == self.FMT_ADL_B:
353
+ cFmt.setForeground(self._theme.altdialog)
354
+ elif fmt == self.FMT_ADL_E:
355
+ cFmt.setForeground(self._theme.text)
340
356
  elif fmt == self.FMT_FNOTE:
341
357
  xFmt = QTextCharFormat(self._cCode)
342
358
  xFmt.setVerticalAlignment(QtVAlignSuper)
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import (
33
33
  from novelwriter import CONFIG, SHARED
34
34
  from novelwriter.common import cssCol, readTextFile
35
35
  from novelwriter.extensions.configlayout import NColourLabel
36
- from novelwriter.extensions.modified import NNonBlockingDialog
36
+ from novelwriter.extensions.modified import NDialog
37
37
  from novelwriter.extensions.versioninfo import VersionInfoWidget
38
38
  from novelwriter.types import QtAlignRightTop, QtDialogClose
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
41
 
42
42
 
43
- class GuiAbout(NNonBlockingDialog):
43
+ class GuiAbout(NDialog):
44
44
 
45
45
  def __init__(self, parent: QWidget) -> None:
46
46
  super().__init__(parent=parent)
@@ -75,7 +75,7 @@ class GuiAbout(NNonBlockingDialog):
75
75
 
76
76
  # Credits
77
77
  self.lblCredits = NColourLabel(
78
- self.tr("Credits"), scale=1.6, parent=self, bold=True
78
+ self.tr("Credits"), self, scale=1.6, bold=True
79
79
  )
80
80
 
81
81
  self.txtCredits = QTextBrowser(self)
@@ -85,7 +85,7 @@ class GuiAbout(NNonBlockingDialog):
85
85
 
86
86
  # Buttons
87
87
  self.btnBox = QDialogButtonBox(QtDialogClose, self)
88
- self.btnBox.rejected.connect(self.close)
88
+ self.btnBox.rejected.connect(self.reject)
89
89
 
90
90
  # Assemble
91
91
  self.innerBox = QVBoxLayout()
@@ -106,7 +106,9 @@ class GuiAbout(NNonBlockingDialog):
106
106
 
107
107
  self.setLayout(self.outerBox)
108
108
  self.setSizeGripEnabled(True)
109
+
109
110
  self._setStyleSheet()
111
+ self._fillCreditsPage()
110
112
 
111
113
  logger.debug("Ready: GuiAbout")
112
114
 
@@ -116,11 +118,6 @@ class GuiAbout(NNonBlockingDialog):
116
118
  logger.debug("Delete: GuiAbout")
117
119
  return
118
120
 
119
- def populateGUI(self) -> None:
120
- """Populate tabs with text."""
121
- self._fillCreditsPage()
122
- return
123
-
124
121
  ##
125
122
  # Events
126
123
  ##
@@ -128,7 +125,7 @@ class GuiAbout(NNonBlockingDialog):
128
125
  def closeEvent(self, event: QCloseEvent) -> None:
129
126
  """Capture the close event and perform cleanup."""
130
127
  event.accept()
131
- self.deleteLater()
128
+ self.softDelete()
132
129
  return
133
130
 
134
131
  ##
@@ -137,16 +134,14 @@ class GuiAbout(NNonBlockingDialog):
137
134
 
138
135
  def _fillCreditsPage(self) -> None:
139
136
  """Load the content for the Credits page."""
140
- docPath = CONFIG.assetPath("text") / "credits_en.htm"
141
- docText = readTextFile(docPath)
142
- if docText:
143
- self.txtCredits.setHtml(docText)
137
+ if html := readTextFile(CONFIG.assetPath("text") / "credits_en.htm"):
138
+ self.txtCredits.setHtml(html)
144
139
  else:
145
140
  self.txtCredits.setHtml("Error loading credits text ...")
146
141
  return
147
142
 
148
143
  def _setStyleSheet(self) -> None:
149
- """Set stylesheet for all browser tabs."""
144
+ """Set stylesheet text document."""
150
145
  baseCol = cssCol(self.palette().window().color())
151
146
  self.txtCredits.setStyleSheet(
152
147
  f"QTextBrowser {{border: none; background: {baseCol};}} "
@@ -27,21 +27,21 @@ from __future__ import annotations
27
27
  import logging
28
28
 
29
29
  from PyQt5.QtCore import Qt, pyqtSlot
30
- from PyQt5.QtGui import QCloseEvent
31
30
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
33
- QListWidget, QListWidgetItem, QVBoxLayout, QWidget
31
+ QAbstractItemView, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
32
+ QListWidgetItem, QVBoxLayout, QWidget
34
33
  )
35
34
 
36
35
  from novelwriter import CONFIG, SHARED
37
36
  from novelwriter.extensions.configlayout import NColourLabel
37
+ from novelwriter.extensions.modified import NDialog
38
38
  from novelwriter.extensions.switch import NSwitch
39
- from novelwriter.types import QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
39
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
40
40
 
41
41
  logger = logging.getLogger(__name__)
42
42
 
43
43
 
44
- class GuiDocMerge(QDialog):
44
+ class GuiDocMerge(NDialog):
45
45
 
46
46
  D_HANDLE = QtUserRole
47
47
 
@@ -58,7 +58,7 @@ class GuiDocMerge(QDialog):
58
58
  self.headLabel.setFont(SHARED.theme.guiFontB)
59
59
  self.helpLabel = NColourLabel(
60
60
  self.tr("Drag and drop items to change the order, or uncheck to exclude."),
61
- SHARED.theme.helpText, parent=self, wrap=True
61
+ self, color=SHARED.theme.helpText, wrap=True
62
62
  )
63
63
 
64
64
  iPx = SHARED.theme.baseIconHeight
@@ -117,7 +117,7 @@ class GuiDocMerge(QDialog):
117
117
  logger.debug("Delete: GuiDocMerge")
118
118
  return
119
119
 
120
- def getData(self) -> dict:
120
+ def data(self) -> dict:
121
121
  """Return the user's choices."""
122
122
  finalItems = []
123
123
  for i in range(self.listBox.count()):
@@ -130,15 +130,15 @@ class GuiDocMerge(QDialog):
130
130
 
131
131
  return self._data
132
132
 
133
- ##
134
- # Events
135
- ##
136
-
137
- def closeEvent(self, event: QCloseEvent) -> None:
138
- """Capture the close event and perform cleanup."""
139
- event.accept()
140
- self.deleteLater()
141
- return
133
+ @classmethod
134
+ def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
135
+ """Pop the dialog and return the result."""
136
+ cls = GuiDocMerge(parent, handle, items)
137
+ cls.exec()
138
+ data = cls.data()
139
+ accepted = cls.result() == QtAccepted
140
+ cls.softDelete()
141
+ return data, accepted
142
142
 
143
143
  ##
144
144
  # Private Slots
@@ -27,21 +27,21 @@ from __future__ import annotations
27
27
  import logging
28
28
 
29
29
  from PyQt5.QtCore import pyqtSlot
30
- from PyQt5.QtGui import QCloseEvent
31
30
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout,
33
- QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget
31
+ QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QLabel,
32
+ QListWidget, QListWidgetItem, QVBoxLayout, QWidget
34
33
  )
35
34
 
36
35
  from novelwriter import CONFIG, SHARED
37
36
  from novelwriter.extensions.configlayout import NColourLabel
37
+ from novelwriter.extensions.modified import NDialog
38
38
  from novelwriter.extensions.switch import NSwitch
39
- from novelwriter.types import QtDialogCancel, QtDialogOk, QtUserRole
39
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtUserRole
40
40
 
41
41
  logger = logging.getLogger(__name__)
42
42
 
43
43
 
44
- class GuiDocSplit(QDialog):
44
+ class GuiDocSplit(NDialog):
45
45
 
46
46
  LINE_ROLE = QtUserRole
47
47
  LEVEL_ROLE = QtUserRole + 1
@@ -62,7 +62,7 @@ class GuiDocSplit(QDialog):
62
62
  self.headLabel.setFont(SHARED.theme.guiFontB)
63
63
  self.helpLabel = NColourLabel(
64
64
  self.tr("Select the maximum level to split into files."),
65
- SHARED.theme.helpText, parent=self, wrap=True
65
+ self, color=SHARED.theme.helpText, wrap=True
66
66
  )
67
67
 
68
68
  # Values
@@ -145,7 +145,7 @@ class GuiDocSplit(QDialog):
145
145
  logger.debug("Delete: GuiDocSplit")
146
146
  return
147
147
 
148
- def getData(self) -> tuple[dict, list]:
148
+ def data(self) -> tuple[dict, list[str]]:
149
149
  """Return the user's choices. Also save the users options for
150
150
  the next time the dialog is used.
151
151
  """
@@ -178,15 +178,15 @@ class GuiDocSplit(QDialog):
178
178
 
179
179
  return self._data, self._text
180
180
 
181
- ##
182
- # Events
183
- ##
184
-
185
- def closeEvent(self, event: QCloseEvent) -> None:
186
- """Capture the close event and perform cleanup."""
187
- event.accept()
188
- self.deleteLater()
189
- return
181
+ @classmethod
182
+ def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
183
+ """Pop the dialog and return the result."""
184
+ cls = GuiDocSplit(parent, handle)
185
+ cls.exec()
186
+ data, text = cls.data()
187
+ accepted = cls.result() == QtAccepted
188
+ cls.softDelete()
189
+ return data, text, accepted
190
190
 
191
191
  ##
192
192
  # Private Slots
@@ -25,18 +25,16 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from PyQt5.QtWidgets import (
29
- QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout,
30
- QWidget
31
- )
28
+ from PyQt5.QtWidgets import QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout, QWidget
32
29
 
33
30
  from novelwriter import CONFIG
34
- from novelwriter.types import QtDialogCancel, QtDialogOk
31
+ from novelwriter.extensions.modified import NDialog
32
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk
35
33
 
36
34
  logger = logging.getLogger(__name__)
37
35
 
38
36
 
39
- class GuiEditLabel(QDialog):
37
+ class GuiEditLabel(NDialog):
40
38
 
41
39
  def __init__(self, parent: QWidget, text: str = "") -> None:
42
40
  super().__init__(parent=parent)
@@ -91,6 +89,6 @@ class GuiEditLabel(QDialog):
91
89
  cls = GuiEditLabel(parent, text=text)
92
90
  cls.exec()
93
91
  label = cls.itemLabel
94
- accepted = cls.result() == QDialog.DialogCode.Accepted
95
- cls.deleteLater()
92
+ accepted = cls.result() == QtAccepted
93
+ cls.softDelete()
96
94
  return label, accepted
@@ -29,16 +29,18 @@ import logging
29
29
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
30
30
  from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox,
33
- QFileDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget
32
+ QAbstractButton, QCompleter, QDialogButtonBox, QFileDialog, QHBoxLayout,
33
+ QLineEdit, QPushButton, QVBoxLayout, QWidget
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
37
  from novelwriter.common import describeFont
38
- from novelwriter.constants import nwConst, nwUnicode
38
+ from novelwriter.constants import nwUnicode
39
39
  from novelwriter.dialogs.quotes import GuiQuoteSelect
40
40
  from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
41
- from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox
41
+ from novelwriter.extensions.modified import (
42
+ NComboBox, NDialog, NDoubleSpinBox, NIconToolButton, NSpinBox
43
+ )
42
44
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
43
45
  from novelwriter.extensions.switch import NSwitch
44
46
  from novelwriter.types import (
@@ -49,7 +51,7 @@ from novelwriter.types import (
49
51
  logger = logging.getLogger(__name__)
50
52
 
51
53
 
52
- class GuiPreferences(QDialog):
54
+ class GuiPreferences(NDialog):
53
55
 
54
56
  newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
55
57
 
@@ -64,8 +66,8 @@ class GuiPreferences(QDialog):
64
66
 
65
67
  # Title
66
68
  self.titleLabel = NColourLabel(
67
- self.tr("Preferences"), SHARED.theme.helpText,
68
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
69
+ self.tr("Preferences"), self, color=SHARED.theme.helpText,
70
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
69
71
  )
70
72
 
71
73
  # Search Box
@@ -546,7 +548,7 @@ class GuiPreferences(QDialog):
546
548
  self.allowOpenDial = NSwitch(self)
547
549
  self.allowOpenDial.setChecked(CONFIG.allowOpenDial)
548
550
  self.mainForm.addRow(
549
- self.tr("Allow open-ended sialogue"), self.allowOpenDial,
551
+ self.tr("Allow open-ended dialogue"), self.allowOpenDial,
550
552
  self.tr("Highlight dialogue line with no closing quote.")
551
553
  )
552
554
 
@@ -583,8 +585,8 @@ class GuiPreferences(QDialog):
583
585
  self.altDialogClose.setText(CONFIG.altDialogClose)
584
586
 
585
587
  self.mainForm.addRow(
586
- self.tr("Alternative dialog symbols"), [self.altDialogOpen, self.altDialogClose],
587
- self.tr("Custom highlighting of dialog text.")
588
+ self.tr("Alternative dialogue symbols"), [self.altDialogOpen, self.altDialogClose],
589
+ self.tr("Custom highlighting of dialogue text.")
588
590
  )
589
591
 
590
592
  self.highlightEmph = NSwitch(self)
@@ -693,64 +695,62 @@ class GuiPreferences(QDialog):
693
695
  self.sidebar.addButton(title, section)
694
696
  self.mainForm.addGroupLabel(title, section)
695
697
 
696
- self.quoteSym = {}
697
-
698
698
  # Single Quote Style
699
- self.quoteSym["SO"] = QLineEdit(self)
700
- self.quoteSym["SO"].setMaxLength(1)
701
- self.quoteSym["SO"].setReadOnly(True)
702
- self.quoteSym["SO"].setFixedWidth(boxFixed)
703
- self.quoteSym["SO"].setAlignment(QtAlignCenter)
704
- self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
705
- self.btnSingleStyleO = NIconToolButton(self, iSz, "quote")
706
- self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
707
- self.mainForm.addRow(
708
- self.tr("Single quote open style"), self.quoteSym["SO"],
699
+ self.fmtSQuoteOpen = QLineEdit(self)
700
+ self.fmtSQuoteOpen.setMaxLength(1)
701
+ self.fmtSQuoteOpen.setReadOnly(True)
702
+ self.fmtSQuoteOpen.setFixedWidth(boxFixed)
703
+ self.fmtSQuoteOpen.setAlignment(QtAlignCenter)
704
+ self.fmtSQuoteOpen.setText(CONFIG.fmtSQuoteOpen)
705
+ self.btnSQuoteOpen = NIconToolButton(self, iSz, "quote")
706
+ self.btnSQuoteOpen.clicked.connect(self._changeSingleQuoteOpen)
707
+ self.mainForm.addRow(
708
+ self.tr("Single quote open style"), self.fmtSQuoteOpen,
709
709
  self.tr("The symbol to use for a leading single quote."),
710
- button=self.btnSingleStyleO
710
+ button=self.btnSQuoteOpen
711
711
  )
712
712
 
713
- self.quoteSym["SC"] = QLineEdit(self)
714
- self.quoteSym["SC"].setMaxLength(1)
715
- self.quoteSym["SC"].setReadOnly(True)
716
- self.quoteSym["SC"].setFixedWidth(boxFixed)
717
- self.quoteSym["SC"].setAlignment(QtAlignCenter)
718
- self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
719
- self.btnSingleStyleC = NIconToolButton(self, iSz, "quote")
720
- self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
713
+ self.fmtSQuoteClose = QLineEdit(self)
714
+ self.fmtSQuoteClose.setMaxLength(1)
715
+ self.fmtSQuoteClose.setReadOnly(True)
716
+ self.fmtSQuoteClose.setFixedWidth(boxFixed)
717
+ self.fmtSQuoteClose.setAlignment(QtAlignCenter)
718
+ self.fmtSQuoteClose.setText(CONFIG.fmtSQuoteClose)
719
+ self.btnSQuoteClose = NIconToolButton(self, iSz, "quote")
720
+ self.btnSQuoteClose.clicked.connect(self._changeSingleQuoteClose)
721
721
  self.mainForm.addRow(
722
- self.tr("Single quote close style"), self.quoteSym["SC"],
722
+ self.tr("Single quote close style"), self.fmtSQuoteClose,
723
723
  self.tr("The symbol to use for a trailing single quote."),
724
- button=self.btnSingleStyleC
724
+ button=self.btnSQuoteClose
725
725
  )
726
726
 
727
727
  # Double Quote Style
728
- self.quoteSym["DO"] = QLineEdit(self)
729
- self.quoteSym["DO"].setMaxLength(1)
730
- self.quoteSym["DO"].setReadOnly(True)
731
- self.quoteSym["DO"].setFixedWidth(boxFixed)
732
- self.quoteSym["DO"].setAlignment(QtAlignCenter)
733
- self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
734
- self.btnDoubleStyleO = NIconToolButton(self, iSz, "quote")
735
- self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
736
- self.mainForm.addRow(
737
- self.tr("Double quote open style"), self.quoteSym["DO"],
728
+ self.fmtDQuoteOpen = QLineEdit(self)
729
+ self.fmtDQuoteOpen.setMaxLength(1)
730
+ self.fmtDQuoteOpen.setReadOnly(True)
731
+ self.fmtDQuoteOpen.setFixedWidth(boxFixed)
732
+ self.fmtDQuoteOpen.setAlignment(QtAlignCenter)
733
+ self.fmtDQuoteOpen.setText(CONFIG.fmtDQuoteOpen)
734
+ self.btnDQuoteOpen = NIconToolButton(self, iSz, "quote")
735
+ self.btnDQuoteOpen.clicked.connect(self._changeDoubleQuoteOpen)
736
+ self.mainForm.addRow(
737
+ self.tr("Double quote open style"), self.fmtDQuoteOpen,
738
738
  self.tr("The symbol to use for a leading double quote."),
739
- button=self.btnDoubleStyleO
739
+ button=self.btnDQuoteOpen
740
740
  )
741
741
 
742
- self.quoteSym["DC"] = QLineEdit(self)
743
- self.quoteSym["DC"].setMaxLength(1)
744
- self.quoteSym["DC"].setReadOnly(True)
745
- self.quoteSym["DC"].setFixedWidth(boxFixed)
746
- self.quoteSym["DC"].setAlignment(QtAlignCenter)
747
- self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
748
- self.btnDoubleStyleC = NIconToolButton(self, iSz, "quote")
749
- self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
742
+ self.fmtDQuoteClose = QLineEdit(self)
743
+ self.fmtDQuoteClose.setMaxLength(1)
744
+ self.fmtDQuoteClose.setReadOnly(True)
745
+ self.fmtDQuoteClose.setFixedWidth(boxFixed)
746
+ self.fmtDQuoteClose.setAlignment(QtAlignCenter)
747
+ self.fmtDQuoteClose.setText(CONFIG.fmtDQuoteClose)
748
+ self.btnDQuoteClose = NIconToolButton(self, iSz, "quote")
749
+ self.btnDQuoteClose.clicked.connect(self._changeDoubleQuoteClose)
750
750
  self.mainForm.addRow(
751
- self.tr("Double quote close style"), self.quoteSym["DC"],
751
+ self.tr("Double quote close style"), self.fmtDQuoteClose,
752
752
  self.tr("The symbol to use for a trailing double quote."),
753
- button=self.btnDoubleStyleC
753
+ button=self.btnDQuoteClose
754
754
  )
755
755
 
756
756
  self.mainForm.finalise()
@@ -767,13 +767,14 @@ class GuiPreferences(QDialog):
767
767
  logger.debug("Close: GuiPreferences")
768
768
  self._saveWindowSize()
769
769
  event.accept()
770
- QApplication.processEvents()
771
- self.done(nwConst.DLG_FINISHED)
772
- self.deleteLater()
770
+ self.softDelete()
773
771
  return
774
772
 
775
773
  def keyPressEvent(self, event: QKeyEvent) -> None:
776
- """Overload keyPressEvent to block enter key to save."""
774
+ """Overload keyPressEvent and only accept escape. The main
775
+ purpose here is to prevent Enter/Return from closing the dialog
776
+ as it is used for the search box.
777
+ """
777
778
  if event.matches(QKeySequence.StandardKey.Cancel):
778
779
  self.close()
779
780
  event.ignore()
@@ -855,11 +856,36 @@ class GuiPreferences(QDialog):
855
856
  self.fmtPadThin.setEnabled(state)
856
857
  return
857
858
 
858
- def _getQuote(self, qType: str) -> None:
859
- """Dialog for single quote open."""
860
- quote, status = GuiQuoteSelect.getQuote(self, current=self.quoteSym[qType].text())
859
+ @pyqtSlot()
860
+ def _changeSingleQuoteOpen(self) -> None:
861
+ """Change single quote open style."""
862
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteOpen.text())
863
+ if status:
864
+ self.fmtSQuoteOpen.setText(quote)
865
+ return
866
+
867
+ @pyqtSlot()
868
+ def _changeSingleQuoteClose(self) -> None:
869
+ """Change single quote close style."""
870
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteClose.text())
871
+ if status:
872
+ self.fmtSQuoteClose.setText(quote)
873
+ return
874
+
875
+ @pyqtSlot()
876
+ def _changeDoubleQuoteOpen(self) -> None:
877
+ """Change double quote open style."""
878
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteOpen.text())
879
+ if status:
880
+ self.fmtDQuoteOpen.setText(quote)
881
+ return
882
+
883
+ @pyqtSlot()
884
+ def _changeDoubleQuoteClose(self) -> None:
885
+ """Change double quote close style."""
886
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteClose.text())
861
887
  if status:
862
- self.quoteSym[qType].setText(quote)
888
+ self.fmtDQuoteClose.setText(quote)
863
889
  return
864
890
 
865
891
  ##
@@ -879,8 +905,8 @@ class GuiPreferences(QDialog):
879
905
  refreshTree = False
880
906
 
881
907
  # Appearance
882
- guiLocale = self.guiLocale.currentData()
883
- guiTheme = self.guiTheme.currentData()
908
+ guiLocale = self.guiLocale.currentData()
909
+ guiTheme = self.guiTheme.currentData()
884
910
 
885
911
  updateTheme |= CONFIG.guiTheme != guiTheme
886
912
  needsRestart |= CONFIG.guiLocale != guiLocale
@@ -977,10 +1003,10 @@ class GuiPreferences(QDialog):
977
1003
  CONFIG.fmtPadThin = self.fmtPadThin.isChecked()
978
1004
 
979
1005
  # Quotation Style
980
- CONFIG.fmtSQuoteOpen = self.quoteSym["SO"].text()
981
- CONFIG.fmtSQuoteClose = self.quoteSym["SC"].text()
982
- CONFIG.fmtDQuoteOpen = self.quoteSym["DO"].text()
983
- CONFIG.fmtDQuoteClose = self.quoteSym["DC"].text()
1006
+ CONFIG.fmtSQuoteOpen = self.fmtSQuoteOpen.text()
1007
+ CONFIG.fmtSQuoteClose = self.fmtSQuoteClose.text()
1008
+ CONFIG.fmtDQuoteOpen = self.fmtDQuoteOpen.text()
1009
+ CONFIG.fmtDQuoteClose = self.fmtDQuoteClose.text()
984
1010
 
985
1011
  # Finalise
986
1012
  CONFIG.saveConfig()
@@ -29,7 +29,7 @@ import logging
29
29
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
30
30
  from PyQt5.QtGui import QCloseEvent, QColor
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QApplication, QColorDialog, QDialog, QDialogButtonBox,
32
+ QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
33
33
  QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
34
34
  QTreeWidgetItem, QVBoxLayout, QWidget
35
35
  )
@@ -40,7 +40,7 @@ from novelwriter.constants import nwLabels, trConst
40
40
  from novelwriter.core.status import NWStatus, StatusEntry
41
41
  from novelwriter.enum import nwStatusShape
42
42
  from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
43
- from novelwriter.extensions.modified import NComboBox, NIconToolButton
43
+ from novelwriter.extensions.modified import NComboBox, NDialog, NIconToolButton
44
44
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
45
45
  from novelwriter.extensions.switch import NSwitch
46
46
  from novelwriter.types import (
@@ -51,7 +51,7 @@ from novelwriter.types import (
51
51
  logger = logging.getLogger(__name__)
52
52
 
53
53
 
54
- class GuiProjectSettings(QDialog):
54
+ class GuiProjectSettings(NDialog):
55
55
 
56
56
  PAGE_SETTINGS = 0
57
57
  PAGE_STATUS = 1
@@ -76,8 +76,8 @@ class GuiProjectSettings(QDialog):
76
76
 
77
77
  # Title
78
78
  self.titleLabel = NColourLabel(
79
- self.tr("Project Settings"), SHARED.theme.helpText,
80
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
79
+ self.tr("Project Settings"), self, color=SHARED.theme.helpText,
80
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
81
81
  )
82
82
 
83
83
  # SideBar
@@ -92,7 +92,7 @@ class GuiProjectSettings(QDialog):
92
92
  # Buttons
93
93
  self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogCancel, self)
94
94
  self.buttonBox.accepted.connect(self._doSave)
95
- self.buttonBox.rejected.connect(self.close)
95
+ self.buttonBox.rejected.connect(self.reject)
96
96
 
97
97
  # Content
98
98
  SHARED.project.countStatus()
@@ -147,7 +147,7 @@ class GuiProjectSettings(QDialog):
147
147
  """Capture the user closing the window and save settings."""
148
148
  self._saveSettings()
149
149
  event.accept()
150
- self.deleteLater()
150
+ self.softDelete()
151
151
  return
152
152
 
153
153
  ##
@@ -345,7 +345,7 @@ class _StatusPage(NFixedPage):
345
345
 
346
346
  # Title
347
347
  self.pageTitle = NColourLabel(
348
- pageLabel, SHARED.theme.helpText, parent=self,
348
+ pageLabel, self, color=SHARED.theme.helpText,
349
349
  scale=NColourLabel.HEADER_SCALE
350
350
  )
351
351
 
@@ -637,8 +637,8 @@ class _ReplacePage(NFixedPage):
637
637
 
638
638
  # Title
639
639
  self.pageTitle = NColourLabel(
640
- self.tr("Text Auto-Replace for Preview and Build"),
641
- SHARED.theme.helpText, parent=self, scale=NColourLabel.HEADER_SCALE
640
+ self.tr("Text Auto-Replace for Preview and Build"), self,
641
+ color=SHARED.theme.helpText, scale=NColourLabel.HEADER_SCALE
642
642
  )
643
643
 
644
644
  # List Box