novelWriter 2.3rc1__py3-none-any.whl → 2.4__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 (125) hide show
  1. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/METADATA +5 -6
  2. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/RECORD +119 -109
  3. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/WHEEL +1 -1
  4. novelWriter-2.4.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +17 -10
  6. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  7. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  8. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  9. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  10. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  11. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  12. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  13. novelwriter/assets/i18n/nw_nl_NL.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_nl_NL.json +11 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  18. novelwriter/assets/icons/none.svg +4 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
  20. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
  21. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
  22. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
  23. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
  25. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +4 -0
  28. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
  29. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
  30. novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
  31. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
  32. novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
  33. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  34. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  38. novelwriter/assets/syntax/default_dark.conf +1 -0
  39. novelwriter/assets/syntax/default_light.conf +1 -0
  40. novelwriter/assets/syntax/grey_dark.conf +1 -0
  41. novelwriter/assets/syntax/grey_light.conf +1 -0
  42. novelwriter/assets/syntax/light_owl.conf +1 -0
  43. novelwriter/assets/syntax/night_owl.conf +1 -0
  44. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  45. novelwriter/assets/syntax/solarized_light.conf +1 -0
  46. novelwriter/assets/syntax/tango.conf +23 -0
  47. novelwriter/assets/syntax/tomorrow.conf +1 -0
  48. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  49. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  50. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  51. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  52. novelwriter/assets/text/credits_en.htm +25 -23
  53. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  54. novelwriter/common.py +12 -4
  55. novelwriter/config.py +47 -16
  56. novelwriter/constants.py +5 -6
  57. novelwriter/core/buildsettings.py +64 -44
  58. novelwriter/core/coretools.py +97 -13
  59. novelwriter/core/docbuild.py +74 -7
  60. novelwriter/core/document.py +24 -3
  61. novelwriter/core/index.py +31 -112
  62. novelwriter/core/project.py +11 -15
  63. novelwriter/core/projectxml.py +3 -2
  64. novelwriter/core/sessions.py +2 -2
  65. novelwriter/core/spellcheck.py +3 -3
  66. novelwriter/core/status.py +6 -5
  67. novelwriter/core/storage.py +16 -6
  68. novelwriter/core/tohtml.py +22 -25
  69. novelwriter/core/tokenizer.py +417 -237
  70. novelwriter/core/tomd.py +17 -8
  71. novelwriter/core/toodt.py +386 -351
  72. novelwriter/core/tree.py +8 -8
  73. novelwriter/dialogs/about.py +10 -12
  74. novelwriter/dialogs/docmerge.py +17 -14
  75. novelwriter/dialogs/docsplit.py +20 -19
  76. novelwriter/dialogs/editlabel.py +5 -4
  77. novelwriter/dialogs/preferences.py +32 -40
  78. novelwriter/dialogs/projectsettings.py +31 -28
  79. novelwriter/dialogs/quotes.py +10 -9
  80. novelwriter/dialogs/wordlist.py +18 -15
  81. novelwriter/enum.py +17 -14
  82. novelwriter/error.py +14 -12
  83. novelwriter/extensions/circularprogress.py +12 -8
  84. novelwriter/extensions/configlayout.py +23 -3
  85. novelwriter/extensions/modified.py +51 -2
  86. novelwriter/extensions/pagedsidebar.py +16 -14
  87. novelwriter/extensions/simpleprogress.py +3 -1
  88. novelwriter/extensions/statusled.py +3 -1
  89. novelwriter/extensions/switch.py +10 -9
  90. novelwriter/extensions/switchbox.py +14 -13
  91. novelwriter/extensions/versioninfo.py +1 -1
  92. novelwriter/gui/doceditor.py +433 -496
  93. novelwriter/gui/dochighlight.py +54 -33
  94. novelwriter/gui/docviewer.py +162 -175
  95. novelwriter/gui/docviewerpanel.py +20 -37
  96. novelwriter/gui/editordocument.py +15 -4
  97. novelwriter/gui/itemdetails.py +51 -54
  98. novelwriter/gui/mainmenu.py +37 -17
  99. novelwriter/gui/noveltree.py +31 -37
  100. novelwriter/gui/outline.py +120 -98
  101. novelwriter/gui/projtree.py +114 -112
  102. novelwriter/gui/search.py +362 -0
  103. novelwriter/gui/sidebar.py +36 -45
  104. novelwriter/gui/statusbar.py +14 -14
  105. novelwriter/gui/theme.py +116 -34
  106. novelwriter/guimain.py +216 -207
  107. novelwriter/shared.py +31 -6
  108. novelwriter/text/counting.py +138 -0
  109. novelwriter/tools/dictionaries.py +15 -14
  110. novelwriter/tools/lipsum.py +20 -17
  111. novelwriter/tools/manusbuild.py +43 -35
  112. novelwriter/tools/manuscript.py +381 -104
  113. novelwriter/tools/manussettings.py +263 -125
  114. novelwriter/tools/noveldetails.py +21 -19
  115. novelwriter/tools/welcome.py +59 -57
  116. novelwriter/tools/writingstats.py +61 -55
  117. novelwriter/types.py +90 -0
  118. novelWriter-2.3rc1.dist-info/entry_points.txt +0 -5
  119. novelwriter/core/__init__.py +0 -3
  120. novelwriter/dialogs/__init__.py +0 -3
  121. novelwriter/extensions/__init__.py +0 -3
  122. novelwriter/gui/__init__.py +0 -3
  123. novelwriter/tools/__init__.py +0 -3
  124. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/LICENSE.md +0 -0
  125. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/top_level.txt +0 -0
novelwriter/gui/theme.py CHANGED
@@ -29,20 +29,24 @@ import logging
29
29
  from math import ceil
30
30
  from pathlib import Path
31
31
 
32
- from PyQt5.QtCore import Qt
33
- from PyQt5.QtWidgets import qApp
32
+ from PyQt5.QtCore import QSize, Qt
34
33
  from PyQt5.QtGui import (
35
34
  QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap
36
35
  )
36
+ from PyQt5.QtWidgets import QApplication
37
37
 
38
38
  from novelwriter import CONFIG
39
+ from novelwriter.common import NWConfigParser, cssCol, minmax
40
+ from novelwriter.constants import nwLabels
39
41
  from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
40
42
  from novelwriter.error import logException
41
- from novelwriter.common import NWConfigParser, minmax
42
- from novelwriter.constants import nwLabels
43
43
 
44
44
  logger = logging.getLogger(__name__)
45
45
 
46
+ STYLES_FLAT_TABS = "flatTabWidget"
47
+ STYLES_MIN_TOOLBUTTON = "minimalToolButton"
48
+ STYLES_BIG_TOOLBUTTON = "bigToolButton"
49
+
46
50
 
47
51
  # =============================================================================================== #
48
52
  # Gui Theme Class
@@ -70,9 +74,9 @@ class GuiTheme:
70
74
  self.isLightTheme = True
71
75
 
72
76
  # GUI
73
- self.statNone = QColor(120, 120, 120)
74
- self.statUnsaved = QColor(200, 15, 39)
75
- self.statSaved = QColor(2, 133, 37)
77
+ self.statNone = QColor(0, 0, 0)
78
+ self.statUnsaved = QColor(0, 0, 0)
79
+ self.statSaved = QColor(0, 0, 0)
76
80
  self.helpText = QColor(0, 0, 0)
77
81
 
78
82
  # Loaded Syntax Settings
@@ -106,6 +110,7 @@ class GuiTheme:
106
110
  self.colError = QColor(0, 0, 0)
107
111
  self.colRepTag = QColor(0, 0, 0)
108
112
  self.colMod = QColor(0, 0, 0)
113
+ self.colMark = QColor(255, 255, 255, 128)
109
114
 
110
115
  # Class Setup
111
116
  # ===========
@@ -119,6 +124,7 @@ class GuiTheme:
119
124
  self._syntaxList: list[tuple[str, str]] = []
120
125
  self._availThemes: dict[str, Path] = {}
121
126
  self._availSyntax: dict[str, Path] = {}
127
+ self._styleSheets: dict[str, str] = {}
122
128
 
123
129
  self._listConf(self._availSyntax, CONFIG.assetPath("syntax"))
124
130
  self._listConf(self._availThemes, CONFIG.assetPath("themes"))
@@ -138,31 +144,42 @@ class GuiTheme:
138
144
  self.getHeaderDecorationNarrow = self.iconCache.getHeaderDecorationNarrow
139
145
 
140
146
  # Extract Other Info
141
- self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
142
- self.guiScale = qApp.primaryScreen().logicalDotsPerInchX()/96.0
147
+ self.guiDPI = QApplication.primaryScreen().logicalDotsPerInchX()
148
+ self.guiScale = QApplication.primaryScreen().logicalDotsPerInchX()/96.0
143
149
  CONFIG.guiScale = self.guiScale
144
150
  logger.debug("GUI DPI: %.1f", self.guiDPI)
145
151
  logger.debug("GUI Scale: %.2f", self.guiScale)
146
152
 
147
153
  # Fonts
148
- self.guiFont = qApp.font()
154
+ self.guiFont = QApplication.font()
155
+ self.guiFontB = QApplication.font()
156
+ self.guiFontB.setBold(True)
149
157
 
150
158
  qMetric = QFontMetrics(self.guiFont)
159
+ fHeight = qMetric.height()
160
+ fAscent = qMetric.ascent()
151
161
  self.fontPointSize = self.guiFont.pointSizeF()
152
- self.fontPixelSize = int(round(qMetric.height()))
153
- self.baseIconSize = int(round(qMetric.ascent()))
162
+ self.fontPixelSize = int(round(fHeight))
163
+ self.baseIconHeight = int(round(fAscent))
164
+ self.baseButtonHeight = int(round(1.35*fAscent))
154
165
  self.textNHeight = qMetric.boundingRect("N").height()
155
166
  self.textNWidth = qMetric.boundingRect("N").width()
156
167
 
168
+ self.baseIconSize = QSize(self.baseIconHeight, self.baseIconHeight)
169
+ self.buttonIconSize = QSize(int(0.9*self.baseIconHeight), int(0.9*self.baseIconHeight))
170
+
157
171
  # Monospace Font
158
172
  self.guiFontFixed = QFont()
159
173
  self.guiFontFixed.setPointSizeF(0.95*self.fontPointSize)
160
- self.guiFontFixed.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
174
+ self.guiFontFixed.setFamily(
175
+ QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont).family()
176
+ )
161
177
 
162
178
  logger.debug("GUI Font Family: %s", self.guiFont.family())
163
179
  logger.debug("GUI Font Point Size: %.2f", self.fontPointSize)
164
180
  logger.debug("GUI Font Pixel Size: %d", self.fontPixelSize)
165
- logger.debug("GUI Base Icon Size: %d", self.baseIconSize)
181
+ logger.debug("GUI Base Icon Height: %d", self.baseIconHeight)
182
+ logger.debug("GUI Base Button Height: %d", self.baseButtonHeight)
166
183
  logger.debug("Text 'N' Height: %d", self.textNHeight)
167
184
  logger.debug("Text 'N' Width: %d", self.textNWidth)
168
185
 
@@ -210,6 +227,10 @@ class GuiTheme:
210
227
  logException()
211
228
  return False
212
229
 
230
+ # Reset Palette
231
+ self._guiPalette = QApplication.style().standardPalette()
232
+ self._resetGuiColors()
233
+
213
234
  # Main
214
235
  sec = "Main"
215
236
  if parser.has_section(sec):
@@ -239,8 +260,6 @@ class GuiTheme:
239
260
  self._setPalette(parser, sec, "highlightedtext", QPalette.ColorRole.HighlightedText)
240
261
  self._setPalette(parser, sec, "link", QPalette.ColorRole.Link)
241
262
  self._setPalette(parser, sec, "linkvisited", QPalette.ColorRole.LinkVisited)
242
- else:
243
- self._guiPalette = qApp.style().standardPalette()
244
263
 
245
264
  # GUI
246
265
  sec = "GUI"
@@ -269,7 +288,10 @@ class GuiTheme:
269
288
  self.iconCache.loadTheme(self.themeIcons or defaultIcons)
270
289
 
271
290
  # Apply Styles
272
- qApp.setPalette(self._guiPalette)
291
+ QApplication.setPalette(self._guiPalette)
292
+
293
+ # Reset stylesheets so that they are regenerated
294
+ self._buildStyleSheets(self._guiPalette)
273
295
 
274
296
  return True
275
297
 
@@ -329,6 +351,7 @@ class GuiTheme:
329
351
  self.colError = self._parseColour(confParser, cnfSec, "errorline")
330
352
  self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
331
353
  self.colMod = self._parseColour(confParser, cnfSec, "modifier")
354
+ self.colMark = self._parseColour(confParser, cnfSec, "texthighlight")
332
355
 
333
356
  return True
334
357
 
@@ -344,7 +367,7 @@ class GuiTheme:
344
367
  if themeName:
345
368
  self._themeList.append((themeKey, themeName))
346
369
 
347
- self._themeList = sorted(self._themeList, key=lambda x: x[1])
370
+ self._themeList = sorted(self._themeList, key=_sortTheme)
348
371
 
349
372
  return self._themeList
350
373
 
@@ -360,14 +383,26 @@ class GuiTheme:
360
383
  if syntaxName:
361
384
  self._syntaxList.append((syntaxKey, syntaxName))
362
385
 
363
- self._syntaxList = sorted(self._syntaxList, key=lambda x: x[1])
386
+ self._syntaxList = sorted(self._syntaxList, key=_sortTheme)
364
387
 
365
388
  return self._syntaxList
366
389
 
390
+ def getStyleSheet(self, name: str) -> str:
391
+ """Load a standard style sheet."""
392
+ return self._styleSheets.get(name, "")
393
+
367
394
  ##
368
395
  # Internal Functions
369
396
  ##
370
397
 
398
+ def _resetGuiColors(self) -> None:
399
+ """Reset GUI colours to default values."""
400
+ self.statNone = QColor(120, 120, 120)
401
+ self.statUnsaved = QColor(200, 15, 39)
402
+ self.statSaved = QColor(2, 133, 37)
403
+ self.helpText = QColor(0, 0, 0)
404
+ return
405
+
371
406
  def _setGuiFont(self) -> None:
372
407
  """Update the GUI's font style from settings."""
373
408
  font = QFont()
@@ -378,14 +413,14 @@ class GuiTheme:
378
413
  font.setFamily("Arial")
379
414
  font.setPointSize(10)
380
415
  else:
381
- font = fontDB.systemFont(QFontDatabase.GeneralFont)
416
+ font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
382
417
  CONFIG.guiFont = font.family()
383
418
  CONFIG.guiFontSize = font.pointSize()
384
419
  else:
385
420
  font.setFamily(CONFIG.guiFont)
386
421
  font.setPointSize(CONFIG.guiFontSize)
387
422
 
388
- qApp.setFont(font)
423
+ QApplication.setFont(font)
389
424
 
390
425
  return
391
426
 
@@ -410,6 +445,41 @@ class GuiTheme:
410
445
  self._guiPalette.setColor(value, self._parseColour(parser, section, name))
411
446
  return
412
447
 
448
+ def _buildStyleSheets(self, palette: QPalette) -> None:
449
+ """Build default style sheets."""
450
+ self._styleSheets = {}
451
+
452
+ aPx = CONFIG.pxInt(2)
453
+ bPx = CONFIG.pxInt(4)
454
+ cPx = CONFIG.pxInt(6)
455
+ dPx = CONFIG.pxInt(8)
456
+
457
+ tCol = palette.text().color()
458
+ hCol = palette.highlight().color()
459
+
460
+ # Flat Tab Widget and Tab Bar:
461
+ self._styleSheets[STYLES_FLAT_TABS] = (
462
+ "QTabWidget::pane {border: 0;} "
463
+ f"QTabWidget QTabBar::tab {{border: 0; padding: {bPx}px {dPx}px;}} "
464
+ f"QTabWidget QTabBar::tab:selected {{color: {cssCol(hCol)};}} "
465
+ )
466
+
467
+ # Minimal Tool Button
468
+ self._styleSheets[STYLES_MIN_TOOLBUTTON] = (
469
+ f"QToolButton {{padding: {aPx}px; margin: 0; border: none; background: transparent;}} "
470
+ f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
471
+ "QToolButton::menu-indicator {image: none;} "
472
+ )
473
+
474
+ # Big Tool Button
475
+ self._styleSheets[STYLES_BIG_TOOLBUTTON] = (
476
+ f"QToolButton {{padding: {cPx}px; margin: 0; border: none; background: transparent;}} "
477
+ f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
478
+ "QToolButton::menu-indicator {image: none;} "
479
+ )
480
+
481
+ return
482
+
413
483
  # End Class GuiTheme
414
484
 
415
485
 
@@ -437,7 +507,7 @@ class GuiIcons:
437
507
  "build_excluded", "build_filtered", "build_included", "proj_chapter", "proj_details",
438
508
  "proj_document", "proj_folder", "proj_note", "proj_nwx", "proj_section", "proj_scene",
439
509
  "proj_stats", "proj_title", "status_idle", "status_lang", "status_lines", "status_stats",
440
- "status_time", "view_build", "view_editor", "view_novel", "view_outline",
510
+ "status_time", "view_build", "view_editor", "view_novel", "view_outline", "view_search",
441
511
 
442
512
  # Class Icons
443
513
  "cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none", "cls_novel",
@@ -448,8 +518,8 @@ class GuiIcons:
448
518
  "search_regex", "search_word",
449
519
 
450
520
  # Format Icons
451
- "fmt_bold", "fmt_bold-md", "fmt_italic", "fmt_italic-md", "fmt_strike", "fmt_strike-md",
452
- "fmt_subscript", "fmt_superscript", "fmt_underline",
521
+ "fmt_bold", "fmt_bold-md", "fmt_italic", "fmt_italic-md", "fmt_mark", "fmt_strike",
522
+ "fmt_strike-md", "fmt_subscript", "fmt_superscript", "fmt_underline",
453
523
 
454
524
  # General Button Icons
455
525
  "add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
@@ -460,6 +530,7 @@ class GuiIcons:
460
530
  # Switches
461
531
  "sticky-on", "sticky-off",
462
532
  "bullet-on", "bullet-off",
533
+ "unfold-show", "unfold-hide",
463
534
 
464
535
  # Decorations
465
536
  "deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4", "deco_doc_more",
@@ -470,6 +541,7 @@ class GuiIcons:
470
541
  TOGGLE_ICON_KEYS: dict[str, tuple[str, str]] = {
471
542
  "sticky": ("sticky-on", "sticky-off"),
472
543
  "bullet": ("bullet-on", "bullet-off"),
544
+ "unfold": ("unfold-show", "unfold-hide"),
473
545
  }
474
546
 
475
547
  IMAGE_MAP: dict[str, tuple[str, str]] = {
@@ -491,6 +563,9 @@ class GuiIcons:
491
563
  self._confName = "icons.conf"
492
564
  self._iconPath = CONFIG.assetPath("icons")
493
565
 
566
+ # None Icon
567
+ self._noIcon = QIcon(str(self._iconPath / "none.svg"))
568
+
494
569
  # Icon Theme Meta
495
570
  self.themeName = ""
496
571
  self.themeDescription = ""
@@ -621,13 +696,13 @@ class GuiIcons:
621
696
  def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon:
622
697
  """Return a toggle icon from the icon buffer. or load it."""
623
698
  if name in self.TOGGLE_ICON_KEYS:
624
- pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
699
+ pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
625
700
  pTwo = self.getPixmap(self.TOGGLE_ICON_KEYS[name][1], size)
626
701
  icon = QIcon()
627
702
  icon.addPixmap(pOne, QIcon.Mode.Normal, QIcon.State.On)
628
703
  icon.addPixmap(pTwo, QIcon.Mode.Normal, QIcon.State.Off)
629
704
  return icon
630
- return QIcon()
705
+ return self._noIcon
631
706
 
632
707
  def getPixmap(self, name: str, size: tuple[int, int]) -> QPixmap:
633
708
  """Return an icon from the icon buffer as a QPixmap. If it
@@ -638,7 +713,7 @@ class GuiIcons:
638
713
  def getItemIcon(self, tType: nwItemType, tClass: nwItemClass,
639
714
  tLayout: nwItemLayout, hLevel: str = "H0") -> QIcon:
640
715
  """Get the correct icon for a project item based on type, class
641
- and header level
716
+ and heading level
642
717
  """
643
718
  iconName = None
644
719
  if tType == nwItemType.ROOT:
@@ -659,14 +734,14 @@ class GuiIcons:
659
734
  elif tLayout == nwItemLayout.NOTE:
660
735
  iconName = "proj_note"
661
736
  if iconName is None:
662
- return QIcon()
737
+ return self._noIcon
663
738
 
664
739
  return self.getIcon(iconName)
665
740
 
666
741
  def getHeaderDecoration(self, hLevel: int) -> QPixmap:
667
- """Get the decoration for a specific header level."""
742
+ """Get the decoration for a specific heading level."""
668
743
  if not self._headerDec:
669
- iPx = self.mainTheme.baseIconSize
744
+ iPx = self.mainTheme.baseIconHeight
670
745
  self._headerDec = [
671
746
  self.loadDecoration("deco_doc_h0", h=iPx),
672
747
  self.loadDecoration("deco_doc_h1", h=iPx),
@@ -677,9 +752,9 @@ class GuiIcons:
677
752
  return self._headerDec[minmax(hLevel, 0, 4)]
678
753
 
679
754
  def getHeaderDecorationNarrow(self, hLevel: int) -> QPixmap:
680
- """Get the narrow decoration for a specific header level."""
755
+ """Get the narrow decoration for a specific heading level."""
681
756
  if not self._headerDecNarrow:
682
- iPx = self.mainTheme.baseIconSize
757
+ iPx = self.mainTheme.baseIconHeight
683
758
  self._headerDecNarrow = [
684
759
  self.loadDecoration("deco_doc_h0_n", h=iPx),
685
760
  self.loadDecoration("deco_doc_h1_n", h=iPx),
@@ -700,7 +775,7 @@ class GuiIcons:
700
775
  """
701
776
  if name not in self.ICON_KEYS:
702
777
  logger.error("Requested unknown icon name '%s'", name)
703
- return QIcon()
778
+ return self._noIcon
704
779
 
705
780
  # If we just want the app icons, return right away
706
781
  if name == "novelwriter":
@@ -716,7 +791,7 @@ class GuiIcons:
716
791
  # If we didn't find one, give up and return an empty icon
717
792
  logger.warning("Did not load an icon for '%s'", name)
718
793
 
719
- return QIcon()
794
+ return self._noIcon
720
795
 
721
796
  # END Class GuiIcons
722
797
 
@@ -725,6 +800,13 @@ class GuiIcons:
725
800
  # Module Functions
726
801
  # =============================================================================================== #
727
802
 
803
+
804
+ def _sortTheme(data: tuple[str, str]) -> str:
805
+ """Key function for theme sorting."""
806
+ key, name = data
807
+ return f"*{name}" if key.startswith("default_") else name
808
+
809
+
728
810
  def _loadInternalName(confParser: NWConfigParser, confFile: str | Path) -> str:
729
811
  """Open a conf file and read the 'name' setting."""
730
812
  try: