novelWriter 2.4b1__py3-none-any.whl → 2.4.1__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.4b1.dist-info → novelWriter-2.4.1.dist-info}/METADATA +5 -6
- {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/RECORD +79 -83
- novelwriter/__init__.py +15 -8
- 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_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/icons/none.svg +4 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -2
- novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -2
- novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/common.py +11 -3
- novelwriter/config.py +12 -4
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +21 -22
- novelwriter/core/docbuild.py +2 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/spellcheck.py +3 -3
- novelwriter/core/status.py +3 -2
- novelwriter/core/tokenizer.py +3 -3
- novelwriter/core/toodt.py +333 -356
- novelwriter/dialogs/about.py +9 -11
- novelwriter/dialogs/docmerge.py +17 -14
- novelwriter/dialogs/docsplit.py +14 -12
- novelwriter/dialogs/editlabel.py +5 -4
- novelwriter/dialogs/preferences.py +29 -34
- novelwriter/dialogs/projectsettings.py +31 -28
- novelwriter/dialogs/quotes.py +10 -9
- novelwriter/dialogs/wordlist.py +17 -14
- novelwriter/error.py +14 -12
- novelwriter/extensions/circularprogress.py +12 -8
- novelwriter/extensions/configlayout.py +1 -3
- novelwriter/extensions/modified.py +33 -2
- novelwriter/extensions/pagedsidebar.py +16 -14
- novelwriter/extensions/simpleprogress.py +3 -1
- novelwriter/extensions/statusled.py +3 -1
- novelwriter/extensions/switch.py +10 -9
- novelwriter/extensions/switchbox.py +14 -13
- novelwriter/gui/doceditor.py +205 -246
- novelwriter/gui/dochighlight.py +26 -9
- novelwriter/gui/docviewer.py +55 -59
- novelwriter/gui/docviewerpanel.py +16 -13
- novelwriter/gui/editordocument.py +4 -4
- novelwriter/gui/itemdetails.py +45 -48
- novelwriter/gui/mainmenu.py +2 -2
- novelwriter/gui/noveltree.py +23 -21
- novelwriter/gui/outline.py +93 -94
- novelwriter/gui/projtree.py +32 -30
- novelwriter/gui/search.py +75 -29
- novelwriter/gui/sidebar.py +24 -28
- novelwriter/gui/statusbar.py +14 -14
- novelwriter/gui/theme.py +61 -39
- novelwriter/guimain.py +37 -33
- novelwriter/shared.py +21 -9
- novelwriter/text/counting.py +1 -0
- novelwriter/tools/dictionaries.py +15 -14
- novelwriter/tools/lipsum.py +20 -17
- novelwriter/tools/manusbuild.py +44 -35
- novelwriter/tools/manuscript.py +112 -112
- novelwriter/tools/manussettings.py +91 -98
- novelwriter/tools/noveldetails.py +20 -18
- novelwriter/tools/welcome.py +51 -48
- novelwriter/tools/writingstats.py +61 -55
- novelwriter/types.py +90 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +0 -4
- novelwriter/core/__init__.py +0 -3
- novelwriter/dialogs/__init__.py +0 -3
- novelwriter/extensions/__init__.py +0 -3
- novelwriter/gui/__init__.py +0 -3
- novelwriter/text/__init__.py +0 -3
- novelwriter/tools/__init__.py +0 -3
- {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/WHEEL +0 -0
- {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/top_level.txt +0 -0
novelwriter/gui/theme.py
CHANGED
@@ -29,17 +29,17 @@ import logging
|
|
29
29
|
from math import ceil
|
30
30
|
from pathlib import Path
|
31
31
|
|
32
|
-
from PyQt5.QtCore import Qt
|
33
|
-
from PyQt5.QtWidgets import qApp
|
32
|
+
from PyQt5.QtCore import QSize, Qt
|
34
33
|
from PyQt5.QtGui import (
|
35
34
|
QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap
|
36
35
|
)
|
36
|
+
from PyQt5.QtWidgets import QApplication
|
37
37
|
|
38
38
|
from novelwriter import CONFIG
|
39
|
+
from novelwriter.common import NWConfigParser, cssCol, minmax
|
40
|
+
from novelwriter.constants import nwLabels
|
39
41
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
40
42
|
from novelwriter.error import logException
|
41
|
-
from novelwriter.common import NWConfigParser, minmax
|
42
|
-
from novelwriter.constants import nwLabels
|
43
43
|
|
44
44
|
logger = logging.getLogger(__name__)
|
45
45
|
|
@@ -74,9 +74,9 @@ class GuiTheme:
|
|
74
74
|
self.isLightTheme = True
|
75
75
|
|
76
76
|
# GUI
|
77
|
-
self.statNone = QColor(
|
78
|
-
self.statUnsaved = QColor(
|
79
|
-
self.statSaved = QColor(
|
77
|
+
self.statNone = QColor(0, 0, 0)
|
78
|
+
self.statUnsaved = QColor(0, 0, 0)
|
79
|
+
self.statSaved = QColor(0, 0, 0)
|
80
80
|
self.helpText = QColor(0, 0, 0)
|
81
81
|
|
82
82
|
# Loaded Syntax Settings
|
@@ -144,33 +144,42 @@ class GuiTheme:
|
|
144
144
|
self.getHeaderDecorationNarrow = self.iconCache.getHeaderDecorationNarrow
|
145
145
|
|
146
146
|
# Extract Other Info
|
147
|
-
self.guiDPI =
|
148
|
-
self.guiScale =
|
147
|
+
self.guiDPI = QApplication.primaryScreen().logicalDotsPerInchX()
|
148
|
+
self.guiScale = QApplication.primaryScreen().logicalDotsPerInchX()/96.0
|
149
149
|
CONFIG.guiScale = self.guiScale
|
150
150
|
logger.debug("GUI DPI: %.1f", self.guiDPI)
|
151
151
|
logger.debug("GUI Scale: %.2f", self.guiScale)
|
152
152
|
|
153
153
|
# Fonts
|
154
|
-
self.guiFont =
|
155
|
-
self.guiFontB =
|
154
|
+
self.guiFont = QApplication.font()
|
155
|
+
self.guiFontB = QApplication.font()
|
156
156
|
self.guiFontB.setBold(True)
|
157
157
|
|
158
158
|
qMetric = QFontMetrics(self.guiFont)
|
159
|
+
fHeight = qMetric.height()
|
160
|
+
fAscent = qMetric.ascent()
|
159
161
|
self.fontPointSize = self.guiFont.pointSizeF()
|
160
|
-
self.fontPixelSize = int(round(
|
161
|
-
self.
|
162
|
+
self.fontPixelSize = int(round(fHeight))
|
163
|
+
self.baseIconHeight = int(round(fAscent))
|
164
|
+
self.baseButtonHeight = int(round(1.35*fAscent))
|
162
165
|
self.textNHeight = qMetric.boundingRect("N").height()
|
163
166
|
self.textNWidth = qMetric.boundingRect("N").width()
|
164
167
|
|
168
|
+
self.baseIconSize = QSize(self.baseIconHeight, self.baseIconHeight)
|
169
|
+
self.buttonIconSize = QSize(int(0.9*self.baseIconHeight), int(0.9*self.baseIconHeight))
|
170
|
+
|
165
171
|
# Monospace Font
|
166
172
|
self.guiFontFixed = QFont()
|
167
173
|
self.guiFontFixed.setPointSizeF(0.95*self.fontPointSize)
|
168
|
-
self.guiFontFixed.setFamily(
|
174
|
+
self.guiFontFixed.setFamily(
|
175
|
+
QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont).family()
|
176
|
+
)
|
169
177
|
|
170
178
|
logger.debug("GUI Font Family: %s", self.guiFont.family())
|
171
179
|
logger.debug("GUI Font Point Size: %.2f", self.fontPointSize)
|
172
180
|
logger.debug("GUI Font Pixel Size: %d", self.fontPixelSize)
|
173
|
-
logger.debug("GUI Base Icon
|
181
|
+
logger.debug("GUI Base Icon Height: %d", self.baseIconHeight)
|
182
|
+
logger.debug("GUI Base Button Height: %d", self.baseButtonHeight)
|
174
183
|
logger.debug("Text 'N' Height: %d", self.textNHeight)
|
175
184
|
logger.debug("Text 'N' Width: %d", self.textNWidth)
|
176
185
|
|
@@ -218,6 +227,10 @@ class GuiTheme:
|
|
218
227
|
logException()
|
219
228
|
return False
|
220
229
|
|
230
|
+
# Reset Palette
|
231
|
+
self._guiPalette = QApplication.style().standardPalette()
|
232
|
+
self._resetGuiColors()
|
233
|
+
|
221
234
|
# Main
|
222
235
|
sec = "Main"
|
223
236
|
if parser.has_section(sec):
|
@@ -247,8 +260,6 @@ class GuiTheme:
|
|
247
260
|
self._setPalette(parser, sec, "highlightedtext", QPalette.ColorRole.HighlightedText)
|
248
261
|
self._setPalette(parser, sec, "link", QPalette.ColorRole.Link)
|
249
262
|
self._setPalette(parser, sec, "linkvisited", QPalette.ColorRole.LinkVisited)
|
250
|
-
else:
|
251
|
-
self._guiPalette = qApp.style().standardPalette()
|
252
263
|
|
253
264
|
# GUI
|
254
265
|
sec = "GUI"
|
@@ -277,7 +288,7 @@ class GuiTheme:
|
|
277
288
|
self.iconCache.loadTheme(self.themeIcons or defaultIcons)
|
278
289
|
|
279
290
|
# Apply Styles
|
280
|
-
|
291
|
+
QApplication.setPalette(self._guiPalette)
|
281
292
|
|
282
293
|
# Reset stylesheets so that they are regenerated
|
283
294
|
self._buildStyleSheets(self._guiPalette)
|
@@ -384,6 +395,14 @@ class GuiTheme:
|
|
384
395
|
# Internal Functions
|
385
396
|
##
|
386
397
|
|
398
|
+
def _resetGuiColors(self) -> None:
|
399
|
+
"""Reset GUI colours to default values."""
|
400
|
+
self.statNone = QColor(120, 120, 120)
|
401
|
+
self.statUnsaved = QColor(200, 15, 39)
|
402
|
+
self.statSaved = QColor(2, 133, 37)
|
403
|
+
self.helpText = QColor(0, 0, 0)
|
404
|
+
return
|
405
|
+
|
387
406
|
def _setGuiFont(self) -> None:
|
388
407
|
"""Update the GUI's font style from settings."""
|
389
408
|
font = QFont()
|
@@ -394,14 +413,14 @@ class GuiTheme:
|
|
394
413
|
font.setFamily("Arial")
|
395
414
|
font.setPointSize(10)
|
396
415
|
else:
|
397
|
-
font = fontDB.systemFont(QFontDatabase.GeneralFont)
|
416
|
+
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
|
398
417
|
CONFIG.guiFont = font.family()
|
399
418
|
CONFIG.guiFontSize = font.pointSize()
|
400
419
|
else:
|
401
420
|
font.setFamily(CONFIG.guiFont)
|
402
421
|
font.setPointSize(CONFIG.guiFontSize)
|
403
422
|
|
404
|
-
|
423
|
+
QApplication.setFont(font)
|
405
424
|
|
406
425
|
return
|
407
426
|
|
@@ -440,24 +459,24 @@ class GuiTheme:
|
|
440
459
|
|
441
460
|
# Flat Tab Widget and Tab Bar:
|
442
461
|
self._styleSheets[STYLES_FLAT_TABS] = (
|
443
|
-
"QTabWidget::pane {
|
444
|
-
"QTabWidget QTabBar::tab {{border: 0; padding: {
|
445
|
-
"QTabWidget QTabBar::tab:selected {{color:
|
446
|
-
)
|
462
|
+
"QTabWidget::pane {border: 0;} "
|
463
|
+
f"QTabWidget QTabBar::tab {{border: 0; padding: {bPx}px {dPx}px;}} "
|
464
|
+
f"QTabWidget QTabBar::tab:selected {{color: {cssCol(hCol)};}} "
|
465
|
+
)
|
447
466
|
|
448
467
|
# Minimal Tool Button
|
449
468
|
self._styleSheets[STYLES_MIN_TOOLBUTTON] = (
|
450
|
-
"QToolButton {{padding: {
|
451
|
-
"QToolButton:hover {{border: none; background:
|
452
|
-
"QToolButton::menu-indicator {
|
453
|
-
)
|
469
|
+
f"QToolButton {{padding: {aPx}px; margin: 0; border: none; background: transparent;}} "
|
470
|
+
f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
|
471
|
+
"QToolButton::menu-indicator {image: none;} "
|
472
|
+
)
|
454
473
|
|
455
474
|
# Big Tool Button
|
456
475
|
self._styleSheets[STYLES_BIG_TOOLBUTTON] = (
|
457
|
-
"QToolButton {{padding: {
|
458
|
-
"QToolButton:hover {{border: none; background:
|
459
|
-
"QToolButton::menu-indicator {
|
460
|
-
)
|
476
|
+
f"QToolButton {{padding: {cPx}px; margin: 0; border: none; background: transparent;}} "
|
477
|
+
f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
|
478
|
+
"QToolButton::menu-indicator {image: none;} "
|
479
|
+
)
|
461
480
|
|
462
481
|
return
|
463
482
|
|
@@ -544,6 +563,9 @@ class GuiIcons:
|
|
544
563
|
self._confName = "icons.conf"
|
545
564
|
self._iconPath = CONFIG.assetPath("icons")
|
546
565
|
|
566
|
+
# None Icon
|
567
|
+
self._noIcon = QIcon(str(self._iconPath / "none.svg"))
|
568
|
+
|
547
569
|
# Icon Theme Meta
|
548
570
|
self.themeName = ""
|
549
571
|
self.themeDescription = ""
|
@@ -674,13 +696,13 @@ class GuiIcons:
|
|
674
696
|
def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon:
|
675
697
|
"""Return a toggle icon from the icon buffer. or load it."""
|
676
698
|
if name in self.TOGGLE_ICON_KEYS:
|
677
|
-
pOne
|
699
|
+
pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
|
678
700
|
pTwo = self.getPixmap(self.TOGGLE_ICON_KEYS[name][1], size)
|
679
701
|
icon = QIcon()
|
680
702
|
icon.addPixmap(pOne, QIcon.Mode.Normal, QIcon.State.On)
|
681
703
|
icon.addPixmap(pTwo, QIcon.Mode.Normal, QIcon.State.Off)
|
682
704
|
return icon
|
683
|
-
return
|
705
|
+
return self._noIcon
|
684
706
|
|
685
707
|
def getPixmap(self, name: str, size: tuple[int, int]) -> QPixmap:
|
686
708
|
"""Return an icon from the icon buffer as a QPixmap. If it
|
@@ -712,14 +734,14 @@ class GuiIcons:
|
|
712
734
|
elif tLayout == nwItemLayout.NOTE:
|
713
735
|
iconName = "proj_note"
|
714
736
|
if iconName is None:
|
715
|
-
return
|
737
|
+
return self._noIcon
|
716
738
|
|
717
739
|
return self.getIcon(iconName)
|
718
740
|
|
719
741
|
def getHeaderDecoration(self, hLevel: int) -> QPixmap:
|
720
742
|
"""Get the decoration for a specific heading level."""
|
721
743
|
if not self._headerDec:
|
722
|
-
iPx = self.mainTheme.
|
744
|
+
iPx = self.mainTheme.baseIconHeight
|
723
745
|
self._headerDec = [
|
724
746
|
self.loadDecoration("deco_doc_h0", h=iPx),
|
725
747
|
self.loadDecoration("deco_doc_h1", h=iPx),
|
@@ -732,7 +754,7 @@ class GuiIcons:
|
|
732
754
|
def getHeaderDecorationNarrow(self, hLevel: int) -> QPixmap:
|
733
755
|
"""Get the narrow decoration for a specific heading level."""
|
734
756
|
if not self._headerDecNarrow:
|
735
|
-
iPx = self.mainTheme.
|
757
|
+
iPx = self.mainTheme.baseIconHeight
|
736
758
|
self._headerDecNarrow = [
|
737
759
|
self.loadDecoration("deco_doc_h0_n", h=iPx),
|
738
760
|
self.loadDecoration("deco_doc_h1_n", h=iPx),
|
@@ -753,7 +775,7 @@ class GuiIcons:
|
|
753
775
|
"""
|
754
776
|
if name not in self.ICON_KEYS:
|
755
777
|
logger.error("Requested unknown icon name '%s'", name)
|
756
|
-
return
|
778
|
+
return self._noIcon
|
757
779
|
|
758
780
|
# If we just want the app icons, return right away
|
759
781
|
if name == "novelwriter":
|
@@ -769,7 +791,7 @@ class GuiIcons:
|
|
769
791
|
# If we didn't find one, give up and return an empty icon
|
770
792
|
logger.warning("Did not load an icon for '%s'", name)
|
771
793
|
|
772
|
-
return
|
794
|
+
return self._noIcon
|
773
795
|
|
774
796
|
# END Class GuiIcons
|
775
797
|
|
novelwriter/guimain.py
CHANGED
@@ -30,11 +30,11 @@ from time import time
|
|
30
30
|
from pathlib import Path
|
31
31
|
from datetime import datetime
|
32
32
|
|
33
|
-
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
|
34
33
|
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
|
34
|
+
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
|
35
35
|
from PyQt5.QtWidgets import (
|
36
|
-
QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
37
|
-
QStackedWidget, QVBoxLayout, QWidget
|
36
|
+
QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
37
|
+
QStackedWidget, QVBoxLayout, QWidget
|
38
38
|
)
|
39
39
|
|
40
40
|
from novelwriter import CONFIG, SHARED, __hexversion__, __version__
|
@@ -109,7 +109,7 @@ class GuiMain(QMainWindow):
|
|
109
109
|
nwIcon = CONFIG.assetPath("icons") / "novelwriter.svg"
|
110
110
|
self.nwIcon = QIcon(str(nwIcon)) if nwIcon.is_file() else QIcon()
|
111
111
|
self.setWindowIcon(self.nwIcon)
|
112
|
-
|
112
|
+
QApplication.setWindowIcon(self.nwIcon)
|
113
113
|
|
114
114
|
# Build the GUI
|
115
115
|
# =============
|
@@ -148,7 +148,7 @@ class GuiMain(QMainWindow):
|
|
148
148
|
self.treePane.setLayout(self.treeBox)
|
149
149
|
|
150
150
|
# Splitter : Document Viewer / Document Meta
|
151
|
-
self.splitView = QSplitter(Qt.Vertical, self)
|
151
|
+
self.splitView = QSplitter(Qt.Orientation.Vertical, self)
|
152
152
|
self.splitView.addWidget(self.docViewer)
|
153
153
|
self.splitView.addWidget(self.docViewerPanel)
|
154
154
|
self.splitView.setHandleWidth(hWd)
|
@@ -158,7 +158,7 @@ class GuiMain(QMainWindow):
|
|
158
158
|
self.splitView.setCollapsible(1, False)
|
159
159
|
|
160
160
|
# Splitter : Document Editor / Document Viewer
|
161
|
-
self.splitDocs = QSplitter(Qt.Horizontal, self)
|
161
|
+
self.splitDocs = QSplitter(Qt.Orientation.Horizontal, self)
|
162
162
|
self.splitDocs.addWidget(self.docEditor)
|
163
163
|
self.splitDocs.addWidget(self.splitView)
|
164
164
|
self.splitDocs.setOpaqueResize(False)
|
@@ -167,7 +167,7 @@ class GuiMain(QMainWindow):
|
|
167
167
|
self.splitDocs.setCollapsible(1, False)
|
168
168
|
|
169
169
|
# Splitter : Project Tree / Document Area
|
170
|
-
self.splitMain = QSplitter(Qt.Horizontal)
|
170
|
+
self.splitMain = QSplitter(Qt.Orientation.Horizontal)
|
171
171
|
self.splitMain.setContentsMargins(0, 0, 0, 0)
|
172
172
|
self.splitMain.addWidget(self.treePane)
|
173
173
|
self.splitMain.addWidget(self.splitDocs)
|
@@ -205,7 +205,7 @@ class GuiMain(QMainWindow):
|
|
205
205
|
self.setMenuBar(self.mainMenu)
|
206
206
|
self.setCentralWidget(self.mainWidget)
|
207
207
|
self.setStatusBar(self.mainStatus)
|
208
|
-
self.setContextMenuPolicy(Qt.NoContextMenu) # Issue #1147
|
208
|
+
self.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu) # Issue #1147
|
209
209
|
|
210
210
|
# Connect Signals
|
211
211
|
# ===============
|
@@ -262,6 +262,7 @@ class GuiMain(QMainWindow):
|
|
262
262
|
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
263
263
|
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
264
264
|
self.docEditor.requestNewNoteCreation.connect(self.projView.createNewNote)
|
265
|
+
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
265
266
|
|
266
267
|
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
|
267
268
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
@@ -327,7 +328,7 @@ class GuiMain(QMainWindow):
|
|
327
328
|
def postLaunchTasks(self, cmdOpen: str | None) -> None:
|
328
329
|
"""Process tasks after the main window has been created."""
|
329
330
|
if cmdOpen:
|
330
|
-
|
331
|
+
QApplication.processEvents()
|
331
332
|
logger.info("Command line path: %s", cmdOpen)
|
332
333
|
self.openProject(cmdOpen)
|
333
334
|
|
@@ -473,12 +474,12 @@ class GuiMain(QMainWindow):
|
|
473
474
|
break
|
474
475
|
|
475
476
|
if lastEdited is not None:
|
476
|
-
|
477
|
+
QApplication.processEvents()
|
477
478
|
self.openDocument(lastEdited, doScroll=True)
|
478
479
|
|
479
480
|
lastViewed = SHARED.project.data.getLastHandle("viewer")
|
480
481
|
if lastViewed is not None:
|
481
|
-
|
482
|
+
QApplication.processEvents()
|
482
483
|
self.viewDocument(lastViewed)
|
483
484
|
|
484
485
|
# Check if we need to rebuild the index
|
@@ -487,7 +488,7 @@ class GuiMain(QMainWindow):
|
|
487
488
|
self.rebuildIndex()
|
488
489
|
|
489
490
|
# Make sure the changed status is set to false on things opened
|
490
|
-
|
491
|
+
QApplication.processEvents()
|
491
492
|
self.docEditor.setDocumentChanged(False)
|
492
493
|
SHARED.project.setProjectChanged(False)
|
493
494
|
|
@@ -737,7 +738,7 @@ class GuiMain(QMainWindow):
|
|
737
738
|
"""Rebuild the entire index."""
|
738
739
|
if SHARED.hasProject:
|
739
740
|
logger.info("Rebuilding index ...")
|
740
|
-
|
741
|
+
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
741
742
|
tStart = time()
|
742
743
|
|
743
744
|
self.projView.saveProjectTasks()
|
@@ -751,7 +752,7 @@ class GuiMain(QMainWindow):
|
|
751
752
|
)
|
752
753
|
self.docEditor.updateTagHighLighting()
|
753
754
|
self._updateStatusWordCount()
|
754
|
-
|
755
|
+
QApplication.restoreOverrideCursor()
|
755
756
|
|
756
757
|
if not beQuiet:
|
757
758
|
SHARED.info(self.tr("The project index has been successfully rebuilt."))
|
@@ -767,7 +768,7 @@ class GuiMain(QMainWindow):
|
|
767
768
|
"""Open the welcome dialog."""
|
768
769
|
dialog = GuiWelcome(self)
|
769
770
|
dialog.openProjectRequest.connect(self._openProjectFromWelcome)
|
770
|
-
dialog.
|
771
|
+
dialog.exec()
|
771
772
|
return
|
772
773
|
|
773
774
|
@pyqtSlot()
|
@@ -775,7 +776,7 @@ class GuiMain(QMainWindow):
|
|
775
776
|
"""Open the preferences dialog."""
|
776
777
|
dialog = GuiPreferences(self)
|
777
778
|
dialog.newPreferencesReady.connect(self._processConfigChanges)
|
778
|
-
dialog.
|
779
|
+
dialog.exec()
|
779
780
|
return
|
780
781
|
|
781
782
|
@pyqtSlot()
|
@@ -785,7 +786,7 @@ class GuiMain(QMainWindow):
|
|
785
786
|
if SHARED.hasProject:
|
786
787
|
dialog = GuiProjectSettings(self, gotoPage=focusTab)
|
787
788
|
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
788
|
-
dialog.
|
789
|
+
dialog.exec()
|
789
790
|
return
|
790
791
|
|
791
792
|
@pyqtSlot()
|
@@ -796,7 +797,7 @@ class GuiMain(QMainWindow):
|
|
796
797
|
dialog.setModal(True)
|
797
798
|
dialog.show()
|
798
799
|
dialog.raise_()
|
799
|
-
|
800
|
+
QApplication.processEvents()
|
800
801
|
dialog.updateValues()
|
801
802
|
return
|
802
803
|
|
@@ -809,7 +810,7 @@ class GuiMain(QMainWindow):
|
|
809
810
|
dialog.setModal(False)
|
810
811
|
dialog.show()
|
811
812
|
dialog.raise_()
|
812
|
-
|
813
|
+
QApplication.processEvents()
|
813
814
|
dialog.loadContent()
|
814
815
|
return
|
815
816
|
|
@@ -819,7 +820,7 @@ class GuiMain(QMainWindow):
|
|
819
820
|
if SHARED.hasProject:
|
820
821
|
dialog = GuiWordList(self)
|
821
822
|
dialog.newWordListReady.connect(self._processWordListChanges)
|
822
|
-
dialog.
|
823
|
+
dialog.exec()
|
823
824
|
return
|
824
825
|
|
825
826
|
@pyqtSlot()
|
@@ -831,7 +832,7 @@ class GuiMain(QMainWindow):
|
|
831
832
|
dialog.setModal(False)
|
832
833
|
dialog.show()
|
833
834
|
dialog.raise_()
|
834
|
-
|
835
|
+
QApplication.processEvents()
|
835
836
|
dialog.populateGUI()
|
836
837
|
return
|
837
838
|
|
@@ -842,7 +843,7 @@ class GuiMain(QMainWindow):
|
|
842
843
|
dialog.setModal(True)
|
843
844
|
dialog.show()
|
844
845
|
dialog.raise_()
|
845
|
-
|
846
|
+
QApplication.processEvents()
|
846
847
|
dialog.populateGUI()
|
847
848
|
return
|
848
849
|
|
@@ -860,7 +861,7 @@ class GuiMain(QMainWindow):
|
|
860
861
|
dialog.setModal(True)
|
861
862
|
dialog.show()
|
862
863
|
dialog.raise_()
|
863
|
-
|
864
|
+
QApplication.processEvents()
|
864
865
|
if not dialog.initDialog():
|
865
866
|
dialog.close()
|
866
867
|
SHARED.error(self.tr("Could not initialise the dialog."))
|
@@ -898,7 +899,8 @@ class GuiMain(QMainWindow):
|
|
898
899
|
CONFIG.setViewPanePos(self.splitView.sizes())
|
899
900
|
|
900
901
|
CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
|
901
|
-
|
902
|
+
wFull = Qt.WindowState.WindowFullScreen
|
903
|
+
if self.windowState() & wFull != wFull:
|
902
904
|
# Ignore window size if in full screen mode
|
903
905
|
CONFIG.setMainWinSize(self.width(), self.height())
|
904
906
|
|
@@ -908,7 +910,7 @@ class GuiMain(QMainWindow):
|
|
908
910
|
CONFIG.saveConfig()
|
909
911
|
self.reportConfErr()
|
910
912
|
|
911
|
-
|
913
|
+
QApplication.quit()
|
912
914
|
|
913
915
|
return True
|
914
916
|
|
@@ -935,14 +937,14 @@ class GuiMain(QMainWindow):
|
|
935
937
|
|
936
938
|
def toggleFullScreenMode(self) -> None:
|
937
939
|
"""Toggle full screen mode"""
|
938
|
-
self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
|
940
|
+
self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
|
939
941
|
return
|
940
942
|
|
941
943
|
##
|
942
944
|
# Events
|
943
945
|
##
|
944
946
|
|
945
|
-
def closeEvent(self, event: QCloseEvent):
|
947
|
+
def closeEvent(self, event: QCloseEvent) -> None:
|
946
948
|
"""Capture the closing event of the GUI and call the close
|
947
949
|
function to handle all the close process steps.
|
948
950
|
"""
|
@@ -1055,7 +1057,7 @@ class GuiMain(QMainWindow):
|
|
1055
1057
|
|
1056
1058
|
if theme:
|
1057
1059
|
# We are doing this manually instead of connecting to
|
1058
|
-
#
|
1060
|
+
# paletteChanged since the processing order matters
|
1059
1061
|
SHARED.theme.loadTheme()
|
1060
1062
|
self.docEditor.updateTheme()
|
1061
1063
|
self.docViewer.updateTheme()
|
@@ -1114,7 +1116,7 @@ class GuiMain(QMainWindow):
|
|
1114
1116
|
@pyqtSlot(Path)
|
1115
1117
|
def _openProjectFromWelcome(self, path: Path) -> None:
|
1116
1118
|
"""Handle an open project request from the welcome dialog."""
|
1117
|
-
|
1119
|
+
QApplication.processEvents()
|
1118
1120
|
self.openProject(path)
|
1119
1121
|
if not SHARED.hasProject:
|
1120
1122
|
self.showWelcomeDialog()
|
@@ -1167,7 +1169,9 @@ class GuiMain(QMainWindow):
|
|
1167
1169
|
elif view == nwView.SEARCH:
|
1168
1170
|
self.mainStack.setCurrentWidget(self.splitMain)
|
1169
1171
|
self.projStack.setCurrentWidget(self.projSearch)
|
1170
|
-
self.projSearch.beginSearch(
|
1172
|
+
self.projSearch.beginSearch(
|
1173
|
+
self.docEditor.getSelectedText() if self.docEditor.anyFocus() else ""
|
1174
|
+
)
|
1171
1175
|
elif view == nwView.OUTLINE:
|
1172
1176
|
self.mainStack.setCurrentWidget(self.outlineView)
|
1173
1177
|
return
|
@@ -1197,7 +1201,7 @@ class GuiMain(QMainWindow):
|
|
1197
1201
|
return
|
1198
1202
|
|
1199
1203
|
@pyqtSlot()
|
1200
|
-
def _toggleViewerPanelVisibility(self):
|
1204
|
+
def _toggleViewerPanelVisibility(self) -> None:
|
1201
1205
|
"""Toggle the visibility of the document viewer panel."""
|
1202
1206
|
CONFIG.showViewerPanel = not CONFIG.showViewerPanel
|
1203
1207
|
self.docViewerPanel.setVisible(CONFIG.showViewerPanel)
|
@@ -1209,7 +1213,7 @@ class GuiMain(QMainWindow):
|
|
1209
1213
|
if SHARED.hasProject:
|
1210
1214
|
currTime = time()
|
1211
1215
|
editIdle = currTime - self.docEditor.lastActive > CONFIG.userIdleTime
|
1212
|
-
userIdle =
|
1216
|
+
userIdle = QApplication.applicationState() != Qt.ApplicationState.ApplicationActive
|
1213
1217
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
1214
1218
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
1215
1219
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
@@ -1266,7 +1270,7 @@ class GuiMain(QMainWindow):
|
|
1266
1270
|
@pyqtSlot()
|
1267
1271
|
def _keyPressEscape(self) -> None:
|
1268
1272
|
"""Process an escape keypress in the main window."""
|
1269
|
-
if self.docEditor.
|
1273
|
+
if self.docEditor.searchVisible():
|
1270
1274
|
self.docEditor.closeSearch()
|
1271
1275
|
elif SHARED.focusMode:
|
1272
1276
|
SHARED.setFocusMode(False)
|
novelwriter/shared.py
CHANGED
@@ -26,21 +26,21 @@ from __future__ import annotations
|
|
26
26
|
|
27
27
|
import logging
|
28
28
|
|
29
|
+
from pathlib import Path
|
29
30
|
from time import time
|
30
31
|
from typing import TYPE_CHECKING, TypeVar
|
31
|
-
from pathlib import Path
|
32
32
|
|
33
33
|
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
|
34
34
|
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
|
35
|
-
from novelwriter.common import formatFileFilter
|
36
35
|
|
36
|
+
from novelwriter.common import formatFileFilter
|
37
37
|
from novelwriter.constants import nwFiles
|
38
38
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
39
39
|
|
40
40
|
if TYPE_CHECKING: # pragma: no cover
|
41
|
-
from novelwriter.guimain import GuiMain
|
42
|
-
from novelwriter.gui.theme import GuiTheme
|
43
41
|
from novelwriter.core.project import NWProject
|
42
|
+
from novelwriter.gui.theme import GuiTheme
|
43
|
+
from novelwriter.guimain import GuiMain
|
44
44
|
|
45
45
|
logger = logging.getLogger(__name__)
|
46
46
|
|
@@ -198,6 +198,7 @@ class SharedData(QObject):
|
|
198
198
|
|
199
199
|
def closeProject(self) -> None:
|
200
200
|
"""Close the current project."""
|
201
|
+
self._closeDialogs()
|
201
202
|
self.project.closeProject(self._idleTime)
|
202
203
|
self._resetProject()
|
203
204
|
self._resetIdleTimer()
|
@@ -292,7 +293,7 @@ class SharedData(QObject):
|
|
292
293
|
self._lastAlert = alert.logMessage
|
293
294
|
if log:
|
294
295
|
logger.info(self._lastAlert, stacklevel=2)
|
295
|
-
alert.
|
296
|
+
alert.exec()
|
296
297
|
alert.deleteLater()
|
297
298
|
return
|
298
299
|
|
@@ -304,7 +305,7 @@ class SharedData(QObject):
|
|
304
305
|
self._lastAlert = alert.logMessage
|
305
306
|
if log:
|
306
307
|
logger.warning(self._lastAlert, stacklevel=2)
|
307
|
-
alert.
|
308
|
+
alert.exec()
|
308
309
|
alert.deleteLater()
|
309
310
|
return
|
310
311
|
|
@@ -319,7 +320,7 @@ class SharedData(QObject):
|
|
319
320
|
self._lastAlert = alert.logMessage
|
320
321
|
if log:
|
321
322
|
logger.error(self._lastAlert, stacklevel=2)
|
322
|
-
alert.
|
323
|
+
alert.exec()
|
323
324
|
alert.deleteLater()
|
324
325
|
return
|
325
326
|
|
@@ -329,7 +330,7 @@ class SharedData(QObject):
|
|
329
330
|
alert.setMessage(text, info, details)
|
330
331
|
alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True)
|
331
332
|
self._lastAlert = alert.logMessage
|
332
|
-
alert.
|
333
|
+
alert.exec()
|
333
334
|
isYes = alert.result() == QMessageBox.StandardButton.Yes
|
334
335
|
alert.deleteLater()
|
335
336
|
return isYes
|
@@ -356,6 +357,17 @@ class SharedData(QObject):
|
|
356
357
|
self._idleTime = 0.0
|
357
358
|
return
|
358
359
|
|
360
|
+
def _closeDialogs(self) -> None:
|
361
|
+
"""Close non-modal dialogs."""
|
362
|
+
from novelwriter.tools.manuscript import GuiManuscript
|
363
|
+
from novelwriter.tools.writingstats import GuiWritingStats
|
364
|
+
|
365
|
+
for widget in self.mainGui.children():
|
366
|
+
if isinstance(widget, (GuiManuscript, GuiWritingStats)):
|
367
|
+
widget.close()
|
368
|
+
|
369
|
+
return
|
370
|
+
|
359
371
|
# END Class SharedData
|
360
372
|
|
361
373
|
|
@@ -404,7 +416,7 @@ class _GuiAlert(QMessageBox):
|
|
404
416
|
self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
405
417
|
else:
|
406
418
|
self.setStandardButtons(QMessageBox.Ok)
|
407
|
-
pSz = 2*self._theme.
|
419
|
+
pSz = 2*self._theme.baseIconHeight
|
408
420
|
if level == self.INFO:
|
409
421
|
self.setIconPixmap(self._theme.getPixmap("alert_info", (pSz, pSz)))
|
410
422
|
self.setWindowTitle(self.tr("Information"))
|
novelwriter/text/counting.py
CHANGED
@@ -57,6 +57,7 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
|
|
57
57
|
continue
|
58
58
|
if line[0] == ">":
|
59
59
|
line = line.lstrip(">").lstrip(" ")
|
60
|
+
if line: # Above block can return empty line (Issue #1816)
|
60
61
|
if line[-1] == "<":
|
61
62
|
line = line.rstrip("<").rstrip(" ")
|
62
63
|
if "[" in line:
|