novelWriter 2.4.3__py3-none-any.whl → 2.5__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.4.3.dist-info → novelWriter-2.5.dist-info}/METADATA +4 -5
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/RECORD +122 -112
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +33 -39
- 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_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_pl_PL.json +116 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
- novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -0
- novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
- novelwriter/assets/syntax/default_dark.conf +32 -18
- novelwriter/assets/syntax/default_light.conf +24 -10
- novelwriter/assets/syntax/dracula.conf +44 -0
- novelwriter/assets/syntax/grey_dark.conf +5 -4
- novelwriter/assets/syntax/grey_light.conf +5 -4
- novelwriter/assets/syntax/light_owl.conf +7 -6
- novelwriter/assets/syntax/night_owl.conf +7 -6
- novelwriter/assets/syntax/snazzy.conf +42 -0
- novelwriter/assets/syntax/solarized_dark.conf +4 -3
- novelwriter/assets/syntax/solarized_light.conf +4 -3
- novelwriter/assets/syntax/tango.conf +27 -11
- novelwriter/assets/syntax/tomorrow.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night.conf +7 -6
- novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
- novelwriter/assets/text/credits_en.htm +52 -41
- novelwriter/assets/themes/cyberpunk_night.conf +3 -0
- novelwriter/assets/themes/default_dark.conf +2 -0
- novelwriter/assets/themes/default_light.conf +2 -0
- novelwriter/assets/themes/dracula.conf +48 -0
- novelwriter/assets/themes/solarized_dark.conf +2 -0
- novelwriter/assets/themes/solarized_light.conf +2 -0
- novelwriter/common.py +33 -12
- novelwriter/config.py +184 -98
- novelwriter/constants.py +47 -35
- novelwriter/core/buildsettings.py +68 -69
- novelwriter/core/coretools.py +5 -23
- novelwriter/core/docbuild.py +52 -40
- novelwriter/core/document.py +3 -5
- novelwriter/core/index.py +115 -45
- novelwriter/core/item.py +8 -19
- novelwriter/core/options.py +2 -4
- novelwriter/core/project.py +37 -61
- novelwriter/core/projectdata.py +1 -3
- novelwriter/core/projectxml.py +12 -15
- novelwriter/core/sessions.py +3 -5
- novelwriter/core/spellcheck.py +4 -9
- novelwriter/core/status.py +211 -164
- novelwriter/core/storage.py +0 -8
- novelwriter/core/tohtml.py +139 -105
- novelwriter/core/tokenizer.py +278 -122
- novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
- novelwriter/core/toodt.py +257 -166
- novelwriter/core/toqdoc.py +419 -0
- novelwriter/core/tree.py +5 -7
- novelwriter/dialogs/about.py +11 -18
- novelwriter/dialogs/docmerge.py +17 -19
- novelwriter/dialogs/docsplit.py +17 -19
- novelwriter/dialogs/editlabel.py +6 -10
- novelwriter/dialogs/preferences.py +200 -164
- novelwriter/dialogs/projectsettings.py +225 -189
- novelwriter/dialogs/quotes.py +12 -9
- novelwriter/dialogs/wordlist.py +9 -15
- novelwriter/enum.py +35 -30
- novelwriter/error.py +8 -15
- novelwriter/extensions/configlayout.py +55 -21
- novelwriter/extensions/eventfilters.py +1 -5
- novelwriter/extensions/modified.py +70 -14
- novelwriter/extensions/novelselector.py +1 -3
- novelwriter/extensions/pagedsidebar.py +9 -12
- novelwriter/extensions/{circularprogress.py → progressbars.py} +30 -8
- novelwriter/extensions/statusled.py +40 -26
- novelwriter/extensions/switch.py +4 -6
- novelwriter/extensions/switchbox.py +7 -6
- novelwriter/extensions/versioninfo.py +3 -9
- novelwriter/gui/doceditor.py +120 -139
- novelwriter/gui/dochighlight.py +231 -186
- novelwriter/gui/docviewer.py +69 -108
- novelwriter/gui/docviewerpanel.py +3 -10
- novelwriter/gui/editordocument.py +1 -3
- novelwriter/gui/itemdetails.py +7 -11
- novelwriter/gui/mainmenu.py +22 -18
- novelwriter/gui/noveltree.py +11 -24
- novelwriter/gui/outline.py +15 -26
- novelwriter/gui/projtree.py +39 -65
- novelwriter/gui/search.py +10 -3
- novelwriter/gui/sidebar.py +2 -6
- novelwriter/gui/statusbar.py +29 -37
- novelwriter/gui/theme.py +26 -48
- novelwriter/guimain.py +162 -160
- novelwriter/shared.py +36 -19
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +10 -20
- novelwriter/tools/lipsum.py +10 -16
- novelwriter/tools/manusbuild.py +9 -11
- novelwriter/tools/manuscript.py +75 -149
- novelwriter/tools/manussettings.py +74 -76
- novelwriter/tools/noveldetails.py +16 -21
- novelwriter/tools/welcome.py +21 -26
- novelwriter/tools/writingstats.py +9 -12
- novelwriter/types.py +49 -4
- novelwriter/extensions/simpleprogress.py +0 -55
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/top_level.txt +0 -0
novelwriter/dialogs/quotes.py
CHANGED
@@ -25,21 +25,25 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
-
from PyQt5.QtGui import QFontMetrics
|
29
28
|
from PyQt5.QtCore import QSize, pyqtSlot
|
29
|
+
from PyQt5.QtGui import QFontMetrics
|
30
30
|
from PyQt5.QtWidgets import (
|
31
|
-
|
31
|
+
QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
|
32
32
|
QListWidgetItem, QVBoxLayout, QWidget
|
33
33
|
)
|
34
34
|
|
35
35
|
from novelwriter import CONFIG
|
36
|
-
from novelwriter.constants import
|
37
|
-
from novelwriter.
|
36
|
+
from novelwriter.constants import nwQuotes, trConst
|
37
|
+
from novelwriter.extensions.modified import NDialog
|
38
|
+
from novelwriter.types import (
|
39
|
+
QtAccepted, QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk,
|
40
|
+
QtUserRole
|
41
|
+
)
|
38
42
|
|
39
43
|
logger = logging.getLogger(__name__)
|
40
44
|
|
41
45
|
|
42
|
-
class GuiQuoteSelect(
|
46
|
+
class GuiQuoteSelect(NDialog):
|
43
47
|
|
44
48
|
_selected = ""
|
45
49
|
|
@@ -50,6 +54,7 @@ class GuiQuoteSelect(QDialog):
|
|
50
54
|
|
51
55
|
logger.debug("Create: GuiQuoteSelect")
|
52
56
|
self.setObjectName("GuiQuoteSelect")
|
57
|
+
self.setWindowTitle(self.tr("Select Quote Style"))
|
53
58
|
|
54
59
|
self.outerBox = QVBoxLayout()
|
55
60
|
self.innerBox = QHBoxLayout()
|
@@ -125,8 +130,8 @@ class GuiQuoteSelect(QDialog):
|
|
125
130
|
cls = GuiQuoteSelect(parent, current=current)
|
126
131
|
cls.exec()
|
127
132
|
quote = cls._selected
|
128
|
-
accepted = cls.result() ==
|
129
|
-
cls.
|
133
|
+
accepted = cls.result() == QtAccepted
|
134
|
+
cls.softDelete()
|
130
135
|
return quote, accepted
|
131
136
|
|
132
137
|
##
|
@@ -141,5 +146,3 @@ class GuiQuoteSelect(QDialog):
|
|
141
146
|
self.previewLabel.setText(quote)
|
142
147
|
self._selected = quote
|
143
148
|
return
|
144
|
-
|
145
|
-
# END Class GuiQuoteSelect
|
novelwriter/dialogs/wordlist.py
CHANGED
@@ -25,35 +25,31 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
-
from typing import TYPE_CHECKING
|
29
28
|
from pathlib import Path
|
30
29
|
|
31
30
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
32
31
|
from PyQt5.QtGui import QCloseEvent
|
33
32
|
from PyQt5.QtWidgets import (
|
34
|
-
QAbstractItemView, QApplication,
|
35
|
-
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout
|
33
|
+
QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
|
34
|
+
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
|
36
35
|
)
|
37
36
|
|
38
37
|
from novelwriter import CONFIG, SHARED
|
39
38
|
from novelwriter.common import formatFileFilter
|
40
39
|
from novelwriter.core.spellcheck import UserDictionary
|
41
40
|
from novelwriter.extensions.configlayout import NColourLabel
|
42
|
-
from novelwriter.extensions.modified import NIconToolButton
|
41
|
+
from novelwriter.extensions.modified import NDialog, NIconToolButton
|
43
42
|
from novelwriter.types import QtDialogClose, QtDialogSave
|
44
43
|
|
45
|
-
if TYPE_CHECKING: # pragma: no cover
|
46
|
-
from novelwriter.guimain import GuiMain
|
47
|
-
|
48
44
|
logger = logging.getLogger(__name__)
|
49
45
|
|
50
46
|
|
51
|
-
class GuiWordList(
|
47
|
+
class GuiWordList(NDialog):
|
52
48
|
|
53
49
|
newWordListReady = pyqtSignal()
|
54
50
|
|
55
|
-
def __init__(self,
|
56
|
-
super().__init__(parent=
|
51
|
+
def __init__(self, parent: QWidget) -> None:
|
52
|
+
super().__init__(parent=parent)
|
57
53
|
|
58
54
|
logger.debug("Create: GuiWordList")
|
59
55
|
self.setObjectName("GuiWordList")
|
@@ -73,7 +69,7 @@ class GuiWordList(QDialog):
|
|
73
69
|
|
74
70
|
# Header
|
75
71
|
self.headLabel = NColourLabel(
|
76
|
-
self.tr("Project Word List"), SHARED.theme.helpText,
|
72
|
+
self.tr("Project Word List"), self, color=SHARED.theme.helpText,
|
77
73
|
scale=NColourLabel.HEADER_SCALE
|
78
74
|
)
|
79
75
|
|
@@ -113,7 +109,7 @@ class GuiWordList(QDialog):
|
|
113
109
|
# Buttons
|
114
110
|
self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogClose, self)
|
115
111
|
self.buttonBox.accepted.connect(self._doSave)
|
116
|
-
self.buttonBox.rejected.connect(self.
|
112
|
+
self.buttonBox.rejected.connect(self.reject)
|
117
113
|
|
118
114
|
# Assemble
|
119
115
|
self.outerBox = QVBoxLayout()
|
@@ -144,7 +140,7 @@ class GuiWordList(QDialog):
|
|
144
140
|
"""Capture the close event and perform cleanup."""
|
145
141
|
self._saveGuiSettings()
|
146
142
|
event.accept()
|
147
|
-
self.
|
143
|
+
self.softDelete()
|
148
144
|
return
|
149
145
|
|
150
146
|
##
|
@@ -257,5 +253,3 @@ class GuiWordList(QDialog):
|
|
257
253
|
if (item := self.listBox.item(i)) and (word := item.text().strip()):
|
258
254
|
result.append(word)
|
259
255
|
return result
|
260
|
-
|
261
|
-
# END Class GuiWordList
|
novelwriter/enum.py
CHANGED
@@ -32,8 +32,6 @@ class nwItemType(Enum):
|
|
32
32
|
FOLDER = 2
|
33
33
|
FILE = 3
|
34
34
|
|
35
|
-
# END Enum nwItemType
|
36
|
-
|
37
35
|
|
38
36
|
class nwItemClass(Enum):
|
39
37
|
|
@@ -50,8 +48,6 @@ class nwItemClass(Enum):
|
|
50
48
|
TEMPLATE = 10
|
51
49
|
TRASH = 11
|
52
50
|
|
53
|
-
# END Enum nwItemClass
|
54
|
-
|
55
51
|
|
56
52
|
class nwItemLayout(Enum):
|
57
53
|
|
@@ -59,16 +55,17 @@ class nwItemLayout(Enum):
|
|
59
55
|
DOCUMENT = 1
|
60
56
|
NOTE = 2
|
61
57
|
|
62
|
-
# END Enum nwItemLayout
|
63
|
-
|
64
58
|
|
65
59
|
class nwComment(Enum):
|
66
60
|
|
67
61
|
PLAIN = 0
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
IGNORE = 1
|
63
|
+
SYNOPSIS = 2
|
64
|
+
SHORT = 3
|
65
|
+
NOTE = 4
|
66
|
+
FOOTNOTE = 5
|
67
|
+
COMMENT = 6
|
68
|
+
STORY = 7
|
72
69
|
|
73
70
|
|
74
71
|
class nwTrinary(Enum):
|
@@ -77,16 +74,12 @@ class nwTrinary(Enum):
|
|
77
74
|
NEUTRAL = 0
|
78
75
|
POSITIVE = 1
|
79
76
|
|
80
|
-
# END Enum nwTrinary
|
81
|
-
|
82
77
|
|
83
78
|
class nwDocMode(Enum):
|
84
79
|
|
85
80
|
VIEW = 0
|
86
81
|
EDIT = 1
|
87
82
|
|
88
|
-
# END Enum nwDocMode
|
89
|
-
|
90
83
|
|
91
84
|
class nwDocAction(Enum):
|
92
85
|
|
@@ -129,8 +122,6 @@ class nwDocAction(Enum):
|
|
129
122
|
SC_SUP = 36
|
130
123
|
SC_SUB = 37
|
131
124
|
|
132
|
-
# END Enum nwDocAction
|
133
|
-
|
134
125
|
|
135
126
|
class nwDocInsert(Enum):
|
136
127
|
|
@@ -145,8 +136,7 @@ class nwDocInsert(Enum):
|
|
145
136
|
VSPACE_S = 8
|
146
137
|
VSPACE_M = 9
|
147
138
|
LIPSUM = 10
|
148
|
-
|
149
|
-
# END Enum nwDocInsert
|
139
|
+
FOOTNOTE = 11
|
150
140
|
|
151
141
|
|
152
142
|
class nwView(Enum):
|
@@ -157,17 +147,12 @@ class nwView(Enum):
|
|
157
147
|
OUTLINE = 3
|
158
148
|
SEARCH = 4
|
159
149
|
|
160
|
-
# END Enum nwView
|
161
|
-
|
162
150
|
|
163
|
-
class
|
151
|
+
class nwFocus(Enum):
|
164
152
|
|
165
|
-
TREE
|
166
|
-
|
167
|
-
|
168
|
-
OUTLINE = 4
|
169
|
-
|
170
|
-
# END Enum nwWidget
|
153
|
+
TREE = 1
|
154
|
+
DOCUMENT = 2
|
155
|
+
OUTLINE = 3
|
171
156
|
|
172
157
|
|
173
158
|
class nwOutline(Enum):
|
@@ -190,8 +175,6 @@ class nwOutline(Enum):
|
|
190
175
|
CUSTOM = 15
|
191
176
|
SYNOP = 16
|
192
177
|
|
193
|
-
# END Enum nwOutline
|
194
|
-
|
195
178
|
|
196
179
|
class nwBuildFmt(Enum):
|
197
180
|
|
@@ -204,4 +187,26 @@ class nwBuildFmt(Enum):
|
|
204
187
|
J_HTML = 6
|
205
188
|
J_NWD = 7
|
206
189
|
|
207
|
-
|
190
|
+
|
191
|
+
class nwStatusShape(Enum):
|
192
|
+
|
193
|
+
SQUARE = 0
|
194
|
+
TRIANGLE = 1
|
195
|
+
NABLA = 2
|
196
|
+
DIAMOND = 3
|
197
|
+
PENTAGON = 4
|
198
|
+
HEXAGON = 5
|
199
|
+
STAR = 6
|
200
|
+
PACMAN = 7
|
201
|
+
CIRCLE_Q = 8
|
202
|
+
CIRCLE_H = 9
|
203
|
+
CIRCLE_T = 10
|
204
|
+
CIRCLE = 11
|
205
|
+
BARS_1 = 12
|
206
|
+
BARS_2 = 13
|
207
|
+
BARS_3 = 14
|
208
|
+
BARS_4 = 15
|
209
|
+
BLOCK_1 = 16
|
210
|
+
BLOCK_2 = 17
|
211
|
+
BLOCK_3 = 18
|
212
|
+
BLOCK_4 = 19
|
novelwriter/error.py
CHANGED
@@ -23,17 +23,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
23
23
|
"""
|
24
24
|
from __future__ import annotations
|
25
25
|
|
26
|
-
import sys
|
27
|
-
import random
|
28
26
|
import logging
|
27
|
+
import random
|
28
|
+
import sys
|
29
29
|
|
30
30
|
from typing import TYPE_CHECKING
|
31
31
|
|
32
32
|
from PyQt5.QtCore import Qt, pyqtSlot
|
33
33
|
from PyQt5.QtGui import QFont, QFontDatabase
|
34
34
|
from PyQt5.QtWidgets import (
|
35
|
-
QApplication,
|
36
|
-
|
35
|
+
QApplication, QDialog, QDialogButtonBox, QGridLayout, QLabel,
|
36
|
+
QPlainTextEdit, QStyle, QWidget
|
37
37
|
)
|
38
38
|
|
39
39
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -42,10 +42,6 @@ if TYPE_CHECKING: # pragma: no cover
|
|
42
42
|
logger = logging.getLogger(__name__)
|
43
43
|
|
44
44
|
|
45
|
-
# =============================================================================================== #
|
46
|
-
# Utility Functions
|
47
|
-
# =============================================================================================== #
|
48
|
-
|
49
45
|
def logException() -> None:
|
50
46
|
"""Log the content of an exception message."""
|
51
47
|
exType, exValue, _ = sys.exc_info()
|
@@ -61,10 +57,6 @@ def formatException(exc: BaseException) -> str:
|
|
61
57
|
return f"{type(exc).__name__}: {str(exc)}"
|
62
58
|
|
63
59
|
|
64
|
-
# =============================================================================================== #
|
65
|
-
# Error Handler
|
66
|
-
# =============================================================================================== #
|
67
|
-
|
68
60
|
class NWErrorMessage(QDialog):
|
69
61
|
|
70
62
|
def __init__(self, parent: QWidget) -> None:
|
@@ -125,9 +117,11 @@ class NWErrorMessage(QDialog):
|
|
125
117
|
error traceback.
|
126
118
|
"""
|
127
119
|
from traceback import format_tb
|
120
|
+
|
121
|
+
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR, QSysInfo
|
122
|
+
|
128
123
|
from novelwriter import __version__
|
129
124
|
from novelwriter.constants import nwConst
|
130
|
-
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QSysInfo
|
131
125
|
|
132
126
|
self.msgHead.setText(
|
133
127
|
"<p>An unhandled error has been encountered.</p>"
|
@@ -175,12 +169,11 @@ class NWErrorMessage(QDialog):
|
|
175
169
|
self.close()
|
176
170
|
return
|
177
171
|
|
178
|
-
# END Class NWErrorMessage
|
179
|
-
|
180
172
|
|
181
173
|
def exceptionHandler(exType: type, exValue: BaseException, exTrace: TracebackType) -> None:
|
182
174
|
"""Function to catch unhandled global exceptions."""
|
183
175
|
from traceback import print_tb
|
176
|
+
|
184
177
|
from PyQt5.QtWidgets import QApplication
|
185
178
|
|
186
179
|
logger.critical("%s: %s", exType.__name__, str(exValue))
|
@@ -27,8 +27,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
27
27
|
"""
|
28
28
|
from __future__ import annotations
|
29
29
|
|
30
|
-
from PyQt5.QtGui import QColor, QFont, QPalette
|
31
30
|
from PyQt5.QtCore import Qt
|
31
|
+
from PyQt5.QtGui import QColor, QFont, QPalette
|
32
32
|
from PyQt5.QtWidgets import (
|
33
33
|
QAbstractButton, QFrame, QHBoxLayout, QLabel, QLayout, QScrollArea,
|
34
34
|
QVBoxLayout, QWidget
|
@@ -64,8 +64,6 @@ class NFixedPage(QFrame):
|
|
64
64
|
self.setLayout(layout)
|
65
65
|
return
|
66
66
|
|
67
|
-
# END Class NFixedPage
|
68
|
-
|
69
67
|
|
70
68
|
class NScrollablePage(QScrollArea):
|
71
69
|
"""Extension: Scrollable Page Widget
|
@@ -89,8 +87,6 @@ class NScrollablePage(QScrollArea):
|
|
89
87
|
self._widget.setLayout(layout)
|
90
88
|
return
|
91
89
|
|
92
|
-
# END Class NScrollablePage
|
93
|
-
|
94
90
|
|
95
91
|
class NScrollableForm(QScrollArea):
|
96
92
|
"""Extension: Scrollable Form Widget
|
@@ -184,20 +180,30 @@ class NScrollableForm(QScrollArea):
|
|
184
180
|
self._sections[identifier] = qLabel
|
185
181
|
return
|
186
182
|
|
187
|
-
def addRow(self, label: str, widget: QWidget, helpText: str = "",
|
188
|
-
button: QWidget | None = None, editable: str | None = None,
|
183
|
+
def addRow(self, label: str, widget: QWidget | list[QWidget], helpText: str = "",
|
184
|
+
unit: str | None = None, button: QWidget | None = None, editable: str | None = None,
|
189
185
|
stretch: tuple[int, int] = (1, 0)) -> None:
|
190
186
|
"""Add a label and a widget as a new row of the form."""
|
191
187
|
row = QHBoxLayout()
|
192
188
|
row.setSpacing(CONFIG.pxInt(12))
|
193
189
|
|
190
|
+
if isinstance(widget, list):
|
191
|
+
wBox = QHBoxLayout()
|
192
|
+
wBox.setContentsMargins(0, 0, 0, 0)
|
193
|
+
for item in widget:
|
194
|
+
wBox.addWidget(item)
|
195
|
+
qWidget = QWidget(self)
|
196
|
+
qWidget.setLayout(wBox)
|
197
|
+
else:
|
198
|
+
qWidget = widget
|
199
|
+
|
194
200
|
qLabel = QLabel(label, self)
|
195
201
|
qLabel.setIndent(self._indent)
|
196
|
-
qLabel.setBuddy(
|
202
|
+
qLabel.setBuddy(qWidget)
|
197
203
|
|
198
204
|
if helpText:
|
199
205
|
qHelp = NColourLabel(
|
200
|
-
str(helpText), color=self._helpCol,
|
206
|
+
str(helpText), self, color=self._helpCol,
|
201
207
|
scale=self._fontScale, wrap=True, indent=self._indent
|
202
208
|
)
|
203
209
|
labelBox = QVBoxLayout()
|
@@ -212,19 +218,19 @@ class NScrollableForm(QScrollArea):
|
|
212
218
|
|
213
219
|
if isinstance(unit, str):
|
214
220
|
box = QHBoxLayout()
|
215
|
-
box.addWidget(
|
221
|
+
box.addWidget(qWidget, 1)
|
216
222
|
box.addWidget(QLabel(unit, self), 0)
|
217
223
|
row.addLayout(box, stretch[1])
|
218
224
|
elif isinstance(button, QAbstractButton):
|
219
225
|
box = QHBoxLayout()
|
220
|
-
box.addWidget(
|
226
|
+
box.addWidget(qWidget, 1)
|
221
227
|
box.addWidget(button, 0)
|
222
228
|
row.addLayout(box, stretch[1])
|
223
229
|
else:
|
224
|
-
row.addWidget(
|
230
|
+
row.addWidget(qWidget, stretch[1])
|
225
231
|
|
226
232
|
self._layout.addLayout(row)
|
227
|
-
self._index[label.strip()] =
|
233
|
+
self._index[label.strip()] = qWidget
|
228
234
|
self._first = False
|
229
235
|
|
230
236
|
return
|
@@ -235,8 +241,6 @@ class NScrollableForm(QScrollArea):
|
|
235
241
|
self._layout.addStretch(1)
|
236
242
|
return
|
237
243
|
|
238
|
-
# END Class NScrollableForm
|
239
|
-
|
240
244
|
|
241
245
|
class NColourLabel(QLabel):
|
242
246
|
"""Extension: A Coloured Label
|
@@ -248,11 +252,20 @@ class NColourLabel(QLabel):
|
|
248
252
|
HELP_SCALE = DEFAULT_SCALE
|
249
253
|
HEADER_SCALE = 1.25
|
250
254
|
|
251
|
-
|
252
|
-
|
253
|
-
|
255
|
+
_state = None
|
256
|
+
|
257
|
+
def __init__(
|
258
|
+
self, text: str, parent: QWidget, *,
|
259
|
+
color: QColor | None = None, faded: QColor | None = None,
|
260
|
+
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
|
261
|
+
bold: bool = False
|
262
|
+
) -> None:
|
254
263
|
super().__init__(text, parent=parent)
|
255
264
|
|
265
|
+
default = self.palette().windowText().color()
|
266
|
+
self._color = color or default
|
267
|
+
self._faded = faded or default
|
268
|
+
|
256
269
|
font = self.font()
|
257
270
|
font.setPointSizeF(scale*font.pointSizeF())
|
258
271
|
font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
|
@@ -264,10 +277,33 @@ class NColourLabel(QLabel):
|
|
264
277
|
self.setFont(font)
|
265
278
|
self.setIndent(indent)
|
266
279
|
self.setWordWrap(wrap)
|
280
|
+
self.setColorState(True)
|
267
281
|
|
268
282
|
return
|
269
283
|
|
270
|
-
|
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
|
+
|
291
|
+
def setColorState(self, state: bool) -> None:
|
292
|
+
"""Change the colour state."""
|
293
|
+
if self._state is not state:
|
294
|
+
self._state = state
|
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)
|
306
|
+
return
|
271
307
|
|
272
308
|
|
273
309
|
class NWrappedWidgetBox(QHBoxLayout):
|
@@ -287,5 +323,3 @@ class NWrappedWidgetBox(QHBoxLayout):
|
|
287
323
|
if after:
|
288
324
|
self.addWidget(QLabel(after.lstrip()))
|
289
325
|
return
|
290
|
-
|
291
|
-
# END Class NWrappedWidgetBox
|
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
24
|
"""
|
25
25
|
from __future__ import annotations
|
26
26
|
|
27
|
-
from PyQt5.QtGui import QStatusTipEvent, QWheelEvent
|
28
27
|
from PyQt5.QtCore import QEvent, QObject
|
28
|
+
from PyQt5.QtGui import QStatusTipEvent, QWheelEvent
|
29
29
|
from PyQt5.QtWidgets import QWidget
|
30
30
|
|
31
31
|
|
@@ -63,13 +63,9 @@ class WheelEventFilter(QObject):
|
|
63
63
|
self._locked = False
|
64
64
|
return False
|
65
65
|
|
66
|
-
# END Class WheelEventFilter
|
67
|
-
|
68
66
|
|
69
67
|
class StatusTipFilter(QObject):
|
70
68
|
|
71
69
|
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
|
72
70
|
"""Filter out status tip events on menus."""
|
73
71
|
return True if isinstance(event, QStatusTipEvent) else super().eventFilter(obj, event)
|
74
|
-
|
75
|
-
# END Class StatusTipFilter
|
@@ -6,6 +6,8 @@ File History:
|
|
6
6
|
Created: 2024-02-01 [2.3b1] NComboBox
|
7
7
|
Created: 2024-02-01 [2.3b1] NSpinBox
|
8
8
|
Created: 2024-02-01 [2.3b1] NDoubleSpinBox
|
9
|
+
Created: 2024-05-01 [2.5b1] NToolDialog
|
10
|
+
Created: 2024-05-01 [2.5b1] NNonBlockingDialog
|
9
11
|
|
10
12
|
This file is a part of novelWriter
|
11
13
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
@@ -25,11 +27,75 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
25
27
|
"""
|
26
28
|
from __future__ import annotations
|
27
29
|
|
28
|
-
from
|
30
|
+
from enum import Enum
|
31
|
+
from typing import TYPE_CHECKING
|
32
|
+
|
33
|
+
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
29
34
|
from PyQt5.QtGui import QWheelEvent
|
30
|
-
from PyQt5.QtWidgets import
|
35
|
+
from PyQt5.QtWidgets import (
|
36
|
+
QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
|
37
|
+
QWidget
|
38
|
+
)
|
39
|
+
|
40
|
+
from novelwriter import CONFIG, SHARED
|
41
|
+
|
42
|
+
if TYPE_CHECKING: # pragma: no cover
|
43
|
+
from novelwriter.guimain import GuiMain
|
44
|
+
|
45
|
+
|
46
|
+
class NDialog(QDialog):
|
47
|
+
|
48
|
+
def softDelete(self) -> None:
|
49
|
+
"""Since calling deleteLater is sometimes not safe from Python,
|
50
|
+
as the C++ object can be deleted before the Python process is
|
51
|
+
done with the object, we instead set the dialog's parent to None
|
52
|
+
so that it gets garbage collected when it runs out of scope.
|
53
|
+
"""
|
54
|
+
self.setParent(None) # type: ignore
|
55
|
+
return
|
56
|
+
|
57
|
+
@pyqtSlot()
|
58
|
+
def reject(self) -> None:
|
59
|
+
"""Overload the reject slot and also call close."""
|
60
|
+
super().reject()
|
61
|
+
self.close()
|
62
|
+
return
|
63
|
+
|
64
|
+
|
65
|
+
class NToolDialog(NDialog):
|
66
|
+
|
67
|
+
def __init__(self, parent: GuiMain) -> None:
|
68
|
+
super().__init__(parent=parent)
|
69
|
+
self.setModal(False)
|
70
|
+
if CONFIG.osDarwin:
|
71
|
+
self.setWindowFlag(Qt.WindowType.Tool)
|
72
|
+
return
|
31
73
|
|
32
|
-
|
74
|
+
def activateDialog(self) -> None:
|
75
|
+
"""Helper function to activate dialog on various systems."""
|
76
|
+
self.show()
|
77
|
+
if CONFIG.osWindows:
|
78
|
+
self.activateWindow()
|
79
|
+
self.raise_()
|
80
|
+
QApplication.processEvents()
|
81
|
+
return
|
82
|
+
|
83
|
+
|
84
|
+
class NNonBlockingDialog(NDialog):
|
85
|
+
|
86
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
87
|
+
super().__init__(parent=parent)
|
88
|
+
self.setModal(True)
|
89
|
+
return
|
90
|
+
|
91
|
+
def activateDialog(self) -> None:
|
92
|
+
"""Helper function to activate dialog on various systems."""
|
93
|
+
self.show()
|
94
|
+
if CONFIG.osWindows:
|
95
|
+
self.activateWindow()
|
96
|
+
self.raise_()
|
97
|
+
QApplication.processEvents()
|
98
|
+
return
|
33
99
|
|
34
100
|
|
35
101
|
class NComboBox(QComboBox):
|
@@ -46,14 +112,12 @@ class NComboBox(QComboBox):
|
|
46
112
|
event.ignore()
|
47
113
|
return
|
48
114
|
|
49
|
-
def setCurrentData(self, data: str, default: str) -> None:
|
115
|
+
def setCurrentData(self, data: str | int | Enum, default: str | int | Enum) -> None:
|
50
116
|
"""Set the current index from data, with a fallback."""
|
51
117
|
idx = self.findData(data)
|
52
118
|
self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
|
53
119
|
return
|
54
120
|
|
55
|
-
# END Class NComboBox
|
56
|
-
|
57
121
|
|
58
122
|
class NSpinBox(QSpinBox):
|
59
123
|
|
@@ -69,8 +133,6 @@ class NSpinBox(QSpinBox):
|
|
69
133
|
event.ignore()
|
70
134
|
return
|
71
135
|
|
72
|
-
# END Class NSpinBox
|
73
|
-
|
74
136
|
|
75
137
|
class NDoubleSpinBox(QDoubleSpinBox):
|
76
138
|
|
@@ -86,8 +148,6 @@ class NDoubleSpinBox(QDoubleSpinBox):
|
|
86
148
|
event.ignore()
|
87
149
|
return
|
88
150
|
|
89
|
-
# END Class NDoubleSpinBox
|
90
|
-
|
91
151
|
|
92
152
|
class NIconToolButton(QToolButton):
|
93
153
|
|
@@ -105,8 +165,6 @@ class NIconToolButton(QToolButton):
|
|
105
165
|
self.setIcon(SHARED.theme.getIcon(iconKey))
|
106
166
|
return
|
107
167
|
|
108
|
-
# END Class NIconToolButton
|
109
|
-
|
110
168
|
|
111
169
|
class NIconToggleButton(QToolButton):
|
112
170
|
|
@@ -126,5 +184,3 @@ class NIconToggleButton(QToolButton):
|
|
126
184
|
iconSize = self.iconSize()
|
127
185
|
self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height())))
|
128
186
|
return
|
129
|
-
|
130
|
-
# END Class NUnfoldButton
|
@@ -29,8 +29,8 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot
|
|
29
29
|
from PyQt5.QtWidgets import QComboBox, QWidget
|
30
30
|
|
31
31
|
from novelwriter import SHARED
|
32
|
-
from novelwriter.enum import nwItemClass
|
33
32
|
from novelwriter.constants import nwLabels
|
33
|
+
from novelwriter.enum import nwItemClass
|
34
34
|
|
35
35
|
logger = logging.getLogger(__name__)
|
36
36
|
|
@@ -126,5 +126,3 @@ class NovelSelector(QComboBox):
|
|
126
126
|
if not self._blockSignal:
|
127
127
|
self.novelSelectionChanged.emit(self.currentData())
|
128
128
|
return
|
129
|
-
|
130
|
-
# END Class NovelSelector
|