novelWriter 2.2.1__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.2.1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/RECORD +116 -101
- novelWriter-2.3.dist-info/entry_points.txt +2 -0
- novelwriter/__init__.py +4 -4
- 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_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/project_nl_NL.json +11 -0
- novelwriter/assets/i18n/project_pt_BR.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +8 -0
- 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/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 +8 -0
- 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/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 +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +196 -156
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +70 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +182 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +109 -82
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -19
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +241 -106
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +69 -70
- novelwriter/guimain.py +51 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +819 -0
- novelwriter/tools/writingstats.py +9 -9
- novelWriter-2.2.1.dist-info/entry_points.txt +0 -5
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,10 @@ novelWriter – Custom Widget: Config Layout
|
|
3
3
|
==========================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2020-05-03 [0.4.5]
|
6
|
+
Created: 2020-05-03 [0.4.5] NColourLabel
|
7
|
+
Created: 2024-01-08 [2.3b1] NScrollableForm
|
8
|
+
Created: 2024-01-26 [2.3b1] NScrollablePage
|
9
|
+
Created: 2024-01-26 [2.3b1] NFixedPage
|
7
10
|
|
8
11
|
This file is a part of novelWriter
|
9
12
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
@@ -23,198 +26,246 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
23
26
|
"""
|
24
27
|
from __future__ import annotations
|
25
28
|
|
26
|
-
from PyQt5.QtGui import QColor, QPalette
|
29
|
+
from PyQt5.QtGui import QColor, QFont, QPalette
|
27
30
|
from PyQt5.QtCore import Qt
|
28
31
|
from PyQt5.QtWidgets import (
|
29
|
-
QAbstractButton,
|
32
|
+
QAbstractButton, QFrame, QHBoxLayout, QLabel, QLayout, QScrollArea,
|
30
33
|
QVBoxLayout, QWidget
|
31
34
|
)
|
32
35
|
|
33
36
|
from novelwriter import CONFIG
|
34
37
|
|
35
|
-
|
38
|
+
DEFAULT_SCALE = 0.9
|
39
|
+
RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
|
40
|
+
LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
|
36
41
|
|
37
42
|
|
38
|
-
class
|
43
|
+
class NFixedPage(QFrame):
|
44
|
+
"""Extension: Fixed Page Widget
|
39
45
|
|
40
|
-
|
41
|
-
|
46
|
+
A custom widget that holds a layout. This is just a wrapper around a
|
47
|
+
QFrame that sets the same frame style as the other Page widgets.
|
48
|
+
"""
|
42
49
|
|
43
|
-
|
44
|
-
|
45
|
-
self.
|
46
|
-
self.
|
50
|
+
def __init__(self, parent: QWidget) -> None:
|
51
|
+
super().__init__(parent=parent)
|
52
|
+
self.setFrameShadow(QFrame.Shadow.Sunken)
|
53
|
+
self.setFrameShape(QFrame.Shape.StyledPanel)
|
54
|
+
return
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
self.
|
51
|
-
|
56
|
+
def setCentralLayout(self, layout: QLayout) -> None:
|
57
|
+
"""Set a layout as the central object."""
|
58
|
+
self.setLayout(layout)
|
59
|
+
return
|
52
60
|
|
61
|
+
def setCentralWidget(self, widget: QWidget) -> None:
|
62
|
+
"""Set a layout as the central object."""
|
63
|
+
layout = QHBoxLayout()
|
64
|
+
layout.addWidget(widget)
|
65
|
+
self.setLayout(layout)
|
53
66
|
return
|
54
67
|
|
55
|
-
|
56
|
-
# Getters and Setters
|
57
|
-
##
|
68
|
+
# END Class NFixedPage
|
58
69
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
70
|
+
|
71
|
+
class NScrollablePage(QScrollArea):
|
72
|
+
"""Extension: Scrollable Page Widget
|
73
|
+
|
74
|
+
A custom widget that holds a layout within a scrollable area.
|
75
|
+
"""
|
76
|
+
|
77
|
+
def __init__(self, parent: QWidget) -> None:
|
78
|
+
super().__init__(parent=parent)
|
79
|
+
self._widget = QWidget(self)
|
80
|
+
self.setWidget(self._widget)
|
81
|
+
self.setWidgetResizable(True)
|
82
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
83
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
84
|
+
self.setFrameShadow(QFrame.Shadow.Sunken)
|
85
|
+
self.setFrameShape(QFrame.Shape.StyledPanel)
|
64
86
|
return
|
65
87
|
|
66
|
-
def
|
67
|
-
"""Set the
|
68
|
-
|
69
|
-
qHelp = self._itemMap[row][1]
|
70
|
-
if isinstance(qHelp, NHelpLabel):
|
71
|
-
qHelp.setText(text)
|
88
|
+
def setCentralLayout(self, layout: QLayout) -> None:
|
89
|
+
"""Set the central layout of the scroll page."""
|
90
|
+
self._widget.setLayout(layout)
|
72
91
|
return
|
73
92
|
|
74
|
-
|
75
|
-
# Class Methods
|
76
|
-
##
|
93
|
+
# END Class NScrollablePage
|
77
94
|
|
78
|
-
def addGroupLabel(self, label: str) -> None:
|
79
|
-
"""Add a text label to separate groups of settings."""
|
80
|
-
hM = CONFIG.pxInt(4)
|
81
|
-
qLabel = QLabel("<b>%s</b>" % label)
|
82
|
-
qLabel.setContentsMargins(0, hM, 0, hM)
|
83
|
-
self.addWidget(qLabel, self._nextRow, 0, 1, 2, Qt.AlignLeft)
|
84
|
-
self.setRowStretch(self._nextRow, 0)
|
85
|
-
self.setRowStretch(self._nextRow + 1, 1)
|
86
|
-
self._nextRow += 1
|
87
|
-
return
|
88
95
|
|
89
|
-
|
90
|
-
|
91
|
-
"""Add a label and a widget as a new row of the grid."""
|
92
|
-
wSp = CONFIG.pxInt(8)
|
93
|
-
qLabel = QLabel(label)
|
94
|
-
qLabel.setIndent(wSp)
|
95
|
-
qLabel.setBuddy(widget)
|
96
|
+
class NScrollableForm(QScrollArea):
|
97
|
+
"""Extension: Scrollable Form Widget
|
96
98
|
|
97
|
-
|
98
|
-
|
99
|
-
qHelp = NHelpLabel(str(helpText), self._helpCol, self._fontScale)
|
100
|
-
qHelp.setIndent(wSp)
|
101
|
-
labelBox = QVBoxLayout()
|
102
|
-
labelBox.addWidget(qLabel)
|
103
|
-
labelBox.addWidget(qHelp)
|
104
|
-
labelBox.setSpacing(0)
|
105
|
-
labelBox.addStretch(1)
|
106
|
-
self.addLayout(labelBox, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
107
|
-
else:
|
108
|
-
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
99
|
+
A custom widget that creates a form within a scrollable area.
|
100
|
+
"""
|
109
101
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
102
|
+
def __init__(self, parent: QWidget) -> None:
|
103
|
+
super().__init__(parent=parent)
|
104
|
+
self._helpCol = QColor(0, 0, 0)
|
105
|
+
self._fontScale = DEFAULT_SCALE
|
106
|
+
self._first = True
|
107
|
+
self._indent = CONFIG.pxInt(12)
|
116
108
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
controlBox.addWidget(button, 0, Qt.AlignVCenter)
|
121
|
-
controlBox.setSpacing(wSp)
|
122
|
-
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
109
|
+
self._sections: dict[int, QLabel] = {}
|
110
|
+
self._editable: dict[str, NColourLabel] = {}
|
111
|
+
self._index: dict[str, QWidget] = {}
|
123
112
|
|
124
|
-
|
125
|
-
|
126
|
-
qLayout = QHBoxLayout()
|
127
|
-
qLayout.addWidget(widget)
|
128
|
-
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
129
|
-
else:
|
130
|
-
self.addWidget(widget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
113
|
+
self._layout = QVBoxLayout()
|
114
|
+
self._layout.setSpacing(CONFIG.pxInt(12))
|
131
115
|
|
132
|
-
self.
|
133
|
-
self.
|
116
|
+
self._widget = QWidget(self)
|
117
|
+
self._widget.setLayout(self._layout)
|
134
118
|
|
135
|
-
self.
|
136
|
-
self.
|
119
|
+
self.setWidget(self._widget)
|
120
|
+
self.setWidgetResizable(True)
|
121
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
122
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
123
|
+
self.setFrameShadow(QFrame.Shadow.Sunken)
|
124
|
+
self.setFrameShape(QFrame.Shape.StyledPanel)
|
137
125
|
|
138
|
-
return
|
126
|
+
return
|
139
127
|
|
140
|
-
|
128
|
+
##
|
129
|
+
# Properties
|
130
|
+
##
|
141
131
|
|
132
|
+
@property
|
133
|
+
def labels(self) -> list[str]:
|
134
|
+
return list(self._index.keys())
|
142
135
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
"""
|
136
|
+
##
|
137
|
+
# Setters
|
138
|
+
##
|
147
139
|
|
148
|
-
def
|
149
|
-
|
150
|
-
self.
|
140
|
+
def setHelpTextStyle(self, color: QColor, scale: float = DEFAULT_SCALE) -> None:
|
141
|
+
"""Set the text color for the help text."""
|
142
|
+
self._helpCol = color
|
143
|
+
self._fontScale = scale
|
144
|
+
return
|
151
145
|
|
152
|
-
|
153
|
-
|
154
|
-
self.
|
155
|
-
|
146
|
+
def setHelpText(self, key: str, text: str) -> None:
|
147
|
+
"""Set the text for the help label."""
|
148
|
+
if qHelp := self._editable.get(key):
|
149
|
+
qHelp.setText(text)
|
150
|
+
return
|
156
151
|
|
152
|
+
def setRowIndent(self, indent: int) -> None:
|
153
|
+
"""Set the indentation of each row."""
|
154
|
+
self._indent = max(indent, 0)
|
157
155
|
return
|
158
156
|
|
159
157
|
##
|
160
158
|
# Methods
|
161
159
|
##
|
162
160
|
|
163
|
-
def
|
161
|
+
def scrollToSection(self, identifier: int) -> None:
|
162
|
+
"""Scroll to the requested section identifier."""
|
163
|
+
if identifier in self._sections:
|
164
|
+
yPos = self._sections[identifier].pos().y() - CONFIG.pxInt(8)
|
165
|
+
self.verticalScrollBar().setValue(yPos)
|
166
|
+
return
|
167
|
+
|
168
|
+
def scrollToLabel(self, label: str) -> None:
|
169
|
+
"""Scroll to the requested label."""
|
170
|
+
if label in self._index:
|
171
|
+
yPos = self._index[label].pos().y() - CONFIG.pxInt(8)
|
172
|
+
self.verticalScrollBar().setValue(yPos)
|
173
|
+
return
|
174
|
+
|
175
|
+
def addGroupLabel(self, label: str, identifier: int | None = None) -> None:
|
164
176
|
"""Add a text label to separate groups of settings."""
|
165
177
|
hM = CONFIG.pxInt(4)
|
166
|
-
qLabel = QLabel("<b
|
178
|
+
qLabel = QLabel(f"<b>{label}</b>", self)
|
167
179
|
qLabel.setContentsMargins(0, hM, 0, hM)
|
168
|
-
|
169
|
-
|
170
|
-
self.
|
171
|
-
self.
|
180
|
+
if not self._first:
|
181
|
+
self._layout.addSpacing(5*hM)
|
182
|
+
self._layout.addWidget(qLabel)
|
183
|
+
self._first = False
|
184
|
+
if identifier is not None:
|
185
|
+
self._sections[identifier] = qLabel
|
172
186
|
return
|
173
187
|
|
174
|
-
def addRow(self, label: str, widget: QWidget
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
if isinstance(widget, QLineEdit):
|
182
|
-
qLayout = QHBoxLayout()
|
183
|
-
qLayout.addWidget(widget)
|
184
|
-
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
185
|
-
else:
|
186
|
-
self.addWidget(widget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
188
|
+
def addRow(self, label: str, widget: QWidget, helpText: str = "", unit: str | None = None,
|
189
|
+
button: QWidget | None = None, editable: str | None = None,
|
190
|
+
stretch: tuple[int, int] = (1, 0)) -> None:
|
191
|
+
"""Add a label and a widget as a new row of the form."""
|
192
|
+
row = QHBoxLayout()
|
193
|
+
row.setSpacing(CONFIG.pxInt(12))
|
187
194
|
|
195
|
+
qLabel = QLabel(label, self)
|
196
|
+
qLabel.setIndent(self._indent)
|
188
197
|
qLabel.setBuddy(widget)
|
189
198
|
|
190
|
-
|
191
|
-
|
192
|
-
|
199
|
+
if helpText:
|
200
|
+
qHelp = NColourLabel(
|
201
|
+
str(helpText), color=self._helpCol, parent=self,
|
202
|
+
scale=self._fontScale, wrap=True, indent=self._indent
|
203
|
+
)
|
204
|
+
labelBox = QVBoxLayout()
|
205
|
+
labelBox.addWidget(qLabel, 0)
|
206
|
+
labelBox.addWidget(qHelp, 1)
|
207
|
+
labelBox.setSpacing(0)
|
208
|
+
row.addLayout(labelBox, stretch[0])
|
209
|
+
if editable:
|
210
|
+
self._editable[editable] = qHelp
|
211
|
+
else:
|
212
|
+
row.addWidget(qLabel, stretch[0])
|
213
|
+
|
214
|
+
if isinstance(unit, str):
|
215
|
+
box = QHBoxLayout()
|
216
|
+
box.addWidget(widget, 1)
|
217
|
+
box.addWidget(QLabel(unit, self), 0)
|
218
|
+
row.addLayout(box, stretch[1])
|
219
|
+
elif isinstance(button, QAbstractButton):
|
220
|
+
box = QHBoxLayout()
|
221
|
+
box.addWidget(widget, 1)
|
222
|
+
box.addWidget(button, 0)
|
223
|
+
row.addLayout(box, stretch[1])
|
224
|
+
else:
|
225
|
+
row.addWidget(widget, stretch[1])
|
226
|
+
|
227
|
+
self._layout.addLayout(row)
|
228
|
+
self._index[label.strip()] = widget
|
229
|
+
self._first = False
|
193
230
|
|
194
231
|
return
|
195
232
|
|
196
|
-
|
233
|
+
def finalise(self) -> None:
|
234
|
+
"""Finalise the layout when the form is built."""
|
235
|
+
self._layout.addSpacing(CONFIG.pxInt(20))
|
236
|
+
self._layout.addStretch(1)
|
237
|
+
return
|
238
|
+
|
239
|
+
# END Class NScrollableForm
|
197
240
|
|
198
241
|
|
199
|
-
class
|
242
|
+
class NColourLabel(QLabel):
|
243
|
+
"""Extension: A Coloured Label
|
200
244
|
|
201
|
-
|
202
|
-
|
203
|
-
|
245
|
+
A custom widget that draws a label in a specific colour, and
|
246
|
+
optionally at a specific size, and word wrapped.
|
247
|
+
"""
|
204
248
|
|
205
|
-
|
249
|
+
HELP_SCALE = DEFAULT_SCALE
|
250
|
+
HEADER_SCALE = 1.25
|
206
251
|
|
207
|
-
|
208
|
-
|
209
|
-
|
252
|
+
def __init__(self, text: str, color: QColor | None = None, parent: QWidget | None = None,
|
253
|
+
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
|
254
|
+
bold: bool = False) -> None:
|
255
|
+
super().__init__(text, parent=parent)
|
210
256
|
|
211
|
-
|
212
|
-
|
213
|
-
|
257
|
+
font = self.font()
|
258
|
+
font.setPointSizeF(scale*font.pointSizeF())
|
259
|
+
font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
|
260
|
+
if color:
|
261
|
+
colour = self.palette()
|
262
|
+
colour.setColor(QPalette.WindowText, color)
|
263
|
+
self.setPalette(colour)
|
214
264
|
|
215
|
-
self.
|
216
|
-
self.
|
265
|
+
self.setFont(font)
|
266
|
+
self.setIndent(indent)
|
267
|
+
self.setWordWrap(wrap)
|
217
268
|
|
218
269
|
return
|
219
270
|
|
220
|
-
# END Class
|
271
|
+
# END Class NColourLabel
|
@@ -0,0 +1,81 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – Custom Widget: Modified Widgets
|
3
|
+
=============================================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2024-02-01 [2.3b1] NComboBox
|
7
|
+
Created: 2024-02-01 [2.3b1] NSpinBox
|
8
|
+
Created: 2024-02-01 [2.3b1] NDoubleSpinBox
|
9
|
+
|
10
|
+
This file is a part of novelWriter
|
11
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
12
|
+
|
13
|
+
This program is free software: you can redistribute it and/or modify
|
14
|
+
it under the terms of the GNU General Public License as published by
|
15
|
+
the Free Software Foundation, either version 3 of the License, or
|
16
|
+
(at your option) any later version.
|
17
|
+
|
18
|
+
This program is distributed in the hope that it will be useful, but
|
19
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
20
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
21
|
+
General Public License for more details.
|
22
|
+
|
23
|
+
You should have received a copy of the GNU General Public License
|
24
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
25
|
+
"""
|
26
|
+
from __future__ import annotations
|
27
|
+
|
28
|
+
from PyQt5.QtCore import Qt
|
29
|
+
from PyQt5.QtGui import QWheelEvent
|
30
|
+
from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QWidget
|
31
|
+
|
32
|
+
|
33
|
+
class NComboBox(QComboBox):
|
34
|
+
|
35
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
36
|
+
super().__init__(parent=parent)
|
37
|
+
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
38
|
+
return
|
39
|
+
|
40
|
+
def wheelEvent(self, event: QWheelEvent) -> None:
|
41
|
+
if self.hasFocus():
|
42
|
+
super().wheelEvent(event)
|
43
|
+
else:
|
44
|
+
event.ignore()
|
45
|
+
return
|
46
|
+
|
47
|
+
# END Class NComboBox
|
48
|
+
|
49
|
+
|
50
|
+
class NSpinBox(QSpinBox):
|
51
|
+
|
52
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
53
|
+
super().__init__(parent=parent)
|
54
|
+
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
55
|
+
return
|
56
|
+
|
57
|
+
def wheelEvent(self, event: QWheelEvent) -> None:
|
58
|
+
if self.hasFocus():
|
59
|
+
super().wheelEvent(event)
|
60
|
+
else:
|
61
|
+
event.ignore()
|
62
|
+
return
|
63
|
+
|
64
|
+
# END Class NSpinBox
|
65
|
+
|
66
|
+
|
67
|
+
class NDoubleSpinBox(QDoubleSpinBox):
|
68
|
+
|
69
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
70
|
+
super().__init__(parent=parent)
|
71
|
+
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
72
|
+
return
|
73
|
+
|
74
|
+
def wheelEvent(self, event: QWheelEvent) -> None:
|
75
|
+
if self.hasFocus():
|
76
|
+
super().wheelEvent(event)
|
77
|
+
else:
|
78
|
+
event.ignore()
|
79
|
+
return
|
80
|
+
|
81
|
+
# END Class NDoubleSpinBox
|
@@ -3,7 +3,7 @@ novelWriter – Custom Widget: Novel Selector
|
|
3
3
|
===========================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2022-11-17 [2.0]
|
6
|
+
Created: 2022-11-17 [2.0] NovelSelector
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
9
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
@@ -43,6 +43,8 @@ class NovelSelector(QComboBox):
|
|
43
43
|
super().__init__(parent=parent)
|
44
44
|
self._blockSignal = False
|
45
45
|
self._firstHandle = None
|
46
|
+
self._includeAll = False
|
47
|
+
self._listFormat = None
|
46
48
|
self.currentIndexChanged.connect(self._indexChanged)
|
47
49
|
return
|
48
50
|
|
@@ -64,17 +66,29 @@ class NovelSelector(QComboBox):
|
|
64
66
|
|
65
67
|
def setHandle(self, tHandle: str | None, blockSignal: bool = True) -> None:
|
66
68
|
"""Set the currently selected handle."""
|
67
|
-
self.
|
68
|
-
|
69
|
-
index = self.count() - 1
|
70
|
-
else:
|
71
|
-
index = self.findData(tHandle)
|
72
|
-
if index >= 0:
|
69
|
+
if (index := self.findData(tHandle) if tHandle else (self.count() - 1)) >= 0:
|
70
|
+
self._blockSignal = blockSignal
|
73
71
|
self.setCurrentIndex(index)
|
74
|
-
|
72
|
+
self._blockSignal = False
|
73
|
+
return
|
74
|
+
|
75
|
+
def setIncludeAll(self, value: bool) -> None:
|
76
|
+
"""Set flag to add an "All Novel Folders" option."""
|
77
|
+
self._includeAll = value
|
78
|
+
return
|
79
|
+
|
80
|
+
def setListFormat(self, value: str | None) -> None:
|
81
|
+
"""Set a format string for the list entries."""
|
82
|
+
if value is None or "{0}" in value:
|
83
|
+
self._listFormat = value
|
75
84
|
return
|
76
85
|
|
77
|
-
|
86
|
+
##
|
87
|
+
# Public Slots
|
88
|
+
##
|
89
|
+
|
90
|
+
@pyqtSlot()
|
91
|
+
def refreshNovelList(self) -> None:
|
78
92
|
"""Rebuild the list of novel items."""
|
79
93
|
self._blockSignal = True
|
80
94
|
self._firstHandle = None
|
@@ -83,8 +97,8 @@ class NovelSelector(QComboBox):
|
|
83
97
|
icon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
|
84
98
|
handle = self.currentData()
|
85
99
|
for tHandle, nwItem in SHARED.project.tree.iterRoots(nwItemClass.NOVEL):
|
86
|
-
if
|
87
|
-
name =
|
100
|
+
if self._listFormat:
|
101
|
+
name = self._listFormat.format(nwItem.itemName)
|
88
102
|
self.addItem(name, tHandle)
|
89
103
|
else:
|
90
104
|
name = nwItem.itemName
|
@@ -92,7 +106,7 @@ class NovelSelector(QComboBox):
|
|
92
106
|
if self._firstHandle is None:
|
93
107
|
self._firstHandle = tHandle
|
94
108
|
|
95
|
-
if
|
109
|
+
if self._includeAll:
|
96
110
|
self.insertSeparator(self.count())
|
97
111
|
self.addItem(icon, self.tr("All Novel Folders"), "")
|
98
112
|
|
@@ -26,7 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
26
26
|
from __future__ import annotations
|
27
27
|
|
28
28
|
from PyQt5.QtGui import QColor, QPaintEvent, QPainter, QPolygon
|
29
|
-
from PyQt5.QtCore import QPoint, QRectF, Qt, pyqtSignal, pyqtSlot
|
29
|
+
from PyQt5.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
|
30
30
|
from PyQt5.QtWidgets import (
|
31
31
|
QAbstractButton, QAction, QButtonGroup, QLabel, QSizePolicy, QStyle,
|
32
32
|
QStyleOptionToolButton, QToolBar, QToolButton, QWidget
|
@@ -45,10 +45,9 @@ class NPagedSideBar(QToolBar):
|
|
45
45
|
def __init__(self, parent: QWidget) -> None:
|
46
46
|
super().__init__(parent=parent)
|
47
47
|
|
48
|
-
self._buttons = []
|
49
|
-
self._actions = []
|
50
48
|
self._labelCol = None
|
51
49
|
self._spacerHeight = self.fontMetrics().height() // 2
|
50
|
+
self._buttons: dict[int, _NPagedToolButton] = {}
|
52
51
|
|
53
52
|
self._group = QButtonGroup(self)
|
54
53
|
self._group.setExclusive(True)
|
@@ -63,17 +62,13 @@ class NPagedSideBar(QToolBar):
|
|
63
62
|
|
64
63
|
return
|
65
64
|
|
66
|
-
def
|
67
|
-
"""
|
68
|
-
self.
|
69
|
-
return
|
65
|
+
def button(self, buttonId: int) -> _NPagedToolButton:
|
66
|
+
"""Return a specific button."""
|
67
|
+
return self._buttons[buttonId]
|
70
68
|
|
71
|
-
def
|
72
|
-
"""
|
73
|
-
|
74
|
-
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
75
|
-
spacer.setFixedHeight(self._spacerHeight)
|
76
|
-
self.insertWidget(self._stretchAction, spacer)
|
69
|
+
def setLabelColor(self, color: QColor) -> None:
|
70
|
+
"""Set the text color for the labels."""
|
71
|
+
self._labelCol = color
|
77
72
|
return
|
78
73
|
|
79
74
|
def addLabel(self, text: str) -> None:
|
@@ -91,8 +86,7 @@ class NPagedSideBar(QToolBar):
|
|
91
86
|
action = self.insertWidget(self._stretchAction, button)
|
92
87
|
self._group.addButton(button, id=buttonId)
|
93
88
|
|
94
|
-
self._buttons
|
95
|
-
self._actions.append(action)
|
89
|
+
self._buttons[buttonId] = button
|
96
90
|
|
97
91
|
return action
|
98
92
|
|
@@ -136,6 +130,10 @@ class _NPagedToolButton(QToolButton):
|
|
136
130
|
|
137
131
|
return
|
138
132
|
|
133
|
+
def sizeHint(self) -> QSize:
|
134
|
+
"""Return a size hint that includes the arrow."""
|
135
|
+
return super().sizeHint() + QSize(4*self._aH, 0)
|
136
|
+
|
139
137
|
def paintEvent(self, event: QPaintEvent) -> None:
|
140
138
|
"""Overload the paint event to draw a simple, left aligned text
|
141
139
|
label, with a highlight when selected and a transparent base
|
@@ -162,7 +160,7 @@ class _NPagedToolButton(QToolButton):
|
|
162
160
|
if self.isChecked():
|
163
161
|
backCol = palette.highlight()
|
164
162
|
paint.setBrush(backCol)
|
165
|
-
paint.setOpacity(0.
|
163
|
+
paint.setOpacity(0.35)
|
166
164
|
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
|
167
165
|
textCol = palette.highlightedText().color()
|
168
166
|
else:
|
@@ -43,11 +43,11 @@ class NProgressSimple(QProgressBar):
|
|
43
43
|
"""Custom painter for the progress bar."""
|
44
44
|
if (value := self.value()) > 0:
|
45
45
|
progress = ceil(self.width()*float(value)/self.maximum())
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
painter = QPainter(self)
|
47
|
+
painter.setRenderHint(QPainter.Antialiasing, True)
|
48
|
+
painter.setPen(self.palette().highlight().color())
|
49
|
+
painter.setBrush(self.palette().highlight())
|
50
|
+
painter.drawRect(0, 0, progress, self.height())
|
51
51
|
return
|
52
52
|
|
53
53
|
# END Class NProgressSimple
|
@@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
24
|
from __future__ import annotations
|
25
25
|
|
26
26
|
import logging
|
27
|
+
|
27
28
|
from typing import Literal
|
28
29
|
|
29
30
|
from PyQt5.QtGui import QColor, QPaintEvent, QPainter
|
@@ -64,14 +65,13 @@ class StatusLED(QAbstractButton):
|
|
64
65
|
return
|
65
66
|
|
66
67
|
def paintEvent(self, event: QPaintEvent) -> None:
|
67
|
-
"""
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
|
68
|
+
"""Draw the LED."""
|
69
|
+
painter = QPainter(self)
|
70
|
+
painter.setRenderHint(QPainter.Antialiasing, True)
|
71
|
+
painter.setPen(self.palette().dark().color())
|
72
|
+
painter.setBrush(self._theCol)
|
73
|
+
painter.setOpacity(1.0)
|
74
|
+
painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
|
75
75
|
return
|
76
76
|
|
77
77
|
# END Class StatusLED
|