novelWriter 2.5rc1__py3-none-any.whl → 2.5.2__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 (41) hide show
  1. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.dist-info}/METADATA +1 -1
  2. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.dist-info}/RECORD +41 -39
  3. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.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/manual.pdf +0 -0
  18. novelwriter/assets/sample.zip +0 -0
  19. novelwriter/assets/text/credits_en.htm +52 -44
  20. novelwriter/common.py +10 -0
  21. novelwriter/core/project.py +15 -4
  22. novelwriter/core/status.py +4 -1
  23. novelwriter/core/storage.py +6 -1
  24. novelwriter/core/tokenizer.py +17 -11
  25. novelwriter/core/toqdoc.py +13 -13
  26. novelwriter/dialogs/preferences.py +14 -27
  27. novelwriter/dialogs/projectsettings.py +6 -10
  28. novelwriter/extensions/configlayout.py +18 -3
  29. novelwriter/extensions/statusled.py +12 -2
  30. novelwriter/gui/doceditor.py +3 -3
  31. novelwriter/gui/docviewer.py +5 -5
  32. novelwriter/gui/docviewerpanel.py +7 -0
  33. novelwriter/gui/outline.py +1 -0
  34. novelwriter/gui/projtree.py +66 -49
  35. novelwriter/guimain.py +50 -42
  36. novelwriter/shared.py +9 -0
  37. novelwriter/tools/manuscript.py +1 -0
  38. novelwriter/tools/welcome.py +9 -0
  39. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.dist-info}/LICENSE.md +0 -0
  40. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.dist-info}/entry_points.txt +0 -0
  41. {novelWriter-2.5rc1.dist-info → novelWriter-2.5.2.dist-info}/top_level.txt +0 -0
@@ -198,6 +198,7 @@ class Tokenizer(ABC):
198
198
  # Instance Variables
199
199
  self._hFormatter = HeadingFormatter(self._project)
200
200
  self._noSep = True # Flag to indicate that we don't want a scene separator
201
+ self._noIndent = False # Flag to disable text indent on next paragraph
201
202
  self._showDialog = False # Flag for dialogue highlighting
202
203
 
203
204
  # This File
@@ -831,7 +832,7 @@ class Tokenizer(ABC):
831
832
  sAlign |= self.A_IND_R
832
833
 
833
834
  # Process formats
834
- tLine, tFmt = self._extractFormats(aLine)
835
+ tLine, tFmt = self._extractFormats(aLine, hDialog=self._isNovel)
835
836
  tokens.append((
836
837
  self.T_TEXT, nHead, tLine, tFmt, sAlign
837
838
  ))
@@ -873,7 +874,6 @@ class Tokenizer(ABC):
873
874
  pLines: list[T_Token] = []
874
875
 
875
876
  tCount = len(tokens)
876
- pIndent = True
877
877
  for n, cToken in enumerate(tokens):
878
878
 
879
879
  if n > 0:
@@ -881,11 +881,11 @@ class Tokenizer(ABC):
881
881
  if n < tCount - 1:
882
882
  nToken = tokens[n+1] # Look ahead
883
883
 
884
- if not self._indentFirst and cToken[0] in self.L_SKIP_INDENT:
884
+ if cToken[0] in self.L_SKIP_INDENT and not self._indentFirst:
885
885
  # Unless the indentFirst flag is set, we set up the next
886
886
  # paragraph to not be indented if we see a block of a
887
887
  # specific type
888
- pIndent = False
888
+ self._noIndent = True
889
889
 
890
890
  if cToken[0] == self.T_EMPTY:
891
891
  # We don't need to keep the empty lines after this pass
@@ -910,7 +910,7 @@ class Tokenizer(ABC):
910
910
  # Next token is not text, so we add the buffer to tokens
911
911
  nLines = len(pLines)
912
912
  cStyle = pLines[0][4]
913
- if self._firstIndent and pIndent and not cStyle & self.M_ALIGNED:
913
+ if self._firstIndent and not (self._noIndent or cStyle & self.M_ALIGNED):
914
914
  # If paragraph indentation is enabled, not temporarily
915
915
  # turned off, and the block is not aligned, we add the
916
916
  # text indentation flag
@@ -938,7 +938,7 @@ class Tokenizer(ABC):
938
938
 
939
939
  # Reset buffer and make sure text indent is on for next pass
940
940
  pLines = []
941
- pIndent = True
941
+ self._noIndent = False
942
942
 
943
943
  else:
944
944
  self._tokens.append(cToken)
@@ -1098,8 +1098,13 @@ class Tokenizer(ABC):
1098
1098
  # Internal Functions
1099
1099
  ##
1100
1100
 
1101
- def _extractFormats(self, text: str, skip: int = 0) -> tuple[str, T_Formats]:
1102
- """Extract format markers from a text paragraph."""
1101
+ def _extractFormats(
1102
+ self, text: str, skip: int = 0, hDialog: bool = False
1103
+ ) -> tuple[str, T_Formats]:
1104
+ """Extract format markers from a text paragraph. In order to
1105
+ also process dialogue highlighting, the hDialog flag must be set
1106
+ to True. See issues #2011 and #2013.
1107
+ """
1103
1108
  temp: list[tuple[int, int, int, str]] = []
1104
1109
 
1105
1110
  # Match Markdown
@@ -1137,7 +1142,7 @@ class Tokenizer(ABC):
1137
1142
  ))
1138
1143
 
1139
1144
  # Match Dialogue
1140
- if self._rxDialogue:
1145
+ if self._rxDialogue and hDialog:
1141
1146
  for regEx, fmtB, fmtE in self._rxDialogue:
1142
1147
  rxItt = regEx.globalMatch(text, 0)
1143
1148
  while rxItt.hasNext():
@@ -1150,8 +1155,9 @@ class Tokenizer(ABC):
1150
1155
  formats = []
1151
1156
  for pos, n, fmt, key in reversed(sorted(temp, key=lambda x: x[0])):
1152
1157
  if fmt > 0:
1153
- result = result[:pos] + result[pos+n:]
1154
- formats = [(p-n, f, k) for p, f, k in formats]
1158
+ if n > 0:
1159
+ result = result[:pos] + result[pos+n:]
1160
+ formats = [(p-n if p > pos else p, f, k) for p, f, k in formats]
1155
1161
  formats.insert(0, (pos, fmt, key))
1156
1162
 
1157
1163
  return result, formats
@@ -26,7 +26,7 @@ from __future__ import annotations
26
26
  import logging
27
27
 
28
28
  from PyQt5.QtGui import (
29
- QColor, QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat,
29
+ QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat,
30
30
  QTextCursor, QTextDocument
31
31
  )
32
32
 
@@ -99,19 +99,19 @@ class ToQTextDocument(Tokenizer):
99
99
  self._document.clear()
100
100
  self._document.setDefaultFont(self._textFont)
101
101
 
102
- qMetric = QFontMetrics(self._textFont)
103
- mScale = qMetric.height()
102
+ qMetric = QFontMetricsF(self._textFont)
103
+ mPx = qMetric.ascent() # 1 em in pixels
104
104
  fPt = self._textFont.pointSizeF()
105
105
 
106
106
  # Scaled Sizes
107
107
  # ============
108
108
 
109
109
  self._mHead = {
110
- self.T_TITLE: (mScale * self._marginTitle[0], mScale * self._marginTitle[1]),
111
- self.T_HEAD1: (mScale * self._marginHead1[0], mScale * self._marginHead1[1]),
112
- self.T_HEAD2: (mScale * self._marginHead2[0], mScale * self._marginHead2[1]),
113
- self.T_HEAD3: (mScale * self._marginHead3[0], mScale * self._marginHead3[1]),
114
- self.T_HEAD4: (mScale * self._marginHead4[0], mScale * self._marginHead4[1]),
110
+ self.T_TITLE: (mPx * self._marginTitle[0], mPx * self._marginTitle[1]),
111
+ self.T_HEAD1: (mPx * self._marginHead1[0], mPx * self._marginHead1[1]),
112
+ self.T_HEAD2: (mPx * self._marginHead2[0], mPx * self._marginHead2[1]),
113
+ self.T_HEAD3: (mPx * self._marginHead3[0], mPx * self._marginHead3[1]),
114
+ self.T_HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]),
115
115
  }
116
116
 
117
117
  self._sHead = {
@@ -122,12 +122,12 @@ class ToQTextDocument(Tokenizer):
122
122
  self.T_HEAD4: nwHeaders.H_SIZES.get(4, 1.0) * fPt,
123
123
  }
124
124
 
125
- self._mText = (mScale * self._marginText[0], mScale * self._marginText[1])
126
- self._mMeta = (mScale * self._marginMeta[0], mScale * self._marginMeta[1])
127
- self._mSep = (mScale * self._marginSep[0], mScale * self._marginSep[1])
125
+ self._mText = (mPx * self._marginText[0], mPx * self._marginText[1])
126
+ self._mMeta = (mPx * self._marginMeta[0], mPx * self._marginMeta[1])
127
+ self._mSep = (mPx * self._marginSep[0], mPx * self._marginSep[1])
128
128
 
129
- self._mIndent = mScale * 2.0
130
- self._tIndent = mScale * self._firstWidth
129
+ self._mIndent = mPx * 2.0
130
+ self._tIndent = mPx * self._firstWidth
131
131
 
132
132
  # Block Format
133
133
  # ============
@@ -29,12 +29,12 @@ 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, QCompleter, QDialogButtonBox, QFileDialog, QHBoxLayout,
33
- QLineEdit, QPushButton, QVBoxLayout, QWidget
32
+ QCompleter, QDialogButtonBox, QFileDialog, QHBoxLayout, QLineEdit,
33
+ QPushButton, QVBoxLayout, QWidget
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
- from novelwriter.common import describeFont
37
+ from novelwriter.common import describeFont, uniqueCompact
38
38
  from novelwriter.constants import nwUnicode
39
39
  from novelwriter.dialogs.quotes import GuiQuoteSelect
40
40
  from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
@@ -43,10 +43,7 @@ from novelwriter.extensions.modified import (
43
43
  )
44
44
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
45
45
  from novelwriter.extensions.switch import NSwitch
46
- from novelwriter.types import (
47
- QtAlignCenter, QtDialogApply, QtDialogClose, QtDialogSave, QtRoleAccept,
48
- QtRoleApply, QtRoleReject
49
- )
46
+ from novelwriter.types import QtAlignCenter, QtDialogCancel, QtDialogSave
50
47
 
51
48
  logger = logging.getLogger(__name__)
52
49
 
@@ -89,8 +86,9 @@ class GuiPreferences(NDialog):
89
86
  self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
90
87
 
91
88
  # Buttons
92
- self.buttonBox = QDialogButtonBox(QtDialogApply | QtDialogSave | QtDialogClose, self)
93
- self.buttonBox.clicked.connect(self._dialogButtonClicked)
89
+ self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogCancel, self)
90
+ self.buttonBox.accepted.connect(self._doSave)
91
+ self.buttonBox.rejected.connect(self.reject)
94
92
 
95
93
  # Assemble
96
94
  self.searchBox = QHBoxLayout()
@@ -784,19 +782,6 @@ class GuiPreferences(NDialog):
784
782
  # Private Slots
785
783
  ##
786
784
 
787
- @pyqtSlot("QAbstractButton*")
788
- def _dialogButtonClicked(self, button: QAbstractButton) -> None:
789
- """Handle button clicks from the dialog button box."""
790
- role = self.buttonBox.buttonRole(button)
791
- if role == QtRoleApply:
792
- self._saveValues()
793
- elif role == QtRoleAccept:
794
- self._saveValues()
795
- self.close()
796
- elif role == QtRoleReject:
797
- self.close()
798
- return
799
-
800
785
  @pyqtSlot(int)
801
786
  def _sidebarClicked(self, section: int) -> None:
802
787
  """Process a user request to switch page."""
@@ -897,7 +882,7 @@ class GuiPreferences(NDialog):
897
882
  CONFIG.setPreferencesWinSize(self.width(), self.height())
898
883
  return
899
884
 
900
- def _saveValues(self) -> None:
885
+ def _doSave(self) -> None:
901
886
  """Save the values set in the form."""
902
887
  updateTheme = False
903
888
  needsRestart = False
@@ -967,8 +952,8 @@ class GuiPreferences(NDialog):
967
952
  # Text Highlighting
968
953
  dialogueStyle = self.dialogStyle.currentData()
969
954
  allowOpenDial = self.allowOpenDial.isChecked()
970
- narratorBreak = self.narratorBreak.text()
971
- dialogueLine = self.dialogLine.text()
955
+ narratorBreak = self.narratorBreak.text().strip()
956
+ dialogueLine = self.dialogLine.text().strip()
972
957
  altDialogOpen = self.altDialogOpen.text()
973
958
  altDialogClose = self.altDialogClose.text()
974
959
  highlightEmph = self.highlightEmph.isChecked()
@@ -998,8 +983,8 @@ class GuiPreferences(NDialog):
998
983
  CONFIG.doReplaceDQuote = self.doReplaceDQuote.isChecked()
999
984
  CONFIG.doReplaceDash = self.doReplaceDash.isChecked()
1000
985
  CONFIG.doReplaceDots = self.doReplaceDots.isChecked()
1001
- CONFIG.fmtPadBefore = self.fmtPadBefore.text().strip()
1002
- CONFIG.fmtPadAfter = self.fmtPadAfter.text().strip()
986
+ CONFIG.fmtPadBefore = uniqueCompact(self.fmtPadBefore.text())
987
+ CONFIG.fmtPadAfter = uniqueCompact(self.fmtPadAfter.text())
1003
988
  CONFIG.fmtPadThin = self.fmtPadThin.isChecked()
1004
989
 
1005
990
  # Quotation Style
@@ -1012,4 +997,6 @@ class GuiPreferences(NDialog):
1012
997
  CONFIG.saveConfig()
1013
998
  self.newPreferencesReady.emit(needsRestart, refreshTree, updateTheme, updateSyntax)
1014
999
 
1000
+ self.close()
1001
+
1015
1002
  return
@@ -58,7 +58,7 @@ class GuiProjectSettings(NDialog):
58
58
  PAGE_IMPORT = 2
59
59
  PAGE_REPLACE = 3
60
60
 
61
- newProjectSettingsReady = pyqtSignal(bool)
61
+ newProjectSettingsReady = pyqtSignal()
62
62
 
63
63
  def __init__(self, parent: QWidget, gotoPage: int = PAGE_SETTINGS) -> None:
64
64
  super().__init__(parent=parent)
@@ -175,7 +175,7 @@ class GuiProjectSettings(NDialog):
175
175
  projAuthor = self.settingsPage.projAuthor.text()
176
176
  projLang = self.settingsPage.projLang.currentData()
177
177
  spellLang = self.settingsPage.spellLang.currentData()
178
- doBackup = not self.settingsPage.doBackup.isChecked()
178
+ doBackup = not self.settingsPage.noBackup.isChecked()
179
179
 
180
180
  project.data.setName(projName)
181
181
  project.data.setAuthor(projAuthor)
@@ -183,23 +183,19 @@ class GuiProjectSettings(NDialog):
183
183
  project.data.setSpellLang(spellLang)
184
184
  project.setProjectLang(projLang)
185
185
 
186
- rebuildTrees = False
187
-
188
186
  if self.statusPage.changed:
189
187
  logger.debug("Updating status labels")
190
188
  project.data.itemStatus.update(self.statusPage.getNewList())
191
- rebuildTrees = True
192
189
 
193
190
  if self.importPage.changed:
194
191
  logger.debug("Updating importance labels")
195
192
  project.data.itemImport.update(self.importPage.getNewList())
196
- rebuildTrees = True
197
193
 
198
194
  if self.replacePage.changed:
199
195
  logger.debug("Updating auto-replace settings")
200
196
  project.data.setAutoReplace(self.replacePage.getNewList())
201
197
 
202
- self.newProjectSettingsReady.emit(rebuildTrees)
198
+ self.newProjectSettingsReady.emit()
203
199
  QApplication.processEvents()
204
200
  self.close()
205
201
 
@@ -289,10 +285,10 @@ class _SettingsPage(NScrollableForm):
289
285
  self.spellLang.setCurrentIndex(idx)
290
286
 
291
287
  # Backup on Close
292
- self.doBackup = NSwitch(self)
293
- self.doBackup.setChecked(not data.doBackup)
288
+ self.noBackup = NSwitch(self)
289
+ self.noBackup.setChecked(not data.doBackup)
294
290
  self.addRow(
295
- self.tr("Disable backup on close"), self.doBackup,
291
+ self.tr("Disable backup on close"), self.noBackup,
296
292
  self.tr("Overrides main preferences.")
297
293
  )
298
294
 
@@ -281,13 +281,28 @@ class NColourLabel(QLabel):
281
281
 
282
282
  return
283
283
 
284
+ def setTextColors(self, *, color: QColor | None = None, faded: QColor | None = None) -> None:
285
+ """Set or update the text colours."""
286
+ self._color = color or self._color
287
+ self._faded = faded or self._faded
288
+ self._refeshTextColor()
289
+ return
290
+
284
291
  def setColorState(self, state: bool) -> None:
285
292
  """Change the colour state."""
286
293
  if self._state is not state:
287
294
  self._state = state
288
- colour = self.palette()
289
- colour.setColor(QPalette.ColorRole.WindowText, self._color if state else self._faded)
290
- self.setPalette(colour)
295
+ self._refeshTextColor()
296
+ return
297
+
298
+ def _refeshTextColor(self) -> None:
299
+ """Refresh the colour of the text on the label."""
300
+ palette = self.palette()
301
+ palette.setColor(
302
+ QPalette.ColorRole.WindowText,
303
+ self._color if self._state else self._faded,
304
+ )
305
+ self.setPalette(palette)
291
306
  return
292
307
 
293
308
 
@@ -28,6 +28,7 @@ import logging
28
28
  from PyQt5.QtGui import QColor, QPainter, QPaintEvent
29
29
  from PyQt5.QtWidgets import QAbstractButton, QWidget
30
30
 
31
+ from novelwriter import CONFIG
31
32
  from novelwriter.enum import nwTrinary
32
33
  from novelwriter.types import QtBlack, QtPaintAnitAlias
33
34
 
@@ -36,6 +37,10 @@ logger = logging.getLogger(__name__)
36
37
 
37
38
  class StatusLED(QAbstractButton):
38
39
 
40
+ __slots__ = (
41
+ "_neutral", "_postitve", "_negative", "_color", "_state", "_bPx"
42
+ )
43
+
39
44
  def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
40
45
  super().__init__(parent=parent)
41
46
  self._neutral = QtBlack
@@ -43,6 +48,7 @@ class StatusLED(QAbstractButton):
43
48
  self._negative = QtBlack
44
49
  self._color = QtBlack
45
50
  self._state = nwTrinary.NEUTRAL
51
+ self._bPx = CONFIG.pxInt(1)
46
52
  self.setFixedWidth(sW)
47
53
  self.setFixedHeight(sH)
48
54
  return
@@ -76,8 +82,12 @@ class StatusLED(QAbstractButton):
76
82
  """Draw the LED."""
77
83
  painter = QPainter(self)
78
84
  painter.setRenderHint(QtPaintAnitAlias, True)
79
- painter.setPen(self.palette().dark().color())
85
+ painter.setPen(self.palette().windowText().color())
80
86
  painter.setBrush(self._color)
81
87
  painter.setOpacity(1.0)
82
- painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
88
+ painter.drawEllipse(
89
+ self._bPx, self._bPx,
90
+ self.width() - 2*self._bPx,
91
+ self.height() - 2*self._bPx
92
+ )
83
93
  return
@@ -2919,10 +2919,10 @@ class GuiDocEditHeader(QWidget):
2919
2919
  palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
2920
2920
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
2921
2921
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
2922
-
2923
2922
  self.setPalette(palette)
2924
- self.itemTitle.setPalette(palette)
2925
-
2923
+ self.itemTitle.setTextColors(
2924
+ color=palette.windowText().color(), faded=SHARED.theme.fadedText
2925
+ )
2926
2926
  return
2927
2927
 
2928
2928
  def changeFocusState(self, state: bool) -> None:
@@ -229,15 +229,13 @@ class GuiDocViewer(QTextBrowser):
229
229
  QApplication.restoreOverrideCursor()
230
230
  return False
231
231
 
232
- # Refresh the tab stops
233
- self.setTabStopDistance(CONFIG.getTabWidth())
234
-
235
- # Must be before setHtml
232
+ # Must be before setDocument
236
233
  if updateHistory:
237
234
  self.docHistory.append(tHandle)
238
235
 
239
236
  self.setDocumentTitle(tHandle)
240
237
  self.setDocument(qDoc.document)
238
+ self.setTabStopDistance(CONFIG.getTabWidth())
241
239
 
242
240
  if self._docHandle == tHandle:
243
241
  # This is a refresh, so we set the scrollbar back to where it was
@@ -741,7 +739,9 @@ class GuiDocViewHeader(QWidget):
741
739
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
742
740
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
743
741
  self.setPalette(palette)
744
- self.itemTitle.setPalette(palette)
742
+ self.itemTitle.setTextColors(
743
+ color=palette.windowText().color(), faded=SHARED.theme.fadedText
744
+ )
745
745
  return
746
746
 
747
747
  def changeFocusState(self, state: bool) -> None:
@@ -189,6 +189,13 @@ class GuiDocViewerPanel(QWidget):
189
189
  self._updateTabVisibility()
190
190
  return
191
191
 
192
+ @pyqtSlot(str)
193
+ def updateStatusLabels(self, kind: str) -> None:
194
+ """Update the importance labels."""
195
+ if kind == "i":
196
+ self._loadAllTags()
197
+ return
198
+
192
199
  ##
193
200
  # Private Slots
194
201
  ##
@@ -268,6 +268,7 @@ class GuiOutlineToolBar(QToolBar):
268
268
  self.aExport.setIcon(SHARED.theme.getIcon("export"))
269
269
  self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
270
270
  self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}")
271
+ self.novelLabel.setTextColors(color=self.palette().windowText().color())
271
272
  return
272
273
 
273
274
  def populateNovelList(self) -> None: