novelWriter 2.2rc1__py3-none-any.whl → 2.3b1__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.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +141 -129
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +1 -1
- 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_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_zh_CN.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +9 -2
- 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-col.svg +8 -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_light/icons.conf +9 -2
- 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-col.svg +8 -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/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/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/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/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 +194 -155
- 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 +72 -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 +179 -141
- novelwriter/core/tohtml.py +6 -4
- novelwriter/core/tokenizer.py +74 -46
- 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 -46
- novelwriter/gui/noveltree.py +53 -61
- novelwriter/gui/outline.py +100 -76
- novelwriter/gui/projtree.py +193 -67
- novelwriter/gui/sidebar.py +9 -8
- novelwriter/gui/statusbar.py +49 -7
- novelwriter/gui/theme.py +65 -74
- novelwriter/guimain.py +173 -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 +802 -0
- novelwriter/tools/writingstats.py +26 -13
- 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.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ Created: 2023-02-21 [2.1b1] NPagedToolButton
|
|
8
8
|
Created: 2023-02-21 [2.1b1] NPagedToolLabel
|
9
9
|
|
10
10
|
This file is a part of novelWriter
|
11
|
-
Copyright 2018–
|
11
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
12
12
|
|
13
13
|
This program is free software: you can redistribute it and/or modify
|
14
14
|
it under the terms of the GNU General Public License as published by
|
@@ -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,20 +62,13 @@ class NPagedSideBar(QToolBar):
|
|
63
62
|
|
64
63
|
return
|
65
64
|
|
66
|
-
def
|
67
|
-
"""
|
68
|
-
|
69
|
-
self._labelCol = QColor(*color)
|
70
|
-
elif isinstance(color, QColor):
|
71
|
-
self._labelCol = color
|
72
|
-
return
|
65
|
+
def button(self, buttonId: int) -> _NPagedToolButton:
|
66
|
+
"""Return a specific button."""
|
67
|
+
return self._buttons[buttonId]
|
73
68
|
|
74
|
-
def
|
75
|
-
"""
|
76
|
-
|
77
|
-
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
78
|
-
spacer.setFixedHeight(self._spacerHeight)
|
79
|
-
self.insertWidget(self._stretchAction, spacer)
|
69
|
+
def setLabelColor(self, color: QColor) -> None:
|
70
|
+
"""Set the text color for the labels."""
|
71
|
+
self._labelCol = color
|
80
72
|
return
|
81
73
|
|
82
74
|
def addLabel(self, text: str) -> None:
|
@@ -94,8 +86,7 @@ class NPagedSideBar(QToolBar):
|
|
94
86
|
action = self.insertWidget(self._stretchAction, button)
|
95
87
|
self._group.addButton(button, id=buttonId)
|
96
88
|
|
97
|
-
self._buttons
|
98
|
-
self._actions.append(action)
|
89
|
+
self._buttons[buttonId] = button
|
99
90
|
|
100
91
|
return action
|
101
92
|
|
@@ -139,6 +130,10 @@ class _NPagedToolButton(QToolButton):
|
|
139
130
|
|
140
131
|
return
|
141
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
|
+
|
142
137
|
def paintEvent(self, event: QPaintEvent) -> None:
|
143
138
|
"""Overload the paint event to draw a simple, left aligned text
|
144
139
|
label, with a highlight when selected and a transparent base
|
@@ -165,7 +160,7 @@ class _NPagedToolButton(QToolButton):
|
|
165
160
|
if self.isChecked():
|
166
161
|
backCol = palette.highlight()
|
167
162
|
paint.setBrush(backCol)
|
168
|
-
paint.setOpacity(0.
|
163
|
+
paint.setOpacity(0.35)
|
169
164
|
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
|
170
165
|
textCol = palette.highlightedText().color()
|
171
166
|
else:
|
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2023-06-09 [2.1b1]
|
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
|
@@ -41,14 +41,13 @@ class NProgressSimple(QProgressBar):
|
|
41
41
|
|
42
42
|
def paintEvent(self, event: QPaintEvent) -> None:
|
43
43
|
"""Custom painter for the progress bar."""
|
44
|
-
if self.value()
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
qPaint.drawRect(0, 0, progress, self.height())
|
44
|
+
if (value := self.value()) > 0:
|
45
|
+
progress = ceil(self.width()*float(value)/self.maximum())
|
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())
|
52
51
|
return
|
53
52
|
|
54
53
|
# END Class NProgressSimple
|
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-05-17 [0.5.1]
|
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
|
@@ -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
|
novelwriter/extensions/switch.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2020-05-03 [0.4.5]
|
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
|
@@ -24,31 +24,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
24
|
from __future__ import annotations
|
25
25
|
|
26
26
|
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
27
|
-
from PyQt5.QtCore import QEvent, QPropertyAnimation,
|
27
|
+
from PyQt5.QtCore import QEvent, QPropertyAnimation, Qt, pyqtProperty
|
28
28
|
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
|
29
29
|
|
30
30
|
from novelwriter import CONFIG
|
31
|
-
from novelwriter.constants import nwUnicode
|
32
31
|
|
33
32
|
|
34
33
|
class NSwitch(QAbstractButton):
|
35
34
|
|
36
|
-
|
37
|
-
width: int | None = None, height: int | None = None) -> None:
|
38
|
-
super().__init__(parent=parent)
|
39
|
-
|
40
|
-
if width is None:
|
41
|
-
self._xW = CONFIG.pxInt(40)
|
42
|
-
else:
|
43
|
-
self._xW = width
|
35
|
+
__slots__ = ("_xW", "_xH", "_xR", "_rB", "_rH", "_rR", "_offset")
|
44
36
|
|
45
|
-
|
46
|
-
|
47
|
-
else:
|
48
|
-
self._xH = height
|
37
|
+
def __init__(self, parent: QWidget | None = None, height: int = 0) -> None:
|
38
|
+
super().__init__(parent=parent)
|
49
39
|
|
40
|
+
self._xH = height or CONFIG.pxInt(20)
|
41
|
+
self._xW = 2*self._xH
|
50
42
|
self._xR = int(self._xH*0.5)
|
51
|
-
self._xT = int(self._xH*0.6)
|
52
43
|
self._rB = int(CONFIG.guiScale*2)
|
53
44
|
self._rH = self._xH - 2*self._rB
|
54
45
|
self._rR = self._xR - self._rB
|
@@ -70,7 +61,7 @@ class NSwitch(QAbstractButton):
|
|
70
61
|
return self._offset
|
71
62
|
|
72
63
|
@offset.setter # type: ignore
|
73
|
-
def offset(self, offset: int):
|
64
|
+
def offset(self, offset: int) -> None:
|
74
65
|
self._offset = offset
|
75
66
|
self.update()
|
76
67
|
return
|
@@ -82,10 +73,7 @@ class NSwitch(QAbstractButton):
|
|
82
73
|
def setChecked(self, checked: bool) -> None:
|
83
74
|
"""Overload setChecked to also alter the offset."""
|
84
75
|
super().setChecked(checked)
|
85
|
-
if checked
|
86
|
-
self._offset = self._xW - self._xR
|
87
|
-
else:
|
88
|
-
self._offset = self._xR
|
76
|
+
self._offset = (self._xW - self._xR) if checked else self._xR
|
89
77
|
return
|
90
78
|
|
91
79
|
##
|
@@ -95,53 +83,36 @@ class NSwitch(QAbstractButton):
|
|
95
83
|
def resizeEvent(self, event: QResizeEvent) -> None:
|
96
84
|
"""Overload resize to ensure correct offset."""
|
97
85
|
super().resizeEvent(event)
|
98
|
-
if self.isChecked()
|
99
|
-
self._offset = self._xW - self._xR
|
100
|
-
else:
|
101
|
-
self._offset = self._xR
|
86
|
+
self._offset = (self._xW - self._xR) if self.isChecked() else self._xR
|
102
87
|
return
|
103
88
|
|
104
89
|
def paintEvent(self, event: QPaintEvent) -> None:
|
105
90
|
"""Drawing the switch itself."""
|
106
|
-
|
107
|
-
|
108
|
-
|
91
|
+
painter = QPainter(self)
|
92
|
+
painter.setRenderHint(QPainter.Antialiasing, True)
|
93
|
+
painter.setPen(Qt.NoPen)
|
109
94
|
|
110
|
-
|
95
|
+
palette = self.palette()
|
111
96
|
if self.isChecked():
|
112
|
-
trackBrush =
|
113
|
-
thumbBrush =
|
114
|
-
textColor = qPalette.highlight().color()
|
115
|
-
thumbText = nwUnicode.U_CHECK
|
97
|
+
trackBrush = palette.highlight()
|
98
|
+
thumbBrush = palette.highlightedText()
|
116
99
|
else:
|
117
|
-
trackBrush =
|
118
|
-
thumbBrush =
|
119
|
-
textColor = qPalette.dark().color()
|
120
|
-
thumbText = nwUnicode.U_CROSS
|
100
|
+
trackBrush = palette.dark()
|
101
|
+
thumbBrush = palette.light()
|
121
102
|
|
122
103
|
if self.isEnabled():
|
123
104
|
trackOpacity = 1.0
|
124
105
|
else:
|
125
106
|
trackOpacity = 0.6
|
126
|
-
trackBrush =
|
127
|
-
thumbBrush =
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
qPaint.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
|
136
|
-
|
137
|
-
theFont = qPaint.font()
|
138
|
-
theFont.setPixelSize(self._xT)
|
139
|
-
qPaint.setPen(textColor)
|
140
|
-
qPaint.setFont(theFont)
|
141
|
-
qPaint.drawText(
|
142
|
-
QRectF(self._offset - self._rR, self._rB, self._rH, self._rH),
|
143
|
-
Qt.AlignCenter, thumbText
|
144
|
-
)
|
107
|
+
trackBrush = palette.shadow()
|
108
|
+
thumbBrush = palette.mid()
|
109
|
+
|
110
|
+
painter.setBrush(trackBrush)
|
111
|
+
painter.setOpacity(trackOpacity)
|
112
|
+
painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
113
|
+
|
114
|
+
painter.setBrush(thumbBrush)
|
115
|
+
painter.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
|
145
116
|
|
146
117
|
return
|
147
118
|
|
@@ -149,14 +120,11 @@ class NSwitch(QAbstractButton):
|
|
149
120
|
"""Animate the switch on mouse release."""
|
150
121
|
super().mouseReleaseEvent(event)
|
151
122
|
if event.button() == Qt.LeftButton:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
if self.isChecked()
|
156
|
-
|
157
|
-
else:
|
158
|
-
doAnim.setEndValue(self._xR)
|
159
|
-
doAnim.start()
|
123
|
+
anim = QPropertyAnimation(self, b"offset", self)
|
124
|
+
anim.setDuration(120)
|
125
|
+
anim.setStartValue(self._offset)
|
126
|
+
anim.setEndValue((self._xW - self._xR) if self.isChecked() else self._xR)
|
127
|
+
anim.start()
|
160
128
|
return
|
161
129
|
|
162
130
|
def enterEvent(self, event: QEvent) -> None:
|
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2023-04-16 [2.1b1]
|
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
|
@@ -87,7 +87,7 @@ class NSwitchBox(QScrollArea):
|
|
87
87
|
label = QLabel(text)
|
88
88
|
self._content.addWidget(label, self._index, 1, Qt.AlignLeft)
|
89
89
|
|
90
|
-
switch = NSwitch(
|
90
|
+
switch = NSwitch(self, height=self._hSwitch)
|
91
91
|
switch.setChecked(default)
|
92
92
|
switch.toggled.connect(lambda state: self._emitSwitchSignal(identifier, state))
|
93
93
|
self._content.addWidget(switch, self._index, 2, Qt.AlignRight)
|
@@ -106,11 +106,6 @@ class NSwitchBox(QScrollArea):
|
|
106
106
|
self._bumpIndex()
|
107
107
|
return
|
108
108
|
|
109
|
-
def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int) -> None:
|
110
|
-
"""Set the contents margins of the inner layout."""
|
111
|
-
self._content.setContentsMargins(left, top, right, bottom)
|
112
|
-
return
|
113
|
-
|
114
109
|
##
|
115
110
|
# Internal Functions
|
116
111
|
##
|
@@ -0,0 +1,153 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – Custom Widget: Version Info
|
3
|
+
=========================================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2024-02-14 [2.3b1] VersionInfoWidget
|
7
|
+
|
8
|
+
This file is a part of novelWriter
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
+
|
11
|
+
This program is free software: you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful, but
|
17
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
+
General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
+
"""
|
24
|
+
from __future__ import annotations
|
25
|
+
|
26
|
+
import json
|
27
|
+
import logging
|
28
|
+
|
29
|
+
from time import sleep
|
30
|
+
from datetime import datetime
|
31
|
+
from urllib.error import HTTPError
|
32
|
+
from urllib.request import Request, urlopen
|
33
|
+
|
34
|
+
from PyQt5.QtGui import QDesktopServices
|
35
|
+
from PyQt5.QtCore import QObject, QRunnable, QUrl, pyqtSignal, pyqtSlot
|
36
|
+
from PyQt5.QtWidgets import QLabel, QVBoxLayout, QWidget
|
37
|
+
|
38
|
+
from novelwriter import CONFIG, SHARED, __version__, __date__, __domain__
|
39
|
+
from novelwriter.common import formatVersion
|
40
|
+
from novelwriter.constants import nwConst
|
41
|
+
|
42
|
+
logger = logging.getLogger(__name__)
|
43
|
+
|
44
|
+
API_URL = "https://api.github.com/repos/vkbo/novelwriter/releases/latest"
|
45
|
+
|
46
|
+
|
47
|
+
class VersionInfoWidget(QWidget):
|
48
|
+
|
49
|
+
def __init__(self, parent: QWidget) -> None:
|
50
|
+
super().__init__(parent=parent)
|
51
|
+
|
52
|
+
# Label Strings
|
53
|
+
self._trLatest = self.tr("Latest Version: {0}")
|
54
|
+
self._trChecking = self.tr("Checking ...")
|
55
|
+
self._trDownload = self.tr("Download from {0}")
|
56
|
+
|
57
|
+
# Labels
|
58
|
+
self._lblInfo = QLabel("{0} {1} \u2013 {2} {3} \u2013 {4}".format(
|
59
|
+
self.tr("Version"), formatVersion(__version__),
|
60
|
+
self.tr("Released on"), datetime.strptime(__date__, "%Y-%m-%d").strftime("%x"),
|
61
|
+
"<a href='#notes'>{0}</a>".format(self.tr("Release Notes")),
|
62
|
+
), self)
|
63
|
+
self._lblInfo.linkActivated.connect(self._processLink)
|
64
|
+
self._lblRelease = QLabel(self._trLatest.format(
|
65
|
+
"<a href='#update'>{0}</a>".format(self.tr("Check Now"))
|
66
|
+
), self)
|
67
|
+
self._lblRelease.linkActivated.connect(self._processLink)
|
68
|
+
|
69
|
+
# Assemble
|
70
|
+
self._layout = QVBoxLayout()
|
71
|
+
self._layout.addWidget(self._lblInfo)
|
72
|
+
self._layout.addWidget(self._lblRelease)
|
73
|
+
self._layout.setSpacing(CONFIG.pxInt(2))
|
74
|
+
self._layout.setContentsMargins(0, 0, 0, 0)
|
75
|
+
|
76
|
+
self.setLayout(self._layout)
|
77
|
+
|
78
|
+
return
|
79
|
+
|
80
|
+
##
|
81
|
+
# Private Slots
|
82
|
+
##
|
83
|
+
|
84
|
+
@pyqtSlot(str)
|
85
|
+
def _processLink(self, link: str) -> None:
|
86
|
+
"""Process an activated link."""
|
87
|
+
if link == "#notes":
|
88
|
+
QDesktopServices.openUrl(QUrl(nwConst.URL_RELEASES))
|
89
|
+
elif link == "#website":
|
90
|
+
QDesktopServices.openUrl(QUrl(nwConst.URL_WEB))
|
91
|
+
elif link == "#update":
|
92
|
+
self._lblRelease.setText(self._trLatest.format(self._trChecking))
|
93
|
+
lookup = _Retriever()
|
94
|
+
lookup.signals.dataReady.connect(self._updateReleaseInfo)
|
95
|
+
SHARED.runInThreadPool(lookup)
|
96
|
+
return
|
97
|
+
|
98
|
+
##
|
99
|
+
# Private Slots
|
100
|
+
##
|
101
|
+
|
102
|
+
@pyqtSlot(str, str)
|
103
|
+
def _updateReleaseInfo(self, tag: str, reason: str) -> None:
|
104
|
+
"""Update the widget release info."""
|
105
|
+
if version := tag.lstrip("v"):
|
106
|
+
download = f"<a href='#website'>{__domain__}</a>"
|
107
|
+
self._lblRelease.setText(self._trLatest.format(
|
108
|
+
f"{version} \u2013 {self._trDownload.format(download)}"
|
109
|
+
))
|
110
|
+
else:
|
111
|
+
self._lblRelease.setText(self._trLatest.format(reason or self.tr("Failed")))
|
112
|
+
return
|
113
|
+
|
114
|
+
# END Class VersionInfoWidget
|
115
|
+
|
116
|
+
|
117
|
+
class _Retriever(QRunnable):
|
118
|
+
|
119
|
+
def __init__(self) -> None:
|
120
|
+
super().__init__()
|
121
|
+
self.signals = _RetrieverSignal()
|
122
|
+
return
|
123
|
+
|
124
|
+
@pyqtSlot()
|
125
|
+
def run(self) -> None:
|
126
|
+
"""Poll the GitHub API in the background.
|
127
|
+
Note: The GitHub API is rate limited at 60 requests per hour.
|
128
|
+
"""
|
129
|
+
logger.info("Contacting: %s", API_URL)
|
130
|
+
req = Request(API_URL)
|
131
|
+
req.add_header("User-Agent", nwConst.USER_AGENT)
|
132
|
+
req.add_header("Accept", "application/vnd.github.v3+json")
|
133
|
+
sleep(0.2)
|
134
|
+
try:
|
135
|
+
with urlopen(req, timeout=15) as ret:
|
136
|
+
if isinstance(raw := json.loads(ret.read().decode()), dict):
|
137
|
+
self.signals.dataReady.emit(raw.get("tag_name", ""), "")
|
138
|
+
except HTTPError as e:
|
139
|
+
reason = f"{e.reason.capitalize()} (HTTP {e.code})"
|
140
|
+
logger.error(reason)
|
141
|
+
self.signals.dataReady.emit("", reason)
|
142
|
+
except Exception as e:
|
143
|
+
logger.error("Failed to retrieve release info")
|
144
|
+
self.signals.dataReady.emit("", str(e))
|
145
|
+
return
|
146
|
+
|
147
|
+
# END Class _Retriever
|
148
|
+
|
149
|
+
|
150
|
+
class _RetrieverSignal(QObject):
|
151
|
+
dataReady = pyqtSignal(str, str)
|
152
|
+
|
153
|
+
# END Class _RetrieverSignal
|