novelWriter 2.2rc1__py3-none-any.whl → 2.3__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.
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/RECORD +149 -132
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/WHEEL +1 -1
- novelWriter-2.3.dist-info/entry_points.txt +2 -0
- novelwriter/__init__.py +11 -6
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +1 -0
- novelwriter/assets/i18n/project_es_419.json +11 -0
- novelwriter/assets/i18n/project_fr_FR.json +11 -0
- novelwriter/assets/i18n/project_it_IT.json +11 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -1
- novelwriter/assets/i18n/project_nb_NO.json +1 -0
- novelwriter/assets/i18n/project_nl_NL.json +11 -0
- novelwriter/assets/i18n/project_pt_BR.json +11 -0
- novelwriter/assets/i18n/project_zh_CN.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +11 -2
- novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-list.svg +9 -0
- novelwriter/assets/icons/typicons_light/icons.conf +11 -2
- novelwriter/assets/icons/typicons_light/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-list.svg +9 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tango.conf +23 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +4 -2
- novelwriter/assets/themes/cyberpunk_night.conf +29 -0
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +64 -66
- novelwriter/config.py +39 -44
- novelwriter/constants.py +39 -17
- novelwriter/core/buildsettings.py +8 -8
- novelwriter/core/coretools.py +198 -157
- novelwriter/core/docbuild.py +7 -4
- novelwriter/core/document.py +7 -7
- novelwriter/core/index.py +90 -57
- novelwriter/core/item.py +23 -5
- novelwriter/core/options.py +11 -10
- novelwriter/core/project.py +73 -47
- novelwriter/core/projectdata.py +3 -16
- novelwriter/core/projectxml.py +14 -42
- novelwriter/core/sessions.py +4 -3
- novelwriter/core/spellcheck.py +6 -4
- novelwriter/core/status.py +5 -4
- novelwriter/core/storage.py +183 -141
- novelwriter/core/tohtml.py +6 -4
- novelwriter/core/tokenizer.py +110 -83
- novelwriter/core/tomd.py +2 -2
- novelwriter/core/toodt.py +41 -31
- novelwriter/core/tree.py +5 -4
- novelwriter/dialogs/about.py +88 -179
- novelwriter/dialogs/docmerge.py +30 -20
- novelwriter/dialogs/docsplit.py +33 -22
- novelwriter/dialogs/editlabel.py +20 -8
- novelwriter/dialogs/preferences.py +562 -725
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
- novelwriter/dialogs/quotes.py +47 -36
- novelwriter/dialogs/wordlist.py +128 -59
- novelwriter/enum.py +25 -22
- novelwriter/error.py +2 -2
- novelwriter/extensions/circularprogress.py +12 -12
- novelwriter/extensions/configlayout.py +185 -146
- novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +27 -13
- novelwriter/extensions/pagedsidebar.py +15 -20
- novelwriter/extensions/simpleprogress.py +8 -9
- novelwriter/extensions/statusled.py +9 -9
- novelwriter/extensions/switch.py +32 -64
- novelwriter/extensions/switchbox.py +2 -7
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +250 -214
- novelwriter/gui/dochighlight.py +66 -94
- novelwriter/gui/docviewer.py +71 -98
- novelwriter/gui/docviewerpanel.py +140 -47
- novelwriter/gui/editordocument.py +3 -3
- novelwriter/gui/itemdetails.py +9 -9
- novelwriter/gui/mainmenu.py +47 -47
- novelwriter/gui/noveltree.py +53 -61
- novelwriter/gui/outline.py +100 -76
- novelwriter/gui/projtree.py +246 -112
- novelwriter/gui/sidebar.py +9 -8
- novelwriter/gui/statusbar.py +49 -7
- novelwriter/gui/theme.py +74 -76
- novelwriter/guimain.py +175 -330
- novelwriter/shared.py +68 -30
- novelwriter/tools/dictionaries.py +7 -8
- novelwriter/tools/lipsum.py +34 -28
- novelwriter/tools/manusbuild.py +3 -4
- novelwriter/tools/manuscript.py +25 -32
- novelwriter/tools/manussettings.py +194 -225
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +819 -0
- novelwriter/tools/writingstats.py +26 -13
- novelWriter-2.2rc1.dist-info/entry_points.txt +0 -5
- novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -17
- novelwriter/dialogs/projdetails.py +0 -525
- novelwriter/dialogs/projload.py +0 -298
- novelwriter/dialogs/updates.py +0 -182
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
novelwriter/gui/sidebar.py
CHANGED
@@ -3,10 +3,10 @@ novelWriter – GUI Main Window SideBar
|
|
3
3
|
=====================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2022-05-10 [2.0rc1]
|
6
|
+
Created: 2022-05-10 [2.0rc1] GuiSideBar
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -27,12 +27,13 @@ import logging
|
|
27
27
|
|
28
28
|
from typing import TYPE_CHECKING
|
29
29
|
|
30
|
-
from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal
|
31
30
|
from PyQt5.QtGui import QPalette
|
31
|
+
from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal
|
32
32
|
from PyQt5.QtWidgets import QMenu, QToolButton, QVBoxLayout, QWidget
|
33
33
|
|
34
34
|
from novelwriter import CONFIG, SHARED
|
35
35
|
from novelwriter.enum import nwView
|
36
|
+
from novelwriter.extensions.eventfilters import StatusTipFilter
|
36
37
|
|
37
38
|
if TYPE_CHECKING: # pragma: no cover
|
38
39
|
from novelwriter.guimain import GuiMain
|
@@ -54,6 +55,7 @@ class GuiSideBar(QWidget):
|
|
54
55
|
iPx = CONFIG.pxInt(24)
|
55
56
|
iconSize = QSize(iPx, iPx)
|
56
57
|
self.setContentsMargins(0, 0, 0, 0)
|
58
|
+
self.installEventFilter(StatusTipFilter(mainGui))
|
57
59
|
|
58
60
|
# Buttons
|
59
61
|
self.tbProject = QToolButton(self)
|
@@ -77,9 +79,9 @@ class GuiSideBar(QWidget):
|
|
77
79
|
self.tbBuild.clicked.connect(self.mainGui.showBuildManuscriptDialog)
|
78
80
|
|
79
81
|
self.tbDetails = QToolButton(self)
|
80
|
-
self.tbDetails.setToolTip("{0} [Shift+F6]".format(self.tr("
|
82
|
+
self.tbDetails.setToolTip("{0} [Shift+F6]".format(self.tr("Novel Details")))
|
81
83
|
self.tbDetails.setIconSize(iconSize)
|
82
|
-
self.tbDetails.clicked.connect(self.mainGui.
|
84
|
+
self.tbDetails.clicked.connect(self.mainGui.showNovelDetailsDialog)
|
83
85
|
|
84
86
|
self.tbStats = QToolButton(self)
|
85
87
|
self.tbStats.setToolTip("{0} [F6]".format(self.tr("Writing Statistics")))
|
@@ -162,11 +164,10 @@ class GuiSideBar(QWidget):
|
|
162
164
|
|
163
165
|
class _PopRightMenu(QMenu):
|
164
166
|
|
165
|
-
def event(self, event: QEvent):
|
167
|
+
def event(self, event: QEvent) -> bool:
|
166
168
|
"""Overload the show event and move the menu popup location."""
|
167
169
|
if event.type() == QEvent.Show:
|
168
|
-
parent
|
169
|
-
if isinstance(parent, QWidget):
|
170
|
+
if isinstance(parent := self.parent(), QWidget):
|
170
171
|
offset = QPoint(parent.width(), parent.height() - self.height())
|
171
172
|
self.move(parent.mapToGlobal(offset))
|
172
173
|
return super(_PopRightMenu, self).event(event)
|
novelwriter/gui/statusbar.py
CHANGED
@@ -3,10 +3,10 @@ novelWriter – GUI Main Window Status Bar
|
|
3
3
|
========================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2019-04-20 [0.0.1]
|
6
|
+
Created: 2019-04-20 [0.0.1] GuiMainStatus
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -27,9 +27,9 @@ import logging
|
|
27
27
|
|
28
28
|
from time import time
|
29
29
|
from typing import TYPE_CHECKING, Literal
|
30
|
+
from datetime import datetime
|
30
31
|
|
31
32
|
from PyQt5.QtCore import pyqtSlot, QLocale
|
32
|
-
from PyQt5.QtGui import QColor
|
33
33
|
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel
|
34
34
|
|
35
35
|
from novelwriter import CONFIG, SHARED
|
@@ -50,12 +50,13 @@ class GuiMainStatus(QStatusBar):
|
|
50
50
|
|
51
51
|
logger.debug("Create: GuiMainStatus")
|
52
52
|
|
53
|
-
self._refTime
|
53
|
+
self._refTime = -1.0
|
54
54
|
self._userIdle = False
|
55
|
+
self._debugInfo = False
|
55
56
|
|
56
|
-
colNone =
|
57
|
-
colSaved =
|
58
|
-
colUnsaved =
|
57
|
+
colNone = SHARED.theme.statNone
|
58
|
+
colSaved = SHARED.theme.statSaved
|
59
|
+
colUnsaved = SHARED.theme.statUnsaved
|
59
60
|
|
60
61
|
iPx = SHARED.theme.baseIconSize
|
61
62
|
|
@@ -223,4 +224,45 @@ class GuiMainStatus(QStatusBar):
|
|
223
224
|
self.setDocumentStatus(StatusLED.S_BAD if status else StatusLED.S_GOOD)
|
224
225
|
return
|
225
226
|
|
227
|
+
##
|
228
|
+
# Debug
|
229
|
+
##
|
230
|
+
|
231
|
+
def memInfo(self) -> None: # pragma: no cover
|
232
|
+
"""Display memory info on the status bar. This is used to
|
233
|
+
investigate memory usage and Qt widgets that get left in memory.
|
234
|
+
Enabled by the --meminfo command line flag.
|
235
|
+
|
236
|
+
By default, this tracks memory usage diff after launch. To track
|
237
|
+
full memory usage, set environment variable PYTHONTRACEMALLOC=1
|
238
|
+
before starting novelWriter.
|
239
|
+
"""
|
240
|
+
import tracemalloc
|
241
|
+
from collections import Counter
|
242
|
+
|
243
|
+
widgets = qApp.allWidgets()
|
244
|
+
if not self._debugInfo:
|
245
|
+
if tracemalloc.is_tracing():
|
246
|
+
self._traceMallocRef = "Total"
|
247
|
+
else:
|
248
|
+
self._traceMallocRef = "Relative"
|
249
|
+
tracemalloc.start()
|
250
|
+
self._debugInfo = True
|
251
|
+
self._wCounts = Counter([type(x).__name__ for x in widgets])
|
252
|
+
|
253
|
+
if hasattr(self, "_wCounts"):
|
254
|
+
diff = Counter([type(x).__name__ for x in widgets]) - self._wCounts
|
255
|
+
for name, count in diff.items():
|
256
|
+
logger.debug("Widget '%s': +%d", name, count)
|
257
|
+
|
258
|
+
mem = tracemalloc.get_traced_memory()
|
259
|
+
stamp = datetime.now().strftime("%H:%M:%S")
|
260
|
+
self.showMessage((
|
261
|
+
f"Debug [{stamp}]"
|
262
|
+
f" \u2013 Widgets: {len(widgets)}"
|
263
|
+
f" \u2013 {self._traceMallocRef} Memory: {mem[0]:n}"
|
264
|
+
f" \u2013 Peak: {mem[1]:n}"
|
265
|
+
), 6000)
|
266
|
+
return
|
267
|
+
|
226
268
|
# END Class GuiMainStatus
|
novelwriter/gui/theme.py
CHANGED
@@ -7,7 +7,7 @@ Created: 2019-05-18 [0.1.3] GuiTheme
|
|
7
7
|
Created: 2019-11-08 [0.4] GuiIcons
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright 2018–
|
10
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -67,12 +67,13 @@ class GuiTheme:
|
|
67
67
|
self.themeLicense = ""
|
68
68
|
self.themeLicenseUrl = ""
|
69
69
|
self.themeIcons = ""
|
70
|
+
self.isLightTheme = True
|
70
71
|
|
71
72
|
# GUI
|
72
|
-
self.statNone =
|
73
|
-
self.statUnsaved =
|
74
|
-
self.statSaved =
|
75
|
-
self.helpText =
|
73
|
+
self.statNone = QColor(120, 120, 120)
|
74
|
+
self.statUnsaved = QColor(200, 15, 39)
|
75
|
+
self.statSaved = QColor(2, 133, 37)
|
76
|
+
self.helpText = QColor(0, 0, 0)
|
76
77
|
|
77
78
|
# Loaded Syntax Settings
|
78
79
|
# ======================
|
@@ -87,23 +88,24 @@ class GuiTheme:
|
|
87
88
|
self.syntaxLicenseUrl = ""
|
88
89
|
|
89
90
|
# Colours
|
90
|
-
self.colBack =
|
91
|
-
self.colText =
|
92
|
-
self.colLink =
|
93
|
-
self.colHead =
|
94
|
-
self.colHeadH =
|
95
|
-
self.colEmph =
|
96
|
-
self.colDialN =
|
97
|
-
self.colDialD =
|
98
|
-
self.colDialS =
|
99
|
-
self.colHidden =
|
100
|
-
self.colCode =
|
101
|
-
self.colKey =
|
102
|
-
self.colVal =
|
103
|
-
self.
|
104
|
-
self.
|
105
|
-
self.
|
106
|
-
self.
|
91
|
+
self.colBack = QColor(255, 255, 255)
|
92
|
+
self.colText = QColor(0, 0, 0)
|
93
|
+
self.colLink = QColor(0, 0, 0)
|
94
|
+
self.colHead = QColor(0, 0, 0)
|
95
|
+
self.colHeadH = QColor(0, 0, 0)
|
96
|
+
self.colEmph = QColor(0, 0, 0)
|
97
|
+
self.colDialN = QColor(0, 0, 0)
|
98
|
+
self.colDialD = QColor(0, 0, 0)
|
99
|
+
self.colDialS = QColor(0, 0, 0)
|
100
|
+
self.colHidden = QColor(0, 0, 0)
|
101
|
+
self.colCode = QColor(0, 0, 0)
|
102
|
+
self.colKey = QColor(0, 0, 0)
|
103
|
+
self.colVal = QColor(0, 0, 0)
|
104
|
+
self.colOpt = QColor(0, 0, 0)
|
105
|
+
self.colSpell = QColor(0, 0, 0)
|
106
|
+
self.colError = QColor(0, 0, 0)
|
107
|
+
self.colRepTag = QColor(0, 0, 0)
|
108
|
+
self.colMod = QColor(0, 0, 0)
|
107
109
|
|
108
110
|
# Class Setup
|
109
111
|
# ===========
|
@@ -112,7 +114,7 @@ class GuiTheme:
|
|
112
114
|
self._setGuiFont()
|
113
115
|
|
114
116
|
# Load Themes
|
115
|
-
self._guiPalette
|
117
|
+
self._guiPalette = QPalette()
|
116
118
|
self._themeList: list[tuple[str, str]] = []
|
117
119
|
self._syntaxList: list[tuple[str, str]] = []
|
118
120
|
self._availThemes: dict[str, Path] = {}
|
@@ -254,13 +256,13 @@ class GuiTheme:
|
|
254
256
|
|
255
257
|
backLNess = backCol.lightnessF()
|
256
258
|
textLNess = textCol.lightnessF()
|
257
|
-
|
258
|
-
if self.helpText ==
|
259
|
-
if
|
259
|
+
self.isLightTheme = backLNess > textLNess
|
260
|
+
if self.helpText == QColor(0, 0, 0):
|
261
|
+
if self.isLightTheme:
|
260
262
|
helpLCol = textLNess + 0.35*(backLNess - textLNess)
|
261
263
|
else:
|
262
264
|
helpLCol = backLNess + 0.65*(textLNess - backLNess)
|
263
|
-
self.helpText =
|
265
|
+
self.helpText = QColor.fromHsl(0, 0, int(255*helpLCol))
|
264
266
|
|
265
267
|
# Icons
|
266
268
|
defaultIcons = "typicons_light" if backLNess >= 0.5 else "typicons_dark"
|
@@ -322,6 +324,7 @@ class GuiTheme:
|
|
322
324
|
self.colCode = self._parseColour(confParser, cnfSec, "shortcode")
|
323
325
|
self.colKey = self._parseColour(confParser, cnfSec, "keyword")
|
324
326
|
self.colVal = self._parseColour(confParser, cnfSec, "value")
|
327
|
+
self.colOpt = self._parseColour(confParser, cnfSec, "optional")
|
325
328
|
self.colSpell = self._parseColour(confParser, cnfSec, "spellcheckline")
|
326
329
|
self.colError = self._parseColour(confParser, cnfSec, "errorline")
|
327
330
|
self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
|
@@ -341,7 +344,7 @@ class GuiTheme:
|
|
341
344
|
if themeName:
|
342
345
|
self._themeList.append((themeKey, themeName))
|
343
346
|
|
344
|
-
self._themeList = sorted(self._themeList, key=
|
347
|
+
self._themeList = sorted(self._themeList, key=_sortTheme)
|
345
348
|
|
346
349
|
return self._themeList
|
347
350
|
|
@@ -357,7 +360,7 @@ class GuiTheme:
|
|
357
360
|
if syntaxName:
|
358
361
|
self._syntaxList.append((syntaxKey, syntaxName))
|
359
362
|
|
360
|
-
self._syntaxList = sorted(self._syntaxList, key=
|
363
|
+
self._syntaxList = sorted(self._syntaxList, key=_sortTheme)
|
361
364
|
|
362
365
|
return self._syntaxList
|
363
366
|
|
@@ -367,22 +370,22 @@ class GuiTheme:
|
|
367
370
|
|
368
371
|
def _setGuiFont(self) -> None:
|
369
372
|
"""Update the GUI's font style from settings."""
|
370
|
-
|
373
|
+
font = QFont()
|
371
374
|
fontDB = QFontDatabase()
|
372
375
|
if CONFIG.guiFont not in fontDB.families():
|
373
376
|
if CONFIG.osWindows and "Arial" in fontDB.families():
|
374
377
|
# On Windows we default to Arial if possible
|
375
|
-
|
376
|
-
|
378
|
+
font.setFamily("Arial")
|
379
|
+
font.setPointSize(10)
|
377
380
|
else:
|
378
|
-
|
379
|
-
CONFIG.guiFont =
|
380
|
-
CONFIG.guiFontSize =
|
381
|
+
font = fontDB.systemFont(QFontDatabase.GeneralFont)
|
382
|
+
CONFIG.guiFont = font.family()
|
383
|
+
CONFIG.guiFontSize = font.pointSize()
|
381
384
|
else:
|
382
|
-
|
383
|
-
|
385
|
+
font.setFamily(CONFIG.guiFont)
|
386
|
+
font.setPointSize(CONFIG.guiFontSize)
|
384
387
|
|
385
|
-
qApp.setFont(
|
388
|
+
qApp.setFont(font)
|
386
389
|
|
387
390
|
return
|
388
391
|
|
@@ -397,29 +400,14 @@ class GuiTheme:
|
|
397
400
|
|
398
401
|
return True
|
399
402
|
|
400
|
-
def _parseColour(self, parser: NWConfigParser, section: str, name: str) ->
|
403
|
+
def _parseColour(self, parser: NWConfigParser, section: str, name: str) -> QColor:
|
401
404
|
"""Parse a colour value from a config string."""
|
402
|
-
|
403
|
-
values = parser.get(section, name).split(",")
|
404
|
-
result = []
|
405
|
-
try:
|
406
|
-
result.append(minmax(int(values[0]), 0, 255))
|
407
|
-
result.append(minmax(int(values[1]), 0, 255))
|
408
|
-
result.append(minmax(int(values[2]), 0, 255))
|
409
|
-
except Exception:
|
410
|
-
logger.error("Could not load theme colours for '%s' from config file", name)
|
411
|
-
result = [0, 0, 0]
|
412
|
-
else:
|
413
|
-
logger.warning("Could not find theme colours for '%s' in config file", name)
|
414
|
-
result = [0, 0, 0]
|
415
|
-
return result
|
405
|
+
return QColor(*parser.rdIntList(section, name, [0, 0, 0, 255]))
|
416
406
|
|
417
407
|
def _setPalette(self, parser: NWConfigParser, section: str,
|
418
408
|
name: str, value: QPalette.ColorRole) -> None:
|
419
409
|
"""Set a palette colour value from a config string."""
|
420
|
-
self._guiPalette.setColor(
|
421
|
-
value, QColor(*self._parseColour(parser, section, name))
|
422
|
-
)
|
410
|
+
self._guiPalette.setColor(value, self._parseColour(parser, section, name))
|
423
411
|
return
|
424
412
|
|
425
413
|
# End Class GuiTheme
|
@@ -443,7 +431,7 @@ class GuiIcons:
|
|
443
431
|
missing, a blank icon is returned and a warning issued.
|
444
432
|
"""
|
445
433
|
|
446
|
-
ICON_KEYS = {
|
434
|
+
ICON_KEYS: set[str] = {
|
447
435
|
# Project and GUI Icons
|
448
436
|
"novelwriter", "alert_error", "alert_info", "alert_question", "alert_warn",
|
449
437
|
"build_excluded", "build_filtered", "build_included", "proj_chapter", "proj_details",
|
@@ -453,20 +441,21 @@ class GuiIcons:
|
|
453
441
|
|
454
442
|
# Class Icons
|
455
443
|
"cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none", "cls_novel",
|
456
|
-
"cls_object", "cls_plot", "cls_timeline", "cls_trash", "cls_world",
|
444
|
+
"cls_object", "cls_plot", "cls_template", "cls_timeline", "cls_trash", "cls_world",
|
457
445
|
|
458
446
|
# Search Icons
|
459
447
|
"search_cancel", "search_case", "search_loop", "search_preserve", "search_project",
|
460
448
|
"search_regex", "search_word",
|
461
449
|
|
462
450
|
# Format Icons
|
463
|
-
"fmt_bold", "
|
464
|
-
"fmt_superscript", "fmt_underline",
|
451
|
+
"fmt_bold", "fmt_bold-md", "fmt_italic", "fmt_italic-md", "fmt_strike", "fmt_strike-md",
|
452
|
+
"fmt_subscript", "fmt_superscript", "fmt_underline",
|
465
453
|
|
466
454
|
# General Button Icons
|
467
|
-
"add", "backward", "bookmark", "browse", "checked", "close", "cross",
|
468
|
-
"
|
469
|
-
"
|
455
|
+
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
|
456
|
+
"document", "down", "edit", "export", "forward", "import", "list", "maximise", "menu",
|
457
|
+
"minimise", "more", "noncheckable", "open", "panel", "refresh", "remove", "revert",
|
458
|
+
"search_replace", "search", "settings", "star", "unchecked", "up", "view",
|
470
459
|
|
471
460
|
# Switches
|
472
461
|
"sticky-on", "sticky-off",
|
@@ -478,14 +467,14 @@ class GuiIcons:
|
|
478
467
|
"deco_doc_nt_n",
|
479
468
|
}
|
480
469
|
|
481
|
-
TOGGLE_ICON_KEYS = {
|
482
|
-
"sticky":
|
483
|
-
"bullet":
|
484
|
-
"fmt_mode": ("fmt_mode-sc", "fmt_mode-md"),
|
470
|
+
TOGGLE_ICON_KEYS: dict[str, tuple[str, str]] = {
|
471
|
+
"sticky": ("sticky-on", "sticky-off"),
|
472
|
+
"bullet": ("bullet-on", "bullet-off"),
|
485
473
|
}
|
486
474
|
|
487
|
-
IMAGE_MAP = {
|
488
|
-
"
|
475
|
+
IMAGE_MAP: dict[str, tuple[str, str]] = {
|
476
|
+
"welcome": ("welcome-light.jpg", "welcome-dark.jpg"),
|
477
|
+
"nw-text": ("novelwriter-text-light.svg", "novelwriter-text-dark.svg"),
|
489
478
|
}
|
490
479
|
|
491
480
|
def __init__(self, mainTheme: GuiTheme) -> None:
|
@@ -599,7 +588,8 @@ class GuiIcons:
|
|
599
588
|
if name in self._themeMap:
|
600
589
|
imgPath = self._themeMap[name]
|
601
590
|
elif name in self.IMAGE_MAP:
|
602
|
-
|
591
|
+
idx = 0 if self.mainTheme.isLightTheme else 1
|
592
|
+
imgPath = CONFIG.assetPath("images") / self.IMAGE_MAP[name][idx]
|
603
593
|
else:
|
604
594
|
logger.error("Decoration with name '%s' does not exist", name)
|
605
595
|
return QPixmap()
|
@@ -609,12 +599,13 @@ class GuiIcons:
|
|
609
599
|
return QPixmap()
|
610
600
|
|
611
601
|
pixmap = QPixmap(str(imgPath))
|
602
|
+
tMode = Qt.TransformationMode.SmoothTransformation
|
612
603
|
if w is not None and h is not None:
|
613
|
-
return pixmap.scaled(w, h, Qt.IgnoreAspectRatio,
|
604
|
+
return pixmap.scaled(w, h, Qt.AspectRatioMode.IgnoreAspectRatio, tMode)
|
614
605
|
elif w is None and h is not None:
|
615
|
-
return pixmap.scaledToHeight(h,
|
606
|
+
return pixmap.scaledToHeight(h, tMode)
|
616
607
|
elif w is not None and h is None:
|
617
|
-
return pixmap.scaledToWidth(w,
|
608
|
+
return pixmap.scaledToWidth(w, tMode)
|
618
609
|
|
619
610
|
return pixmap
|
620
611
|
|
@@ -633,8 +624,8 @@ class GuiIcons:
|
|
633
624
|
pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
|
634
625
|
pTwo = self.getPixmap(self.TOGGLE_ICON_KEYS[name][1], size)
|
635
626
|
icon = QIcon()
|
636
|
-
icon.addPixmap(pOne, QIcon.Normal, QIcon.On)
|
637
|
-
icon.addPixmap(pTwo, QIcon.Normal, QIcon.Off)
|
627
|
+
icon.addPixmap(pOne, QIcon.Mode.Normal, QIcon.State.On)
|
628
|
+
icon.addPixmap(pTwo, QIcon.Mode.Normal, QIcon.State.Off)
|
638
629
|
return icon
|
639
630
|
return QIcon()
|
640
631
|
|
@@ -642,7 +633,7 @@ class GuiIcons:
|
|
642
633
|
"""Return an icon from the icon buffer as a QPixmap. If it
|
643
634
|
doesn't exist, return an empty QPixmap.
|
644
635
|
"""
|
645
|
-
return self.getIcon(name).pixmap(size[0], size[1], QIcon.Normal)
|
636
|
+
return self.getIcon(name).pixmap(size[0], size[1], QIcon.Mode.Normal)
|
646
637
|
|
647
638
|
def getItemIcon(self, tType: nwItemType, tClass: nwItemClass,
|
648
639
|
tLayout: nwItemLayout, hLevel: str = "H0") -> QIcon:
|
@@ -734,6 +725,13 @@ class GuiIcons:
|
|
734
725
|
# Module Functions
|
735
726
|
# =============================================================================================== #
|
736
727
|
|
728
|
+
|
729
|
+
def _sortTheme(data: tuple[str, str]) -> str:
|
730
|
+
"""Key function for theme sorting."""
|
731
|
+
key, name = data
|
732
|
+
return f"*{name}" if key.startswith("default_") else name
|
733
|
+
|
734
|
+
|
737
735
|
def _loadInternalName(confParser: NWConfigParser, confFile: str | Path) -> str:
|
738
736
|
"""Open a conf file and read the 'name' setting."""
|
739
737
|
try:
|