novelWriter 2.7b1__py3-none-any.whl → 2.7rc1__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 (56) hide show
  1. novelwriter/__init__.py +17 -4
  2. novelwriter/assets/i18n/project_en_GB.json +1 -0
  3. novelwriter/assets/icons/remix_filled.icons +1 -0
  4. novelwriter/assets/icons/remix_outline.icons +1 -0
  5. novelwriter/assets/images/splash.png +0 -0
  6. novelwriter/assets/manual.pdf +0 -0
  7. novelwriter/assets/manual_fr.pdf +0 -0
  8. novelwriter/assets/sample.zip +0 -0
  9. novelwriter/assets/syntax/snazzy.conf +3 -3
  10. novelwriter/assets/themes/snazzy.conf +48 -0
  11. novelwriter/common.py +10 -1
  12. novelwriter/config.py +96 -25
  13. novelwriter/constants.py +17 -0
  14. novelwriter/core/buildsettings.py +2 -0
  15. novelwriter/core/coretools.py +41 -34
  16. novelwriter/core/docbuild.py +1 -0
  17. novelwriter/core/index.py +25 -1
  18. novelwriter/core/indexdata.py +4 -1
  19. novelwriter/core/item.py +35 -24
  20. novelwriter/core/itemmodel.py +17 -13
  21. novelwriter/core/novelmodel.py +2 -0
  22. novelwriter/core/project.py +14 -14
  23. novelwriter/core/projectdata.py +42 -24
  24. novelwriter/core/projectxml.py +17 -7
  25. novelwriter/core/sessions.py +29 -13
  26. novelwriter/core/tree.py +9 -5
  27. novelwriter/dialogs/docmerge.py +2 -1
  28. novelwriter/dialogs/docsplit.py +6 -3
  29. novelwriter/dialogs/editlabel.py +11 -8
  30. novelwriter/dialogs/preferences.py +37 -26
  31. novelwriter/dialogs/projectsettings.py +3 -0
  32. novelwriter/extensions/configlayout.py +6 -2
  33. novelwriter/extensions/switch.py +16 -15
  34. novelwriter/extensions/switchbox.py +1 -0
  35. novelwriter/formats/tokenizer.py +2 -1
  36. novelwriter/gui/doceditor.py +98 -40
  37. novelwriter/gui/noveltree.py +11 -5
  38. novelwriter/gui/outline.py +9 -1
  39. novelwriter/gui/projtree.py +1 -0
  40. novelwriter/gui/search.py +1 -0
  41. novelwriter/gui/statusbar.py +14 -6
  42. novelwriter/gui/theme.py +25 -10
  43. novelwriter/guimain.py +29 -9
  44. novelwriter/splash.py +74 -0
  45. novelwriter/tools/lipsum.py +2 -1
  46. novelwriter/tools/manuscript.py +1 -1
  47. novelwriter/tools/manussettings.py +38 -13
  48. novelwriter/tools/noveldetails.py +9 -8
  49. novelwriter/tools/welcome.py +1 -0
  50. novelwriter/tools/writingstats.py +68 -45
  51. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/METADATA +1 -1
  52. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/RECORD +56 -53
  53. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/WHEEL +1 -1
  54. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/entry_points.txt +0 -0
  55. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/licenses/LICENSE.md +0 -0
  56. {novelwriter-2.7b1.dist-info → novelwriter-2.7rc1.dist-info}/top_level.txt +0 -0
@@ -79,29 +79,36 @@ class NWSessionLog:
79
79
  return False
80
80
 
81
81
  now = time()
82
- iNovel, iNotes = self._project.data.initCounts
83
- cNovel, cNotes = self._project.data.currCounts
84
- iTotal = iNovel + iNotes
85
- wDiff = cNovel + cNotes - iTotal
82
+ iWNovel, iWNotes, iCNovel, iCNotes = self._project.data.initCounts
83
+ cWNovel, cWNotes, cCNovel, cCNotes = self._project.data.currCounts
84
+ iWTotal = iWNovel + iWNotes
85
+ iCTotal = iCNovel + iCNotes
86
+ wDiff = cWNovel + cWNotes - iWTotal
87
+ cDiff = cCNovel + cCNotes - iCTotal
86
88
  sTime = now - self._start
87
89
 
88
- logger.info("The session lasted %d sec and added %d words", int(sTime), wDiff)
89
- if sTime < 300 and wDiff == 0:
90
+ logger.info(
91
+ "The session lasted %d sec and added %d words abd %d characters",
92
+ int(sTime), wDiff, cDiff
93
+ )
94
+ if sTime < 300 and (wDiff == 0 or cDiff == 0):
90
95
  logger.info("Session too short, skipping log entry")
91
96
  return False
92
97
 
93
98
  try:
94
99
  if not sessFile.exists():
95
100
  with open(sessFile, mode="w", encoding="utf-8") as fObj:
96
- fObj.write(self.createInitial(iTotal))
101
+ fObj.write(self.createInitial(iWTotal))
97
102
 
98
103
  with open(sessFile, mode="a+", encoding="utf-8") as fObj:
99
104
  fObj.write(self.createRecord(
100
105
  start=formatTimeStamp(self._start),
101
106
  end=formatTimeStamp(now),
102
- novel=cNovel,
103
- notes=cNotes,
104
- idle=round(idleTime)
107
+ novel=cWNovel,
108
+ notes=cWNotes,
109
+ idle=round(idleTime),
110
+ cnovel=cCNovel,
111
+ cnotes=cCNotes,
105
112
  ))
106
113
 
107
114
  except Exception:
@@ -129,10 +136,19 @@ class NWSessionLog:
129
136
  data = json.dumps({"type": "initial", "offset": total})
130
137
  return f"{data}\n"
131
138
 
132
- def createRecord(self, start: str, end: str, novel: int, notes: int, idle: int) -> str:
139
+ def createRecord(
140
+ self, start: str, end: str, novel: int, notes: int, idle: int,
141
+ cnovel: int = 0, cnotes: int = 0,
142
+ ) -> str:
133
143
  """Low level function to create a log record."""
134
144
  data = json.dumps({
135
- "type": "record", "start": start, "end": end,
136
- "novel": novel, "notes": notes, "idle": idle,
145
+ "type": "record",
146
+ "start": start,
147
+ "end": end,
148
+ "novel": novel,
149
+ "notes": notes,
150
+ "cnovel": cnovel,
151
+ "cnotes": cnotes,
152
+ "idle": idle,
137
153
  })
138
154
  return f"{data}\n"
novelwriter/core/tree.py CHANGED
@@ -410,16 +410,20 @@ class NWTree:
410
410
 
411
411
  return True
412
412
 
413
- def sumWords(self) -> tuple[int, int]:
414
- """Loop over all entries and add up the word counts."""
415
- noteWords = 0
413
+ def sumCounts(self) -> tuple[int, int, int, int]:
414
+ """Loop over all entries and add up the word and char counts."""
416
415
  novelWords = 0
416
+ notesWords = 0
417
+ novelChars = 0
418
+ notesChars = 0
417
419
  for item in self._items.values():
418
420
  if item.itemLayout == nwItemLayout.NOTE:
419
- noteWords += item.wordCount
421
+ notesWords += item.wordCount
422
+ notesChars += item.charCount
420
423
  elif item.itemLayout == nwItemLayout.DOCUMENT:
421
424
  novelWords += item.wordCount
422
- return novelWords, noteWords
425
+ novelChars += item.charCount
426
+ return novelWords, notesWords, novelChars, notesChars
423
427
 
424
428
  ##
425
429
  # Tree Item Methods
@@ -73,8 +73,9 @@ class GuiDocMerge(NDialog):
73
73
  self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
74
74
 
75
75
  # Merge Options
76
- self.trashLabel = QLabel(self.tr("Move merged items to Trash"), self)
77
76
  self.trashSwitch = NSwitch(self, height=iPx)
77
+ self.trashLabel = QLabel(self.tr("Move merged items to Trash"), self)
78
+ self.trashLabel.setBuddy(self.trashSwitch)
78
79
 
79
80
  self.optBox = QGridLayout()
80
81
  self.optBox.addWidget(self.trashLabel, 0, 0)
@@ -90,16 +90,19 @@ class GuiDocSplit(NDialog):
90
90
  self.splitLevel.currentIndexChanged.connect(self._reloadList)
91
91
 
92
92
  # Split Options
93
- self.folderLabel = QLabel(self.tr("Split into a new folder"), self)
94
93
  self.folderSwitch = NSwitch(self, height=iPx)
95
94
  self.folderSwitch.setChecked(intoFolder)
95
+ self.folderLabel = QLabel(self.tr("Split into a new folder"), self)
96
+ self.folderLabel.setBuddy(self.folderSwitch)
96
97
 
97
- self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"), self)
98
98
  self.hierarchySwitch = NSwitch(self, height=iPx)
99
99
  self.hierarchySwitch.setChecked(docHierarchy)
100
+ self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"), self)
101
+ self.hierarchyLabel.setBuddy(self.hierarchySwitch)
100
102
 
101
- self.trashLabel = QLabel(self.tr("Move split document to Trash"), self)
102
103
  self.trashSwitch = NSwitch(self, height=iPx)
104
+ self.trashLabel = QLabel(self.tr("Move split document to Trash"), self)
105
+ self.trashLabel.setBuddy(self.trashSwitch)
103
106
 
104
107
  self.optBox = QGridLayout()
105
108
  self.optBox.addWidget(self.folderLabel, 0, 0)
@@ -43,11 +43,14 @@ class GuiEditLabel(NDialog):
43
43
  self.setWindowTitle(self.tr("Item Label"))
44
44
 
45
45
  # Item Label
46
- self.labelValue = QLineEdit(self)
47
- self.labelValue.setMinimumWidth(220)
48
- self.labelValue.setMaxLength(200)
49
- self.labelValue.setText(text)
50
- self.labelValue.selectAll()
46
+ self.edtValue = QLineEdit(self)
47
+ self.edtValue.setMinimumWidth(220)
48
+ self.edtValue.setMaxLength(200)
49
+ self.edtValue.setText(text)
50
+ self.edtValue.selectAll()
51
+
52
+ self.lblValue = QLabel(self.tr("Label"), self)
53
+ self.lblValue.setBuddy(self.lblValue)
51
54
 
52
55
  # Buttons
53
56
  self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
@@ -56,8 +59,8 @@ class GuiEditLabel(NDialog):
56
59
 
57
60
  # Assemble
58
61
  self.innerBox = QHBoxLayout()
59
- self.innerBox.addWidget(QLabel(self.tr("Label"), self), 0)
60
- self.innerBox.addWidget(self.labelValue, 1)
62
+ self.innerBox.addWidget(self.lblValue, 0)
63
+ self.innerBox.addWidget(self.edtValue, 1)
61
64
  self.innerBox.setSpacing(12)
62
65
 
63
66
  self.outerBox = QVBoxLayout()
@@ -77,7 +80,7 @@ class GuiEditLabel(NDialog):
77
80
 
78
81
  @property
79
82
  def itemLabel(self) -> str:
80
- return self.labelValue.text()
83
+ return self.edtValue.text()
81
84
 
82
85
  @classmethod
83
86
  def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
@@ -34,9 +34,9 @@ from PyQt6.QtWidgets import (
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
- from novelwriter.common import compact, describeFont, uniqueCompact
37
+ from novelwriter.common import compact, describeFont, processDialogSymbols, uniqueCompact
38
38
  from novelwriter.config import DEF_GUI, DEF_ICONS, DEF_SYNTAX, DEF_TREECOL
39
- from novelwriter.constants import nwLabels, nwUnicode, trConst
39
+ from novelwriter.constants import nwLabels, nwQuotes, nwUnicode, trConst
40
40
  from novelwriter.dialogs.quotes import GuiQuoteSelect
41
41
  from novelwriter.extensions.configlayout import NColorLabel, NScrollableForm
42
42
  from novelwriter.extensions.modified import (
@@ -80,6 +80,7 @@ class GuiPreferences(NDialog):
80
80
  # SideBar
81
81
  self.sidebar = NPagedSideBar(self)
82
82
  self.sidebar.setLabelColor(SHARED.theme.helpText)
83
+ self.sidebar.setAccessibleName(self.titleLabel.text())
83
84
  self.sidebar.buttonClicked.connect(self._sidebarClicked)
84
85
 
85
86
  # Form
@@ -225,6 +226,14 @@ class GuiPreferences(NDialog):
225
226
  self.tr("Turn off to use the Qt font dialog, which may have more options.")
226
227
  )
227
228
 
229
+ # Use Character Count
230
+ self.useCharCount = NSwitch(self)
231
+ self.useCharCount.setChecked(CONFIG.useCharCount)
232
+ self.mainForm.addRow(
233
+ self.tr("Prefer character count over word count"), self.useCharCount,
234
+ self.tr("Display character count instead where available.")
235
+ )
236
+
228
237
  # Document Style
229
238
  # ==============
230
239
 
@@ -636,21 +645,20 @@ class GuiPreferences(NDialog):
636
645
  self.tr("Lines starting with any of these symbols are dialogue.")
637
646
  )
638
647
 
639
- self.narratorBreak = QLineEdit(self)
640
- self.narratorBreak.setMaxLength(1)
641
- self.narratorBreak.setFixedWidth(boxFixed)
642
- self.narratorBreak.setAlignment(QtAlignCenter)
643
- self.narratorBreak.setText(CONFIG.narratorBreak)
648
+ self.narratorBreak = NComboBox(self)
649
+ self.narratorDialog = NComboBox(self)
650
+ for key, value in nwQuotes.DASHES.items():
651
+ label = trConst(value)
652
+ self.narratorBreak.addItem(label, key)
653
+ self.narratorDialog.addItem(label, key)
654
+
655
+ self.narratorBreak.setCurrentData(CONFIG.narratorBreak, "")
656
+ self.narratorDialog.setCurrentData(CONFIG.narratorDialog, "")
657
+
644
658
  self.mainForm.addRow(
645
659
  self.tr("Narrator break symbol"), self.narratorBreak,
646
660
  self.tr("Symbol to indicate a narrator break in dialogue.")
647
661
  )
648
-
649
- self.narratorDialog = QLineEdit(self)
650
- self.narratorDialog.setMaxLength(1)
651
- self.narratorDialog.setFixedWidth(boxFixed)
652
- self.narratorDialog.setAlignment(QtAlignCenter)
653
- self.narratorDialog.setText(CONFIG.narratorDialog)
654
662
  self.mainForm.addRow(
655
663
  self.tr("Alternating dialogue/narration symbol"), self.narratorDialog,
656
664
  self.tr("Alternates dialogue highlighting within any paragraph.")
@@ -957,21 +965,24 @@ class GuiPreferences(NDialog):
957
965
  refreshTree = False
958
966
 
959
967
  # Appearance
960
- guiLocale = self.guiLocale.currentData()
961
- guiTheme = self.guiTheme.currentData()
962
- iconTheme = self.iconTheme.currentData()
968
+ guiLocale = self.guiLocale.currentData()
969
+ guiTheme = self.guiTheme.currentData()
970
+ iconTheme = self.iconTheme.currentData()
971
+ useCharCount = self.useCharCount.isChecked()
963
972
 
964
973
  updateTheme |= CONFIG.guiTheme != guiTheme
965
974
  updateTheme |= CONFIG.iconTheme != iconTheme
966
975
  needsRestart |= CONFIG.guiLocale != guiLocale
967
976
  needsRestart |= CONFIG.guiFont != self._guiFont
968
-
969
- CONFIG.guiLocale = guiLocale
970
- CONFIG.guiTheme = guiTheme
971
- CONFIG.iconTheme = iconTheme
972
- CONFIG.hideVScroll = self.hideVScroll.isChecked()
973
- CONFIG.hideHScroll = self.hideHScroll.isChecked()
974
- CONFIG.nativeFont = self.nativeFont.isChecked()
977
+ refreshTree |= CONFIG.useCharCount != useCharCount
978
+
979
+ CONFIG.guiLocale = guiLocale
980
+ CONFIG.guiTheme = guiTheme
981
+ CONFIG.iconTheme = iconTheme
982
+ CONFIG.hideVScroll = self.hideVScroll.isChecked()
983
+ CONFIG.hideHScroll = self.hideHScroll.isChecked()
984
+ CONFIG.nativeFont = self.nativeFont.isChecked()
985
+ CONFIG.useCharCount = useCharCount
975
986
  CONFIG.setGuiFont(self._guiFont)
976
987
 
977
988
  # Document Style
@@ -1034,9 +1045,9 @@ class GuiPreferences(NDialog):
1034
1045
  # Text Highlighting
1035
1046
  dialogueStyle = self.dialogStyle.currentData()
1036
1047
  allowOpenDial = self.allowOpenDial.isChecked()
1037
- dialogueLine = uniqueCompact(self.dialogLine.text())
1038
- narratorBreak = self.narratorBreak.text().strip()
1039
- narratorDialog = self.narratorDialog.text().strip()
1048
+ dialogueLine = processDialogSymbols(self.dialogLine.text())
1049
+ narratorBreak = self.narratorBreak.currentData()
1050
+ narratorDialog = self.narratorDialog.currentData()
1040
1051
  altDialogOpen = compact(self.altDialogOpen.text())
1041
1052
  altDialogClose = compact(self.altDialogClose.text())
1042
1053
  highlightEmph = self.highlightEmph.isChecked()
@@ -86,6 +86,7 @@ class GuiProjectSettings(NDialog):
86
86
  # SideBar
87
87
  self.sidebar = NPagedSideBar(self)
88
88
  self.sidebar.setLabelColor(SHARED.theme.helpText)
89
+ self.sidebar.setAccessibleName(self.titleLabel.text())
89
90
  self.sidebar.addButton(self.tr("Settings"), self.PAGE_SETTINGS)
90
91
  self.sidebar.addButton(self.tr("Status"), self.PAGE_STATUS)
91
92
  self.sidebar.addButton(self.tr("Importance"), self.PAGE_IMPORT)
@@ -349,6 +350,7 @@ class _StatusPage(NFixedPage):
349
350
  self.listBox.setHeaderLabels([self.tr("Label"), self.tr("Usage")])
350
351
  self.listBox.setColumnWidth(self.C_LABEL, wCol0)
351
352
  self.listBox.setIndentation(0)
353
+ self.listBox.setAccessibleName(pageLabel)
352
354
  self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
353
355
  self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
354
356
  self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
@@ -686,6 +688,7 @@ class _ReplacePage(NFixedPage):
686
688
  self.listBox.setHeaderLabels([self.tr("Keyword"), self.tr("Replace With")])
687
689
  self.listBox.setColumnWidth(self.C_KEY, wCol0)
688
690
  self.listBox.setIndentation(0)
691
+ self.listBox.setAccessibleName(self.pageTitle.text())
689
692
  self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
690
693
  self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
691
694
  self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
@@ -211,7 +211,8 @@ class NScrollableForm(QScrollArea):
211
211
  else:
212
212
  qWidget = widget
213
213
 
214
- qLabel = QLabel(label or "", self)
214
+ text = label or ""
215
+ qLabel = QLabel(text, self)
215
216
  qLabel.setIndent(self._indent)
216
217
  qLabel.setBuddy(qWidget)
217
218
 
@@ -227,6 +228,7 @@ class NScrollableForm(QScrollArea):
227
228
  row.addLayout(labelBox, stretch[0])
228
229
  if editable:
229
230
  self._editable[editable] = qHelp
231
+ text = f"{text}: {helpText}"
230
232
  else:
231
233
  row.addWidget(qLabel, stretch[0])
232
234
 
@@ -235,6 +237,7 @@ class NScrollableForm(QScrollArea):
235
237
  box.addWidget(qWidget, 1)
236
238
  box.addWidget(QLabel(unit, self), 0)
237
239
  row.addLayout(box, stretch[1])
240
+ text = f"{text} Unit: {unit}"
238
241
  elif isinstance(button, QAbstractButton):
239
242
  box = QHBoxLayout()
240
243
  box.addWidget(qWidget, 1)
@@ -243,10 +246,11 @@ class NScrollableForm(QScrollArea):
243
246
  else:
244
247
  row.addWidget(qWidget, stretch[1])
245
248
 
249
+ self._first = False
246
250
  self._layout.addLayout(row)
247
251
  if label:
248
252
  self._index[label.strip()] = qWidget
249
- self._first = False
253
+ qLabel.setAccessibleName(text)
250
254
 
251
255
  return
252
256
 
@@ -23,12 +23,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
23
  """
24
24
  from __future__ import annotations
25
25
 
26
- from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty # pyright: ignore
27
- from PyQt6.QtGui import QEnterEvent, QMouseEvent, QPainter, QPaintEvent, QResizeEvent
26
+ from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty, pyqtSlot # pyright: ignore
27
+ from PyQt6.QtGui import QEnterEvent, QPainter, QPaintEvent, QResizeEvent
28
28
  from PyQt6.QtWidgets import QAbstractButton, QWidget
29
29
 
30
30
  from novelwriter import SHARED
31
- from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixed
31
+ from novelwriter.types import QtNoPen, QtPaintAntiAlias, QtSizeFixed
32
32
 
33
33
 
34
34
  class NSwitch(QAbstractButton):
@@ -50,6 +50,8 @@ class NSwitch(QAbstractButton):
50
50
  self.setFixedHeight(self._xH)
51
51
  self._offset = self._xR
52
52
 
53
+ self.clicked.connect(self._onClick)
54
+
53
55
  return
54
56
 
55
57
  ##
@@ -94,7 +96,7 @@ class NSwitch(QAbstractButton):
94
96
  painter.setRenderHint(QtPaintAntiAlias, True)
95
97
  painter.setOpacity(1.0 if self.isEnabled() else 0.5)
96
98
 
97
- painter.setPen(palette.mid().color())
99
+ painter.setPen(palette.highlight().color() if self.hasFocus() else palette.mid().color())
98
100
  painter.setBrush(palette.highlight() if self.isChecked() else palette.alternateBase())
99
101
  painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
100
102
 
@@ -106,19 +108,18 @@ class NSwitch(QAbstractButton):
106
108
 
107
109
  return
108
110
 
109
- def mouseReleaseEvent(self, event: QMouseEvent) -> None:
110
- """Animate the switch on mouse release."""
111
- super().mouseReleaseEvent(event)
112
- if event.button() == QtMouseLeft:
113
- anim = QPropertyAnimation(self, b"offset", self)
114
- anim.setDuration(120)
115
- anim.setStartValue(self._offset)
116
- anim.setEndValue((self._xW - self._xR) if self.isChecked() else self._xR)
117
- anim.start()
118
- return
119
-
120
111
  def enterEvent(self, event: QEnterEvent) -> None:
121
112
  """Change the cursor when hovering the button."""
122
113
  self.setCursor(Qt.CursorShape.PointingHandCursor)
123
114
  super().enterEvent(event)
124
115
  return
116
+
117
+ @pyqtSlot(bool)
118
+ def _onClick(self, checked: bool) -> None:
119
+ """Animate the toggle action."""
120
+ anim = QPropertyAnimation(self, b"offset", self)
121
+ anim.setDuration(120)
122
+ anim.setStartValue(self._offset)
123
+ anim.setEndValue((self._xW - self._xR) if checked else self._xR)
124
+ anim.start()
125
+ return
@@ -100,6 +100,7 @@ class NSwitchBox(QScrollArea):
100
100
  switch.toggled.connect(lambda state: self._emitSwitchSignal(identifier, state))
101
101
  self._content.addWidget(switch, self._index, 2, QtAlignRight)
102
102
 
103
+ label.setBuddy(switch)
103
104
  self._widgets.append(switch)
104
105
  self._bumpIndex()
105
106
 
@@ -614,7 +614,8 @@ class Tokenizer(ABC):
614
614
  tStyle |= BlockFmt.JUSTIFY
615
615
 
616
616
  if cStyle in (
617
- nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN, nwComment.STORY
617
+ nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN,
618
+ nwComment.STORY, nwComment.NOTE,
618
619
  ):
619
620
  bStyle = COMMENT_STYLE[cStyle]
620
621
  tLine, tFmt = self._formatComment(bStyle, cKey, cText)