Clean up after switching to Qt6 (#2187)

This commit is contained in:
Veronica Berglyd Olsen
2025-01-14 22:51:52 +01:00
committed by GitHub
54 changed files with 1280 additions and 1279 deletions
+16 -1
View File
@@ -38,7 +38,8 @@ from urllib.parse import urljoin
from urllib.request import pathname2url
from PyQt6.QtCore import QCoreApplication, QMimeData, QUrl
from PyQt6.QtGui import QColor, QDesktopServices, QFont, QFontDatabase, QFontInfo
from PyQt6.QtGui import QAction, QColor, QDesktopServices, QFont, QFontDatabase, QFontInfo
from PyQt6.QtWidgets import QMenu, QMenuBar, QWidget
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
@@ -461,6 +462,20 @@ def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable:
return wrapper
def qtAddAction(parent: QWidget, label: str) -> QAction:
"""Helper to add action to widget and always return the action."""
action = QAction(label, parent)
parent.addAction(action)
return action
def qtAddMenu(parent: QMenuBar | QMenu, label: str) -> QMenu:
"""Helper to add menu to menu and always return the menu."""
menu = QMenu(label, parent)
parent.addMenu(menu)
return menu
def encodeMimeHandles(mimeData: QMimeData, handles: list[str]) -> None:
"""Encode handles into a mime data object."""
mimeData.setData(nwConst.MIME_HANDLE, b"|".join(h.encode() for h in handles))
+51 -81
View File
@@ -52,6 +52,31 @@ logger = logging.getLogger(__name__)
class Config:
__slots__ = (
"_confPath", "_dataPath", "_homePath", "_backPath", "_appPath", "_appRoot", "_hasError",
"_errData", "_nwLangPath", "_qtLangPath", "_qLocale", "_dLocale", "_dShortDate",
"_dShortDateTime", "_qtTrans", "_recentProjects", "_recentPaths", "_backupPath",
"appName", "appHandle", "pdfDocs", "guiLocale", "guiTheme", "guiSyntax", "guiFont",
"hideVScroll", "hideHScroll", "lastNotes", "nativeFont", "iconTheme", "iconColTree",
"iconColDocs", "mainWinSize", "welcomeWinSize", "prefsWinSize", "mainPanePos",
"viewPanePos", "outlinePanePos", "autoSaveProj", "autoSaveDoc", "emphLabels",
"backupOnClose", "askBeforeBackup", "textFont", "textWidth", "textMargin", "tabWidth",
"focusWidth", "hideFocusFooter", "showFullPath", "autoSelect", "doJustify",
"showTabsNSpaces", "showLineEndings", "showMultiSpaces", "doReplace", "doReplaceSQuote",
"doReplaceDQuote", "doReplaceDash", "doReplaceDots", "autoScroll", "autoScrollPos",
"scrollPastEnd", "dialogStyle", "allowOpenDial", "dialogLine", "narratorBreak",
"narratorDialog", "altDialogOpen", "altDialogClose", "highlightEmph", "stopWhenIdle",
"userIdleTime", "incNotesWCount", "fmtApostrophe", "fmtSQuoteOpen", "fmtSQuoteClose",
"fmtDQuoteOpen", "fmtDQuoteClose", "fmtPadBefore", "fmtPadAfter", "fmtPadThin",
"spellLanguage", "showViewerPanel", "showEditToolBar", "showSessionTime", "viewComments",
"viewSynopsis", "searchCase", "searchWord", "searchRegEx", "searchLoop", "searchNextFile",
"searchMatchCap", "searchProjCase", "searchProjWord", "searchProjRegEx", "verQtString",
"verQtValue", "verPyQtString", "verPyQtValue", "verPyString", "osType", "osLinux",
"osWindows", "osDarwin", "osUnknown", "hostName", "kernelVer", "isDebug", "memInfo",
"hasEnchant",
)
LANG_NW = 1
LANG_PROJ = 2
@@ -126,12 +151,12 @@ class Config:
self.iconColDocs = False # Keep theme colours on documents
# Size Settings
self._mainWinSize = [1200, 650] # Last size of the main GUI window
self._welcomeSize = [800, 550] # Last size of the welcome window
self._prefsWinSize = [700, 615] # Last size of the Preferences dialog
self._mainPanePos = [300, 800] # Last position of the main window splitter
self._viewPanePos = [500, 150] # Last position of the document viewer splitter
self._outlnPanePos = [500, 150] # Last position of the outline panel splitter
self.mainWinSize = [1200, 650] # Last size of the main GUI window
self.welcomeWinSize = [800, 550] # Last size of the welcome window
self.prefsWinSize = [700, 615] # Last size of the Preferences dialog
self.mainPanePos = [300, 800] # Last position of the main window splitter
self.viewPanePos = [500, 150] # Last position of the document viewer splitter
self.outlinePanePos = [500, 150] # Last position of the outline panel splitter
# Project Settings
self.autoSaveProj = 60 # Interval for auto-saving project, in seconds
@@ -269,30 +294,6 @@ class Config:
def recentProjects(self) -> RecentProjects:
return self._recentProjects
@property
def mainWinSize(self) -> list[int]:
return self._mainWinSize
@property
def welcomeWinSize(self) -> list[int]:
return self._welcomeSize
@property
def preferencesWinSize(self) -> list[int]:
return self._prefsWinSize
@property
def mainPanePos(self) -> list[int]:
return self._mainPanePos
@property
def viewPanePos(self) -> list[int]:
return self._viewPanePos
@property
def outlinePanePos(self) -> list[int]:
return self._outlnPanePos
##
# Getters
##
@@ -300,17 +301,9 @@ class Config:
def getTextWidth(self, focusMode: bool = False) -> int:
"""Get the text with for the correct editor mode."""
if focusMode:
return self.pxInt(max(self.focusWidth, 200))
return max(self.focusWidth, 200)
else:
return self.pxInt(max(self.textWidth, 200))
def getTextMargin(self) -> int:
"""Get the scaled text margin."""
return self.pxInt(max(self.textMargin, 0))
def getTabWidth(self) -> int:
"""Get the scaled tab width."""
return self.pxInt(max(self.tabWidth, 0))
return max(self.textWidth, 200)
##
# Setters
@@ -322,35 +315,20 @@ class Config:
adjust it a bit, and we don't want the main window to shrink or
grow each time the app is opened.
"""
if abs(self._mainWinSize[0] - width) > 5:
self._mainWinSize[0] = width
if abs(self._mainWinSize[1] - height) > 5:
self._mainWinSize[1] = height
if abs(self.mainWinSize[0] - width) > 5:
self.mainWinSize[0] = width
if abs(self.mainWinSize[1] - height) > 5:
self.mainWinSize[1] = height
return
def setWelcomeWinSize(self, width: int, height: int) -> None:
"""Set the size of the Preferences dialog window."""
self._welcomeSize = [width, height]
self.welcomeWinSize = [width, height]
return
def setPreferencesWinSize(self, width: int, height: int) -> None:
"""Set the size of the Preferences dialog window."""
self._prefsWinSize = [width, height]
return
def setMainPanePos(self, pos: list[int]) -> None:
"""Set the position of the main GUI splitter."""
self._mainPanePos = pos
return
def setViewPanePos(self, pos: list[int]) -> None:
"""Set the position of the viewer meta data splitter."""
self._viewPanePos = pos
return
def setOutlinePanePos(self, pos: list[int]) -> None:
"""Set the position of the outline details splitter."""
self._outlnPanePos = pos
self.prefsWinSize = [width, height]
return
def setLastPath(self, key: str, path: str | Path) -> None:
@@ -421,14 +399,6 @@ class Config:
# Methods
##
def pxInt(self, value: int) -> int:
"""Deprecated. Do not use."""
return value
def rpxInt(self, value: int) -> int:
"""Deprecated. Do not use."""
return value
def homePath(self) -> Path:
"""The user's home folder."""
return self._homePath
@@ -614,12 +584,12 @@ class Config:
# Sizes
sec = "Sizes"
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
self._welcomeSize = conf.rdIntList(sec, "welcome", self._welcomeSize)
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
self.mainWinSize = conf.rdIntList(sec, "mainwindow", self.mainWinSize)
self.welcomeWinSize = conf.rdIntList(sec, "welcome", self.welcomeWinSize)
self.prefsWinSize = conf.rdIntList(sec, "preferences", self.prefsWinSize)
self.mainPanePos = conf.rdIntList(sec, "mainpane", self.mainPanePos)
self.viewPanePos = conf.rdIntList(sec, "viewpane", self.viewPanePos)
self.outlinePanePos = conf.rdIntList(sec, "outlinepane", self.outlinePanePos)
# Project
sec = "Project"
@@ -728,12 +698,12 @@ class Config:
}
conf["Sizes"] = {
"mainwindow": self._packList(self._mainWinSize),
"welcome": self._packList(self._welcomeSize),
"preferences": self._packList(self._prefsWinSize),
"mainpane": self._packList(self._mainPanePos),
"viewpane": self._packList(self._viewPanePos),
"outlinepane": self._packList(self._outlnPanePos),
"mainwindow": self._packList(self.mainWinSize),
"welcome": self._packList(self.welcomeWinSize),
"preferences": self._packList(self.prefsWinSize),
"mainpane": self._packList(self.mainPanePos),
"viewpane": self._packList(self.viewPanePos),
"outlinepane": self._packList(self.outlinePanePos),
}
conf["Project"] = {
+8 -14
View File
@@ -49,18 +49,13 @@ class GuiAbout(NDialog):
self.setObjectName("GuiAbout")
self.setWindowTitle(self.tr("About novelWriter"))
self.resize(CONFIG.pxInt(700), CONFIG.pxInt(500))
hA = CONFIG.pxInt(8)
hB = CONFIG.pxInt(16)
nwH = CONFIG.pxInt(36)
nwPx = CONFIG.pxInt(128)
self.resize(700, 500)
# Logo and Banner
self.nwImage = SHARED.theme.loadDecoration("nw-text", h=nwH)
self.nwImage = SHARED.theme.loadDecoration("nw-text", h=36)
self.nwLogo = QLabel(self)
self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (nwPx, nwPx)))
self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (128, 128)))
self.nwLabel = QLabel(self)
self.nwLabel.setPixmap(self.nwImage)
@@ -79,8 +74,7 @@ class GuiAbout(NDialog):
self.txtCredits = QTextBrowser(self)
self.txtCredits.setOpenExternalLinks(True)
self.txtCredits.document().setDocumentMargin(0)
self.txtCredits.setViewportMargins(0, hA, hA, 0)
self.txtCredits.setViewportMargins(0, 8, 8, 0)
# Buttons
self.btnBox = QDialogButtonBox(QtDialogClose, self)
@@ -88,15 +82,15 @@ class GuiAbout(NDialog):
# Assemble
self.innerBox = QVBoxLayout()
self.innerBox.addSpacing(hB)
self.innerBox.addSpacing(16)
self.innerBox.addWidget(self.nwLabel)
self.innerBox.addWidget(self.nwInfo)
self.innerBox.addSpacing(hA)
self.innerBox.addSpacing(8)
self.innerBox.addWidget(self.nwLicence)
self.innerBox.addSpacing(hA)
self.innerBox.addSpacing(8)
self.innerBox.addWidget(self.lblCredits)
self.innerBox.addWidget(self.txtCredits)
self.innerBox.addSpacing(hB)
self.innerBox.addSpacing(16)
self.innerBox.addWidget(self.btnBox)
self.outerBox = QHBoxLayout()
+9 -11
View File
@@ -32,7 +32,7 @@ from PyQt6.QtWidgets import (
QListWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.switch import NSwitch
@@ -63,14 +63,11 @@ class GuiDocMerge(NDialog):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
hSp = CONFIG.pxInt(12)
vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12)
self.listBox = QListWidget(self)
self.listBox.setIconSize(iSz)
self.listBox.setMinimumWidth(CONFIG.pxInt(400))
self.listBox.setMinimumHeight(CONFIG.pxInt(180))
self.listBox.setMinimumWidth(400)
self.listBox.setMinimumHeight(180)
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
@@ -82,7 +79,7 @@ class GuiDocMerge(NDialog):
self.optBox = QGridLayout()
self.optBox.addWidget(self.trashLabel, 0, 0)
self.optBox.addWidget(self.trashSwitch, 0, 1)
self.optBox.setHorizontalSpacing(hSp)
self.optBox.setHorizontalSpacing(12)
self.optBox.setColumnStretch(2, 1)
# Buttons
@@ -91,18 +88,19 @@ class GuiDocMerge(NDialog):
self.buttonBox.rejected.connect(self.reject)
self.resetButton = self.buttonBox.addButton(QtDialogReset)
self.resetButton.clicked.connect(self._resetList)
if self.resetButton:
self.resetButton.clicked.connect(self._resetList)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(0)
self.outerBox.addWidget(self.headLabel)
self.outerBox.addWidget(self.helpLabel)
self.outerBox.addSpacing(vSp)
self.outerBox.addSpacing(8)
self.outerBox.addWidget(self.listBox)
self.outerBox.addSpacing(vSp)
self.outerBox.addSpacing(8)
self.outerBox.addLayout(self.optBox)
self.outerBox.addSpacing(bSp)
self.outerBox.addSpacing(12)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
+9 -12
View File
@@ -32,7 +32,7 @@ from PyQt6.QtWidgets import (
QListWidget, QListWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.switch import NSwitch
@@ -67,9 +67,6 @@ class GuiDocSplit(NDialog):
# Values
iPx = SHARED.theme.baseIconHeight
hSp = CONFIG.pxInt(12)
vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12)
pOptions = SHARED.project.options
spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3)
@@ -79,11 +76,11 @@ class GuiDocSplit(NDialog):
# Heading Selection
self.listBox = QListWidget(self)
self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.NoDragDrop)
self.listBox.setMinimumWidth(CONFIG.pxInt(400))
self.listBox.setMinimumHeight(CONFIG.pxInt(180))
self.listBox.setMinimumWidth(400)
self.listBox.setMinimumHeight(180)
self.splitLevel = QComboBox(self)
self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
self.splitLevel.addItem(self.tr("Split up to Heading Level 2 (Chapter)"), 2)
self.splitLevel.addItem(self.tr("Split up to Heading Level 3 (Scene)"), 3)
self.splitLevel.addItem(self.tr("Split up to Heading Level 4 (Section)"), 4)
@@ -111,8 +108,8 @@ class GuiDocSplit(NDialog):
self.optBox.addWidget(self.hierarchySwitch, 1, 1)
self.optBox.addWidget(self.trashLabel, 2, 0)
self.optBox.addWidget(self.trashSwitch, 2, 1)
self.optBox.setVerticalSpacing(vSp)
self.optBox.setHorizontalSpacing(hSp)
self.optBox.setVerticalSpacing(8)
self.optBox.setHorizontalSpacing(12)
self.optBox.setColumnStretch(3, 1)
# Buttons
@@ -125,12 +122,12 @@ class GuiDocSplit(NDialog):
self.outerBox.setSpacing(0)
self.outerBox.addWidget(self.headLabel)
self.outerBox.addWidget(self.helpLabel)
self.outerBox.addSpacing(vSp)
self.outerBox.addSpacing(8)
self.outerBox.addWidget(self.listBox)
self.outerBox.addWidget(self.splitLevel)
self.outerBox.addSpacing(vSp)
self.outerBox.addSpacing(8)
self.outerBox.addLayout(self.optBox)
self.outerBox.addSpacing(bSp)
self.outerBox.addSpacing(12)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
+3 -7
View File
@@ -27,7 +27,6 @@ import logging
from PyQt6.QtWidgets import QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout, QWidget
from novelwriter import CONFIG
from novelwriter.extensions.modified import NDialog
from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk
@@ -43,12 +42,9 @@ class GuiEditLabel(NDialog):
self.setObjectName("GuiEditLabel")
self.setWindowTitle(self.tr("Item Label"))
mVd = CONFIG.pxInt(220)
mSp = CONFIG.pxInt(12)
# Item Label
self.labelValue = QLineEdit(self)
self.labelValue.setMinimumWidth(mVd)
self.labelValue.setMinimumWidth(220)
self.labelValue.setMaxLength(200)
self.labelValue.setText(text)
self.labelValue.selectAll()
@@ -62,10 +58,10 @@ class GuiEditLabel(NDialog):
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(QLabel(self.tr("Label"), self), 0)
self.innerBox.addWidget(self.labelValue, 1)
self.innerBox.setSpacing(mSp)
self.innerBox.setSpacing(12)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(mSp)
self.outerBox.setSpacing(12)
self.outerBox.addLayout(self.innerBox, 1)
self.outerBox.addWidget(self.buttonBox, 0)
+20 -24
View File
@@ -27,7 +27,7 @@ from __future__ import annotations
import logging
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QCloseEvent, QKeyEvent, QKeySequence
from PyQt6.QtGui import QAction, QCloseEvent, QKeyEvent, QKeySequence
from PyQt6.QtWidgets import (
QCompleter, QDialogButtonBox, QFileDialog, QHBoxLayout, QLineEdit,
QPushButton, QVBoxLayout, QWidget
@@ -58,23 +58,23 @@ class GuiPreferences(NDialog):
logger.debug("Create: GuiPreferences")
self.setObjectName("GuiPreferences")
self.setWindowTitle(self.tr("Preferences"))
self.setMinimumSize(CONFIG.pxInt(600), CONFIG.pxInt(500))
self.resize(*CONFIG.preferencesWinSize)
self.setMinimumSize(600, 500)
self.resize(*CONFIG.prefsWinSize)
# Title
self.titleLabel = NColourLabel(
self.tr("Preferences"), self, color=SHARED.theme.helpText,
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
scale=NColourLabel.HEADER_SCALE, indent=4,
)
# Search Box
self.searchAction = QAction(SHARED.theme.getIcon("search"), "")
self.searchAction.triggered.connect(self._gotoSearch)
self.searchText = QLineEdit(self)
self.searchText.setPlaceholderText(self.tr("Search"))
self.searchText.setMinimumWidth(CONFIG.pxInt(200))
self.searchAction = self.searchText.addAction(
SHARED.theme.getIcon("search"), QLineEdit.ActionPosition.TrailingPosition
)
self.searchAction.triggered.connect(self._gotoSearch)
self.searchText.setMinimumWidth(200)
self.searchText.addAction(self.searchAction, QLineEdit.ActionPosition.TrailingPosition)
# SideBar
self.sidebar = NPagedSideBar(self)
@@ -105,7 +105,7 @@ class GuiPreferences(NDialog):
self.outerBox.addLayout(self.searchBox)
self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(8))
self.outerBox.setSpacing(8)
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
@@ -134,8 +134,6 @@ class GuiPreferences(NDialog):
section = 0
iSz = SHARED.theme.baseIconSize
boxFixed = 6*SHARED.theme.textNWidth
minWidth = CONFIG.pxInt(200)
fontWidth = CONFIG.pxInt(162)
# Temporary Variables
self._guiFont = CONFIG.guiFont
@@ -154,7 +152,7 @@ class GuiPreferences(NDialog):
# Display Language
self.guiLocale = NComboBox(self)
self.guiLocale.setMinimumWidth(minWidth)
self.guiLocale.setMinimumWidth(200)
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
self.guiLocale.addItem(name, lang)
self.guiLocale.setCurrentData(CONFIG.guiLocale, "en_GB")
@@ -166,7 +164,7 @@ class GuiPreferences(NDialog):
# Colour Theme
self.guiTheme = NComboBox(self)
self.guiTheme.setMinimumWidth(minWidth)
self.guiTheme.setMinimumWidth(200)
for theme, name in SHARED.theme.listThemes():
self.guiTheme.addItem(name, theme)
self.guiTheme.setCurrentData(CONFIG.guiTheme, "default")
@@ -178,7 +176,7 @@ class GuiPreferences(NDialog):
# Icon Theme
self.iconTheme = NComboBox(self)
self.iconTheme.setMinimumWidth(minWidth)
self.iconTheme.setMinimumWidth(200)
for theme, name in SHARED.theme.iconCache.listThemes():
self.iconTheme.addItem(name, theme)
self.iconTheme.setCurrentData(CONFIG.iconTheme, "material_rounded_bold")
@@ -190,7 +188,7 @@ class GuiPreferences(NDialog):
# Tree Icon Colours
self.iconColTree = NComboBox(self)
self.iconColTree.setMinimumWidth(minWidth)
self.iconColTree.setMinimumWidth(200)
for key, label in nwLabels.THEME_COLORS.items():
self.iconColTree.addItem(trConst(label), key)
self.iconColTree.setCurrentData(CONFIG.iconColTree, "theme")
@@ -211,7 +209,7 @@ class GuiPreferences(NDialog):
# Application Font Family
self.guiFont = QLineEdit(self)
self.guiFont.setReadOnly(True)
self.guiFont.setMinimumWidth(fontWidth)
self.guiFont.setMinimumWidth(162)
self.guiFont.setText(describeFont(self._guiFont))
self.guiFont.setCursorPosition(0)
self.guiFontButton = NIconToolButton(self, iSz, "font")
@@ -256,7 +254,7 @@ class GuiPreferences(NDialog):
# Document Colour Theme
self.guiSyntax = NComboBox(self)
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
self.guiSyntax.setMinimumWidth(200)
for syntax, name in SHARED.theme.listSyntax():
self.guiSyntax.addItem(name, syntax)
self.guiSyntax.setCurrentData(CONFIG.guiSyntax, "default_light")
@@ -269,7 +267,7 @@ class GuiPreferences(NDialog):
# Document Font Family
self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True)
self.textFont.setMinimumWidth(fontWidth)
self.textFont.setMinimumWidth(162)
self.textFont.setText(describeFont(CONFIG.textFont))
self.textFont.setCursorPosition(0)
self.textFontButton = NIconToolButton(self, iSz, "font")
@@ -482,7 +480,7 @@ class GuiPreferences(NDialog):
# Spell Checking
self.spellLanguage = NComboBox(self)
self.spellLanguage.setMinimumWidth(minWidth)
self.spellLanguage.setMinimumWidth(200)
if CONFIG.hasEnchant:
for tag, language in SHARED.spelling.listDictionaries():
@@ -651,8 +649,6 @@ class GuiPreferences(NDialog):
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
boxWidth = CONFIG.pxInt(150)
# Auto-Replace as You Type Main Switch
self.doReplace = NSwitch(self)
self.doReplace.setChecked(CONFIG.doReplace)
@@ -701,7 +697,7 @@ class GuiPreferences(NDialog):
# Pad Before
self.fmtPadBefore = QLineEdit(self)
self.fmtPadBefore.setMaxLength(32)
self.fmtPadBefore.setMinimumWidth(boxWidth)
self.fmtPadBefore.setMinimumWidth(150)
self.fmtPadBefore.setText(CONFIG.fmtPadBefore)
self.mainForm.addRow(
self.tr("Insert non-breaking space before"), self.fmtPadBefore,
@@ -711,7 +707,7 @@ class GuiPreferences(NDialog):
# Pad After
self.fmtPadAfter = QLineEdit(self)
self.fmtPadAfter.setMaxLength(32)
self.fmtPadAfter.setMinimumWidth(boxWidth)
self.fmtPadAfter.setMinimumWidth(150)
self.fmtPadAfter.setText(CONFIG.fmtPadAfter)
self.mainForm.addRow(
self.tr("Insert non-breaking space after"), self.fmtPadAfter,
+26 -33
View File
@@ -30,7 +30,7 @@ import logging
from pathlib import Path
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QCloseEvent, QColor
from PyQt6.QtGui import QAction, QCloseEvent, QColor
from PyQt6.QtWidgets import (
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
QFileDialog, QGridLayout, QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
@@ -71,16 +71,16 @@ class GuiProjectSettings(NDialog):
self.setWindowTitle(self.tr("Project Settings"))
options = SHARED.project.options
self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400))
self.setMinimumSize(500, 400)
self.resize(
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winWidth", CONFIG.pxInt(650))),
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winHeight", CONFIG.pxInt(500)))
options.getInt("GuiProjectSettings", "winWidth", 650),
options.getInt("GuiProjectSettings", "winHeight", 500),
)
# Title
self.titleLabel = NColourLabel(
self.tr("Project Settings"), self, color=SHARED.theme.helpText,
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
scale=NColourLabel.HEADER_SCALE, indent=4,
)
# SideBar
@@ -125,7 +125,7 @@ class GuiProjectSettings(NDialog):
self.outerBox.addLayout(self.topBox)
self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(8))
self.outerBox.setSpacing(8)
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
@@ -210,16 +210,14 @@ class GuiProjectSettings(NDialog):
def _saveSettings(self) -> None:
"""Save GUI settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
statusColW = CONFIG.rpxInt(self.statusPage.columnWidth())
importColW = CONFIG.rpxInt(self.importPage.columnWidth())
replaceColW = CONFIG.rpxInt(self.replacePage.columnWidth())
statusColW = self.statusPage.columnWidth()
importColW = self.importPage.columnWidth()
replaceColW = self.replacePage.columnWidth()
logger.debug("Saving State: GuiProjectSettings")
options = SHARED.project.options
options.setValue("GuiProjectSettings", "winWidth", winWidth)
options.setValue("GuiProjectSettings", "winHeight", winHeight)
options.setValue("GuiProjectSettings", "winWidth", self.width())
options.setValue("GuiProjectSettings", "winHeight", self.height())
options.setValue("GuiProjectSettings", "statusColW", statusColW)
options.setValue("GuiProjectSettings", "importColW", importColW)
options.setValue("GuiProjectSettings", "replaceColW", replaceColW)
@@ -232,7 +230,6 @@ class _SettingsPage(NScrollableForm):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
xW = CONFIG.pxInt(200)
data = SHARED.project.data
self.setHelpTextStyle(SHARED.theme.helpText)
self.setRowIndent(0)
@@ -240,7 +237,7 @@ class _SettingsPage(NScrollableForm):
# Project Name
self.projName = QLineEdit(self)
self.projName.setMaxLength(200)
self.projName.setMinimumWidth(xW)
self.projName.setMinimumWidth(200)
self.projName.setText(data.name)
self.addRow(
self.tr("Project name"), self.projName,
@@ -251,7 +248,7 @@ class _SettingsPage(NScrollableForm):
# Project Author
self.projAuthor = QLineEdit(self)
self.projAuthor.setMaxLength(200)
self.projAuthor.setMinimumWidth(xW)
self.projAuthor.setMinimumWidth(200)
self.projAuthor.setText(data.author)
self.addRow(
self.tr("Author(s)"), self.projAuthor,
@@ -262,7 +259,7 @@ class _SettingsPage(NScrollableForm):
# Project Language
projLang = data.language or CONFIG.guiLocale
self.projLang = NComboBox(self)
self.projLang.setMinimumWidth(xW)
self.projLang.setMinimumWidth(200)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag)
self.projLang.setCurrentData(projLang, projLang)
@@ -274,7 +271,7 @@ class _SettingsPage(NScrollableForm):
# Spell Check Language
self.spellLang = NComboBox(self)
self.spellLang.setMinimumWidth(xW)
self.spellLang.setMinimumWidth(200)
self.spellLang.addItem(self.tr("Default"), "None")
if CONFIG.hasEnchant:
for tag, language in SHARED.spelling.listDictionaries():
@@ -323,9 +320,7 @@ class _StatusPage(NFixedPage):
pageLabel = self.tr("Project Note Importance Levels")
colSetting = "importColW"
wCol0 = CONFIG.pxInt(
SHARED.project.options.getInt("GuiProjectSettings", colSetting, 130)
)
wCol0 = SHARED.project.options.getInt("GuiProjectSettings", colSetting, 130)
self._changed = False
self._color = QColor(100, 100, 100)
@@ -334,7 +329,6 @@ class _StatusPage(NFixedPage):
self._iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
bPd = CONFIG.pxInt(4)
iColor = self.palette().text().color()
@@ -395,7 +389,7 @@ class _StatusPage(NFixedPage):
self.labelText.textEdited.connect(self._onNameEdit)
buttonStyle = (
f"QToolButton {{padding: 0 {bPd}px;}} "
"QToolButton {padding: 0 4px;} "
"QToolButton::menu-indicator {image: none;}"
)
@@ -406,12 +400,14 @@ class _StatusPage(NFixedPage):
self.colorButton.setEnabled(False)
self.colorButton.clicked.connect(self._onColourSelect)
def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
for shape, label in items.items():
icon = NWStatus.createIcon(self._iPx, iColor, shape)
action = menu.addAction(icon, trConst(label))
action.triggered.connect(qtLambda(self._selectShape, shape))
self._icons[shape] = icon
def buildMenu(menu: QMenu | None, items: dict[nwStatusShape, str]) -> None:
if menu is not None:
for shape, label in items.items():
icon = NWStatus.createIcon(self._iPx, iColor, shape)
action = QAction(icon, trConst(label))
action.triggered.connect(qtLambda(self._selectShape, shape))
menu.addAction(action)
self._icons[shape] = icon
self.shapeMenu = QMenu(self)
buildMenu(self.shapeMenu, nwLabels.SHAPES_PLAIN)
@@ -676,10 +672,7 @@ class _ReplacePage(NFixedPage):
self._changed = False
iSz = SHARED.theme.baseIconSize
wCol0 = CONFIG.pxInt(
SHARED.project.options.getInt("GuiProjectSettings", "replaceColW", 130)
)
wCol0 = SHARED.project.options.getInt("GuiProjectSettings", "replaceColW", 130)
# Title
self.pageTitle = NColourLabel(
+5 -10
View File
@@ -32,7 +32,6 @@ from PyQt6.QtWidgets import (
QListWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG
from novelwriter.constants import nwQuotes, trConst
from novelwriter.extensions.modified import NDialog
from novelwriter.types import (
@@ -62,18 +61,14 @@ class GuiQuoteSelect(NDialog):
self._selected = current
qMetrics = QFontMetrics(self.font())
pxW = 7*qMetrics.boundingRectChar("M").width()
pxH = 7*qMetrics.boundingRectChar("M").height()
pxH = 7*qMetrics.boundingRectChar("M").height()
lblFont = self.font()
lblFont.setPointSizeF(4*lblFont.pointSizeF())
metrics = QFontMetrics(self.font())
# Preview Label
self.previewLabel = QLabel(current, self)
self.previewLabel.setFont(lblFont)
self.previewLabel.setFixedSize(QSize(pxW, pxH))
self.previewLabel.setFixedSize(QSize(4*metrics.maxWidth(), 5*metrics.height()))
self.previewLabel.setAlignment(QtAlignCenter)
self.previewLabel.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Plain)
@@ -84,15 +79,15 @@ class GuiQuoteSelect(NDialog):
minSize = 100
for sKey, sLabel in nwQuotes.SYMBOLS.items():
text = "[ %s ] %s" % (sKey, trConst(sLabel))
minSize = max(minSize, qMetrics.boundingRect(text).width())
minSize = max(minSize, metrics.boundingRect(text).width())
qtItem = QListWidgetItem(text)
qtItem.setData(self.D_KEY, sKey)
self.listBox.addItem(qtItem)
if sKey == current:
self.listBox.setCurrentItem(qtItem)
self.listBox.setMinimumWidth(minSize + CONFIG.pxInt(40))
self.listBox.setMinimumHeight(CONFIG.pxInt(150))
self.listBox.setMinimumWidth(minSize + 40)
self.listBox.setMinimumHeight(150)
# Buttons
self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
+8 -15
View File
@@ -56,15 +56,12 @@ class GuiWordList(NDialog):
self.setWindowTitle(self.tr("Project Word List"))
iSz = SHARED.theme.baseIconSize
mS = CONFIG.pxInt(250)
wW = CONFIG.pxInt(320)
wH = CONFIG.pxInt(340)
self.setMinimumWidth(mS)
self.setMinimumHeight(mS)
self.setMinimumWidth(250)
self.setMinimumHeight(250)
self.resize(
CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winWidth", wW)),
CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winHeight", wH))
SHARED.project.options.getInt("GuiWordList", "winWidth", 320),
SHARED.project.options.getInt("GuiWordList", "winHeight", 340),
)
# Header
@@ -118,9 +115,9 @@ class GuiWordList(NDialog):
self.outerBox.addLayout(self.headerBox, 0)
self.outerBox.addWidget(self.listBox, 1)
self.outerBox.addLayout(self.editBox, 0)
self.outerBox.addSpacing(CONFIG.pxInt(12))
self.outerBox.addSpacing(12)
self.outerBox.addWidget(self.buttonBox, 0)
self.outerBox.setSpacing(CONFIG.pxInt(4))
self.outerBox.setSpacing(4)
self.setLayout(self.outerBox)
@@ -230,14 +227,10 @@ class GuiWordList(NDialog):
def _saveGuiSettings(self) -> None:
"""Save GUI settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
logger.debug("Saving State: GuiWordList")
pOptions = SHARED.project.options
pOptions.setValue("GuiWordList", "winWidth", winWidth)
pOptions.setValue("GuiWordList", "winHeight", winHeight)
pOptions.setValue("GuiWordList", "winWidth", self.width())
pOptions.setValue("GuiWordList", "winHeight", self.height())
return
def _addWord(self, word: str) -> None:
+5 -5
View File
@@ -65,11 +65,11 @@ class NWErrorMessage(QDialog):
# Widgets
self.msgIcon = QLabel()
self.msgIcon.setPixmap(
QApplication.style().standardIcon(
QStyle.StandardPixmap.SP_MessageBoxCritical
).pixmap(64, 64)
)
if style := QApplication.style():
self.msgIcon.setPixmap(
style.standardIcon(QStyle.StandardPixmap.SP_MessageBoxCritical).pixmap(64, 64)
)
self.msgHead = QLabel()
self.msgHead.setOpenExternalLinks(True)
self.msgHead.setWordWrap(True)
+13 -13
View File
@@ -33,7 +33,6 @@ from PyQt6.QtWidgets import (
QVBoxLayout, QWidget
)
from novelwriter import CONFIG
from novelwriter.types import QtScrollAsNeeded
DEFAULT_SCALE = 0.9
@@ -99,14 +98,14 @@ class NScrollableForm(QScrollArea):
self._helpCol = QColor(0, 0, 0)
self._fontScale = DEFAULT_SCALE
self._first = True
self._indent = CONFIG.pxInt(12)
self._indent = 12
self._sections: dict[int, QLabel] = {}
self._editable: dict[str, NColourLabel] = {}
self._index: dict[str, QWidget] = {}
self._layout = QVBoxLayout()
self._layout.setSpacing(CONFIG.pxInt(12))
self._layout.setSpacing(12)
self._widget = QWidget(self)
self._widget.setLayout(self._layout)
@@ -156,24 +155,25 @@ class NScrollableForm(QScrollArea):
def scrollToSection(self, identifier: int) -> None:
"""Scroll to the requested section identifier."""
if identifier in self._sections:
yPos = self._sections[identifier].pos().y() - CONFIG.pxInt(8)
self.verticalScrollBar().setValue(yPos)
yPos = self._sections[identifier].pos().y() - 8
if vBar := self.verticalScrollBar():
vBar.setValue(yPos)
return
def scrollToLabel(self, label: str) -> None:
"""Scroll to the requested label."""
if label in self._index:
yPos = self._index[label].pos().y() - CONFIG.pxInt(8)
self.verticalScrollBar().setValue(yPos)
yPos = self._index[label].pos().y() - 8
if vBar := self.verticalScrollBar():
vBar.setValue(yPos)
return
def addGroupLabel(self, label: str, identifier: int | None = None) -> None:
"""Add a text label to separate groups of settings."""
hM = CONFIG.pxInt(4)
qLabel = QLabel(f"<b>{label}</b>", self)
qLabel.setContentsMargins(0, hM, 0, hM)
qLabel.setContentsMargins(0, 4, 0, 4)
if not self._first:
self._layout.addSpacing(5*hM)
self._layout.addSpacing(20)
self._layout.addWidget(qLabel)
self._first = False
if identifier is not None:
@@ -192,7 +192,7 @@ class NScrollableForm(QScrollArea):
) -> None:
"""Add a label and a widget as a new row of the form."""
row = QHBoxLayout()
row.setSpacing(CONFIG.pxInt(12))
row.setSpacing(12)
if isinstance(widget, list):
wBox = QHBoxLayout()
@@ -205,7 +205,7 @@ class NScrollableForm(QScrollArea):
icon.setPixmap(item)
wBox.addWidget(icon)
elif isinstance(item, int):
wBox.addSpacing(CONFIG.pxInt(item))
wBox.addSpacing(item)
qWidget = QWidget(self)
qWidget.setLayout(wBox)
else:
@@ -252,7 +252,7 @@ class NScrollableForm(QScrollArea):
def finalise(self) -> None:
"""Finalise the layout when the form is built."""
self._layout.addSpacing(CONFIG.pxInt(20))
self._layout.addSpacing(20)
self._layout.addStretch(1)
return
+36 -46
View File
@@ -26,10 +26,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from PyQt6.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QAction, QColor, QPainter, QPaintEvent, QPolygon
from PyQt6.QtGui import QColor, QPainter, QPaintEvent, QPolygon
from PyQt6.QtWidgets import (
QAbstractButton, QButtonGroup, QLabel, QStyle, QStyleOptionToolButton,
QToolBar, QToolButton, QWidget
QAbstractButton, QButtonGroup, QLabel, QStyleOptionToolButton, QToolBar,
QToolButton, QWidget
)
from novelwriter.types import (
@@ -83,21 +83,19 @@ class NPagedSideBar(QToolBar):
self.insertWidget(self._stretchAction, label)
return
def addButton(self, text: str, buttonId: int = -1) -> QAction:
def addButton(self, text: str, buttonId: int = -1) -> None:
"""Add a new button to the toolbar."""
button = _PagedToolButton(self)
button.setText(text)
action = self.insertWidget(self._stretchAction, button)
self.insertWidget(self._stretchAction, button)
self._group.addButton(button, id=buttonId)
self._buttons[buttonId] = button
return action
return
def setSelected(self, buttonId: int) -> None:
"""Set the selected button."""
self._group.button(buttonId).setChecked(True)
if button := self._group.button(buttonId):
button.setChecked(True)
return
##
@@ -115,7 +113,7 @@ class NPagedSideBar(QToolBar):
class _PagedToolButton(QToolButton):
__slots__ = ("_bH", "_tM", "_lM", "_cR", "_aH")
__slots__ = ("_bH", "_tM", "_aH")
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
@@ -126,8 +124,6 @@ class _PagedToolButton(QToolButton):
fH = self.fontMetrics().height()
self._bH = round(fH * 1.7)
self._tM = (self._bH - fH)//2
self._lM = 3*self.style().pixelMetric(QStyle.PixelMetric.PM_ButtonMargin)//2
self._cR = self._lM//2
self._aH = 2*fH//7
self.setFixedHeight(self._bH)
@@ -145,53 +141,51 @@ class _PagedToolButton(QToolButton):
opt = QStyleOptionToolButton()
opt.initFrom(self)
paint = QPainter(self)
paint.setRenderHint(QtPaintAntiAlias, True)
paint.setPen(QtNoPen)
paint.setBrush(QtNoBrush)
painter = QPainter(self)
painter.setRenderHint(QtPaintAntiAlias, True)
painter.setPen(QtNoPen)
painter.setBrush(QtNoBrush)
width = self.width()
height = self.height()
palette = self.palette()
if opt.state & QtMouseOver == QtMouseOver: # pragma: no cover
backCol = palette.base()
paint.setBrush(backCol)
paint.setOpacity(0.75)
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
painter.setBrush(palette.light())
painter.drawRoundedRect(0, 0, width, height, 4, 4)
if self.isChecked():
backCol = palette.highlight()
paint.setBrush(backCol)
paint.setOpacity(0.35)
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
painter.setBrush(palette.highlight())
painter.setOpacity(0.35)
painter.drawRoundedRect(0, 0, width, height, 4, 4)
textCol = palette.highlightedText().color()
else:
textCol = palette.text().color()
tW = width - 2*self._lM
tW = width - 24
tH = height - 2*self._tM
paint.setPen(textCol)
paint.setOpacity(1.0)
paint.drawText(QRectF(self._lM, self._tM, tW, tH), QtAlignLeft, self.text())
painter.setPen(textCol)
painter.setOpacity(1.0)
painter.drawText(QRectF(12, self._tM, tW, tH), QtAlignLeft, self.text())
tC = self.height()//2
tW = self.width() - self._aH - self._lM
tW = self.width() - self._aH - 12
if self.isChecked():
paint.setBrush(textCol)
paint.drawPolygon(QPolygon([
painter.setBrush(textCol)
painter.drawPolygon(QPolygon([
QPoint(tW, tC - self._aH),
QPoint(tW + self._aH, tC),
QPoint(tW, tC + self._aH),
]))
painter.end()
return
class _NPagedToolLabel(QLabel):
__slots__ = ("_bH", "_tM", "_lM", "_textCol")
__slots__ = ("_bH", "_tM", "_textCol")
def __init__(self, parent: QWidget, textColor: QColor | None = None) -> None:
super().__init__(parent=parent)
@@ -201,7 +195,6 @@ class _NPagedToolLabel(QLabel):
fH = self.fontMetrics().height()
self._bH = round(fH * 1.7)
self._tM = (self._bH - fH)//2
self._lM = self.style().pixelMetric(QStyle.PixelMetric.PM_ButtonMargin)//2
self.setFixedHeight(self._bH)
self._textCol = textColor or self.palette().text().color()
@@ -212,18 +205,15 @@ class _NPagedToolLabel(QLabel):
"""Overload the paint event to draw a simple, left aligned text
label that matches the button style.
"""
paint = QPainter(self)
paint.setRenderHint(QtPaintAntiAlias, True)
paint.setPen(QtNoPen)
tW = self.width() - 8
tH = self.height() - 2*self._tM
width = self.width()
height = self.height()
tW = width - 2*self._lM
tH = height - 2*self._tM
paint.setPen(self._textCol)
paint.setOpacity(1.0)
paint.drawText(QRectF(self._lM, self._tM, tW, tH), QtAlignLeft, self.text())
painter = QPainter(self)
painter.setRenderHint(QtPaintAntiAlias, True)
painter.setPen(QtNoPen)
painter.setPen(self._textCol)
painter.setOpacity(1.0)
painter.drawText(QRectF(4, self._tM, tW, tH), QtAlignLeft, self.text())
painter.end()
return
+4 -11
View File
@@ -28,7 +28,6 @@ import logging
from PyQt6.QtGui import QColor, QPainter, QPaintEvent
from PyQt6.QtWidgets import QAbstractButton, QWidget
from novelwriter import CONFIG
from novelwriter.enum import nwTrinary
from novelwriter.types import QtBlack, QtPaintAntiAlias
@@ -37,9 +36,7 @@ logger = logging.getLogger(__name__)
class StatusLED(QAbstractButton):
__slots__ = (
"_neutral", "_postitve", "_negative", "_color", "_state", "_bPx"
)
__slots__ = ("_neutral", "_postitve", "_negative", "_color", "_state")
def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)
@@ -48,7 +45,6 @@ class StatusLED(QAbstractButton):
self._negative = QtBlack
self._color = QtBlack
self._state = nwTrinary.NEUTRAL
self._bPx = CONFIG.pxInt(1)
self.setFixedWidth(sW)
self.setFixedHeight(sH)
return
@@ -82,12 +78,9 @@ class StatusLED(QAbstractButton):
"""Draw the LED."""
painter = QPainter(self)
painter.setRenderHint(QtPaintAntiAlias, True)
painter.setPen(self.palette().windowText().color())
painter.setPen(self.palette().text().color())
painter.setBrush(self._color)
painter.setOpacity(1.0)
painter.drawEllipse(
self._bPx, self._bPx,
self.width() - 2*self._bPx,
self.height() - 2*self._bPx
)
painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
painter.end()
return
+7 -8
View File
@@ -27,13 +27,13 @@ from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty
from PyQt6.QtGui import QEnterEvent, QMouseEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt6.QtWidgets import QAbstractButton, QWidget
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixed
class NSwitch(QAbstractButton):
__slots__ = ("_xW", "_xH", "_xR", "_rB", "_rH", "_rR", "_offset")
__slots__ = ("_xW", "_xH", "_xR", "_rH", "_rR", "_offset")
def __init__(self, parent: QWidget, height: int = 0) -> None:
super().__init__(parent=parent)
@@ -41,9 +41,8 @@ class NSwitch(QAbstractButton):
self._xH = height or SHARED.theme.baseButtonHeight
self._xW = 2*self._xH
self._xR = int(self._xH*0.5)
self._rB = CONFIG.pxInt(2)
self._rH = self._xH - 2*self._rB
self._rR = self._xR - self._rB
self._rH = self._xH - 4
self._rR = self._xR - 2
self.setCheckable(True)
self.setSizePolicy(QtSizeFixed, QtSizeFixed)
@@ -98,14 +97,14 @@ class NSwitch(QAbstractButton):
trackBrush = palette.highlight()
thumbBrush = palette.highlightedText()
else:
trackBrush = palette.mid()
trackBrush = palette.midlight()
thumbBrush = palette.light()
if self.isEnabled():
trackOpacity = 1.0
else:
trackOpacity = 0.6
trackBrush = palette.dark()
trackBrush = palette.mid()
thumbBrush = palette.mid()
painter.setBrush(trackBrush)
@@ -113,7 +112,7 @@ class NSwitch(QAbstractButton):
painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
painter.setBrush(thumbBrush)
painter.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
painter.drawEllipse(self._offset - self._rR, 2, self._rH, self._rH)
painter.end()
return
+1 -1
View File
@@ -70,7 +70,7 @@ class VersionInfoWidget(QWidget):
self._layout = QVBoxLayout()
self._layout.addWidget(self._lblInfo)
self._layout.addWidget(self._lblRelease)
self._layout.setSpacing(CONFIG.pxInt(2))
self._layout.setSpacing(2)
self._layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self._layout)
-3
View File
@@ -29,8 +29,6 @@ from enum import Flag, IntEnum
from PyQt6.QtGui import QColor
from novelwriter.types import nwDataClass
ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": ""}
RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL)
@@ -42,7 +40,6 @@ def stripEscape(text: str) -> str:
return text
@nwDataClass
class TextDocumentTheme:
"""Default document theme."""
+6 -3
View File
@@ -278,8 +278,10 @@ class ToQTextDocument(Tokenizer):
printer.setPageMargins(self._pageMargins, QPageLayout.Unit.Millimeter)
printer.setOutputFileName(str(path))
self._document.documentLayout().setPaintDevice(printer)
self._document.setPageSize(printer.pageRect(QPrinter.Unit.Millimeter).size())
if layout := self._document.documentLayout():
layout.setPaintDevice(printer)
self._document.setPageSize(printer.pageRect(QPrinter.Unit.DevicePixel).size())
self._document.print(printer)
return
@@ -458,7 +460,8 @@ class ToQTextDocument(Tokenizer):
cursor.insertFrame(fFmt)
cursor.setBlockFormat(bFmt)
cursor.insertText(self._project.localLookup("New Page"), cFmt)
cursor.swap(self._document.rootFrame().lastCursorPosition())
if root := self._document.rootFrame():
cursor.swap(root.lastCursorPosition())
return
+87 -92
View File
@@ -52,7 +52,9 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import decodeMimeHandles, fontMatcher, minmax, qtLambda, transferCase
from novelwriter.common import (
decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase
)
from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
from novelwriter.core.document import NWDocument
from novelwriter.enum import (
@@ -168,7 +170,7 @@ class GuiDocEditor(QPlainTextEdit):
self.customContextMenuRequested.connect(self._openContextMenu)
# Editor Settings
self.setMinimumWidth(CONFIG.pxInt(300))
self.setMinimumWidth(300)
self.setAutoFillBackground(True)
self.setFrameStyle(QFrame.Shape.NoFrame)
self.setAcceptDrops(True)
@@ -295,10 +297,11 @@ class GuiDocEditor(QPlainTextEdit):
palette.setColor(QPalette.ColorRole.Text, syntax.text)
self.setPalette(palette)
palette = self.viewport().palette()
palette.setColor(QPalette.ColorRole.Base, syntax.back)
palette.setColor(QPalette.ColorRole.Text, syntax.text)
self.viewport().setPalette(palette)
if viewport := self.viewport():
palette = viewport.palette()
palette.setColor(QPalette.ColorRole.Base, syntax.back)
palette.setColor(QPalette.ColorRole.Text, syntax.text)
viewport.setPalette(palette)
self.docHeader.matchColours()
self.docFooter.matchColours()
@@ -331,7 +334,7 @@ class GuiDocEditor(QPlainTextEdit):
# Due to cursor visibility, a part of the margin must be
# allocated to the document itself. See issue #1112.
self._qDocument.setDocumentMargin(4)
self._vpMargin = max(CONFIG.getTextMargin() - 4, 0)
self._vpMargin = max(CONFIG.textMargin - 4, 0)
self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin)
# Also set the document text options for the document text flow
@@ -359,7 +362,7 @@ class GuiDocEditor(QPlainTextEdit):
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
# Refresh the tab stops
self.setTabStopDistance(CONFIG.getTabWidth())
self.setTabStopDistance(CONFIG.tabWidth)
# If we have a document open, we should refresh it in case the
# font changed, otherwise we just clear the editor entirely,
@@ -513,29 +516,27 @@ class GuiDocEditor(QPlainTextEdit):
def cursorIsVisible(self) -> bool:
"""Check if the cursor is visible in the editor."""
return (
0 < self.cursorRect().top()
and self.cursorRect().bottom() < self.viewport().height()
)
viewport = self.viewport()
height = viewport.height() if viewport else 0
return 0 < self.cursorRect().top() and self.cursorRect().bottom() < height
def ensureCursorVisibleNoCentre(self) -> None:
"""Ensure cursor is visible, but don't force it to centre."""
cT = self.cursorRect().top()
cB = self.cursorRect().bottom()
vH = self.viewport().height()
if cT < 0:
count = 0
vBar = self.verticalScrollBar()
while self.cursorRect().top() < 0 and count < 100000:
vBar.setValue(vBar.value() - 1)
count += 1
elif cB > vH:
count = 0
vBar = self.verticalScrollBar()
while self.cursorRect().bottom() > vH and count < 100000:
vBar.setValue(vBar.value() + 1)
count += 1
QApplication.processEvents()
if (viewport := self.viewport()) and (vBar := self.verticalScrollBar()):
cT = self.cursorRect().top()
cB = self.cursorRect().bottom()
vH = viewport.height()
if cT < 0:
count = 0
while self.cursorRect().top() < 0 and count < 100000:
vBar.setValue(vBar.value() - 1)
count += 1
elif cB > vH:
count = 0
while self.cursorRect().bottom() > vH and count < 100000:
vBar.setValue(vBar.value() + 1)
count += 1
QApplication.processEvents()
return
def updateDocMargins(self) -> None:
@@ -547,10 +548,10 @@ class GuiDocEditor(QPlainTextEdit):
wH = self.height()
vBar = self.verticalScrollBar()
sW = vBar.width() if vBar.isVisible() else 0
sW = vBar.width() if vBar and vBar.isVisible() else 0
hBar = self.horizontalScrollBar()
sH = hBar.height() if hBar.isVisible() else 0
sH = hBar.height() if hBar and hBar.isVisible() else 0
tM = self._vpMargin
if CONFIG.textWidth > 0 or SHARED.focusMode:
@@ -959,10 +960,9 @@ class GuiDocEditor(QPlainTextEdit):
kMod = event.modifiers()
okMod = kMod in (QtModNone, QtModShift)
okKey = event.key() not in self.MOVE_KEYS
if nPos != cPos and okMod and okKey:
mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
if cPos > mPos:
vBar = self.verticalScrollBar()
if nPos != cPos and okMod and okKey and (viewport := self.viewport()):
mPos = CONFIG.autoScrollPos*0.01 * viewport.height()
if cPos > mPos and (vBar := self.verticalScrollBar()):
vBar.setValue(vBar.value() + (1 if nPos > cPos else -1))
else:
super().keyPressEvent(event)
@@ -971,7 +971,7 @@ class GuiDocEditor(QPlainTextEdit):
def dragEnterEvent(self, event: QDragEnterEvent) -> None:
"""Overload drag enter event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
event.acceptProposedAction()
else:
super().dragEnterEvent(event)
@@ -979,7 +979,7 @@ class GuiDocEditor(QPlainTextEdit):
def dragMoveEvent(self, event: QDragMoveEvent) -> None:
"""Overload drag move event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
event.acceptProposedAction()
else:
super().dragMoveEvent(event)
@@ -987,8 +987,8 @@ class GuiDocEditor(QPlainTextEdit):
def dropEvent(self, event: QDropEvent) -> None:
"""Overload drop event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if handles := decodeMimeHandles(event.mimeData()):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
if handles := decodeMimeHandles(data):
if SHARED.project.tree.checkType(handles[0], nwItemType.FILE):
self.openDocumentRequest.emit(handles[0], nwDocMode.EDIT, "", True)
else:
@@ -1098,10 +1098,10 @@ class GuiDocEditor(QPlainTextEdit):
# at unwanted times when other changes are made to the document
cursor = self.textCursor()
bPos = cursor.positionInBlock()
if bPos > 0:
if bPos > 0 and (viewport := self.viewport()):
show = self._completer.updateText(text, bPos)
point = self.cursorRect().bottomRight()
self._completer.move(self.viewport().mapToGlobal(point))
self._completer.move(viewport.mapToGlobal(point))
self._completer.setVisible(show)
else:
self._completer.setVisible(False)
@@ -1148,46 +1148,46 @@ class GuiDocEditor(QPlainTextEdit):
ctxMenu = QMenu(self)
ctxMenu.setObjectName("ContextMenu")
if pBlock.userState() == BLOCK_TITLE:
action = ctxMenu.addAction(self.tr("Set as Document Name"))
action = qtAddAction(ctxMenu, self.tr("Set as Document Name"))
action.triggered.connect(qtLambda(self._emitRenameItem, pBlock))
# URL
(mData, mType) = self._qDocument.metaDataAtPos(pCursor.position())
if mData and mType == "url":
action = ctxMenu.addAction(self.tr("Open URL"))
action = qtAddAction(ctxMenu, self.tr("Open URL"))
action.triggered.connect(qtLambda(SHARED.openWebsite, mData))
ctxMenu.addSeparator()
# Follow
status = self._processTag(cursor=pCursor, follow=False)
if status == nwTrinary.POSITIVE:
action = ctxMenu.addAction(self.tr("Follow Tag"))
action = qtAddAction(ctxMenu, self.tr("Follow Tag"))
action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, follow=True))
ctxMenu.addSeparator()
elif status == nwTrinary.NEGATIVE:
action = ctxMenu.addAction(self.tr("Create Note for Tag"))
action = qtAddAction(ctxMenu, self.tr("Create Note for Tag"))
action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, create=True))
ctxMenu.addSeparator()
# Cut, Copy and Paste
if uCursor.hasSelection():
action = ctxMenu.addAction(self.tr("Cut"))
action = qtAddAction(ctxMenu, self.tr("Cut"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.CUT))
action = ctxMenu.addAction(self.tr("Copy"))
action = qtAddAction(ctxMenu, self.tr("Copy"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
action = ctxMenu.addAction(self.tr("Paste"))
action = qtAddAction(ctxMenu, self.tr("Paste"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.PASTE))
ctxMenu.addSeparator()
# Selections
action = ctxMenu.addAction(self.tr("Select All"))
action = qtAddAction(ctxMenu, self.tr("Select All"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
action = ctxMenu.addAction(self.tr("Select Word"))
action = qtAddAction(ctxMenu, self.tr("Select Word"))
action.triggered.connect(qtLambda(
self._makePosSelection, QTextCursor.SelectionType.WordUnderCursor, pos,
))
action = ctxMenu.addAction(self.tr("Select Paragraph"))
action = qtAddAction(ctxMenu, self.tr("Select Paragraph"))
action.triggered.connect(qtLambda(
self._makePosSelection, QTextCursor.SelectionType.BlockUnderCursor, pos
))
@@ -1203,23 +1203,24 @@ class GuiDocEditor(QPlainTextEdit):
sCursor.movePosition(QtMoveRight, QtKeepAnchor, cLen)
if suggest:
ctxMenu.addSeparator()
ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
qtAddAction(ctxMenu, self.tr("Spelling Suggestion(s)"))
for option in suggest[:15]:
action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
action = qtAddAction(ctxMenu, f"{nwUnicode.U_ENDASH} {option}")
action.triggered.connect(qtLambda(self._correctWord, sCursor, option))
else:
trNone = self.tr("No Suggestions")
ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {trNone}")
qtAddAction(ctxMenu, f"{nwUnicode.U_ENDASH} {trNone}")
ctxMenu.addSeparator()
action = ctxMenu.addAction(self.tr("Ignore Word"))
action = qtAddAction(ctxMenu, self.tr("Ignore Word"))
action.triggered.connect(qtLambda(self._addWord, word, block, False))
action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
action = qtAddAction(ctxMenu, self.tr("Add Word to Dictionary"))
action.triggered.connect(qtLambda(self._addWord, word, block, True))
# Execute the context menu
ctxMenu.exec(self.viewport().mapToGlobal(pos))
ctxMenu.deleteLater()
if viewport := self.viewport():
ctxMenu.exec(viewport.mapToGlobal(pos))
ctxMenu.deleteLater()
return
@@ -2007,8 +2008,8 @@ class GuiDocEditor(QPlainTextEdit):
sPos = cPos
for i in range(cPos - bPos):
sPos = cPos - i - 1
cOne = self._qDocument.characterAt(sPos)
cTwo = self._qDocument.characterAt(sPos - 1)
cOne = str(self._qDocument.characterAt(sPos))
cTwo = str(self._qDocument.characterAt(sPos - 1))
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
sPos += 1
break
@@ -2017,8 +2018,8 @@ class GuiDocEditor(QPlainTextEdit):
ePos = cPos
for i in range(bPos + bLen - cPos):
ePos = cPos + i
cOne = self._qDocument.characterAt(ePos)
cTwo = self._qDocument.characterAt(ePos + 1)
cOne = str(self._qDocument.characterAt(ePos))
cTwo = str(self._qDocument.characterAt(ePos + 1))
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
break
@@ -2122,7 +2123,7 @@ class MetaCompleter(QMenu):
for value in sorted(options):
rep = value + suffix
action = self.addAction(value)
action = qtAddAction(self, value)
action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep))
return True
@@ -2339,7 +2340,6 @@ class GuiDocToolBar(QWidget):
logger.debug("Create: GuiDocToolBar")
iSz = SHARED.theme.baseIconSize
cM = CONFIG.pxInt(4)
self.setContentsMargins(0, 0, 0, 0)
# General Buttons
@@ -2412,7 +2412,7 @@ class GuiDocToolBar(QWidget):
self.outerBox.addWidget(self.tbBoldMD)
self.outerBox.addWidget(self.tbItalicMD)
self.outerBox.addWidget(self.tbStrikeMD)
self.outerBox.addSpacing(cM)
self.outerBox.addSpacing(4)
self.outerBox.addWidget(self.tbBold)
self.outerBox.addWidget(self.tbItalic)
self.outerBox.addWidget(self.tbStrike)
@@ -2420,8 +2420,8 @@ class GuiDocToolBar(QWidget):
self.outerBox.addWidget(self.tbMark)
self.outerBox.addWidget(self.tbSuperscript)
self.outerBox.addWidget(self.tbSubscript)
self.outerBox.setContentsMargins(cM, cM, cM, cM)
self.outerBox.setSpacing(cM)
self.outerBox.setContentsMargins(4, 4, 4, 4)
self.outerBox.setSpacing(4)
self.setLayout(self.outerBox)
self.updateTheme()
@@ -2472,7 +2472,6 @@ class GuiDocEditSearch(QFrame):
self.docEditor = docEditor
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(6)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -2498,7 +2497,7 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchLabel = QLabel(self.tr("Search"), self)
self.searchLabel.setIndent(CONFIG.pxInt(6))
self.searchLabel.setIndent(6)
self.resultLabel = QLabel("?/?", self)
@@ -2575,12 +2574,11 @@ class GuiDocEditSearch(QFrame):
self.mainBox.setColumnStretch(3, 0)
self.mainBox.setColumnStretch(4, 0)
self.mainBox.setColumnStretch(5, 0)
self.mainBox.setSpacing(CONFIG.pxInt(2))
self.mainBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.mainBox.setSpacing(2)
self.mainBox.setContentsMargins(6, 6, 6, 6)
boxWidth = CONFIG.pxInt(200)
self.searchBox.setFixedWidth(boxWidth)
self.replaceBox.setFixedWidth(boxWidth)
self.searchBox.setFixedWidth(200)
self.replaceBox.setFixedWidth(200)
self.replaceBox.setVisible(False)
self.replaceButton.setVisible(False)
self.adjustSize()
@@ -2848,7 +2846,6 @@ class GuiDocEditHeader(QWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(4)
# Main Widget Settings
self.setAutoFillBackground(True)
@@ -2895,13 +2892,13 @@ class GuiDocEditHeader(QWidget):
self.outerBox.addWidget(self.tbButton, 0)
self.outerBox.addWidget(self.outlineButton, 0)
self.outerBox.addWidget(self.searchButton, 0)
self.outerBox.addSpacing(mPx)
self.outerBox.addSpacing(4)
self.outerBox.addWidget(self.itemTitle, 1)
self.outerBox.addSpacing(mPx)
self.outerBox.addSpacing(4)
self.outerBox.addSpacing(iPx)
self.outerBox.addWidget(self.minmaxButton, 0)
self.outerBox.addWidget(self.closeButton, 0)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.outerBox.setContentsMargins(4, 4, 4, 4)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
@@ -2912,7 +2909,7 @@ class GuiDocEditHeader(QWidget):
# Fix Margins and Size
# This is needed for high DPI systems. See issue #499.
self.setContentsMargins(0, 0, 0, 0)
self.setMinimumHeight(iPx + 2*mPx)
self.setMinimumHeight(iPx + 8)
self.updateFont()
self.updateTheme()
@@ -2945,7 +2942,7 @@ class GuiDocEditHeader(QWidget):
tStart = time()
self.outlineMenu.clear()
for number, text in data.items():
action = self.outlineMenu.addAction(text)
action = qtAddAction(self.outlineMenu, text)
action.triggered.connect(qtLambda(self._gotoBlock, number))
self._docOutline = data
logger.debug("Document outline updated in %.3f ms", 1000*(time() - tStart))
@@ -3070,9 +3067,6 @@ class GuiDocEditFooter(QWidget):
iPx = round(0.9*SHARED.theme.baseIconHeight)
fPx = int(0.9*SHARED.theme.fontPixelSize)
mPx = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(4)
hSp = CONFIG.pxInt(6)
# Cached Translations
self._trLineCount = self.tr("Line: {0} ({1})")
@@ -3127,23 +3121,23 @@ class GuiDocEditFooter(QWidget):
# Assemble Layout
self.outerBox = QHBoxLayout()
self.outerBox.setSpacing(bSp)
self.outerBox.setSpacing(4)
self.outerBox.addWidget(self.statusIcon)
self.outerBox.addWidget(self.statusText)
self.outerBox.addStretch(1)
self.outerBox.addWidget(self.linesIcon)
self.outerBox.addWidget(self.linesText)
self.outerBox.addSpacing(hSp)
self.outerBox.addSpacing(6)
self.outerBox.addWidget(self.wordsIcon)
self.outerBox.addWidget(self.wordsText)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.outerBox.setContentsMargins(8, 8, 8, 8)
self.setLayout(self.outerBox)
# Fix Margins and Size
# This is needed for high DPI systems. See issue #499.
self.setContentsMargins(0, 0, 0, 0)
self.setMinimumHeight(fPx + 2*mPx)
self.setMinimumHeight(fPx + 16)
# Fix the Colours
self.updateFont()
@@ -3226,12 +3220,13 @@ class GuiDocEditFooter(QWidget):
def updateLineCount(self, cursor: QTextCursor) -> None:
"""Update the line and document position counter."""
cPos = cursor.position() + 1
cLine = cursor.blockNumber() + 1
cCount = max(cursor.document().characterCount(), 1)
self.linesText.setText(
self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %")
)
if document := cursor.document():
cPos = cursor.position() + 1
cLine = cursor.blockNumber() + 1
cCount = max(document.characterCount(), 1)
self.linesText.setText(
self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %")
)
return
def updateWordCount(self, wCount: int, selection: bool) -> None:
+8 -8
View File
@@ -272,14 +272,14 @@ class GuiDocHighlighter(QSyntaxHighlighter):
"""Loop through all blocks and re-highlight those of a given
content type.
"""
qDoc = self.document()
nBlocks = qDoc.blockCount()
tStart = time()
for i in range(nBlocks):
block = qDoc.findBlockByNumber(i)
if block.userState() & cType > 0:
self.rehighlightBlock(block)
logger.debug("Document highlighted in %.3f ms" % (1000*(time() - tStart)))
if document := self.document():
nBlocks = document.blockCount()
tStart = time()
for i in range(nBlocks):
block = document.findBlockByNumber(i)
if block.userState() & cType > 0:
self.rehighlightBlock(block)
logger.debug("Document highlighted in %.3f ms" % (1000*(time() - tStart)))
return
##
+48 -53
View File
@@ -32,8 +32,8 @@ from enum import Enum
from PyQt6.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
from PyQt6.QtGui import (
QAction, QCursor, QDesktopServices, QDragEnterEvent, QDragMoveEvent,
QDropEvent, QMouseEvent, QPalette, QResizeEvent, QTextCursor
QCursor, QDesktopServices, QDragEnterEvent, QDragMoveEvent, QDropEvent,
QMouseEvent, QPalette, QResizeEvent, QTextCursor
)
from PyQt6.QtWidgets import (
QApplication, QFrame, QHBoxLayout, QMenu, QTextBrowser, QToolButton,
@@ -41,7 +41,7 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import decodeMimeHandles, qtLambda
from novelwriter.common import decodeMimeHandles, qtAddAction, qtLambda
from novelwriter.constants import nwConst, nwStyles, nwUnicode
from novelwriter.enum import nwChange, nwDocAction, nwDocMode, nwItemType
from novelwriter.error import logException
@@ -79,7 +79,7 @@ class GuiDocViewer(QTextBrowser):
self._docTheme = TextDocumentTheme()
# Settings
self.setMinimumWidth(CONFIG.pxInt(300))
self.setMinimumWidth(300)
self.setAutoFillBackground(True)
self.setOpenLinks(False)
self.setOpenExternalLinks(False)
@@ -124,8 +124,7 @@ class GuiDocViewer(QTextBrowser):
@property
def scrollPosition(self) -> int:
"""Return the scrollbar position."""
vBar = self.verticalScrollBar()
if vBar.isVisible():
if (vBar := self.verticalScrollBar()) and vBar.isVisible():
return vBar.value()
return 0
@@ -163,12 +162,13 @@ class GuiDocViewer(QTextBrowser):
palette.setColor(QPalette.ColorRole.Text, syntax.text)
self.setPalette(palette)
palette = self.viewport().palette()
palette.setColor(QPalette.ColorRole.Base, syntax.back)
palette.setColor(QPalette.ColorRole.Text, syntax.text)
self.viewport().setPalette(palette)
self.docHeader.matchColours()
self.docFooter.matchColours()
if viewport := self.viewport():
palette = viewport.palette()
palette.setColor(QPalette.ColorRole.Base, syntax.back)
palette.setColor(QPalette.ColorRole.Text, syntax.text)
viewport.setPalette(palette)
self.docHeader.matchColours()
self.docFooter.matchColours()
# Update theme colours
self._docTheme.text = syntax.text
@@ -186,7 +186,8 @@ class GuiDocViewer(QTextBrowser):
self._docTheme.altdialog = syntax.dialA
# Set default text margins
self.document().setDocumentMargin(0)
if document := self.document():
document.setDocumentMargin(0)
# Scroll bars
if CONFIG.hideVScroll:
@@ -200,7 +201,7 @@ class GuiDocViewer(QTextBrowser):
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
# Refresh the tab stops
self.setTabStopDistance(CONFIG.getTabWidth())
self.setTabStopDistance(CONFIG.tabWidth)
# If we have a document open, we should reload it in case the font changed
self.reloadText()
@@ -217,7 +218,9 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Generating preview for item '%s'", tHandle)
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
sPos = self.verticalScrollBar().value()
vBar = self.verticalScrollBar()
sPos = vBar.value() if vBar else 0
qDoc = ToQTextDocument(SHARED.project)
qDoc.setJustify(CONFIG.doJustify)
qDoc.setDialogHighlight(True)
@@ -250,11 +253,11 @@ class GuiDocViewer(QTextBrowser):
self.setDocumentTitle(tHandle)
self.setDocument(qDoc.document)
self.setTabStopDistance(CONFIG.getTabWidth())
self.setTabStopDistance(CONFIG.tabWidth)
if self._docHandle == tHandle:
if self._docHandle == tHandle and vBar:
# This is a refresh, so we set the scrollbar back to where it was
self.verticalScrollBar().setValue(sPos)
vBar.setValue(sPos)
self._docHandle = tHandle
SHARED.project.data.setLastHandle(tHandle, "viewer")
@@ -308,13 +311,13 @@ class GuiDocViewer(QTextBrowser):
"""Automatically adjust the margins so the text is centred."""
wW = self.width()
wH = self.height()
cM = CONFIG.getTextMargin()
cM = CONFIG.textMargin
vBar = self.verticalScrollBar()
sW = vBar.width() if vBar.isVisible() else 0
sW = vBar.width() if vBar and vBar.isVisible() else 0
hBar = self.horizontalScrollBar()
sH = hBar.height() if hBar.isVisible() else 0
sH = hBar.height() if hBar and hBar.isVisible() else 0
tM = cM
if CONFIG.textWidth > 0:
@@ -339,8 +342,7 @@ class GuiDocViewer(QTextBrowser):
def setScrollPosition(self, pos: int) -> None:
"""Set the scrollbar position."""
vBar = self.verticalScrollBar()
if vBar.isVisible():
if (vBar := self.verticalScrollBar()) and vBar.isVisible():
vBar.setValue(pos)
return
@@ -402,31 +404,27 @@ class GuiDocViewer(QTextBrowser):
ctxMenu = QMenu(self)
if userSelection:
mnuCopy = QAction(self.tr("Copy"), ctxMenu)
mnuCopy.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
ctxMenu.addAction(mnuCopy)
action = qtAddAction(ctxMenu, self.tr("Copy"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
ctxMenu.addSeparator()
mnuSelAll = QAction(self.tr("Select All"), ctxMenu)
mnuSelAll.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
ctxMenu.addAction(mnuSelAll)
action = qtAddAction(ctxMenu, self.tr("Select All"))
action.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
mnuSelWord = QAction(self.tr("Select Word"), ctxMenu)
mnuSelWord.triggered.connect(qtLambda(
action = qtAddAction(ctxMenu, self.tr("Select Word"))
action.triggered.connect(qtLambda(
self._makePosSelection, QTextCursor.SelectionType.WordUnderCursor, point
))
ctxMenu.addAction(mnuSelWord)
mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu)
mnuSelPara.triggered.connect(qtLambda(
action = qtAddAction(ctxMenu, self.tr("Select Paragraph"))
action.triggered.connect(qtLambda(
self._makePosSelection, QTextCursor.SelectionType.BlockUnderCursor, point
))
ctxMenu.addAction(mnuSelPara)
# Open the context menu
ctxMenu.exec(self.viewport().mapToGlobal(point))
ctxMenu.deleteLater()
if viewport := self.viewport():
ctxMenu.exec(viewport.mapToGlobal(point))
ctxMenu.deleteLater()
return
@@ -452,7 +450,7 @@ class GuiDocViewer(QTextBrowser):
def dragEnterEvent(self, event: QDragEnterEvent) -> None:
"""Overload drag enter event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
event.acceptProposedAction()
else:
super().dragEnterEvent(event)
@@ -460,7 +458,7 @@ class GuiDocViewer(QTextBrowser):
def dragMoveEvent(self, event: QDragMoveEvent) -> None:
"""Overload drag move event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
event.acceptProposedAction()
else:
super().dragMoveEvent(event)
@@ -468,8 +466,8 @@ class GuiDocViewer(QTextBrowser):
def dropEvent(self, event: QDropEvent) -> None:
"""Overload drop event to handle dragged items."""
if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
if handles := decodeMimeHandles(event.mimeData()):
if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
if handles := decodeMimeHandles(data):
if SHARED.project.tree.checkType(handles[0], nwItemType.FILE):
self.openDocumentRequest.emit(handles[0], nwDocMode.VIEW, "", True)
else:
@@ -640,7 +638,6 @@ class GuiDocViewHeader(QWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(4)
# Main Widget Settings
self.setAutoFillBackground(True)
@@ -692,9 +689,9 @@ class GuiDocViewHeader(QWidget):
self.outerBox.addWidget(self.outlineButton, 0)
self.outerBox.addWidget(self.backButton, 0)
self.outerBox.addWidget(self.forwardButton, 0)
self.outerBox.addSpacing(mPx)
self.outerBox.addSpacing(4)
self.outerBox.addWidget(self.itemTitle, 1)
self.outerBox.addSpacing(mPx)
self.outerBox.addSpacing(4)
self.outerBox.addWidget(self.editButton, 0)
self.outerBox.addWidget(self.refreshButton, 0)
self.outerBox.addWidget(self.closeButton, 0)
@@ -705,8 +702,8 @@ class GuiDocViewHeader(QWidget):
# Fix Margins and Size
# This is needed for high DPI systems. See issue #499.
self.setContentsMargins(0, 0, 0, 0)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.setMinimumHeight(iPx + 2*mPx)
self.outerBox.setContentsMargins(4, 4, 4, 4)
self.setMinimumHeight(iPx + 8)
self.updateFont()
self.updateTheme()
@@ -747,7 +744,7 @@ class GuiDocViewHeader(QWidget):
minLevel = min(minLevel, level)
for title, text, level in entries[:30]:
indent = " "*(level - minLevel)
action = self.outlineMenu.addAction(f"{indent}{text}")
action = qtAddAction(self.outlineMenu, f"{indent}{text}")
action.triggered.connect(
lambda _, title=title: self.docViewer.navigateTo(f"#{tHandle}:{title}")
)
@@ -885,8 +882,6 @@ class GuiDocViewFooter(QWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
hSp = CONFIG.pxInt(4)
mPx = CONFIG.pxInt(4)
# Main Widget Settings
self.setContentsMargins(0, 0, 0, 0)
@@ -923,14 +918,14 @@ class GuiDocViewFooter(QWidget):
self.outerBox.addStretch(1)
self.outerBox.addWidget(self.showComments, 0)
self.outerBox.addWidget(self.showSynopsis, 0)
self.outerBox.setSpacing(hSp)
self.outerBox.setSpacing(4)
self.setLayout(self.outerBox)
# Fix Margins and Size
# This is needed for high DPI systems. See issue #499.
self.setContentsMargins(0, 0, 0, 0)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.setMinimumHeight(iPx + 2*mPx)
self.outerBox.setContentsMargins(4, 4, 4, 4)
self.setMinimumHeight(iPx + 8)
self.updateFont()
self.updateTheme()
+29 -31
View File
@@ -33,8 +33,8 @@ from PyQt6.QtWidgets import (
QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt
from novelwriter import SHARED
from novelwriter.common import checkInt, qtAddAction
from novelwriter.constants import nwLabels, nwLists, nwStyles, trConst
from novelwriter.core.index import IndexHeading, IndexItem
from novelwriter.enum import nwChange, nwDocMode, nwItemClass
@@ -63,7 +63,7 @@ class GuiDocViewerPanel(QWidget):
self.optsMenu = QMenu(self)
self.aInactive = self.optsMenu.addAction(self.tr("Hide Inactive Tags"))
self.aInactive = qtAddAction(self.optsMenu, self.tr("Hide Inactive Tags"))
self.aInactive.setCheckable(True)
self.aInactive.toggled.connect(self._toggleHideInactive)
@@ -248,7 +248,6 @@ class _ViewPanelBackRefs(QTreeWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setHeaderLabels([self.tr("Document"), "", "", self.tr("First Heading")])
self.setIndentation(0)
@@ -257,16 +256,16 @@ class _ViewPanelBackRefs(QTreeWidget):
self.setFrameStyle(QFrame.Shape.NoFrame)
# Set Header Sizes
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
treeHeader.setSectionResizeMode(self.C_DOC, QtHeaderToContents)
treeHeader.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
treeHeader.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
treeHeader.setSectionResizeMode(self.C_TITLE, QtHeaderToContents)
treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
treeHeader.setSectionsMovable(False)
if header := self.header():
header.setStretchLastSection(True)
header.setMinimumSectionSize(iPx + 6) # See Issue #1627
header.setSectionResizeMode(self.C_DOC, QtHeaderToContents)
header.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
header.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
header.setSectionResizeMode(self.C_TITLE, QtHeaderToContents)
header.resizeSection(self.C_EDIT, iPx + 6)
header.resizeSection(self.C_VIEW, iPx + 6)
header.setSectionsMovable(False)
# Cache Icons Locally
self._editIcon = SHARED.theme.getIcon("edit", "green")
@@ -385,7 +384,6 @@ class _ViewPanelKeyWords(QTreeWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setHeaderLabels([
self.tr("Tag"), "", "", self.tr("Importance"), self.tr("Document"),
@@ -401,14 +399,14 @@ class _ViewPanelKeyWords(QTreeWidget):
self.sortByColumn(self.C_NAME, Qt.SortOrder.AscendingOrder)
# Set Header Sizes
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
treeHeader.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
treeHeader.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
treeHeader.setSectionsMovable(False)
if header := self.header():
header.setStretchLastSection(True)
header.setMinimumSectionSize(iPx + 6) # See Issue #1627
header.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
header.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
header.resizeSection(self.C_EDIT, iPx + 6)
header.resizeSection(self.C_VIEW, iPx + 6)
header.setSectionsMovable(False)
# Cache Icons Locally
self.updateTheme()
@@ -482,19 +480,19 @@ class _ViewPanelKeyWords(QTreeWidget):
def setColumnWidths(self, widths: list[int]) -> None:
"""Set the column widths."""
if isinstance(widths, list) and len(widths) >= 4:
self.setColumnWidth(self.C_NAME, CONFIG.pxInt(checkInt(widths[0], 100)))
self.setColumnWidth(self.C_IMPORT, CONFIG.pxInt(checkInt(widths[1], 100)))
self.setColumnWidth(self.C_DOC, CONFIG.pxInt(checkInt(widths[2], 100)))
self.setColumnWidth(self.C_TITLE, CONFIG.pxInt(checkInt(widths[3], 100)))
self.setColumnWidth(self.C_NAME, checkInt(widths[0], 100))
self.setColumnWidth(self.C_IMPORT, checkInt(widths[1], 100))
self.setColumnWidth(self.C_DOC, checkInt(widths[2], 100))
self.setColumnWidth(self.C_TITLE, checkInt(widths[3], 100))
return
def getColumnWidths(self) -> list[int]:
"""Get the widths of the user-adjustable columns."""
return [
CONFIG.rpxInt(self.columnWidth(self.C_NAME)),
CONFIG.rpxInt(self.columnWidth(self.C_IMPORT)),
CONFIG.rpxInt(self.columnWidth(self.C_DOC)),
CONFIG.rpxInt(self.columnWidth(self.C_TITLE)),
self.columnWidth(self.C_NAME),
self.columnWidth(self.C_IMPORT),
self.columnWidth(self.C_DOC),
self.columnWidth(self.C_TITLE),
]
##
+4 -7
View File
@@ -30,7 +30,7 @@ from enum import Enum
from PyQt6.QtCore import pyqtSlot
from PyQt6.QtWidgets import QGridLayout, QLabel, QWidget
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.common import elide
from novelwriter.constants import nwLabels, nwStats, trConst
from novelwriter.enum import nwChange
@@ -53,9 +53,6 @@ class GuiItemDetails(QWidget):
self._handle = None
# Sizes
hSp = CONFIG.pxInt(6)
vSp = CONFIG.pxInt(1)
mPx = CONFIG.pxInt(6)
fPt = SHARED.theme.fontPointSize
fntLabel = self.font()
@@ -176,9 +173,9 @@ class GuiItemDetails(QWidget):
self.mainBox.setColumnStretch(3, 0)
self.mainBox.setColumnStretch(4, 0)
self.mainBox.setHorizontalSpacing(hSp)
self.mainBox.setVerticalSpacing(vSp)
self.mainBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.mainBox.setHorizontalSpacing(6)
self.mainBox.setVerticalSpacing(1)
self.mainBox.setContentsMargins(6, 6, 6, 6)
self.setLayout(self.mainBox)
+136 -136
View File
@@ -33,7 +33,7 @@ from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QMenuBar
from novelwriter import CONFIG, SHARED
from novelwriter.common import openExternalPath, qtLambda
from novelwriter.common import openExternalPath, qtAddAction, qtAddMenu, qtLambda
from novelwriter.constants import (
nwConst, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
trConst
@@ -128,21 +128,21 @@ class GuiMainMenu(QMenuBar):
def _buildProjectMenu(self) -> None:
"""Assemble the Project menu."""
# Project
self.projMenu = self.addMenu(self.tr("&Project"))
self.projMenu = qtAddMenu(self, self.tr("&Project"))
# Project > Create or Open Project
self.aOpenProject = self.projMenu.addAction(self.tr("Create or Open Project"))
self.aOpenProject = qtAddAction(self.projMenu, self.tr("Create or Open Project"))
self.aOpenProject.setShortcut("Ctrl+Shift+O")
self.aOpenProject.triggered.connect(self.mainGui.showWelcomeDialog)
# Project > Save Project
self.aSaveProject = self.projMenu.addAction(self.tr("Save Project"))
self.aSaveProject = qtAddAction(self.projMenu, self.tr("Save Project"))
self.aSaveProject.setShortcut("Ctrl+Shift+S")
self.aSaveProject.triggered.connect(qtLambda(self.mainGui.saveProject))
self.mainGui.addAction(self.aSaveProject)
# Project > Close Project
self.aCloseProject = self.projMenu.addAction(self.tr("Close Project"))
self.aCloseProject = qtAddAction(self.projMenu, self.tr("Close Project"))
self.aCloseProject.setShortcut("Ctrl+Shift+W")
self.aCloseProject.triggered.connect(qtLambda(self.mainGui.closeProject, False))
@@ -150,12 +150,12 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Project Settings
self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings"))
self.aProjectSettings = qtAddAction(self.projMenu, self.tr("Project Settings"))
self.aProjectSettings.setShortcut("Ctrl+Shift+,")
self.aProjectSettings.triggered.connect(self.mainGui.showProjectSettingsDialog)
# Project > Novel Details
self.aNovelDetails = self.projMenu.addAction(self.tr("Novel Details"))
self.aNovelDetails = qtAddAction(self.projMenu, self.tr("Novel Details"))
self.aNovelDetails.setShortcut("Shift+F6")
self.aNovelDetails.triggered.connect(self.mainGui.showNovelDetailsDialog)
@@ -163,16 +163,16 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Edit
self.aRenameItem = self.projMenu.addAction(self.tr("Rename Item"))
self.aRenameItem = qtAddAction(self.projMenu, self.tr("Rename Item"))
self.aRenameItem.setShortcut("F2")
# Project > Delete
self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item"))
self.aDeleteItem = qtAddAction(self.projMenu, self.tr("Delete Item"))
self.aDeleteItem.setShortcut("Del")
self.aDeleteItem.setShortcutContext(Qt.ShortcutContext.WidgetShortcut)
# Project > Empty Trash
self.aEmptyTrash = self.projMenu.addAction(self.tr("Empty Trash"))
self.aEmptyTrash = qtAddAction(self.projMenu, self.tr("Empty Trash"))
self.mainGui.projView.connectMenuActions(
self.aRenameItem, self.aDeleteItem, self.aEmptyTrash
@@ -182,7 +182,7 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator()
# Project > Exit
self.aExitNW = self.projMenu.addAction(self.tr("Exit"))
self.aExitNW = qtAddAction(self.projMenu, self.tr("Exit"))
self.aExitNW.setShortcut("Ctrl+Q")
self.aExitNW.setMenuRole(QAction.MenuRole.QuitRole)
self.aExitNW.triggered.connect(qtLambda(self.mainGui.closeMain))
@@ -193,21 +193,21 @@ class GuiMainMenu(QMenuBar):
def _buildDocumentMenu(self) -> None:
"""Assemble the Document menu."""
# Document
self.docuMenu = self.addMenu(self.tr("&Document"))
self.docuMenu = qtAddMenu(self, self.tr("&Document"))
# Document > Open
self.aOpenDoc = self.docuMenu.addAction(self.tr("Open Document"))
self.aOpenDoc = qtAddAction(self.docuMenu, self.tr("Open Document"))
self.aOpenDoc.setShortcut("Ctrl+O")
self.aOpenDoc.triggered.connect(self.mainGui.openSelectedItem)
# Document > Save
self.aSaveDoc = self.docuMenu.addAction(self.tr("Save Document"))
self.aSaveDoc = qtAddAction(self.docuMenu, self.tr("Save Document"))
self.aSaveDoc.setShortcut("Ctrl+S")
self.aSaveDoc.triggered.connect(self.mainGui.forceSaveDocument)
self.mainGui.addAction(self.aSaveDoc)
# Document > Close
self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
self.aCloseDoc = qtAddAction(self.docuMenu, self.tr("Close Document"))
self.aCloseDoc.setShortcut("Ctrl+W")
self.aCloseDoc.triggered.connect(self.mainGui.closeDocEditor)
self.mainGui.addAction(self.aCloseDoc)
@@ -216,12 +216,12 @@ class GuiMainMenu(QMenuBar):
self.docuMenu.addSeparator()
# Document > Preview
self.aViewDoc = self.docuMenu.addAction(self.tr("View Document"))
self.aViewDoc = qtAddAction(self.docuMenu, self.tr("View Document"))
self.aViewDoc.setShortcut("Ctrl+R")
self.aViewDoc.triggered.connect(qtLambda(self.mainGui.viewDocument, None))
# Document > Close Preview
self.aCloseView = self.docuMenu.addAction(self.tr("Close Document View"))
self.aCloseView = qtAddAction(self.docuMenu, self.tr("Close Document View"))
self.aCloseView.setShortcut("Ctrl+Shift+R")
self.aCloseView.triggered.connect(self.mainGui.closeDocViewer)
@@ -229,11 +229,11 @@ class GuiMainMenu(QMenuBar):
self.docuMenu.addSeparator()
# Document > Show File Details
self.aFileDetails = self.docuMenu.addAction(self.tr("Show File Details"))
self.aFileDetails = qtAddAction(self.docuMenu, self.tr("Show File Details"))
self.aFileDetails.triggered.connect(qtLambda(self.mainGui.docEditor.revealLocation))
# Document > Import From File
self.aImportFile = self.docuMenu.addAction(self.tr("Import Text from File"))
self.aImportFile = qtAddAction(self.docuMenu, self.tr("Import Text from File"))
self.aImportFile.triggered.connect(qtLambda(self.mainGui.importDocument))
return
@@ -241,10 +241,10 @@ class GuiMainMenu(QMenuBar):
def _buildEditMenu(self) -> None:
"""Assemble the Edit menu."""
# Edit
self.editMenu = self.addMenu(self.tr("&Edit"))
self.editMenu = qtAddMenu(self, self.tr("&Edit"))
# Edit > Undo
self.aEditUndo = self.editMenu.addAction(self.tr("Undo"))
self.aEditUndo = qtAddAction(self.editMenu, self.tr("Undo"))
self.aEditUndo.setShortcut("Ctrl+Z")
self.aEditUndo.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.UNDO)
@@ -252,7 +252,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aEditUndo)
# Edit > Redo
self.aEditRedo = self.editMenu.addAction(self.tr("Redo"))
self.aEditRedo = qtAddAction(self.editMenu, self.tr("Redo"))
self.aEditRedo.setShortcut("Ctrl+Y")
self.aEditRedo.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.REDO)
@@ -263,7 +263,7 @@ class GuiMainMenu(QMenuBar):
self.editMenu.addSeparator()
# Edit > Cut
self.aEditCut = self.editMenu.addAction(self.tr("Cut"))
self.aEditCut = qtAddAction(self.editMenu, self.tr("Cut"))
self.aEditCut.setShortcut("Ctrl+X")
self.aEditCut.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.CUT)
@@ -271,7 +271,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aEditCut)
# Edit > Copy
self.aEditCopy = self.editMenu.addAction(self.tr("Copy"))
self.aEditCopy = qtAddAction(self.editMenu, self.tr("Copy"))
self.aEditCopy.setShortcut("Ctrl+C")
self.aEditCopy.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.COPY)
@@ -279,7 +279,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aEditCopy)
# Edit > Paste
self.aEditPaste = self.editMenu.addAction(self.tr("Paste"))
self.aEditPaste = qtAddAction(self.editMenu, self.tr("Paste"))
self.aEditPaste.setShortcut("Ctrl+V")
self.aEditPaste.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.PASTE)
@@ -290,7 +290,7 @@ class GuiMainMenu(QMenuBar):
self.editMenu.addSeparator()
# Edit > Select All
self.aSelectAll = self.editMenu.addAction(self.tr("Select All"))
self.aSelectAll = qtAddAction(self.editMenu, self.tr("Select All"))
self.aSelectAll.setShortcut("Ctrl+A")
self.aSelectAll.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL)
@@ -298,7 +298,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aSelectAll)
# Edit > Select Paragraph
self.aSelectPar = self.editMenu.addAction(self.tr("Select Paragraph"))
self.aSelectPar = qtAddAction(self.editMenu, self.tr("Select Paragraph"))
self.aSelectPar.setShortcut("Ctrl+Shift+A")
self.aSelectPar.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA)
@@ -310,24 +310,24 @@ class GuiMainMenu(QMenuBar):
def _buildViewMenu(self) -> None:
"""Assemble the View menu."""
# View
self.viewMenu = self.addMenu(self.tr("&View"))
self.viewMenu = qtAddMenu(self, self.tr("&View"))
# View > TreeView
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Tree View"))
self.aFocusTree = qtAddAction(self.viewMenu, self.tr("Go to Tree View"))
self.aFocusTree.setShortcut("Ctrl+T")
self.aFocusTree.triggered.connect(
lambda: self.requestFocusChange.emit(nwFocus.TREE)
)
# View > Document Editor
self.aFocusDocument = self.viewMenu.addAction(self.tr("Go to Document"))
self.aFocusDocument = qtAddAction(self.viewMenu, self.tr("Go to Document"))
self.aFocusDocument.setShortcut("Ctrl+E")
self.aFocusDocument.triggered.connect(
lambda: self.requestFocusChange.emit(nwFocus.DOCUMENT)
)
# View > Outline
self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
self.aFocusOutline = qtAddAction(self.viewMenu, self.tr("Go to Outline"))
self.aFocusOutline.setShortcut("Ctrl+Shift+T")
self.aFocusOutline.triggered.connect(
lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
@@ -337,14 +337,14 @@ class GuiMainMenu(QMenuBar):
self.viewMenu.addSeparator()
# View > Go Backward
self.aViewPrev = self.viewMenu.addAction(self.tr("Navigate Backward"))
self.aViewPrev = qtAddAction(self.viewMenu, self.tr("Navigate Backward"))
self.aViewPrev.setShortcut("Alt+Left")
self.aViewPrev.setShortcutContext(Qt.ShortcutContext.WidgetShortcut)
self.aViewPrev.triggered.connect(self.mainGui.docViewer.navBackward)
self.mainGui.docViewer.addAction(self.aViewPrev)
# View > Go Forward
self.aViewNext = self.viewMenu.addAction(self.tr("Navigate Forward"))
self.aViewNext = qtAddAction(self.viewMenu, self.tr("Navigate Forward"))
self.aViewNext.setShortcut("Alt+Right")
self.aViewNext.setShortcutContext(Qt.ShortcutContext.WidgetShortcut)
self.aViewNext.triggered.connect(self.mainGui.docViewer.navForward)
@@ -354,13 +354,13 @@ class GuiMainMenu(QMenuBar):
self.viewMenu.addSeparator()
# View > Focus Mode
self.aFocusMode = self.viewMenu.addAction(self.tr("Focus Mode"))
self.aFocusMode = qtAddAction(self.viewMenu, self.tr("Focus Mode"))
self.aFocusMode.setShortcut("F8")
self.aFocusMode.triggered.connect(self.mainGui.toggleFocusMode)
self.mainGui.addAction(self.aFocusMode)
# View > Toggle Full Screen
self.aFullScreen = self.viewMenu.addAction(self.tr("Full Screen Mode"))
self.aFullScreen = qtAddAction(self.viewMenu, self.tr("Full Screen Mode"))
self.aFullScreen.setShortcut("F11")
self.aFullScreen.triggered.connect(self.mainGui.toggleFullScreenMode)
self.mainGui.addAction(self.aFullScreen)
@@ -370,13 +370,13 @@ class GuiMainMenu(QMenuBar):
def _buildInsertMenu(self) -> None:
"""Assemble the Insert menu."""
# Insert
self.insMenu = self.addMenu(self.tr("&Insert"))
self.insMenu = qtAddMenu(self, self.tr("&Insert"))
# Insert > Dashes and Dots
self.mInsDashes = self.insMenu.addMenu(self.tr("Dashes"))
self.mInsDashes = qtAddMenu(self.insMenu, self.tr("Dashes"))
# Insert > Short Dash
self.aInsENDash = self.mInsDashes.addAction(self.tr("Short Dash"))
self.aInsENDash = qtAddAction(self.mInsDashes, self.tr("Short Dash"))
self.aInsENDash.setShortcut("Ctrl+K, -")
self.aInsENDash.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_ENDASH)
@@ -384,7 +384,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsENDash)
# Insert > Long Dash
self.aInsEMDash = self.mInsDashes.addAction(self.tr("Long Dash"))
self.aInsEMDash = qtAddAction(self.mInsDashes, self.tr("Long Dash"))
self.aInsEMDash.setShortcut("Ctrl+K, _")
self.aInsEMDash.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_EMDASH)
@@ -392,7 +392,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsEMDash)
# Insert > Long Dash
self.aInsHorBar = self.mInsDashes.addAction(self.tr("Horizontal Bar"))
self.aInsHorBar = qtAddAction(self.mInsDashes, self.tr("Horizontal Bar"))
self.aInsHorBar.setShortcut("Ctrl+K, Ctrl+_")
self.aInsHorBar.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_HBAR)
@@ -400,7 +400,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsHorBar)
# Insert > Figure Dash
self.aInsFigDash = self.mInsDashes.addAction(self.tr("Figure Dash"))
self.aInsFigDash = qtAddAction(self.mInsDashes, self.tr("Figure Dash"))
self.aInsFigDash.setShortcut("Ctrl+K, ~")
self.aInsFigDash.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_FGDASH)
@@ -408,10 +408,10 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsFigDash)
# Insert > Quote Marks
self.mInsQuotes = self.insMenu.addMenu(self.tr("Quote Marks"))
self.mInsQuotes = qtAddMenu(self.insMenu, self.tr("Quote Marks"))
# Insert > Left Single Quote
self.aInsQuoteLS = self.mInsQuotes.addAction(self.tr("Left Single Quote"))
self.aInsQuoteLS = qtAddAction(self.mInsQuotes, self.tr("Left Single Quote"))
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
self.aInsQuoteLS.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_LS)
@@ -419,7 +419,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsQuoteLS)
# Insert > Right Single Quote
self.aInsQuoteRS = self.mInsQuotes.addAction(self.tr("Right Single Quote"))
self.aInsQuoteRS = qtAddAction(self.mInsQuotes, self.tr("Right Single Quote"))
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
self.aInsQuoteRS.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_RS)
@@ -427,7 +427,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsQuoteRS)
# Insert > Left Double Quote
self.aInsQuoteLD = self.mInsQuotes.addAction(self.tr("Left Double Quote"))
self.aInsQuoteLD = qtAddAction(self.mInsQuotes, self.tr("Left Double Quote"))
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
self.aInsQuoteLD.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_LD)
@@ -435,7 +435,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsQuoteLD)
# Insert > Right Double Quote
self.aInsQuoteRD = self.mInsQuotes.addAction(self.tr("Right Double Quote"))
self.aInsQuoteRD = qtAddAction(self.mInsQuotes, self.tr("Right Double Quote"))
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
self.aInsQuoteRD.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_RD)
@@ -443,7 +443,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsQuoteRD)
# Insert > Alternative Apostrophe
self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
self.aInsMSApos = qtAddAction(self.mInsQuotes, self.tr("Alternative Apostrophe"))
self.aInsMSApos.setShortcut("Ctrl+K, '")
self.aInsMSApos.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOS)
@@ -451,10 +451,10 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsMSApos)
# Insert > Symbols
self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation"))
self.mInsPunct = qtAddMenu(self.insMenu, self.tr("General Punctuation"))
# Insert > Ellipsis
self.aInsEllipsis = self.mInsPunct.addAction(self.tr("Ellipsis"))
self.aInsEllipsis = qtAddAction(self.mInsPunct, self.tr("Ellipsis"))
self.aInsEllipsis.setShortcut("Ctrl+K, .")
self.aInsEllipsis.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_HELLIP)
@@ -462,7 +462,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsEllipsis)
# Insert > Prime
self.aInsPrime = self.mInsPunct.addAction(self.tr("Prime"))
self.aInsPrime = qtAddAction(self.mInsPunct, self.tr("Prime"))
self.aInsPrime.setShortcut("Ctrl+K, Ctrl+'")
self.aInsPrime.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_PRIME)
@@ -470,7 +470,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsPrime)
# Insert > Double Prime
self.aInsDPrime = self.mInsPunct.addAction(self.tr("Double Prime"))
self.aInsDPrime = qtAddAction(self.mInsPunct, self.tr("Double Prime"))
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
self.aInsDPrime.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_DPRIME)
@@ -478,10 +478,10 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsDPrime)
# Insert > White Spaces
self.mInsSpace = self.insMenu.addMenu(self.tr("White Spaces"))
self.mInsSpace = qtAddMenu(self.insMenu, self.tr("White Spaces"))
# Insert > Non-Breaking Space
self.aInsNBSpace = self.mInsSpace.addAction(self.tr("Non-Breaking Space"))
self.aInsNBSpace = qtAddAction(self.mInsSpace, self.tr("Non-Breaking Space"))
self.aInsNBSpace.setShortcut("Ctrl+K, Space")
self.aInsNBSpace.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_NBSP)
@@ -489,7 +489,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsNBSpace)
# Insert > Thin Space
self.aInsThinSpace = self.mInsSpace.addAction(self.tr("Thin Space"))
self.aInsThinSpace = qtAddAction(self.mInsSpace, self.tr("Thin Space"))
self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space")
self.aInsThinSpace.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_THSP)
@@ -497,7 +497,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsThinSpace)
# Insert > Thin Non-Breaking Space
self.aInsThinNBSpace = self.mInsSpace.addAction(self.tr("Thin Non-Breaking Space"))
self.aInsThinNBSpace = qtAddAction(self.mInsSpace, self.tr("Thin Non-Breaking Space"))
self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space")
self.aInsThinNBSpace.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_THNBSP)
@@ -505,10 +505,10 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsThinNBSpace)
# Insert > Symbols
self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols"))
self.mInsSymbol = qtAddMenu(self.insMenu, self.tr("Other Symbols"))
# Insert > List Bullet
self.aInsBullet = self.mInsSymbol.addAction(self.tr("List Bullet"))
self.aInsBullet = qtAddAction(self.mInsSymbol, self.tr("List Bullet"))
self.aInsBullet.setShortcut("Ctrl+K, *")
self.aInsBullet.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_BULL)
@@ -516,7 +516,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsBullet)
# Insert > Hyphen Bullet
self.aInsHyBull = self.mInsSymbol.addAction(self.tr("Hyphen Bullet"))
self.aInsHyBull = qtAddAction(self.mInsSymbol, self.tr("Hyphen Bullet"))
self.aInsHyBull.setShortcut("Ctrl+K, Ctrl+-")
self.aInsHyBull.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_HYBULL)
@@ -524,7 +524,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsHyBull)
# Insert > Flower Mark
self.aInsFlower = self.mInsSymbol.addAction(self.tr("Flower Mark"))
self.aInsFlower = qtAddAction(self.mInsSymbol, self.tr("Flower Mark"))
self.aInsFlower.setShortcut("Ctrl+K, Ctrl+*")
self.aInsFlower.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_FLOWER)
@@ -532,7 +532,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsFlower)
# Insert > Per Mille
self.aInsPerMille = self.mInsSymbol.addAction(self.tr("Per Mille"))
self.aInsPerMille = qtAddAction(self.mInsSymbol, self.tr("Per Mille"))
self.aInsPerMille.setShortcut("Ctrl+K, %")
self.aInsPerMille.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_PERMIL)
@@ -540,7 +540,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsPerMille)
# Insert > Degree Symbol
self.aInsDegree = self.mInsSymbol.addAction(self.tr("Degree Symbol"))
self.aInsDegree = qtAddAction(self.mInsSymbol, self.tr("Degree Symbol"))
self.aInsDegree.setShortcut("Ctrl+K, Ctrl+O")
self.aInsDegree.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_DEGREE)
@@ -548,7 +548,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsDegree)
# Insert > Minus Sign
self.aInsMinus = self.mInsSymbol.addAction(self.tr("Minus Sign"))
self.aInsMinus = qtAddAction(self.mInsSymbol, self.tr("Minus Sign"))
self.aInsMinus.setShortcut("Ctrl+K, Ctrl+M")
self.aInsMinus.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_MINUS)
@@ -556,7 +556,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsMinus)
# Insert > Times Sign
self.aInsTimes = self.mInsSymbol.addAction(self.tr("Times Sign"))
self.aInsTimes = qtAddAction(self.mInsSymbol, self.tr("Times Sign"))
self.aInsTimes.setShortcut("Ctrl+K, Ctrl+X")
self.aInsTimes.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_TIMES)
@@ -564,7 +564,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsTimes)
# Insert > Division
self.aInsDivide = self.mInsSymbol.addAction(self.tr("Division Sign"))
self.aInsDivide = qtAddAction(self.mInsSymbol, self.tr("Division Sign"))
self.aInsDivide.setShortcut("Ctrl+K, Ctrl+D")
self.aInsDivide.triggered.connect(
lambda: self.requestDocInsertText.emit(nwUnicode.U_DIVIDE)
@@ -572,18 +572,18 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsDivide)
# Insert > Tags and References
self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References"))
self.mInsKeywords = qtAddMenu(self.insMenu, self.tr("Tags and References"))
for key in nwKeyWords.ALL_KEYS:
action = self.mInsKeywords.addAction(trConst(nwLabels.KEY_NAME[key]))
action = qtAddAction(self.mInsKeywords, trConst(nwLabels.KEY_NAME[key]))
action.setShortcut(nwLabels.KEY_SHORTCUT[key])
action.triggered.connect(qtLambda(self.requestDocKeyWordInsert.emit, key))
self.mainGui.addAction(action)
# Insert > Special Comments
self.mInsComments = self.insMenu.addMenu(self.tr("Special Comments"))
self.mInsComments = qtAddMenu(self.insMenu, self.tr("Special Comments"))
# Insert > Synopsis Comment
self.aInsSynopsis = self.mInsComments.addAction(self.tr("Synopsis Comment"))
self.aInsSynopsis = qtAddAction(self.mInsComments, self.tr("Synopsis Comment"))
self.aInsSynopsis.setShortcut("Ctrl+K, S")
self.aInsSynopsis.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.SYNOPSIS)
@@ -591,7 +591,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsSynopsis)
# Insert > Short Description Comment
self.aInsShort = self.mInsComments.addAction(self.tr("Short Description Comment"))
self.aInsShort = qtAddAction(self.mInsComments, self.tr("Short Description Comment"))
self.aInsShort.setShortcut("Ctrl+K, H")
self.aInsShort.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.SHORT)
@@ -599,47 +599,47 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aInsShort)
# Insert > Word/Character Count
self.mInsField = self.insMenu.addMenu(self.tr("Word/Character Count"))
self.mInsField = qtAddMenu(self.insMenu, self.tr("Word/Character Count"))
for field in nwStats.ALL_FIELDS:
value = nwShortcode.FIELD_VALUE.format(field)
action = self.mInsField.addAction(trConst(nwLabels.STATS_NAME[field]))
action = qtAddAction(self.mInsField, trConst(nwLabels.STATS_NAME[field]))
action.triggered.connect(qtLambda(self.requestDocInsertText.emit, value))
# Insert > Breaks and Vertical Space
self.mInsBreaks = self.insMenu.addMenu(self.tr("Breaks and Vertical Space"))
self.mInsBreaks = qtAddMenu(self.insMenu, self.tr("Breaks and Vertical Space"))
# Insert > New Page
self.aInsNewPage = self.mInsBreaks.addAction(self.tr("Page Break"))
self.aInsNewPage = qtAddAction(self.mInsBreaks, self.tr("Page Break"))
self.aInsNewPage.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.NEW_PAGE)
)
# Insert > Forced Line Break
self.aInsLineBreak = self.mInsBreaks.addAction(self.tr("Forced Line Break"))
self.aInsLineBreak = qtAddAction(self.mInsBreaks, self.tr("Forced Line Break"))
self.aInsLineBreak.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.LINE_BRK)
)
# Insert > Vertical Space (Single)
self.aInsVSpaceS = self.mInsBreaks.addAction(self.tr("Vertical Space (Single)"))
self.aInsVSpaceS = qtAddAction(self.mInsBreaks, self.tr("Vertical Space (Single)"))
self.aInsVSpaceS.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.VSPACE_S)
)
# Insert > Vertical Space (Multi)
self.aInsVSpaceM = self.mInsBreaks.addAction(self.tr("Vertical Space (Multi)"))
self.aInsVSpaceM = qtAddAction(self.mInsBreaks, self.tr("Vertical Space (Multi)"))
self.aInsVSpaceM.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.VSPACE_M)
)
# Insert > Placeholder Text
self.aLipsumText = self.insMenu.addAction(self.tr("Placeholder Text"))
self.aLipsumText = qtAddAction(self.insMenu, self.tr("Placeholder Text"))
self.aLipsumText.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
)
# Insert > Footnote
self.aFootnote = self.insMenu.addAction(self.tr("Footnote"))
self.aFootnote = qtAddAction(self.insMenu, self.tr("Footnote"))
self.aFootnote.triggered.connect(
lambda: self.requestDocInsert.emit(nwDocInsert.FOOTNOTE)
)
@@ -649,10 +649,10 @@ class GuiMainMenu(QMenuBar):
def _buildFormatMenu(self) -> None:
"""Assemble the Format menu."""
# Format
self.fmtMenu = self.addMenu(self.tr("&Format"))
self.fmtMenu = qtAddMenu(self, self.tr("&Format"))
# Format > Bold
self.aFmtBold = self.fmtMenu.addAction(self.tr("Bold"))
self.aFmtBold = qtAddAction(self.fmtMenu, self.tr("Bold"))
self.aFmtBold.setShortcut("Ctrl+B")
self.aFmtBold.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.MD_BOLD)
@@ -660,7 +660,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtBold)
# Format > Italic
self.aFmtItalic = self.fmtMenu.addAction(self.tr("Italic"))
self.aFmtItalic = qtAddAction(self.fmtMenu, self.tr("Italic"))
self.aFmtItalic.setShortcut("Ctrl+I")
self.aFmtItalic.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.MD_ITALIC)
@@ -668,7 +668,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtItalic)
# Format > Strikethrough
self.aFmtStrike = self.fmtMenu.addAction(self.tr("Strikethrough"))
self.aFmtStrike = qtAddAction(self.fmtMenu, self.tr("Strikethrough"))
self.aFmtStrike.setShortcut("Ctrl+D")
self.aFmtStrike.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.MD_STRIKE)
@@ -679,7 +679,7 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Double Quotes
self.aFmtDQuote = self.fmtMenu.addAction(self.tr("Wrap Double Quotes"))
self.aFmtDQuote = qtAddAction(self.fmtMenu, self.tr("Wrap Double Quotes"))
self.aFmtDQuote.setShortcut("Ctrl+\"")
self.aFmtDQuote.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.D_QUOTE)
@@ -687,7 +687,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtDQuote)
# Format > Single Quotes
self.aFmtSQuote = self.fmtMenu.addAction(self.tr("Wrap Single Quotes"))
self.aFmtSQuote = qtAddAction(self.fmtMenu, self.tr("Wrap Single Quotes"))
self.aFmtSQuote.setShortcut("Ctrl+'")
self.aFmtSQuote.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.S_QUOTE)
@@ -698,46 +698,46 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Shortcodes
self.mShortcodes = self.fmtMenu.addMenu(self.tr("More Formats ..."))
self.mShortcodes = qtAddMenu(self.fmtMenu, self.tr("More Formats ..."))
# Shortcode Bold
self.aScBold = self.mShortcodes.addAction(self.tr("Bold (Shortcode)"))
self.aScBold = qtAddAction(self.mShortcodes, self.tr("Bold (Shortcode)"))
self.aScBold.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_BOLD)
)
# Shortcode Italic
self.aScItalic = self.mShortcodes.addAction(self.tr("Italics (Shortcode)"))
self.aScItalic = qtAddAction(self.mShortcodes, self.tr("Italics (Shortcode)"))
self.aScItalic.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
)
# Shortcode Strikethrough
self.aScStrike = self.mShortcodes.addAction(self.tr("Strikethrough (Shortcode)"))
self.aScStrike = qtAddAction(self.mShortcodes, self.tr("Strikethrough (Shortcode)"))
self.aScStrike.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_STRIKE)
)
# Shortcode Underline
self.aScULine = self.mShortcodes.addAction(self.tr("Underline"))
self.aScULine = qtAddAction(self.mShortcodes, self.tr("Underline"))
self.aScULine.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
)
# Shortcode Mark
self.aScMark = self.mShortcodes.addAction(self.tr("Highlight"))
self.aScMark = qtAddAction(self.mShortcodes, self.tr("Highlight"))
self.aScMark.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_MARK)
)
# Shortcode Superscript
self.aScSuper = self.mShortcodes.addAction(self.tr("Superscript"))
self.aScSuper = qtAddAction(self.mShortcodes, self.tr("Superscript"))
self.aScSuper.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_SUP)
)
# Shortcode Subscript
self.aScSub = self.mShortcodes.addAction(self.tr("Subscript"))
self.aScSub = qtAddAction(self.mShortcodes, self.tr("Subscript"))
self.aScSub.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.SC_SUB)
)
@@ -746,7 +746,7 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Heading 1 (Partition)
self.aFmtHead1 = self.fmtMenu.addAction(trConst(nwStyles.T_LABEL["H1"]))
self.aFmtHead1 = qtAddAction(self.fmtMenu, trConst(nwStyles.T_LABEL["H1"]))
self.aFmtHead1.setShortcut("Ctrl+1")
self.aFmtHead1.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1)
@@ -754,7 +754,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtHead1)
# Format > Heading 2 (Chapter)
self.aFmtHead2 = self.fmtMenu.addAction(trConst(nwStyles.T_LABEL["H2"]))
self.aFmtHead2 = qtAddAction(self.fmtMenu, trConst(nwStyles.T_LABEL["H2"]))
self.aFmtHead2.setShortcut("Ctrl+2")
self.aFmtHead2.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2)
@@ -762,7 +762,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtHead2)
# Format > Heading 3 (Scene)
self.aFmtHead3 = self.fmtMenu.addAction(trConst(nwStyles.T_LABEL["H3"]))
self.aFmtHead3 = qtAddAction(self.fmtMenu, trConst(nwStyles.T_LABEL["H3"]))
self.aFmtHead3.setShortcut("Ctrl+3")
self.aFmtHead3.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3)
@@ -770,7 +770,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtHead3)
# Format > Heading 4 (Section)
self.aFmtHead4 = self.fmtMenu.addAction(trConst(nwStyles.T_LABEL["H4"]))
self.aFmtHead4 = qtAddAction(self.fmtMenu, trConst(nwStyles.T_LABEL["H4"]))
self.aFmtHead4.setShortcut("Ctrl+4")
self.aFmtHead4.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4)
@@ -781,19 +781,19 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Novel Title
self.aFmtTitle = self.fmtMenu.addAction(self.tr("Novel Title"))
self.aFmtTitle = qtAddAction(self.fmtMenu, self.tr("Novel Title"))
self.aFmtTitle.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_TTL)
)
# Format > Unnumbered Chapter
self.aFmtUnNum = self.fmtMenu.addAction(self.tr("Unnumbered Chapter"))
self.aFmtUnNum = qtAddAction(self.fmtMenu, self.tr("Unnumbered Chapter"))
self.aFmtUnNum.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
)
# Format > Alternative Scene
self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Alternative Scene"))
self.aFmtHardSc = qtAddAction(self.fmtMenu, self.tr("Alternative Scene"))
self.aFmtHardSc.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
)
@@ -802,7 +802,7 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Align Left
self.aFmtAlignLeft = self.fmtMenu.addAction(self.tr("Align Left"))
self.aFmtAlignLeft = qtAddAction(self.fmtMenu, self.tr("Align Left"))
self.aFmtAlignLeft.setShortcut("Ctrl+5")
self.aFmtAlignLeft.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_L)
@@ -810,7 +810,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtAlignLeft)
# Format > Align Centre
self.aFmtAlignCentre = self.fmtMenu.addAction(self.tr("Align Centre"))
self.aFmtAlignCentre = qtAddAction(self.fmtMenu, self.tr("Align Centre"))
self.aFmtAlignCentre.setShortcut("Ctrl+6")
self.aFmtAlignCentre.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_C)
@@ -818,7 +818,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtAlignCentre)
# Format > Align Right
self.aFmtAlignRight = self.fmtMenu.addAction(self.tr("Align Right"))
self.aFmtAlignRight = qtAddAction(self.fmtMenu, self.tr("Align Right"))
self.aFmtAlignRight.setShortcut("Ctrl+7")
self.aFmtAlignRight.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_R)
@@ -829,7 +829,7 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Indent Left
self.aFmtIndentLeft = self.fmtMenu.addAction(self.tr("Indent Left"))
self.aFmtIndentLeft = qtAddAction(self.fmtMenu, self.tr("Indent Left"))
self.aFmtIndentLeft.setShortcut("Ctrl+8")
self.aFmtIndentLeft.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.INDENT_L)
@@ -837,7 +837,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtIndentLeft)
# Format > Indent Right
self.aFmtIndentRight = self.fmtMenu.addAction(self.tr("Indent Right"))
self.aFmtIndentRight = qtAddAction(self.fmtMenu, self.tr("Indent Right"))
self.aFmtIndentRight.setShortcut("Ctrl+9")
self.aFmtIndentRight.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.INDENT_R)
@@ -848,7 +848,7 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Comment
self.aFmtComment = self.fmtMenu.addAction(self.tr("Toggle Comment"))
self.aFmtComment = qtAddAction(self.fmtMenu, self.tr("Toggle Comment"))
self.aFmtComment.setShortcut("Ctrl+/")
self.aFmtComment.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_COM)
@@ -856,14 +856,14 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFmtComment)
# Format > Ignore Text
self.aFmtIgnore = self.fmtMenu.addAction(self.tr("Toggle Ignore Text"))
self.aFmtIgnore = qtAddAction(self.fmtMenu, self.tr("Toggle Ignore Text"))
self.aFmtIgnore.setShortcut("Ctrl+Shift+D")
self.aFmtIgnore.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_IGN)
)
# Format > Remove Block Format
self.aFmtNoFormat = self.fmtMenu.addAction(self.tr("Remove Block Format"))
self.aFmtNoFormat = qtAddAction(self.fmtMenu, self.tr("Remove Block Format"))
self.aFmtNoFormat.setShortcuts(["Ctrl+0", "Ctrl+Shift+/"])
self.aFmtNoFormat.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_TXT)
@@ -874,19 +874,19 @@ class GuiMainMenu(QMenuBar):
self.fmtMenu.addSeparator()
# Format > Replace Straight Single Quotes
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Replace Straight Single Quotes"))
self.aFmtReplSng = qtAddAction(self.fmtMenu, self.tr("Replace Straight Single Quotes"))
self.aFmtReplSng.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG)
)
# Format > Replace Straight Double Quotes
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Replace Straight Double Quotes"))
self.aFmtReplDbl = qtAddAction(self.fmtMenu, self.tr("Replace Straight Double Quotes"))
self.aFmtReplDbl.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL)
)
# Format > Remove In-Paragraph Breaks
self.aFmtRmBreaks = self.fmtMenu.addAction(self.tr("Remove In-Paragraph Breaks"))
self.aFmtRmBreaks = qtAddAction(self.fmtMenu, self.tr("Remove In-Paragraph Breaks"))
self.aFmtRmBreaks.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.RM_BREAKS)
)
@@ -896,28 +896,28 @@ class GuiMainMenu(QMenuBar):
def _buildSearchMenu(self) -> None:
"""Assemble the Search menu."""
# Search
self.srcMenu = self.addMenu(self.tr("&Search"))
self.srcMenu = qtAddMenu(self, self.tr("&Search"))
# Search > Find
self.aFind = self.srcMenu.addAction(self.tr("Find"))
self.aFind = qtAddAction(self.srcMenu, self.tr("Find"))
self.aFind.setShortcut("Ctrl+F")
self.aFind.triggered.connect(qtLambda(self.mainGui.docEditor.beginSearch))
self.mainGui.addAction(self.aFind)
# Search > Replace
self.aReplace = self.srcMenu.addAction(self.tr("Replace"))
self.aReplace = qtAddAction(self.srcMenu, self.tr("Replace"))
self.aReplace.setShortcut("Ctrl+=" if CONFIG.osDarwin else "Ctrl+H")
self.aReplace.triggered.connect(qtLambda(self.mainGui.docEditor.beginReplace))
self.mainGui.addAction(self.aReplace)
# Search > Find Next
self.aFindNext = self.srcMenu.addAction(self.tr("Find Next"))
self.aFindNext = qtAddAction(self.srcMenu, self.tr("Find Next"))
self.aFindNext.setShortcuts(["Ctrl+G", "F3"] if CONFIG.osDarwin else ["F3", "Ctrl+G"])
self.aFindNext.triggered.connect(qtLambda(self.mainGui.docEditor.findNext))
self.mainGui.addAction(self.aFindNext)
# Search > Find Prev
self.aFindPrev = self.srcMenu.addAction(self.tr("Find Previous"))
self.aFindPrev = qtAddAction(self.srcMenu, self.tr("Find Previous"))
self.aFindPrev.setShortcuts(
["Ctrl+Shift+G", "Shift+F3"] if CONFIG.osDarwin else ["Shift+F3", "Ctrl+Shift+G"]
)
@@ -925,7 +925,7 @@ class GuiMainMenu(QMenuBar):
self.mainGui.addAction(self.aFindPrev)
# Search > Replace Next
self.aReplaceNext = self.srcMenu.addAction(self.tr("Replace Next"))
self.aReplaceNext = qtAddAction(self.srcMenu, self.tr("Replace Next"))
self.aReplaceNext.setShortcut("Ctrl+Shift+1")
self.aReplaceNext.triggered.connect(qtLambda(self.mainGui.docEditor.replaceNext))
self.mainGui.addAction(self.aReplaceNext)
@@ -934,7 +934,7 @@ class GuiMainMenu(QMenuBar):
self.srcMenu.addSeparator()
# Search > Find in Project
self.aFindProj = self.srcMenu.addAction(self.tr("Find in Project"))
self.aFindProj = qtAddAction(self.srcMenu, self.tr("Find in Project"))
self.aFindProj.setShortcut("Ctrl+Shift+F")
self.aFindProj.triggered.connect(lambda: self.requestViewChange.emit(nwView.SEARCH))
@@ -943,17 +943,17 @@ class GuiMainMenu(QMenuBar):
def _buildToolsMenu(self) -> None:
"""Assemble the Tools menu."""
# Tools
self.toolsMenu = self.addMenu(self.tr("&Tools"))
self.toolsMenu = qtAddMenu(self, self.tr("&Tools"))
# Tools > Check Spelling
self.aSpellCheck = self.toolsMenu.addAction(self.tr("Check Spelling"))
self.aSpellCheck = qtAddAction(self.toolsMenu, self.tr("Check Spelling"))
self.aSpellCheck.setCheckable(True)
self.aSpellCheck.setChecked(SHARED.project.data.spellCheck)
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
self.aSpellCheck.setShortcut("Ctrl+F7")
self.mainGui.addAction(self.aSpellCheck)
self.mSelectLanguage = self.toolsMenu.addMenu(self.tr("Spell Check Language"))
self.mSelectLanguage = qtAddMenu(self.toolsMenu, self.tr("Spell Check Language"))
languages = SHARED.spelling.listDictionaries()
languages.insert(0, ("None", self.tr("Default")))
for n, (tag, language) in enumerate(languages):
@@ -963,25 +963,25 @@ class GuiMainMenu(QMenuBar):
self.mSelectLanguage.addAction(aSpell)
# Tools > Re-Run Spell Check
self.aReRunSpell = self.toolsMenu.addAction(self.tr("Re-Run Spell Check"))
self.aReRunSpell = qtAddAction(self.toolsMenu, self.tr("Re-Run Spell Check"))
self.aReRunSpell.setShortcut("F7")
self.aReRunSpell.triggered.connect(qtLambda(self.mainGui.docEditor.spellCheckDocument))
self.mainGui.addAction(self.aReRunSpell)
# Tools > Project Word List
self.aEditWordList = self.toolsMenu.addAction(self.tr("Project Word List"))
self.aEditWordList = qtAddAction(self.toolsMenu, self.tr("Project Word List"))
self.aEditWordList.triggered.connect(self.mainGui.showProjectWordListDialog)
# Tools > Add Dictionaries
if CONFIG.osWindows or CONFIG.isDebug:
self.aAddDicts = self.toolsMenu.addAction(self.tr("Add Dictionaries"))
self.aAddDicts = qtAddAction(self.toolsMenu, self.tr("Add Dictionaries"))
self.aAddDicts.triggered.connect(self.mainGui.showDictionariesDialog)
# Tools > Separator
self.toolsMenu.addSeparator()
# Tools > Rebuild Index
self.aRebuildIndex = self.toolsMenu.addAction(self.tr("Rebuild Index"))
self.aRebuildIndex = qtAddAction(self.toolsMenu, self.tr("Rebuild Index"))
self.aRebuildIndex.setShortcut("F9")
self.aRebuildIndex.triggered.connect(qtLambda(self.mainGui.rebuildIndex))
@@ -989,21 +989,21 @@ class GuiMainMenu(QMenuBar):
self.toolsMenu.addSeparator()
# Tools > Backup Project
self.aBackupProject = self.toolsMenu.addAction(self.tr("Backup Project"))
self.aBackupProject = qtAddAction(self.toolsMenu, self.tr("Backup Project"))
self.aBackupProject.triggered.connect(qtLambda(SHARED.project.backupProject, True))
# Tools > Build Manuscript
self.aBuildManuscript = self.toolsMenu.addAction(self.tr("Build Manuscript"))
self.aBuildManuscript = qtAddAction(self.toolsMenu, self.tr("Build Manuscript"))
self.aBuildManuscript.setShortcut("F5")
self.aBuildManuscript.triggered.connect(self.mainGui.showBuildManuscriptDialog)
# Tools > Writing Statistics
self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics"))
self.aWritingStats = qtAddAction(self.toolsMenu, self.tr("Writing Statistics"))
self.aWritingStats.setShortcut("F6")
self.aWritingStats.triggered.connect(self.mainGui.showWritingStatsDialog)
# Tools > Preferences
self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences"))
self.aPreferences = qtAddAction(self.toolsMenu, self.tr("Preferences"))
self.aPreferences.setShortcut("Ctrl+,")
self.aPreferences.setMenuRole(QAction.MenuRole.PreferencesRole)
self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
@@ -1014,15 +1014,15 @@ class GuiMainMenu(QMenuBar):
def _buildHelpMenu(self) -> None:
"""Assemble the Help menu."""
# Help
self.helpMenu = self.addMenu(self.tr("&Help"))
self.helpMenu = qtAddMenu(self, self.tr("&Help"))
# Help > About
self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter"))
self.aAboutNW = qtAddAction(self.helpMenu, self.tr("About novelWriter"))
self.aAboutNW.setMenuRole(QAction.MenuRole.AboutRole)
self.aAboutNW.triggered.connect(self.mainGui.showAboutNWDialog)
# Help > About Qt
self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt"))
self.aAboutQt = qtAddAction(self.helpMenu, self.tr("About Qt"))
self.aAboutQt.setMenuRole(QAction.MenuRole.AboutQtRole)
self.aAboutQt.triggered.connect(self.mainGui.showAboutQtDialog)
@@ -1030,14 +1030,14 @@ class GuiMainMenu(QMenuBar):
self.helpMenu.addSeparator()
# Help > User Manual (Online)
self.aHelpDocs = self.helpMenu.addAction(self.tr("User Manual (Online)"))
self.aHelpDocs = qtAddAction(self.helpMenu, self.tr("User Manual (Online)"))
self.aHelpDocs.setShortcut("F1")
self.aHelpDocs.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_DOCS))
self.mainGui.addAction(self.aHelpDocs)
# Help > User Manual (PDF)
if isinstance(CONFIG.pdfDocs, Path):
self.aPdfDocs = self.helpMenu.addAction(self.tr("User Manual (PDF)"))
self.aPdfDocs = qtAddAction(self.helpMenu, self.tr("User Manual (PDF)"))
self.aPdfDocs.setShortcut("Shift+F1")
self.aPdfDocs.triggered.connect(self._openUserManualFile)
self.mainGui.addAction(self.aPdfDocs)
@@ -1046,15 +1046,15 @@ class GuiMainMenu(QMenuBar):
self.helpMenu.addSeparator()
# Document > Report an Issue
self.aIssue = self.helpMenu.addAction(self.tr("Report an Issue (GitHub)"))
self.aIssue = qtAddAction(self.helpMenu, self.tr("Report an Issue (GitHub)"))
self.aIssue.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_REPORT))
# Document > Ask a Question
self.aQuestion = self.helpMenu.addAction(self.tr("Ask a Question (GitHub)"))
self.aQuestion = qtAddAction(self.helpMenu, self.tr("Ask a Question (GitHub)"))
self.aQuestion.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_HELP))
# Document > Main Website
self.aWebsite = self.helpMenu.addAction(self.tr("The novelWriter Website"))
self.aWebsite = qtAddAction(self.helpMenu, self.tr("The novelWriter Website"))
self.aWebsite.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_WEB))
return
+18 -18
View File
@@ -38,7 +38,7 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import minmax, qtLambda
from novelwriter.common import minmax, qtAddAction, qtAddMenu, qtLambda
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
from novelwriter.core.index import IndexHeading
from novelwriter.enum import nwChange, nwDocMode, nwItemClass, nwOutline
@@ -199,7 +199,6 @@ class GuiNovelToolBar(QWidget):
self.novelView = novelView
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -211,7 +210,7 @@ class GuiNovelToolBar(QWidget):
self.novelValue = NovelSelector(self)
self.novelValue.setFont(selFont)
self.novelValue.setListFormat(self.tr("Outline of {0}"))
self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
self.novelValue.setMinimumWidth(150)
self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
@@ -227,7 +226,7 @@ class GuiNovelToolBar(QWidget):
# More Options Menu
self.mMore = QMenu(self)
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
self.mLastCol = qtAddMenu(self.mMore, self.tr("Last Column"))
self.gLastCol = QActionGroup(self.mMore)
self.aLastCol = {}
self._addLastColAction(NovelTreeColumn.HIDDEN, self.tr("Hidden"))
@@ -236,7 +235,7 @@ class GuiNovelToolBar(QWidget):
self._addLastColAction(NovelTreeColumn.PLOT, self.tr("Novel Plot"))
self.mLastCol.addSeparator()
self.aLastColSize = self.mLastCol.addAction(self.tr("Column Size"))
self.aLastColSize = qtAddAction(self.mLastCol, self.tr("Column Size"))
self.aLastColSize.triggered.connect(self._selectLastColumnSize)
self.tbMore = NIconToolButton(self, iSz)
@@ -249,7 +248,7 @@ class GuiNovelToolBar(QWidget):
self.outerBox.addWidget(self.tbNovel)
self.outerBox.addWidget(self.tbRefresh)
self.outerBox.addWidget(self.tbMore)
self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
self.outerBox.setContentsMargins(2, 2, 0, 2)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
@@ -343,7 +342,7 @@ class GuiNovelToolBar(QWidget):
def _addLastColAction(self, colType: NovelTreeColumn, actionLabel: str) -> None:
"""Add a column selection entry to the last column menu."""
aLast = self.mLastCol.addAction(actionLabel)
aLast = qtAddAction(self.mLastCol, actionLabel)
aLast.setCheckable(True)
aLast.setActionGroup(self.gLastCol)
aLast.triggered.connect(qtLambda(self.setLastColType, colType))
@@ -388,7 +387,6 @@ class GuiNovelTree(QTreeWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setIconSize(iSz)
self.setFrameStyle(QFrame.Shape.NoFrame)
@@ -403,13 +401,13 @@ class GuiNovelTree(QTreeWidget):
self.setDragEnabled(False)
# Lock the column sizes
treeHeader = self.header()
treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + cMg)
treeHeader.setSectionResizeMode(self.C_TITLE, QtHeaderStretch)
treeHeader.setSectionResizeMode(self.C_WORDS, QtHeaderToContents)
treeHeader.setSectionResizeMode(self.C_EXTRA, QtHeaderToContents)
treeHeader.setSectionResizeMode(self.C_MORE, QtHeaderToContents)
if header := self.header():
header.setStretchLastSection(False)
header.setMinimumSectionSize(iPx + 6)
header.setSectionResizeMode(self.C_TITLE, QtHeaderStretch)
header.setSectionResizeMode(self.C_WORDS, QtHeaderToContents)
header.setSectionResizeMode(self.C_EXTRA, QtHeaderToContents)
header.setSectionResizeMode(self.C_MORE, QtHeaderToContents)
# Pre-Generate Tree Formatting
fH1 = self.font()
@@ -678,8 +676,9 @@ class GuiNovelTree(QTreeWidget):
return
def _updateTreeItemValues(self, trItem: QTreeWidgetItem, idxItem: IndexHeading,
tHandle: str, sTitle: str) -> None:
def _updateTreeItemValues(
self, trItem: QTreeWidgetItem, idxItem: IndexHeading, tHandle: str, sTitle: str
) -> None:
"""Set the tree item values from the index entry."""
iLevel = nwStyles.H_LEVEL.get(idxItem.level, 0)
hDec = SHARED.theme.getHeaderDecoration(iLevel)
@@ -691,7 +690,8 @@ class GuiNovelTree(QTreeWidget):
trItem.setData(self.C_MORE, QtDecoration, self._pMore)
# Custom column
mW = int(self._lastColSize * self.viewport().width())
viewport = self.viewport()
mW = int(self._lastColSize * (viewport.width() if viewport else 100))
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
trItem.setText(self.C_EXTRA, elideText)
+30 -39
View File
@@ -78,7 +78,7 @@ class GuiOutlineView(QWidget):
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.setContentsMargins(0, 0, CONFIG.pxInt(4), 0)
self.outerBox.setContentsMargins(0, 0, 4, 0)
self.outerBox.addWidget(self.outlineBar)
self.outerBox.addWidget(self.splitOutline)
@@ -225,11 +225,11 @@ class GuiOutlineToolBar(QToolBar):
self.novelLabel = NColourLabel(
self.tr("Outline of"), self, scale=NColourLabel.HEADER_SCALE, bold=True
)
self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
self.novelLabel.setContentsMargins(0, 0, 12, 0)
self.novelValue = NovelSelector(self)
self.novelValue.setIncludeAll(True)
self.novelValue.setMinimumWidth(CONFIG.pxInt(200))
self.novelValue.setMinimumWidth(200)
self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
# Actions
@@ -390,8 +390,8 @@ class GuiOutlineTree(QTreeWidget):
self.setIconSize(SHARED.theme.baseIconSize)
self.setIndentation(0)
self.treeHead = self.header()
self.treeHead.sectionMoved.connect(self._columnMoved)
if header := self.header():
header.sectionMoved.connect(self._columnMoved)
# Pre-Generate Tree Formatting
fH1 = self.font()
@@ -623,7 +623,7 @@ class GuiOutlineTree(QTreeWidget):
continue
tmpOrder.append(nwOutline[name])
tmpHidden[nwOutline[name]] = hidden
tmpWidth[nwOutline[name]] = CONFIG.pxInt(width)
tmpWidth[nwOutline[name]] = width
except Exception:
logger.error("Invalid column state")
logException()
@@ -647,26 +647,22 @@ class GuiOutlineTree(QTreeWidget):
save the current width of hidden columns though. This preserves
the last known width in case they're unhidden again.
"""
# If we haven't built the tree, there is nothing to save.
if self._lastBuild == 0:
return
colState = {}
for iCol in range(self.columnCount()):
hItem = self._treeOrder[iCol]
iLog = self.treeHead.logicalIndex(iCol)
logHidden = self.isColumnHidden(iLog)
orgWidth = CONFIG.rpxInt(self._colWidth[hItem])
logWidth = CONFIG.rpxInt(self.columnWidth(iLog))
colState[hItem.name] = [
logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
]
logger.debug("Saving State: GuiOutline")
pOptions = SHARED.project.options
pOptions.setValue("GuiOutline", "columnState", colState)
pOptions.saveSettings()
if self._lastBuild > 0 and (header := self.header()):
colState = {}
for iCol in range(self.columnCount()):
hItem = self._treeOrder[iCol]
iLog = header.logicalIndex(iCol)
logHidden = self.isColumnHidden(iLog)
orgWidth = self._colWidth[hItem]
logWidth = self.columnWidth(iLog)
colState[hItem.name] = [
logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
]
logger.debug("Saving State: GuiOutline")
pOptions = SHARED.project.options
pOptions.setValue("GuiOutline", "columnState", colState)
pOptions.saveSettings()
return
def _populateTree(self, rootHandle: str | None) -> None:
@@ -819,8 +815,6 @@ class GuiOutlineDetails(QScrollArea):
minTitle = 30*SHARED.theme.textNWidth
maxTitle = 40*SHARED.theme.textNWidth
wCount = SHARED.theme.getTextWidth("999,999")
hSpace = int(CONFIG.pxInt(10))
vSpace = int(CONFIG.pxInt(4))
bFont = SHARED.theme.guiFontB
@@ -899,8 +893,8 @@ class GuiOutlineDetails(QScrollArea):
self.mainForm.setColumnStretch(1, 1)
self.mainForm.setRowStretch(4, 1)
self.mainForm.setHorizontalSpacing(hSpace)
self.mainForm.setVerticalSpacing(vSpace)
self.mainForm.setHorizontalSpacing(10)
self.mainForm.setVerticalSpacing(4)
# Selected Item Tags
self.tagsForm = QGridLayout()
@@ -923,8 +917,8 @@ class GuiOutlineDetails(QScrollArea):
self.tagsForm.setColumnStretch(1, 1)
self.tagsForm.setRowStretch(len(self.tagValues), 1)
self.tagsForm.setHorizontalSpacing(hSpace)
self.tagsForm.setVerticalSpacing(vSpace)
self.tagsForm.setHorizontalSpacing(10)
self.tagsForm.setVerticalSpacing(4)
# Assemble
self.mainSplit = QSplitter(Qt.Orientation.Horizontal)
@@ -965,21 +959,18 @@ class GuiOutlineDetails(QScrollArea):
width = parent.width() if isinstance(parent, QWidget) else 1000
pOptions = SHARED.project.options
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "detailsWidth", width//3)),
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "tagsWidth", 2*width//3))
pOptions.getInt("GuiOutlineDetails", "detailsWidth", width//3),
pOptions.getInt("GuiOutlineDetails", "tagsWidth", 2*width//3),
])
return
def saveGuiSettings(self) -> None:
"""Run close project tasks."""
mainSplit = self.mainSplit.sizes()
detailsWidth = CONFIG.rpxInt(mainSplit[0])
tagsWidth = CONFIG.rpxInt(mainSplit[1])
logger.debug("Saving State: GuiOutlineDetails")
mainSplit = self.mainSplit.sizes()
pOptions = SHARED.project.options
pOptions.setValue("GuiOutlineDetails", "detailsWidth", detailsWidth)
pOptions.setValue("GuiOutlineDetails", "tagsWidth", tagsWidth)
pOptions.setValue("GuiOutlineDetails", "detailsWidth", mainSplit[0])
pOptions.setValue("GuiOutlineDetails", "tagsWidth", mainSplit[1])
return
def clearDetails(self) -> None:
+60 -56
View File
@@ -40,7 +40,7 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import qtLambda
from novelwriter.common import qtAddAction, qtAddMenu, qtLambda
from novelwriter.constants import nwLabels, nwStyles, nwUnicode, trConst
from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
from novelwriter.core.item import NWItem
@@ -243,7 +243,6 @@ class GuiProjectToolBar(QWidget):
self.projTree = projView.projTree
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -274,27 +273,27 @@ class GuiProjectToolBar(QWidget):
# Add Item Menu
self.mAdd = QMenu(self)
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
self.aAddEmpty = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["document"]))
self.aAddEmpty.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
)
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
self.aAddChap = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
self.aAddChap.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False)
)
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
)
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
self.aAddNote = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["note"]))
self.aAddNote.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=True)
)
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
self.aAddFolder = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
self.aAddFolder.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FOLDER)
)
@@ -304,7 +303,7 @@ class GuiProjectToolBar(QWidget):
self.mTemplates.menuItemTriggered.connect(lambda h: self.newDocumentFromTemplate.emit(h))
self.mAdd.addMenu(self.mTemplates)
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
self.mAddRoot = qtAddMenu(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["root"]))
self._buildRootMenu()
self.tbAdd = NIconToolButton(self, iSz)
@@ -315,13 +314,13 @@ class GuiProjectToolBar(QWidget):
# More Options Menu
self.mMore = QMenu(self)
self.aExpand = self.mMore.addAction(self.tr("Expand All"))
self.aExpand = qtAddAction(self.mMore, self.tr("Expand All"))
self.aExpand.triggered.connect(self.projTree.expandAll)
self.aCollapse = self.mMore.addAction(self.tr("Collapse All"))
self.aCollapse = qtAddAction(self.mMore, self.tr("Collapse All"))
self.aCollapse.triggered.connect(self.projTree.collapseAll)
self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
self.aEmptyTrash = qtAddAction(self.mMore, self.tr("Empty Trash"))
self.aEmptyTrash.triggered.connect(self.projTree.emptyTrash)
self.tbMore = NIconToolButton(self, iSz)
@@ -336,7 +335,7 @@ class GuiProjectToolBar(QWidget):
self.outerBox.addWidget(self.tbMoveD)
self.outerBox.addWidget(self.tbAdd)
self.outerBox.addWidget(self.tbMore)
self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
self.outerBox.setContentsMargins(2, 2, 0, 2)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
@@ -392,7 +391,7 @@ class GuiProjectToolBar(QWidget):
logger.debug("Rebuilding quick links menu")
self.mQuick.clear()
for tHandle, nwItem in SHARED.project.tree.iterRoots(None):
action = self.mQuick.addAction(nwItem.itemName)
action = qtAddAction(self.mQuick, nwItem.itemName)
action.setData(tHandle)
action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass], "root"))
action.triggered.connect(
@@ -440,7 +439,7 @@ class GuiProjectToolBar(QWidget):
def _buildRootMenu(self) -> None:
"""Build the rood folder menu."""
def addClass(itemClass: nwItemClass) -> None:
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
aNew = qtAddAction(self.mAddRoot, trConst(nwLabels.CLASS_NAME[itemClass]))
aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass], "root"))
aNew.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.ROOT, itemClass)
@@ -561,17 +560,16 @@ class GuiProjectTree(QTreeView):
# Lock the column sizes
iPx = SHARED.theme.baseIconHeight
cMg = CONFIG.pxInt(6)
treeHeader = self.header()
treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + cMg)
treeHeader.setSectionResizeMode(ProjectNode.C_NAME, QtHeaderStretch)
treeHeader.setSectionResizeMode(ProjectNode.C_COUNT, QtHeaderToContents)
treeHeader.setSectionResizeMode(ProjectNode.C_ACTIVE, QtHeaderFixed)
treeHeader.setSectionResizeMode(ProjectNode.C_STATUS, QtHeaderFixed)
treeHeader.resizeSection(ProjectNode.C_ACTIVE, iPx + cMg)
treeHeader.resizeSection(ProjectNode.C_STATUS, iPx + cMg)
if header := self.header():
header.setStretchLastSection(False)
header.setMinimumSectionSize(iPx + 6)
header.setSectionResizeMode(ProjectNode.C_NAME, QtHeaderStretch)
header.setSectionResizeMode(ProjectNode.C_COUNT, QtHeaderToContents)
header.setSectionResizeMode(ProjectNode.C_ACTIVE, QtHeaderFixed)
header.setSectionResizeMode(ProjectNode.C_STATUS, QtHeaderFixed)
header.resizeSection(ProjectNode.C_ACTIVE, iPx + 6)
header.resizeSection(ProjectNode.C_STATUS, iPx + 6)
self.restoreExpandedState()
@@ -969,8 +967,9 @@ class GuiProjectTree(QTreeView):
else:
ctxMenu.buildSingleSelectMenu()
ctxMenu.exec(self.viewport().mapToGlobal(point))
ctxMenu.deleteLater()
if viewport := self.viewport():
ctxMenu.exec(viewport.mapToGlobal(point))
ctxMenu.deleteLater()
return
@@ -1091,7 +1090,8 @@ class _UpdatableMenu(QMenu):
def setActionsVisible(self, value: bool) -> None:
"""Set the visibility of root action."""
self.menuAction().setVisible(value)
if action := self.menuAction():
action.setVisible(value)
return
##
@@ -1135,7 +1135,7 @@ class _TreeContextMenu(QMenu):
def buildTrashMenu(self) -> None:
"""Build the special menu for the Trash folder."""
action = self.addAction(self.tr("Empty Trash"))
action = qtAddAction(self, self.tr("Empty Trash"))
action.triggered.connect(self._tree.emptyTrash)
if self._children:
self._expandCollapse()
@@ -1156,7 +1156,7 @@ class _TreeContextMenu(QMenu):
self.addSeparator()
# Edit Item Settings
action = self.addAction(self.tr("Rename"))
action = qtAddAction(self, self.tr("Rename"))
action.triggered.connect(qtLambda(self._view.renameTreeItem, self._handle))
if isFile:
self._itemHeader()
@@ -1171,7 +1171,7 @@ class _TreeContextMenu(QMenu):
# Process Item
if self._children:
self._expandCollapse()
action = self.addAction(self.tr("Duplicate"))
action = qtAddAction(self, self.tr("Duplicate"))
action.triggered.connect(qtLambda(self._tree.duplicateFromHandle, self._handle))
self._deleteOrTrash()
@@ -1191,12 +1191,12 @@ class _TreeContextMenu(QMenu):
def _docActions(self) -> None:
"""Add document actions."""
action = self.addAction(self.tr("Open Document"))
action = qtAddAction(self, self.tr("Open Document"))
action.triggered.connect(qtLambda(
self._view.openDocumentRequest.emit,
self._handle, nwDocMode.EDIT, "", True
))
action = self.addAction(self.tr("View Document"))
action = qtAddAction(self, self.tr("View Document"))
action.triggered.connect(qtLambda(
self._view.openDocumentRequest.emit,
self._handle, nwDocMode.VIEW, "", False
@@ -1205,7 +1205,7 @@ class _TreeContextMenu(QMenu):
def _itemCreation(self) -> None:
"""Add create item actions."""
menu = self.addMenu(self.tr("Create New ..."))
menu = qtAddMenu(self, self.tr("Create New ..."))
menu.addAction(self._view.projBar.aAddEmpty)
menu.addAction(self._view.projBar.aAddChap)
menu.addAction(self._view.projBar.aAddScene)
@@ -1217,7 +1217,7 @@ class _TreeContextMenu(QMenu):
"""Check if there is a header that can be used for rename."""
SHARED.saveEditor()
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
action = self.addAction(self.tr("Rename to Heading"))
action = qtAddAction(self, self.tr("Rename to Heading"))
action.triggered.connect(
qtLambda(self._view.renameTreeItem, self._handle, hItem.title)
)
@@ -1226,50 +1226,54 @@ class _TreeContextMenu(QMenu):
def _itemActive(self) -> None:
"""Add Active/Inactive actions."""
if len(self._indices) > 1:
mSub = self.addMenu(self.tr("Set Active to ..."))
aOne = mSub.addAction(SHARED.theme.getIcon("checked"), self._tree.trActive)
mSub = qtAddMenu(self, self.tr("Set Active to ..."))
aOne = qtAddAction(mSub, self._tree.trActive)
aOne.setIcon(SHARED.theme.getIcon("checked", "green"))
aOne.triggered.connect(qtLambda(self._iterItemActive, True))
aTwo = mSub.addAction(SHARED.theme.getIcon("unchecked"), self._tree.trInactive)
aTwo = qtAddAction(mSub, self._tree.trInactive)
aTwo.setIcon(SHARED.theme.getIcon("unchecked", "red"))
aTwo.triggered.connect(qtLambda(self._iterItemActive, False))
else:
action = self.addAction(self.tr("Toggle Active"))
action = qtAddAction(self, self.tr("Toggle Active"))
action.triggered.connect(self._toggleItemActive)
return
def _itemStatusImport(self, multi: bool) -> None:
"""Add actions for changing status or importance."""
if self._item.isNovelLike():
menu = self.addMenu(self.tr("Set Status to ..."))
menu = qtAddMenu(self, self.tr("Set Status to ..."))
current = self._item.itemStatus
for key, entry in SHARED.project.data.itemStatus.iterItems():
name = entry.name
if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})"
action = menu.addAction(entry.icon, name)
action = qtAddAction(menu, name)
action.setIcon(entry.icon)
if multi:
action.triggered.connect(qtLambda(self._iterSetItemStatus, key))
else:
action.triggered.connect(qtLambda(self._changeItemStatus, key))
menu.addSeparator()
action = menu.addAction(self.tr("Manage Labels ..."))
action = qtAddAction(menu, self.tr("Manage Labels ..."))
action.triggered.connect(qtLambda(
self._view.projectSettingsRequest.emit,
GuiProjectSettings.PAGE_STATUS
))
else:
menu = self.addMenu(self.tr("Set Importance to ..."))
menu = qtAddMenu(self, self.tr("Set Importance to ..."))
current = self._item.itemImport
for key, entry in SHARED.project.data.itemImport.iterItems():
name = entry.name
if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})"
action = menu.addAction(entry.icon, name)
action = qtAddAction(menu, name)
action.setIcon(entry.icon)
if multi:
action.triggered.connect(qtLambda(self._iterSetItemImport, key))
else:
action.triggered.connect(qtLambda(self._changeItemImport, key))
menu.addSeparator()
action = menu.addAction(self.tr("Manage Labels ..."))
action = qtAddAction(menu, self.tr("Manage Labels ..."))
action.triggered.connect(qtLambda(
self._view.projectSettingsRequest.emit,
GuiProjectSettings.PAGE_IMPORT
@@ -1278,7 +1282,7 @@ class _TreeContextMenu(QMenu):
def _itemTransform(self, isFile: bool, isFolder: bool) -> None:
"""Add actions for the Transform menu."""
menu = self.addMenu(self.tr("Transform ..."))
menu = qtAddMenu(self, self.tr("Transform ..."))
trDoc = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.DOCUMENT])
trNote = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.NOTE])
@@ -1288,42 +1292,42 @@ class _TreeContextMenu(QMenu):
isNoteFile = isFile and self._item.isNoteLayout()
if isNoteFile and self._item.documentAllowed():
action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
action.triggered.connect(qtLambda(self._changeItemLayout, loDoc))
if isDocFile:
action = menu.addAction(self.tr("Convert to {0}").format(trNote))
action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
action.triggered.connect(qtLambda(self._changeItemLayout, loNote))
if isFolder and self._item.documentAllowed():
action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
action.triggered.connect(qtLambda(self._convertFolderToFile, loDoc))
if isFolder:
action = menu.addAction(self.tr("Convert to {0}").format(trNote))
action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
action.triggered.connect(qtLambda(self._convertFolderToFile, loNote))
if self._children and isFile:
action = menu.addAction(self.tr("Merge Child Items into Self"))
action = qtAddAction(menu, self.tr("Merge Child Items into Self"))
action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, False))
action = menu.addAction(self.tr("Merge Child Items into New"))
action = qtAddAction(menu, self.tr("Merge Child Items into New"))
action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
if self._children and isFolder:
action = menu.addAction(self.tr("Merge Documents in Folder"))
action = qtAddAction(menu, self.tr("Merge Documents in Folder"))
action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
if isFile:
action = menu.addAction(self.tr("Split Document by Headings"))
action = qtAddAction(menu, self.tr("Split Document by Headings"))
action.triggered.connect(qtLambda(self._tree.splitDocument, self._handle))
return
def _expandCollapse(self) -> None:
"""Add actions for expand and collapse."""
action = self.addAction(self.tr("Expand All"))
action = qtAddAction(self, self.tr("Expand All"))
action.triggered.connect(qtLambda(self._tree.expandFromIndex, self._indices[0]))
action = self.addAction(self.tr("Collapse All"))
action = qtAddAction(self, self.tr("Collapse All"))
action.triggered.connect(qtLambda(self._tree.collapseFromIndex, self._indices[0]))
return
@@ -1336,7 +1340,7 @@ class _TreeContextMenu(QMenu):
text = self.tr("Delete Permanently")
else:
text = self.tr("Move to Trash")
action = self.addAction(text)
action = qtAddAction(self, text)
action.triggered.connect(self._tree.processDeleteRequest)
return
+18 -22
View File
@@ -28,14 +28,14 @@ import logging
from time import time
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QCursor, QKeyEvent
from PyQt6.QtGui import QAction, QCursor, QKeyEvent
from PyQt6.QtWidgets import (
QApplication, QFrame, QHBoxLayout, QLabel, QLineEdit, QToolBar,
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt, cssCol
from novelwriter.common import checkInt, cssCol, qtAddAction
from novelwriter.core.coretools import DocSearch
from novelwriter.core.item import NWItem
from novelwriter.types import (
@@ -65,8 +65,6 @@ class GuiProjectSearch(QWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(2)
tPx = CONFIG.pxInt(4)
self._time = time()
self._search = DocSearch()
@@ -76,7 +74,7 @@ class GuiProjectSearch(QWidget):
# Header
self.viewLabel = QLabel(self.tr("Project Search"), self)
self.viewLabel.setFont(SHARED.theme.guiFontB)
self.viewLabel.setContentsMargins(mPx, tPx, 0, mPx)
self.viewLabel.setContentsMargins(2, 4, 0, 2)
# Options
self.searchOpt = QToolBar(self)
@@ -84,30 +82,30 @@ class GuiProjectSearch(QWidget):
self.searchOpt.setIconSize(iSz)
self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.toggleCase = self.searchOpt.addAction(self.tr("Case Sensitive"))
self.toggleCase = qtAddAction(self.searchOpt, self.tr("Case Sensitive"))
self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(CONFIG.searchProjCase)
self.toggleCase.toggled.connect(self._toggleCase)
self.toggleWord = self.searchOpt.addAction(self.tr("Whole Words Only"))
self.toggleWord = qtAddAction(self.searchOpt, self.tr("Whole Words Only"))
self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(CONFIG.searchProjWord)
self.toggleWord.toggled.connect(self._toggleWord)
self.toggleRegEx = self.searchOpt.addAction(self.tr("RegEx Mode"))
self.toggleRegEx = qtAddAction(self.searchOpt, self.tr("RegEx Mode"))
self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(CONFIG.searchProjRegEx)
self.toggleRegEx.toggled.connect(self._toggleRegEx)
# Search Box
self.searchAction = QAction("", self)
self.searchAction.setIcon(SHARED.theme.getIcon("search", "blue"))
self.searchAction.triggered.connect(self._processSearch)
self.searchText = QLineEdit(self)
self.searchText.setPlaceholderText(self.tr("Search for"))
self.searchText.setClearButtonEnabled(True)
self.searchAction = self.searchText.addAction(
SHARED.theme.getIcon("search", "blue"), QLineEdit.ActionPosition.TrailingPosition
)
self.searchAction.triggered.connect(self._processSearch)
self.searchText.addAction(self.searchAction, QLineEdit.ActionPosition.TrailingPosition)
# Search Result
self.searchResult = QTreeWidget(self)
@@ -121,10 +119,10 @@ class GuiProjectSearch(QWidget):
self.searchResult.itemDoubleClicked.connect(self._searchResultDoubleClicked)
self.searchResult.itemSelectionChanged.connect(self._searchResultSelected)
treeHeader = self.searchResult.header()
treeHeader.setStretchLastSection(False)
treeHeader.setSectionResizeMode(self.C_NAME, QtHeaderStretch)
treeHeader.setSectionResizeMode(self.C_COUNT, QtHeaderToContents)
if header := self.searchResult.header():
header.setStretchLastSection(False)
header.setSectionResizeMode(self.C_NAME, QtHeaderStretch)
header.setSectionResizeMode(self.C_COUNT, QtHeaderToContents)
# Assemble
self.headerBox = QHBoxLayout()
@@ -138,7 +136,7 @@ class GuiProjectSearch(QWidget):
self.outerBox.addWidget(self.searchText, 0)
self.outerBox.addWidget(self.searchResult, 1)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.outerBox.setSpacing(mPx)
self.outerBox.setSpacing(2)
self.setLayout(self.outerBox)
self.updateTheme()
@@ -153,8 +151,6 @@ class GuiProjectSearch(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
bPx = CONFIG.pxInt(1)
mPx = CONFIG.pxInt(2)
qPalette = self.palette()
colBase = cssCol(qPalette.base().color())
@@ -162,8 +158,8 @@ class GuiProjectSearch(QWidget):
self.setStyleSheet(
"QToolBar {padding: 0; background: none;} "
f"QLineEdit {{border: {bPx}px solid {colBase}; padding: {mPx}px;}} "
f"QLineEdit:focus {{border: {bPx}px solid {colFocus};}} "
f"QLineEdit {{border: 1px solid {colBase}; padding: 2px;}} "
f"QLineEdit:focus {{border: 1px solid {colFocus};}} "
)
self.searchAction.setIcon(SHARED.theme.getIcon("search", "blue"))
+2 -2
View File
@@ -31,7 +31,7 @@ from PyQt6.QtCore import QEvent, QPoint, QSize, pyqtSignal
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import QMenu, QVBoxLayout, QWidget
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.common import qtLambda
from novelwriter.enum import nwView
from novelwriter.extensions.eventfilters import StatusTipFilter
@@ -114,7 +114,7 @@ class GuiSideBar(QWidget):
self.outerBox.addWidget(self.tbStats)
self.outerBox.addWidget(self.tbSettings)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.outerBox.setSpacing(CONFIG.pxInt(6))
self.outerBox.setSpacing(6)
self.setLayout(self.outerBox)
self.updateTheme()
+4 -6
View File
@@ -57,13 +57,11 @@ class GuiMainStatus(QStatusBar):
# Permanent Widgets
# =================
xM = CONFIG.pxInt(8)
# The Spell Checker Language
self.langIcon = QLabel("", self)
self.langText = QLabel(self.tr("None"), self)
self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0)
self.langText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.langIcon)
self.addPermanentWidget(self.langText)
@@ -71,7 +69,7 @@ class GuiMainStatus(QStatusBar):
self.docIcon = StatusLED(iPx, iPx, self)
self.docText = QLabel(self.tr("Editor"), self)
self.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, xM, 0)
self.docText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.docIcon)
self.addPermanentWidget(self.docText)
@@ -79,7 +77,7 @@ class GuiMainStatus(QStatusBar):
self.projIcon = StatusLED(iPx, iPx, self)
self.projText = QLabel(self.tr("Project"), self)
self.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, xM, 0)
self.projText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.projIcon)
self.addPermanentWidget(self.projText)
@@ -87,7 +85,7 @@ class GuiMainStatus(QStatusBar):
self.statsIcon = QLabel(self)
self.statsText = QLabel("", self)
self.statsIcon.setContentsMargins(0, 0, 0, 0)
self.statsText.setContentsMargins(0, 0, xM, 0)
self.statsText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.statsIcon)
self.addPermanentWidget(self.statsText)
+4 -11
View File
@@ -41,7 +41,7 @@ from novelwriter.common import NWConfigParser, cssCol, minmax
from novelwriter.constants import nwLabels
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
from novelwriter.types import QtPaintAntiAlias, QtTransparent, nwDataClass
from novelwriter.types import QtPaintAntiAlias, QtTransparent
logger = logging.getLogger(__name__)
@@ -50,7 +50,6 @@ STYLES_MIN_TOOLBUTTON = "minimalToolButton"
STYLES_BIG_TOOLBUTTON = "bigToolButton"
@nwDataClass
class ThemeMeta:
name: str = ""
@@ -62,7 +61,6 @@ class ThemeMeta:
licenseUrl: str = ""
@nwDataClass
class SyntaxColors:
back: QColor = QColor(255, 255, 255)
@@ -546,31 +544,26 @@ class GuiTheme:
"""Build default style sheets."""
self._styleSheets = {}
aPx = CONFIG.pxInt(2)
bPx = CONFIG.pxInt(4)
cPx = CONFIG.pxInt(6)
dPx = CONFIG.pxInt(8)
tCol = palette.text().color()
hCol = palette.highlight().color()
# Flat Tab Widget and Tab Bar:
self._styleSheets[STYLES_FLAT_TABS] = (
"QTabWidget::pane {border: 0;} "
f"QTabWidget QTabBar::tab {{border: 0; padding: {bPx}px {dPx}px;}} "
"QTabWidget QTabBar::tab {border: 0; padding: 4px 8px;} "
f"QTabWidget QTabBar::tab:selected {{color: {cssCol(hCol)};}} "
)
# Minimal Tool Button
self._styleSheets[STYLES_MIN_TOOLBUTTON] = (
f"QToolButton {{padding: {aPx}px; margin: 0; border: none; background: transparent;}} "
"QToolButton {padding: 2px; margin: 0; border: none; background: transparent;} "
f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
"QToolButton::menu-indicator {image: none;} "
)
# Big Tool Button
self._styleSheets[STYLES_BIG_TOOLBUTTON] = (
f"QToolButton {{padding: {cPx}px; margin: 0; border: none; background: transparent;}} "
"QToolButton {padding: 6px; margin: 0; border: none; background: transparent;} "
f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
"QToolButton::menu-indicator {image: none;} "
)
+7 -11
View File
@@ -114,10 +114,6 @@ class GuiMain(QMainWindow):
# Build the GUI
# =============
# Sizes
mPx = CONFIG.pxInt(4)
hWd = CONFIG.pxInt(4)
# Main GUI Elements
self.mainStatus = GuiMainStatus(self)
self.projView = GuiProjectView(self)
@@ -142,7 +138,7 @@ class GuiMain(QMainWindow):
self.treePane = QWidget(self)
self.treeBox = QVBoxLayout()
self.treeBox.setContentsMargins(0, 0, 0, 0)
self.treeBox.setSpacing(mPx)
self.treeBox.setSpacing(4)
self.treeBox.addWidget(self.projStack)
self.treeBox.addWidget(self.itemDetails)
self.treePane.setLayout(self.treeBox)
@@ -151,7 +147,7 @@ class GuiMain(QMainWindow):
self.splitView = QSplitter(Qt.Orientation.Vertical, self)
self.splitView.addWidget(self.docViewer)
self.splitView.addWidget(self.docViewerPanel)
self.splitView.setHandleWidth(hWd)
self.splitView.setHandleWidth(4)
self.splitView.setOpaqueResize(False)
self.splitView.setSizes(CONFIG.viewPanePos)
self.splitView.setCollapsible(0, False)
@@ -162,7 +158,7 @@ class GuiMain(QMainWindow):
self.splitDocs.addWidget(self.docEditor)
self.splitDocs.addWidget(self.splitView)
self.splitDocs.setOpaqueResize(False)
self.splitDocs.setHandleWidth(hWd)
self.splitDocs.setHandleWidth(4)
self.splitDocs.setCollapsible(0, False)
self.splitDocs.setCollapsible(1, False)
@@ -172,7 +168,7 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.splitDocs)
self.splitMain.setOpaqueResize(False)
self.splitMain.setHandleWidth(hWd)
self.splitMain.setHandleWidth(4)
self.splitMain.setSizes(CONFIG.mainPanePos)
self.splitMain.setCollapsible(0, False)
self.splitMain.setCollapsible(0, False)
@@ -860,10 +856,10 @@ class GuiMain(QMainWindow):
logger.info("Exiting novelWriter")
if not SHARED.focusMode:
CONFIG.setMainPanePos(self.splitMain.sizes())
CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
CONFIG.mainPanePos = self.splitMain.sizes()
CONFIG.outlinePanePos = self.outlineView.splitSizes()
if self.docViewerPanel.isVisible():
CONFIG.setViewPanePos(self.splitView.sizes())
CONFIG.viewPanePos = self.splitView.sizes()
CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
wFull = Qt.WindowState.WindowFullScreen
+7 -10
View File
@@ -57,12 +57,9 @@ class GuiDictionaries(NNonBlockingDialog):
self._currDicts = set()
iSz = SHARED.theme.baseIconSize
iPx = CONFIG.pxInt(4)
mPx = CONFIG.pxInt(8)
sPx = CONFIG.pxInt(16)
self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(300))
self.setMinimumWidth(500)
self.setMinimumHeight(300)
# Hunspell Dictionaries
foUrl = "https://www.freeoffice.com/en/download/dictionaries"
@@ -84,7 +81,7 @@ class GuiDictionaries(NNonBlockingDialog):
self.huPathBox = QHBoxLayout()
self.huPathBox.addWidget(self.huInput)
self.huPathBox.addWidget(self.huBrowse)
self.huPathBox.setSpacing(iPx)
self.huPathBox.setSpacing(4)
self.huAddBox = QHBoxLayout()
self.huAddBox.addStretch(1)
self.huAddBox.addWidget(self.huImport)
@@ -99,7 +96,7 @@ class GuiDictionaries(NNonBlockingDialog):
self.inBox = QHBoxLayout()
self.inBox.addWidget(self.inPath)
self.inBox.addWidget(self.inBrowse)
self.inBox.setSpacing(iPx)
self.inBox.setSpacing(4)
# Info Box
self.infoBox = QPlainTextEdit(self)
@@ -116,17 +113,17 @@ class GuiDictionaries(NNonBlockingDialog):
self.innerBox.addWidget(self.huInfo)
self.innerBox.addLayout(self.huPathBox)
self.innerBox.addLayout(self.huAddBox)
self.innerBox.addSpacing(mPx)
self.innerBox.addSpacing(8)
self.innerBox.addWidget(self.inInfo)
self.innerBox.addLayout(self.inBox)
self.innerBox.addWidget(self.infoBox)
self.innerBox.setSpacing(iPx)
self.innerBox.setSpacing(4)
self.outerBox = QVBoxLayout()
self.outerBox.addLayout(self.innerBox, 0)
self.outerBox.addStretch(1)
self.outerBox.addWidget(self.buttonBox, 0)
self.outerBox.setSpacing(sPx)
self.outerBox.setSpacing(16)
self.setLayout(self.outerBox)
+10 -11
View File
@@ -52,18 +52,15 @@ class GuiLipsum(NDialog):
self._lipsumText = ""
vSp = CONFIG.pxInt(4)
nPx = CONFIG.pxInt(64)
self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(CONFIG.pxInt(16))
self.innerBox.setSpacing(16)
# Icon
self.docIcon = QLabel(self)
self.docIcon.setPixmap(SHARED.theme.getPixmap("text", (nPx, nPx), "blue"))
self.docIcon.setPixmap(SHARED.theme.getPixmap("text", (64, 64), "blue"))
self.leftBox = QVBoxLayout()
self.leftBox.setSpacing(vSp)
self.leftBox.setSpacing(4)
self.leftBox.addWidget(self.docIcon)
self.leftBox.addStretch(1)
self.innerBox.addLayout(self.leftBox)
@@ -87,7 +84,7 @@ class GuiLipsum(NDialog):
self.formBox.addWidget(self.paraCount, 1, 1, 1, 1, QtAlignRight)
self.formBox.addWidget(self.randLabel, 2, 0, 1, 1, QtAlignLeft)
self.formBox.addWidget(self.randSwitch, 2, 1, 1, 1, QtAlignRight)
self.formBox.setVerticalSpacing(vSp)
self.formBox.setVerticalSpacing(4)
self.formBox.setRowStretch(3, 1)
self.innerBox.addLayout(self.formBox)
@@ -96,17 +93,19 @@ class GuiLipsum(NDialog):
self.buttonBox.rejected.connect(self.reject)
self.btnClose = self.buttonBox.addButton(QtDialogClose)
self.btnClose.setAutoDefault(False)
if self.btnClose:
self.btnClose.setAutoDefault(False)
self.btnInsert = self.buttonBox.addButton(self.tr("Insert"), QtRoleAction)
self.btnInsert.clicked.connect(self._doInsert)
self.btnInsert.setAutoDefault(False)
if self.btnInsert:
self.btnInsert.clicked.connect(self._doInsert)
self.btnInsert.setAutoDefault(False)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.addLayout(self.innerBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(16))
self.outerBox.setSpacing(16)
self.setLayout(self.outerBox)
logger.debug("Ready: GuiLipsum")
+24 -35
View File
@@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
QPushButton, QSplitter, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.common import makeFileNameSafe, openExternalPath
from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings
@@ -68,21 +68,16 @@ class GuiManuscriptBuild(NDialog):
self._build = build
self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(300))
self.setMinimumWidth(500)
self.setMinimumHeight(300)
iSz = SHARED.theme.baseIconSize
bSz = SHARED.theme.buttonIconSize
sp4 = CONFIG.pxInt(4)
sp8 = CONFIG.pxInt(8)
sp16 = CONFIG.pxInt(16)
wWin = CONFIG.pxInt(620)
hWin = CONFIG.pxInt(360)
pOptions = SHARED.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
pOptions.getInt("GuiManuscriptBuild", "winWidth", 620),
pOptions.getInt("GuiManuscriptBuild", "winHeight", 360),
)
# Output Format
@@ -149,7 +144,7 @@ class GuiManuscriptBuild(NDialog):
self.pathBox = QHBoxLayout()
self.pathBox.addWidget(self.buildPath)
self.pathBox.addWidget(self.btnBrowse)
self.pathBox.setSpacing(sp8)
self.pathBox.setSpacing(8)
# Build Name
self.lblName = QLabel(self.tr("File Name"), self)
@@ -160,14 +155,14 @@ class GuiManuscriptBuild(NDialog):
self.nameBox = QHBoxLayout()
self.nameBox.addWidget(self.buildName)
self.nameBox.addWidget(self.btnReset)
self.nameBox.setSpacing(sp8)
self.nameBox.setSpacing(8)
# Build Progress
self.buildProgress = NProgressSimple(self)
self.buildProgress.setMinimum(0)
self.buildProgress.setValue(0)
self.buildProgress.setTextVisible(False)
self.buildProgress.setFixedHeight(sp8)
self.buildProgress.setFixedHeight(8)
# Build Box
self.buildBox = QGridLayout()
@@ -175,8 +170,8 @@ class GuiManuscriptBuild(NDialog):
self.buildBox.addLayout(self.pathBox, 0, 1)
self.buildBox.addWidget(self.lblName, 1, 0)
self.buildBox.addLayout(self.nameBox, 1, 1)
self.buildBox.setHorizontalSpacing(sp8)
self.buildBox.setVerticalSpacing(sp4)
self.buildBox.setHorizontalSpacing(8)
self.buildBox.setVerticalSpacing(4)
# Dialog Buttons
self.buttonBox = QDialogButtonBox(self)
@@ -196,7 +191,8 @@ class GuiManuscriptBuild(NDialog):
self.buttonBox.addButton(self.btnBuild, QtRoleAction)
self.btnClose = self.buttonBox.addButton(QtDialogClose)
self.btnClose.setAutoDefault(False)
if self.btnClose:
self.btnClose.setAutoDefault(False)
# Assemble GUI
# ============
@@ -204,25 +200,25 @@ class GuiManuscriptBuild(NDialog):
self.mainSplit = QSplitter(self)
self.mainSplit.addWidget(self.formatWidget)
self.mainSplit.addWidget(self.contentWidget)
self.mainSplit.setHandleWidth(sp16)
self.mainSplit.setHandleWidth(16)
self.mainSplit.setCollapsible(0, False)
self.mainSplit.setCollapsible(1, False)
self.mainSplit.setStretchFactor(0, 0)
self.mainSplit.setStretchFactor(1, 1)
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "fmtWidth", wWin//2)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "sumWidth", wWin//2)),
pOptions.getInt("GuiManuscriptBuild", "fmtWidth", 360),
pOptions.getInt("GuiManuscriptBuild", "sumWidth", 360),
])
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(self.lblMain, 0, QtAlignCenter)
self.outerBox.addSpacing(sp16)
self.outerBox.addSpacing(16)
self.outerBox.addWidget(self.mainSplit, 1)
self.outerBox.addSpacing(sp4)
self.outerBox.addSpacing(4)
self.outerBox.addWidget(self.buildProgress, 0)
self.outerBox.addSpacing(sp4)
self.outerBox.addSpacing(4)
self.outerBox.addLayout(self.buildBox, 0)
self.outerBox.addSpacing(sp16)
self.outerBox.addSpacing(16)
self.outerBox.addWidget(self.buttonBox, 0)
self.outerBox.setSpacing(0)
@@ -363,21 +359,14 @@ class GuiManuscriptBuild(NDialog):
def _saveSettings(self) -> None:
"""Save the user GUI settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
mainSplit = self.mainSplit.sizes()
fmtWidth = CONFIG.rpxInt(mainSplit[0])
sumWidth = CONFIG.rpxInt(mainSplit[1])
logger.debug("Saving State: GuiManuscriptBuild")
mainSplit = self.mainSplit.sizes()
pOptions = SHARED.project.options
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
pOptions.setValue("GuiManuscriptBuild", "sumWidth", sumWidth)
pOptions.setValue("GuiManuscriptBuild", "winWidth", self.width())
pOptions.setValue("GuiManuscriptBuild", "winHeight", self.height())
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", mainSplit[0])
pOptions.setValue("GuiManuscriptBuild", "sumWidth", mainSplit[1])
pOptions.saveSettings()
return
def _populateContentList(self) -> None:
+64 -77
View File
@@ -85,18 +85,16 @@ class GuiManuscript(NToolDialog):
self._buildMap: dict[str, QListWidgetItem] = {}
self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(600))
self.setMinimumHeight(CONFIG.pxInt(500))
self.setMinimumWidth(600)
self.setMinimumHeight(500)
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
wWin = CONFIG.pxInt(900)
hWin = CONFIG.pxInt(600)
pOptions = SHARED.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin))
pOptions.getInt("GuiManuscript", "winWidth", 900),
pOptions.getInt("GuiManuscript", "winHeight", 600),
)
# Build Controls
@@ -153,9 +151,7 @@ class GuiManuscript(NToolDialog):
# ============
self.buildDetails = _DetailsWidget(self)
self.buildDetails.setColumnWidth(
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsWidth", 100)),
)
self.buildDetails.setColumnWidth(pOptions.getInt("GuiManuscript", "detailsWidth", 100))
self.buildOutline = _OutlineWidget(self)
@@ -168,8 +164,8 @@ class GuiManuscript(NToolDialog):
self.buildSplit.addWidget(self.buildList)
self.buildSplit.addWidget(self.detailsTabs)
self.buildSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "listHeight", 50)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsHeight", 50)),
pOptions.getInt("GuiManuscript", "listHeight", 50),
pOptions.getInt("GuiManuscript", "detailsHeight", 50),
])
# Process Controls
@@ -240,8 +236,8 @@ class GuiManuscript(NToolDialog):
self.mainSplit.setStretchFactor(0, 0)
self.mainSplit.setStretchFactor(1, 1)
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "optsWidth", wWin//4)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "viewWidth", 3*wWin//4)),
pOptions.getInt("GuiManuscript", "optsWidth", 225),
pOptions.getInt("GuiManuscript", "viewWidth", 675),
])
self.outerBox = QVBoxLayout()
@@ -441,28 +437,20 @@ class GuiManuscript(NToolDialog):
self._builds.setBuildsState(lastBuild, buildOrder)
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
mainSplit = self.mainSplit.sizes()
optsWidth = CONFIG.rpxInt(mainSplit[0])
viewWidth = CONFIG.rpxInt(mainSplit[1])
buildSplit = self.buildSplit.sizes()
listHeight = CONFIG.rpxInt(buildSplit[0])
detailsHeight = CONFIG.rpxInt(buildSplit[1])
detailsWidth = CONFIG.rpxInt(self.buildDetails.getColumnWidth())
detailsWidth = self.buildDetails.getColumnWidth()
detailsExpanded = self.buildDetails.getExpandedState()
showNewPage = self.swtNewPage.isChecked()
logger.debug("Saving State: GuiManuscript")
pOptions = SHARED.project.options
pOptions.setValue("GuiManuscript", "winWidth", winWidth)
pOptions.setValue("GuiManuscript", "winHeight", winHeight)
pOptions.setValue("GuiManuscript", "optsWidth", optsWidth)
pOptions.setValue("GuiManuscript", "viewWidth", viewWidth)
pOptions.setValue("GuiManuscript", "listHeight", listHeight)
pOptions.setValue("GuiManuscript", "detailsHeight", detailsHeight)
pOptions.setValue("GuiManuscript", "winWidth", self.width())
pOptions.setValue("GuiManuscript", "winHeight", self.height())
pOptions.setValue("GuiManuscript", "optsWidth", mainSplit[0])
pOptions.setValue("GuiManuscript", "viewWidth", mainSplit[1])
pOptions.setValue("GuiManuscript", "listHeight", buildSplit[0])
pOptions.setValue("GuiManuscript", "detailsHeight", buildSplit[1])
pOptions.setValue("GuiManuscript", "detailsWidth", detailsWidth)
pOptions.setValue("GuiManuscript", "detailsExpanded", detailsExpanded)
pOptions.setValue("GuiManuscript", "showNewPage", showNewPage)
@@ -693,33 +681,31 @@ class _OutlineWidget(QWidget):
hFont.setBold(True)
hFont.setUnderline(True)
root = self.listView.invisibleRootItem()
parent = root
indent = False
for anchor, entry in data.items():
prefix, _, text = entry.partition("|")
if prefix in ("TT", "PT", "CH", "SC", "H1", "H2"):
item = QTreeWidgetItem([text])
item.setData(0, self.D_LINE, anchor)
if prefix == "TT":
item.setFont(0, tFont)
item.setForeground(0, tBrush)
root.addChild(item)
parent = root
elif prefix == "PT":
item.setFont(0, hFont)
root.addChild(item)
parent = root
elif prefix in ("CH", "H1"):
root.addChild(item)
parent = item
elif prefix in ("SC", "H2"):
parent.addChild(item)
indent = True
if root := self.listView.invisibleRootItem():
parent = root
indent = False
for anchor, entry in data.items():
prefix, _, text = entry.partition("|")
if prefix in ("TT", "PT", "CH", "SC", "H1", "H2"):
item = QTreeWidgetItem([text])
item.setData(0, self.D_LINE, anchor)
if prefix == "TT":
item.setFont(0, tFont)
item.setForeground(0, tBrush)
root.addChild(item)
parent = root
elif prefix == "PT":
item.setFont(0, hFont)
root.addChild(item)
parent = root
elif prefix in ("CH", "H1"):
root.addChild(item)
parent = item
elif prefix in ("SC", "H2"):
parent.addChild(item)
indent = True
self.listView.setIndentation(
SHARED.theme.baseIconHeight if indent else CONFIG.pxInt(4)
)
self.listView.setIndentation(SHARED.theme.baseIconHeight if indent else 4)
self._outline = data
return
@@ -750,11 +736,13 @@ class _PreviewWidget(QTextBrowser):
self.setPalette(dPalette)
self.setMinimumWidth(40*SHARED.theme.textNWidth)
self.setTabStopDistance(CONFIG.getTabWidth())
self.setTabStopDistance(CONFIG.tabWidth)
self.setOpenExternalLinks(False)
self.setOpenLinks(False)
self.document().setDocumentMargin(CONFIG.getTextMargin())
if document := self.document():
document.setDocumentMargin(CONFIG.textMargin)
self.setPlaceholderText(self.tr(
"Press the \"Preview\" button to generate ..."
))
@@ -781,7 +769,7 @@ class _PreviewWidget(QTextBrowser):
self.ageLabel.setFixedHeight(int(2.1*SHARED.theme.fontPixelSize))
# Progress
self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16))
self.buildProgress = NProgressCircle(self, 160, 16)
self.buildProgress.setVisible(False)
self.buildProgress.setMaximum(1)
self.buildProgress.setValue(0)
@@ -834,7 +822,8 @@ class _PreviewWidget(QTextBrowser):
self.buildProgress.setValue(0)
self.buildProgress.setCentreText(None)
self.buildProgress.setVisible(True)
self._scrollPos = self.verticalScrollBar().value()
if vBar := self.verticalScrollBar():
self._scrollPos = vBar.value()
self.setPlaceholderText("")
self.clear()
return
@@ -852,9 +841,9 @@ class _PreviewWidget(QTextBrowser):
self.buildProgress.setCentreText(self.tr("Processing ..."))
QApplication.processEvents()
document.setDocumentMargin(CONFIG.getTextMargin())
document.setDocumentMargin(CONFIG.textMargin)
self.setDocument(document)
self.setTabStopDistance(CONFIG.getTabWidth())
self.setTabStopDistance(CONFIG.tabWidth)
self._docTime = int(time())
self._updateBuildAge()
@@ -883,10 +872,11 @@ class _PreviewWidget(QTextBrowser):
@pyqtSlot("QPrinter*")
def printPreview(self, printer: QPrinter) -> None:
"""Connect the print preview painter to the document viewer."""
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
printer.setPageOrientation(QPageLayout.Orientation.Portrait)
self.document().print(printer)
QApplication.restoreOverrideCursor()
if document := self.document():
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
printer.setPageOrientation(QPageLayout.Orientation.Portrait)
document.print(printer)
QApplication.restoreOverrideCursor()
return
@pyqtSlot(str)
@@ -928,7 +918,8 @@ class _PreviewWidget(QTextBrowser):
def _postUpdate(self) -> None:
"""Run tasks after content update."""
self.buildProgress.setVisible(False)
self.verticalScrollBar().setValue(self._scrollPos)
if vBar := self.verticalScrollBar():
vBar.setValue(self._scrollPos)
return
##
@@ -941,7 +932,7 @@ class _PreviewWidget(QTextBrowser):
"""
vBar = self.verticalScrollBar()
tB = self.frameWidth()
vW = self.width() - 2*tB - vBar.width()
vW = self.width() - 2*tB - (vBar.width() if vBar else 0)
vH = self.height() - 2*tB
tH = self.ageLabel.height()
pS = self.buildProgress.width()
@@ -1033,10 +1024,6 @@ class _StatsWidget(QWidget):
def _buildBottomPanel(self) -> None:
"""Build the bottom page."""
mPx = CONFIG.pxInt(8)
hPx = CONFIG.pxInt(12)
vPx = CONFIG.pxInt(4)
trAllChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS])
trTextChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS_TEXT])
trTitleChars = trConst(nwLabels.STATS_NAME[nwStats.CHARS_TITLE])
@@ -1073,8 +1060,8 @@ class _StatsWidget(QWidget):
self.leftForm.addRow("", QLabel(self))
self.leftForm.addRow(trTitleCount, self.maxTitleCount)
self.leftForm.addRow(trParagraphCount, self.maxParCount)
self.leftForm.setHorizontalSpacing(hPx)
self.leftForm.setVerticalSpacing(vPx)
self.leftForm.setHorizontalSpacing(12)
self.leftForm.setVerticalSpacing(4)
# Maximal Form, Right Column
self.maxTotalChars = QLabel(self)
@@ -1098,25 +1085,25 @@ class _StatsWidget(QWidget):
self.rightForm.addRow(trAllWordChars, self.maxTotalWordChars)
self.rightForm.addRow(trTitleWordChars, self.maxHeadWordChars)
self.rightForm.addRow(trTextWordChars, self.maxTextWordChars)
self.rightForm.setHorizontalSpacing(hPx)
self.rightForm.setVerticalSpacing(vPx)
self.rightForm.setHorizontalSpacing(12)
self.rightForm.setVerticalSpacing(4)
# Assemble
self.minLayout = QHBoxLayout()
self.minLayout.addWidget(QLabel(trAllWords, self))
self.minLayout.addWidget(self.minWordCount)
self.minLayout.addSpacing(mPx)
self.minLayout.addSpacing(8)
self.minLayout.addWidget(QLabel(trAllChars, self))
self.minLayout.addWidget(self.minCharCount)
self.minLayout.addStretch(1)
self.minLayout.setSpacing(mPx)
self.minLayout.setSpacing(8)
self.minLayout.setContentsMargins(0, 0, 0, 0)
self.maxLayout = QHBoxLayout()
self.maxLayout.addLayout(self.leftForm)
self.maxLayout.addLayout(self.rightForm)
self.maxLayout.addStretch(1)
self.maxLayout.setSpacing(CONFIG.pxInt(32))
self.maxLayout.setSpacing(32)
self.maxLayout.setContentsMargins(0, 0, 0, 0)
self.minWidget.setLayout(self.minLayout)
+44 -54
View File
@@ -37,7 +37,7 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import describeFont, fontMatcher, qtLambda
from novelwriter.common import describeFont, fontMatcher, qtAddAction, qtLambda
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, trConst
from novelwriter.core.buildsettings import BuildSettings, FilterMode
from novelwriter.extensions.configlayout import (
@@ -83,21 +83,18 @@ class GuiBuildSettings(NToolDialog):
self._build = build
self.setWindowTitle(self.tr("Manuscript Build Settings"))
self.setMinimumSize(CONFIG.pxInt(700), CONFIG.pxInt(400))
wWin = CONFIG.pxInt(750)
hWin = CONFIG.pxInt(550)
self.setMinimumSize(700, 400)
options = SHARED.project.options
self.resize(
CONFIG.pxInt(options.getInt("GuiBuildSettings", "winWidth", wWin)),
CONFIG.pxInt(options.getInt("GuiBuildSettings", "winHeight", hWin))
options.getInt("GuiBuildSettings", "winWidth", 750),
options.getInt("GuiBuildSettings", "winHeight", 550),
)
# Title
self.titleLabel = NColourLabel(
self.tr("Manuscript Build Settings"), self, color=SHARED.theme.helpText,
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
scale=NColourLabel.HEADER_SCALE, indent=4,
)
# Settings Name
@@ -145,7 +142,7 @@ class GuiBuildSettings(NToolDialog):
self.outerBox.addLayout(self.topBox)
self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(12))
self.outerBox.setSpacing(12)
self.setLayout(self.outerBox)
@@ -240,14 +237,11 @@ class GuiBuildSettings(NToolDialog):
def _saveSettings(self) -> None:
"""Save the various user settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
logger.debug("Saving State: GuiBuildSettings")
pOptions = SHARED.project.options
pOptions.setValue("GuiBuildSettings", "winWidth", winWidth)
pOptions.setValue("GuiBuildSettings", "winHeight", winHeight)
pOptions.setValue("GuiBuildSettings", "winWidth", self.width())
pOptions.setValue("GuiBuildSettings", "winHeight", self.height())
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth)
pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth)
pOptions.saveSettings()
@@ -300,7 +294,6 @@ class _FilterTab(NFixedPage):
iSz = SHARED.theme.baseIconSize
iPx = SHARED.theme.baseIconHeight
cMg = CONFIG.pxInt(6)
# Tree Widget
self.optTree = QTreeWidget(self)
@@ -311,14 +304,14 @@ class _FilterTab(NFixedPage):
self.optTree.setIndentation(iPx)
self.optTree.setColumnCount(3)
treeHeader = self.optTree.header()
treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1551
treeHeader.setSectionResizeMode(self.C_NAME, QtHeaderStretch)
treeHeader.setSectionResizeMode(self.C_ACTIVE, QtHeaderFixed)
treeHeader.setSectionResizeMode(self.C_STATUS, QtHeaderFixed)
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
if header := self.optTree.header():
header.setStretchLastSection(False)
header.setMinimumSectionSize(iPx + 6) # See Issue #1551
header.setSectionResizeMode(self.C_NAME, QtHeaderStretch)
header.setSectionResizeMode(self.C_ACTIVE, QtHeaderFixed)
header.setSectionResizeMode(self.C_STATUS, QtHeaderFixed)
header.resizeSection(self.C_ACTIVE, iPx + 6)
header.resizeSection(self.C_STATUS, iPx + 6)
self.optTree.setDragDropMode(QAbstractItemView.DragDropMode.NoDragDrop)
self.optTree.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
@@ -347,7 +340,7 @@ class _FilterTab(NFixedPage):
self.modeBox.addWidget(self.includedButton)
self.modeBox.addWidget(self.excludedButton)
self.modeBox.addWidget(self.resetButton)
self.modeBox.setSpacing(CONFIG.pxInt(4))
self.modeBox.setSpacing(4)
# Filer Options
self.filterOpt = NSwitchBox(self, iPx)
@@ -375,8 +368,8 @@ class _FilterTab(NFixedPage):
self.mainSplit.setStretchFactor(0, 1)
self.mainSplit.setStretchFactor(1, 0)
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "treeWidth", 300)),
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "filterWidth", 300))
pOptions.getInt("GuiBuildSettings", "treeWidth", 300),
pOptions.getInt("GuiBuildSettings", "filterWidth", 300),
])
self.setCentralWidget(self.mainSplit)
@@ -393,7 +386,7 @@ class _FilterTab(NFixedPage):
"""Extract the sizes of the main splitter."""
sizes = self.mainSplit.sizes()
m, n = (sizes[0], sizes[1]) if len(sizes) >= 2 else (0, 0)
return CONFIG.rpxInt(m), CONFIG.rpxInt(n)
return m, n
##
# Slots
@@ -558,15 +551,12 @@ class _HeadingsTab(NScrollablePage):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
sSp = CONFIG.pxInt(16)
vSp = CONFIG.pxInt(12)
bSp = CONFIG.pxInt(6)
trHide = self.tr("Hide")
# Format Boxes
# ============
self.formatBox = QGridLayout()
self.formatBox.setHorizontalSpacing(bSp)
self.formatBox.setHorizontalSpacing(6)
# Title Heading
self.lblPart = QLabel(self._build.getLabel("headings.fmtPart"), self)
@@ -575,7 +565,7 @@ class _HeadingsTab(NScrollablePage):
self.btnPart = NIconToolButton(self, iSz, "edit", "green")
self.btnPart.clicked.connect(qtLambda(self._editHeading, self.EDIT_TITLE))
self.hdePart = QLabel(trHide, self)
self.hdePart.setIndent(bSp)
self.hdePart.setIndent(6)
self.swtPart = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblPart, 0, 0)
@@ -591,7 +581,7 @@ class _HeadingsTab(NScrollablePage):
self.btnChapter = NIconToolButton(self, iSz, "edit", "green")
self.btnChapter.clicked.connect(qtLambda(self._editHeading, self.EDIT_CHAPTER))
self.hdeChapter = QLabel(trHide, self)
self.hdeChapter.setIndent(bSp)
self.hdeChapter.setIndent(6)
self.swtChapter = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblChapter, 1, 0)
@@ -607,7 +597,7 @@ class _HeadingsTab(NScrollablePage):
self.btnUnnumbered = NIconToolButton(self, iSz, "edit", "green")
self.btnUnnumbered.clicked.connect(qtLambda(self._editHeading, self.EDIT_UNNUM))
self.hdeUnnumbered = QLabel(trHide, self)
self.hdeUnnumbered.setIndent(bSp)
self.hdeUnnumbered.setIndent(6)
self.swtUnnumbered = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblUnnumbered, 2, 0)
@@ -623,7 +613,7 @@ class _HeadingsTab(NScrollablePage):
self.btnScene = NIconToolButton(self, iSz, "edit", "green")
self.btnScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_SCENE))
self.hdeScene = QLabel(trHide, self)
self.hdeScene.setIndent(bSp)
self.hdeScene.setIndent(6)
self.swtScene = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblScene, 3, 0)
@@ -639,7 +629,7 @@ class _HeadingsTab(NScrollablePage):
self.btnAScene = NIconToolButton(self, iSz, "edit", "green")
self.btnAScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_HSCENE))
self.hdeAScene = QLabel(trHide, self)
self.hdeAScene.setIndent(bSp)
self.hdeAScene.setIndent(6)
self.swtAScene = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblAScene, 4, 0)
@@ -655,7 +645,7 @@ class _HeadingsTab(NScrollablePage):
self.btnSection = NIconToolButton(self, iSz, "edit", "green")
self.btnSection.clicked.connect(qtLambda(self._editHeading, self.EDIT_SECTION))
self.hdeSection = QLabel(trHide, self)
self.hdeSection.setIndent(bSp)
self.hdeSection.setIndent(6)
self.swtSection = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblSection, 5, 0)
@@ -675,16 +665,16 @@ class _HeadingsTab(NScrollablePage):
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
self.menuInsert = QMenu(self)
self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
self.aInsChNum = self.menuInsert.addAction(self.tr("Chapter Number"))
self.aInsChWord = self.menuInsert.addAction(self.tr("Chapter Number (Word)"))
self.aInsChRomU = self.menuInsert.addAction(self.tr("Chapter Number (Upper Case Roman)"))
self.aInsChRomL = self.menuInsert.addAction(self.tr("Chapter Number (Lower Case Roman)"))
self.aInsScNum = self.menuInsert.addAction(self.tr("Scene Number (In Chapter)"))
self.aInsScAbs = self.menuInsert.addAction(self.tr("Scene Number (Absolute)"))
self.aInsCharPOV = self.menuInsert.addAction(self.tr("Point of View Character"))
self.aInsCharFocus = self.menuInsert.addAction(self.tr("Focus Character"))
self.mInsert = QMenu(self)
self.aInsTitle = qtAddAction(self.mInsert, self.tr("Title"))
self.aInsChNum = qtAddAction(self.mInsert, self.tr("Chapter Number"))
self.aInsChWord = qtAddAction(self.mInsert, self.tr("Chapter Number (Word)"))
self.aInsChRomU = qtAddAction(self.mInsert, self.tr("Chapter Number (Upper Case Roman)"))
self.aInsChRomL = qtAddAction(self.mInsert, self.tr("Chapter Number (Lower Case Roman)"))
self.aInsScNum = qtAddAction(self.mInsert, self.tr("Scene Number (In Chapter)"))
self.aInsScAbs = qtAddAction(self.mInsert, self.tr("Scene Number (Absolute)"))
self.aInsCharPOV = qtAddAction(self.mInsert, self.tr("Point of View Character"))
self.aInsCharFocus = qtAddAction(self.mInsert, self.tr("Focus Character"))
self.aInsTitle.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.TITLE))
self.aInsChNum.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_NUM))
@@ -697,7 +687,7 @@ class _HeadingsTab(NScrollablePage):
self.aInsCharFocus.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CHAR_FOCUS))
self.btnInsert = QPushButton(self.tr("Insert"), self)
self.btnInsert.setMenu(self.menuInsert)
self.btnInsert.setMenu(self.mInsert)
self.btnApply = QPushButton(self.tr("Apply"), self)
self.btnApply.clicked.connect(self._saveFormat)
@@ -715,8 +705,8 @@ class _HeadingsTab(NScrollablePage):
# Layout Matrix
# =============
self.layoutMatrix = QGridLayout()
self.layoutMatrix.setVerticalSpacing(vSp)
self.layoutMatrix.setHorizontalSpacing(vSp)
self.layoutMatrix.setVerticalSpacing(12)
self.layoutMatrix.setHorizontalSpacing(12)
self.layoutMatrix.addWidget(QLabel(self.tr("Centre"), self), 0, 1)
self.layoutMatrix.addWidget(QLabel(self.tr("Page Break"), self), 0, 2)
@@ -764,9 +754,9 @@ class _HeadingsTab(NScrollablePage):
self.outerBox = QVBoxLayout()
self.outerBox.addLayout(self.formatBox)
self.outerBox.addSpacing(sSp)
self.outerBox.addSpacing(16)
self.outerBox.addLayout(self.editFormBox)
self.outerBox.addSpacing(sSp)
self.outerBox.addSpacing(16)
self.outerBox.addLayout(self.layoutMatrix)
self.outerBox.addStretch(1)
@@ -904,7 +894,7 @@ class _HeadingsTab(NScrollablePage):
class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, document: QTextDocument) -> None:
def __init__(self, document: QTextDocument | None) -> None:
super().__init__(document)
syntax = SHARED.theme.syntaxTheme
self._fmtSymbol = QTextCharFormat()
@@ -1223,7 +1213,7 @@ class _FormattingTab(NScrollableForm):
# Header
self.odtPageHeader = QLineEdit(self)
self.odtPageHeader.setMinimumWidth(CONFIG.pxInt(200))
self.odtPageHeader.setMinimumWidth(200)
self.btnPageHeader = NIconToolButton(self, iSz, "revert", "green")
self.btnPageHeader.clicked.connect(self._resetPageHeader)
self.addRow(
+32 -50
View File
@@ -34,7 +34,7 @@ from PyQt6.QtWidgets import (
QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter import SHARED
from novelwriter.common import formatTime, numberToRoman
from novelwriter.constants import nwUnicode
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollablePage
@@ -60,16 +60,16 @@ class GuiNovelDetails(NNonBlockingDialog):
self.setWindowTitle(self.tr("Novel Details"))
options = SHARED.project.options
self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400))
self.setMinimumSize(500, 400)
self.resize(
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", CONFIG.pxInt(650))),
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", CONFIG.pxInt(500)))
options.getInt("GuiNovelDetails", "winWidth", 650),
options.getInt("GuiNovelDetails", "winHeight", 500),
)
# Title
self.titleLabel = NColourLabel(
self.tr("Novel Details"), self, color=SHARED.theme.helpText,
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
scale=NColourLabel.HEADER_SCALE, indent=4,
)
# Novel Selector
@@ -114,7 +114,7 @@ class GuiNovelDetails(NNonBlockingDialog):
self.outerBox.addLayout(self.topBox)
self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(8))
self.outerBox.setSpacing(8)
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
@@ -172,18 +172,13 @@ class GuiNovelDetails(NNonBlockingDialog):
def _saveSettings(self) -> None:
"""Save the user GUI settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
novelRoot = self.novelSelector.handle
logger.debug("Saving State: GuiNovelDetails")
novelRoot = self.novelSelector.handle
options = SHARED.project.options
options.setValue("GuiNovelDetails", "winWidth", winWidth)
options.setValue("GuiNovelDetails", "winHeight", winHeight)
options.setValue("GuiNovelDetails", "winWidth", self.width())
options.setValue("GuiNovelDetails", "winHeight", self.height())
options.setValue("GuiNovelDetails", "novelRoot", novelRoot)
self.contentsPage.saveSettings()
return
@@ -192,11 +187,6 @@ class _OverviewPage(NScrollablePage):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
mPx = CONFIG.pxInt(8)
sPx = CONFIG.pxInt(16)
hPx = CONFIG.pxInt(24)
vPx = CONFIG.pxInt(4)
# Project Info
self.projLabel = NColourLabel(
self.tr("Project"), self, color=SHARED.theme.helpText,
@@ -217,9 +207,9 @@ class _OverviewPage(NScrollablePage):
self.projForm.addRow("<b>{0}</b>".format(self.tr("Word Count")), self.projWords)
self.projForm.addRow("<b>\u2026 {0}</b>".format(self.tr("In Novels")), self.projNovels)
self.projForm.addRow("<b>\u2026 {0}</b>".format(self.tr("In Notes")), self.projNotes)
self.projForm.setContentsMargins(mPx, 0, 0, 0)
self.projForm.setHorizontalSpacing(hPx)
self.projForm.setVerticalSpacing(vPx)
self.projForm.setContentsMargins(8, 0, 0, 0)
self.projForm.setHorizontalSpacing(24)
self.projForm.setVerticalSpacing(4)
# Novel Info
self.novelLabel = NColourLabel(
@@ -237,9 +227,9 @@ class _OverviewPage(NScrollablePage):
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Word Count")), self.novelWords)
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Chapters")), self.novelChapters)
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Scenes")), self.novelScenes)
self.novelForm.setContentsMargins(mPx, 0, 0, 0)
self.novelForm.setHorizontalSpacing(hPx)
self.novelForm.setVerticalSpacing(vPx)
self.novelForm.setContentsMargins(8, 0, 0, 0)
self.novelForm.setHorizontalSpacing(24)
self.novelForm.setVerticalSpacing(4)
# Assemble
self.outerBox = QVBoxLayout()
@@ -247,7 +237,7 @@ class _OverviewPage(NScrollablePage):
self.outerBox.addLayout(self.projForm)
self.outerBox.addWidget(self.novelLabel)
self.outerBox.addLayout(self.novelForm)
self.outerBox.setSpacing(sPx)
self.outerBox.setSpacing(16)
self.outerBox.addStretch(1)
self.setCentralLayout(self.outerBox)
@@ -309,8 +299,6 @@ class _ContentsPage(NFixedPage):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
hPx = CONFIG.pxInt(12)
vPx = CONFIG.pxInt(4)
options = SHARED.project.options
# Title
@@ -341,22 +329,22 @@ class _ContentsPage(NFixedPage):
treeHeadItem.setTextAlignment(self.C_PAGE, QtAlignRight)
treeHeadItem.setTextAlignment(self.C_PROG, QtAlignRight)
treeHeader = self.tocTree.header()
treeHeader.setStretchLastSection(True)
treeHeader.setMinimumSectionSize(hPx)
if header := self.tocTree.header():
header.setStretchLastSection(True)
header.setMinimumSectionSize(12)
wCol0 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol0", 200))
wCol1 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol1", 60))
wCol2 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol2", 60))
wCol3 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol3", 60))
wCol4 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol4", 90))
wCol0 = options.getInt("GuiNovelDetails", "widthCol0", 200)
wCol1 = options.getInt("GuiNovelDetails", "widthCol1", 60)
wCol2 = options.getInt("GuiNovelDetails", "widthCol2", 60)
wCol3 = options.getInt("GuiNovelDetails", "widthCol3", 60)
wCol4 = options.getInt("GuiNovelDetails", "widthCol4", 90)
self.tocTree.setColumnWidth(0, wCol0)
self.tocTree.setColumnWidth(1, wCol1)
self.tocTree.setColumnWidth(2, wCol2)
self.tocTree.setColumnWidth(3, wCol3)
self.tocTree.setColumnWidth(4, wCol4)
self.tocTree.setColumnWidth(5, hPx)
self.tocTree.setColumnWidth(5, 12)
# Options
wordsPerPage = options.getInt("GuiNovelDetails", "wordsPerPage", 350)
@@ -394,8 +382,8 @@ class _ContentsPage(NFixedPage):
self.optionsBox.addWidget(self.dblValue, 0, 4)
self.optionsBox.addWidget(self.poLabel, 1, 0)
self.optionsBox.addWidget(self.poValue, 1, 1)
self.optionsBox.setHorizontalSpacing(hPx)
self.optionsBox.setVerticalSpacing(vPx)
self.optionsBox.setHorizontalSpacing(12)
self.optionsBox.setVerticalSpacing(4)
self.optionsBox.setColumnStretch(2, 1)
# Assemble
@@ -410,18 +398,12 @@ class _ContentsPage(NFixedPage):
def saveSettings(self) -> None:
"""Save the user GUI settings."""
widthCol0 = CONFIG.rpxInt(self.tocTree.columnWidth(0))
widthCol1 = CONFIG.rpxInt(self.tocTree.columnWidth(1))
widthCol2 = CONFIG.rpxInt(self.tocTree.columnWidth(2))
widthCol3 = CONFIG.rpxInt(self.tocTree.columnWidth(3))
widthCol4 = CONFIG.rpxInt(self.tocTree.columnWidth(4))
options = SHARED.project.options
options.setValue("GuiNovelDetails", "widthCol0", widthCol0)
options.setValue("GuiNovelDetails", "widthCol1", widthCol1)
options.setValue("GuiNovelDetails", "widthCol2", widthCol2)
options.setValue("GuiNovelDetails", "widthCol3", widthCol3)
options.setValue("GuiNovelDetails", "widthCol4", widthCol4)
options.setValue("GuiNovelDetails", "widthCol0", self.tocTree.columnWidth(0))
options.setValue("GuiNovelDetails", "widthCol1", self.tocTree.columnWidth(1))
options.setValue("GuiNovelDetails", "widthCol2", self.tocTree.columnWidth(2))
options.setValue("GuiNovelDetails", "widthCol3", self.tocTree.columnWidth(3))
options.setValue("GuiNovelDetails", "widthCol4", self.tocTree.columnWidth(4))
options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value())
options.setValue("GuiNovelDetails", "countFrom", self.poValue.value())
options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked())
+26 -37
View File
@@ -40,7 +40,7 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import cssCol, formatInt, makeFileNameSafe, qtLambda
from novelwriter.common import cssCol, formatInt, makeFileNameSafe, qtAddAction, qtLambda
from novelwriter.constants import nwFiles
from novelwriter.core.coretools import ProjectBuilder
from novelwriter.enum import nwItemClass
@@ -66,29 +66,21 @@ class GuiWelcome(NDialog):
self.setObjectName("GuiWelcome")
self.setWindowTitle(self.tr("Welcome"))
self.setMinimumWidth(CONFIG.pxInt(650))
self.setMinimumHeight(CONFIG.pxInt(450))
hA = CONFIG.pxInt(8)
hB = CONFIG.pxInt(16)
hC = CONFIG.pxInt(24)
hD = CONFIG.pxInt(36)
hE = CONFIG.pxInt(48)
hF = CONFIG.pxInt(128)
btnIconSize = SHARED.theme.buttonIconSize
self._hPx = CONFIG.pxInt(600)
self.setMinimumWidth(650)
self.setMinimumHeight(450)
self.resize(*CONFIG.welcomeWinSize)
btnIconSize = SHARED.theme.buttonIconSize
# Elements
# ========
self.bgImage = SHARED.theme.loadDecoration("welcome")
self.nwImage = SHARED.theme.loadDecoration("nw-text", h=hD)
self.nwImage = SHARED.theme.loadDecoration("nw-text", h=36)
self.bgColor = QColor(54, 54, 54) if SHARED.theme.isDarkTheme else QColor(255, 255, 255)
self.nwLogo = QLabel(self)
self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (hF, hF)))
self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (128, 128)))
self.nwLabel = QLabel(self)
self.nwLabel.setPixmap(self.nwImage)
@@ -152,18 +144,18 @@ class GuiWelcome(NDialog):
# ========
self.innerBox = QVBoxLayout()
self.innerBox.addSpacing(hB)
self.innerBox.addSpacing(16)
self.innerBox.addWidget(self.nwLabel)
self.innerBox.addWidget(self.nwInfo)
self.innerBox.addSpacing(hA)
self.innerBox.addSpacing(8)
self.innerBox.addWidget(self.mainStack)
self.innerBox.addSpacing(hB)
self.innerBox.addSpacing(16)
self.innerBox.addLayout(self.btnBox)
self.outerBox = QHBoxLayout()
self.outerBox.addWidget(self.nwLogo, 3, QtAlignRightTop)
self.outerBox.addLayout(self.innerBox, 9)
self.outerBox.setContentsMargins(hF, hE, hC, hE)
self.outerBox.setContentsMargins(128, 48, 24, 48)
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
@@ -183,7 +175,7 @@ class GuiWelcome(NDialog):
def paintEvent(self, event: QPaintEvent) -> None:
"""Overload the paint event to draw the background image."""
hWin = self.height()
hPix = min(hWin, self._hPx)
hPix = min(hWin, 600)
tMode = Qt.TransformationMode.SmoothTransformation
painter = QPainter(self)
painter.fillRect(self.rect(), self.bgColor)
@@ -309,11 +301,10 @@ class _OpenProjectPage(QWidget):
self._selectFirstItem()
mPx = CONFIG.pxInt(4)
baseCol = cssCol(self.palette().base().color(), PANEL_ALPHA)
self.setStyleSheet(
f"QListView {{border: none; background: {baseCol};}} "
f"QLineEdit {{border: none; background: {baseCol}; padding: {mPx}px;}} "
f"QLineEdit {{border: none; background: {baseCol}; padding: 4px;}} "
)
return
@@ -370,9 +361,9 @@ class _OpenProjectPage(QWidget):
"""Open the custom context menu."""
ctxMenu = QMenu(self)
ctxMenu.setObjectName("ContextMenu") # Used for testing
action = ctxMenu.addAction(self.tr("Open Project"))
action = qtAddAction(ctxMenu, self.tr("Open Project"))
action.triggered.connect(self.openSelectedItem)
action = ctxMenu.addAction(self.tr("Remove Project"))
action = qtAddAction(ctxMenu, self.tr("Remove Project"))
action.triggered.connect(self._deleteSelectedItem)
ctxMenu.exec(self.mapToGlobal(pos))
ctxMenu.deleteLater()
@@ -400,11 +391,10 @@ class _ProjectListItem(QStyledItemDelegate):
fPx = SHARED.theme.fontPixelSize
fPt = SHARED.theme.fontPointSize
tPx = round(1.2 * fPx)
mPx = CONFIG.pxInt(4)
iPx = tPx + fPx
self._pPx = (mPx//2, 3*mPx//2, iPx + mPx, mPx, mPx + tPx) # Painter coordinates
self._hPx = 2*mPx + tPx + fPx # Fixed height
self._pPx = (iPx + 4, tPx + 4) # Painter coordinates
self._hPx = 8 + tPx + fPx # Fixed height
self._tFont = QApplication.font()
self._tFont.setPointSizeF(1.2*fPt)
@@ -423,7 +413,7 @@ class _ProjectListItem(QStyledItemDelegate):
rect = opt.rect
title, _, details = index.data()
tFlag = Qt.TextFlag.TextSingleLine
ix, iy, x, y1, y2 = self._pPx
x, y = self._pPx
painter.save()
if opt.state & QtSelected == QtSelected:
@@ -431,12 +421,12 @@ class _ProjectListItem(QStyledItemDelegate):
painter.fillRect(rect, QApplication.palette().highlight())
painter.setOpacity(1.0)
painter.drawPixmap(ix, rect.top() + iy, self._icon)
painter.drawPixmap(2, rect.top() + 6, self._icon)
painter.setFont(self._tFont)
painter.drawText(rect.adjusted(x, y1, 0, 0), tFlag, title)
painter.drawText(rect.adjusted(x, 4, 0, 0), tFlag, title)
painter.setFont(self._dFont)
painter.setPen(self._dPen)
painter.drawText(rect.adjusted(x, y2, 0, 0), tFlag, details)
painter.drawText(rect.adjusted(x, y, 0, 0), tFlag, details)
painter.restore()
return
@@ -557,7 +547,6 @@ class _NewProjectForm(QWidget):
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
sPx = CONFIG.pxInt(16)
# Project Settings
# ================
@@ -592,15 +581,15 @@ class _NewProjectForm(QWidget):
self.fillMenu = QMenu(self.browseFill)
self.fillBlank = self.fillMenu.addAction(self.tr("Create a fresh project"))
self.fillBlank = qtAddAction(self.fillMenu, self.tr("Create a fresh project"))
self.fillBlank.setIcon(SHARED.theme.getIcon("document"))
self.fillBlank.triggered.connect(self._setFillBlank)
self.fillSample = self.fillMenu.addAction(self.tr("Create an example project"))
self.fillSample = qtAddAction(self.fillMenu, self.tr("Create an example project"))
self.fillSample.setIcon(SHARED.theme.getIcon("document_add", "blue"))
self.fillSample.triggered.connect(self._setFillSample)
self.fillCopy = self.fillMenu.addAction(self.tr("Copy an existing project"))
self.fillCopy = qtAddAction(self.fillMenu, self.tr("Copy an existing project"))
self.fillCopy.setIcon(SHARED.theme.getIcon("project_copy", "green"))
self.fillCopy.triggered.connect(self._setFillCopy)
@@ -675,7 +664,7 @@ class _NewProjectForm(QWidget):
self.extraBox = QVBoxLayout()
self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Chapters and Scenes")), self))
self.extraBox.addLayout(self.novelForm)
self.extraBox.addSpacing(sPx)
self.extraBox.addSpacing(16)
self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Project Notes")), self))
self.extraBox.addLayout(self.notesForm)
self.extraBox.setContentsMargins(0, 0, 0, 0)
@@ -687,7 +676,7 @@ class _NewProjectForm(QWidget):
self.formBox = QVBoxLayout()
self.formBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Create New Project")), self))
self.formBox.addLayout(self.projectForm)
self.formBox.addSpacing(sPx)
self.formBox.addSpacing(16)
self.formBox.addWidget(self.extraWidget)
self.formBox.addStretch(1)
+32 -43
View File
@@ -82,26 +82,18 @@ class GuiWritingStats(NToolDialog):
pOptions = SHARED.project.options
self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(CONFIG.pxInt(420))
self.setMinimumHeight(CONFIG.pxInt(400))
self.setMinimumWidth(420)
self.setMinimumHeight(400)
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiWritingStats", "winWidth", 550)),
CONFIG.pxInt(pOptions.getInt("GuiWritingStats", "winHeight", 500))
pOptions.getInt("GuiWritingStats", "winWidth", 550),
pOptions.getInt("GuiWritingStats", "winHeight", 500),
)
# List Box
wCol0 = CONFIG.pxInt(
pOptions.getInt("GuiWritingStats", "widthCol0", 180)
)
wCol1 = CONFIG.pxInt(
pOptions.getInt("GuiWritingStats", "widthCol1", 80)
)
wCol2 = CONFIG.pxInt(
pOptions.getInt("GuiWritingStats", "widthCol2", 80)
)
wCol3 = CONFIG.pxInt(
pOptions.getInt("GuiWritingStats", "widthCol3", 80)
)
wCol0 = pOptions.getInt("GuiWritingStats", "widthCol0", 180)
wCol1 = pOptions.getInt("GuiWritingStats", "widthCol1", 80)
wCol2 = pOptions.getInt("GuiWritingStats", "widthCol2", 80)
wCol3 = pOptions.getInt("GuiWritingStats", "widthCol3", 80)
self.listBox = QTreeWidget(self)
self.listBox.setHeaderLabels([
@@ -133,7 +125,6 @@ class GuiWritingStats(NToolDialog):
# Word Bar
self.barHeight = int(round(0.5*SHARED.theme.fontPixelSize))
self.barWidth = CONFIG.pxInt(200)
self.barImage = QPixmap(self.barHeight, self.barHeight)
self.barImage.fill(self.palette().highlight().color())
@@ -262,25 +253,27 @@ class GuiWritingStats(NToolDialog):
self.optsBox.addWidget(self.histMax, 0)
# Buttons
self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
self.saveJSON.triggered.connect(qtLambda(self._saveData, self.FMT_JSON))
self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
self.saveCSV.triggered.connect(qtLambda(self._saveData, self.FMT_CSV))
self.saveMenu = QMenu(self)
self.saveMenu.addAction(self.saveJSON)
self.saveMenu.addAction(self.saveCSV)
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.rejected.connect(self._doClose)
self.btnClose = self.buttonBox.addButton(QtDialogClose)
self.btnClose.setAutoDefault(False)
if self.btnClose:
self.btnClose.setAutoDefault(False)
self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QtRoleAction)
self.btnSave.setAutoDefault(False)
self.saveMenu = QMenu(self)
self.btnSave.setMenu(self.saveMenu)
self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
self.saveJSON.triggered.connect(qtLambda(self._saveData, self.FMT_JSON))
self.saveMenu.addAction(self.saveJSON)
self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
self.saveCSV.triggered.connect(qtLambda(self._saveData, self.FMT_CSV))
self.saveMenu.addAction(self.saveCSV)
if self.btnSave:
self.btnSave.setAutoDefault(False)
self.btnSave.setMenu(self.saveMenu)
# Assemble
self.outerBox = QGridLayout()
@@ -328,14 +321,10 @@ class GuiWritingStats(NToolDialog):
"""Save the state of the window, clear cache, end close."""
self.logData = []
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
widthCol0 = CONFIG.rpxInt(self.listBox.columnWidth(0))
widthCol1 = CONFIG.rpxInt(self.listBox.columnWidth(1))
widthCol2 = CONFIG.rpxInt(self.listBox.columnWidth(2))
widthCol3 = CONFIG.rpxInt(self.listBox.columnWidth(3))
header = self.listBox.header()
sortCol = self.listBox.sortColumn()
sortOrder = self.listBox.header().sortIndicatorOrder()
sortOrder = header.sortIndicatorOrder() if header else 0
incNovel = self.incNovel.isChecked()
incNotes = self.incNotes.isChecked()
hideZeros = self.hideZeros.isChecked()
@@ -346,12 +335,12 @@ class GuiWritingStats(NToolDialog):
logger.debug("Saving State: GuiWritingStats")
pOptions = SHARED.project.options
pOptions.setValue("GuiWritingStats", "winWidth", winWidth)
pOptions.setValue("GuiWritingStats", "winHeight", winHeight)
pOptions.setValue("GuiWritingStats", "widthCol0", widthCol0)
pOptions.setValue("GuiWritingStats", "widthCol1", widthCol1)
pOptions.setValue("GuiWritingStats", "widthCol2", widthCol2)
pOptions.setValue("GuiWritingStats", "widthCol3", widthCol3)
pOptions.setValue("GuiWritingStats", "winWidth", self.width())
pOptions.setValue("GuiWritingStats", "winHeight", self.height())
pOptions.setValue("GuiWritingStats", "widthCol0", self.listBox.columnWidth(0))
pOptions.setValue("GuiWritingStats", "widthCol1", self.listBox.columnWidth(1))
pOptions.setValue("GuiWritingStats", "widthCol2", self.listBox.columnWidth(2))
pOptions.setValue("GuiWritingStats", "widthCol3", self.listBox.columnWidth(3))
pOptions.setValue("GuiWritingStats", "sortCol", sortCol)
pOptions.setValue("GuiWritingStats", "sortOrder", sortOrder)
pOptions.setValue("GuiWritingStats", "incNovel", incNovel)
-29
View File
@@ -23,8 +23,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from typing import Any, TypeVar
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor, QFont, QPainter, QTextCharFormat, QTextCursor, QTextFormat
from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QHeaderView, QSizePolicy, QStyle
@@ -148,30 +146,3 @@ FONT_STYLE: dict[QFont.Style, str] = {
QFont.Style.StyleItalic: "italic",
QFont.Style.StyleOblique: "oblique",
}
##
# Decorators and MetaClasses
##
T_ = TypeVar("T_", bound=object)
def nwDataClass(cls: T_) -> T_:
"""A simple data class decorator that generates slots automatically
and creates an init function to match.
"""
def wrap(cls: T_) -> T_:
fields = tuple(a for a in dir(cls) if not a.startswith("__"))
values = {a: getattr(cls, a) for a in fields}
def init(self: Any) -> None:
nonlocal values
for a, v in values.items():
self.__setattr__(a, v)
return type(cls.__class__.__name__, (object,), { # type: ignore
"__slots__": fields, "__init__": init
})
return wrap(cls)
+4 -27
View File
@@ -254,52 +254,29 @@ def testBaseConfig_SettersGetters(fncPath):
tstConf.setMainWinSize(70, 70)
assert tstConf.mainWinSize == [70, 70]
assert tstConf._mainWinSize == [70, 70]
assert tstConf.mainWinSize == [70, 70]
tstConf.setMainWinSize(1200, 650)
# Welcome Window Size
tstConf.setWelcomeWinSize(70, 70)
assert tstConf.welcomeWinSize == [70, 70]
assert tstConf._welcomeSize == [70, 70]
assert tstConf.welcomeWinSize == [70, 70]
tstConf.setWelcomeWinSize(800, 500)
# Preferences Size
tstConf.setPreferencesWinSize(70, 70)
assert tstConf.preferencesWinSize == [70, 70]
assert tstConf._prefsWinSize == [70, 70]
assert tstConf.prefsWinSize == [70, 70]
assert tstConf.prefsWinSize == [70, 70]
tstConf.setPreferencesWinSize(700, 615)
# Main Pane Splitter
tstConf.setMainPanePos([200, 700])
assert tstConf.mainPanePos == [200, 700]
assert tstConf._mainPanePos == [200, 700]
tstConf.setMainPanePos([300, 800])
# View Pane Splitter
tstConf.setViewPanePos([400, 250])
assert tstConf.viewPanePos == [400, 250]
assert tstConf._viewPanePos == [400, 250]
tstConf.setViewPanePos([500, 150])
# Outline Pane Splitter
tstConf.setOutlinePanePos([400, 250])
assert tstConf.outlinePanePos == [400, 250]
assert tstConf._outlnPanePos == [400, 250]
tstConf.setOutlinePanePos([500, 150])
# Getters Only
# ============
assert tstConf.getTextWidth(False) == 700
assert tstConf.getTextWidth(True) == 800
assert tstConf.getTextMargin() == 40
assert tstConf.getTabWidth() == 40
@pytest.mark.base
+1 -1
View File
@@ -398,7 +398,7 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
buildTwo.setName("Build Two")
buildIDTwo = buildTwo.buildID
# Check that we can extract infor about the builds
# Check that we can extract info about the builds
builds.setBuild(buildTwo)
assert len(builds) == 2
assert buildsFile.exists()
+3 -1
View File
@@ -41,7 +41,9 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
msgAbout = SHARED.findTopLevelWidget(GuiAbout)
assert isinstance(msgAbout, GuiAbout)
assert msgAbout.txtCredits.document().characterCount() > 100
document = msgAbout.txtCredits.document()
assert document is not None
assert document.characterCount() > 100
with monkeypatch.context() as mp:
mp.setattr("novelwriter.config.Config.assetPath", lambda *a: Path("whatever"))
+23 -16
View File
@@ -72,17 +72,16 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
prefs.close()
# Check Fallback Values
with monkeypatch.context() as mp:
mp.setattr(CONFIG, "hasEnchant", False)
prefs = GuiPreferences(nwGUI)
prefs.show()
CONFIG.hasEnchant = False
prefs = GuiPreferences(nwGUI)
prefs.show()
# Check Spell Checking
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
assert len(spelling) == 1
assert spelling == [""]
# Check Spell Checking
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
assert len(spelling) == 1
assert spelling == [""]
prefs.close()
prefs.close()
# qtbot.stop()
@@ -97,6 +96,7 @@ def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
# Check Navigation
vBar = prefs.mainForm.verticalScrollBar()
assert vBar is not None
old = -1
with qtbot.waitSignal(vBar.valueChanged) as value:
prefs.sidebar.button(1).click()
@@ -120,12 +120,16 @@ def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
# Check Save Button
prefs.show()
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
prefs.buttonBox.button(QtDialogSave).click()
button = prefs.buttonBox.button(QtDialogSave)
assert button is not None
button.click()
assert signal.args == [False, False, False, False]
# Check Close Button
prefs.show()
prefs.buttonBox.button(QtDialogCancel).click()
button = prefs.buttonBox.button(QtDialogCancel)
assert button is not None
button.click()
assert prefs.isHidden() is True
# Close Using Escape Key
@@ -138,13 +142,14 @@ def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
@pytest.mark.gui
def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
"""Test the preferences dialog settings."""
spelling = [("en", "English [en]"), ("de", "Deutch [de]")]
languages = [("en_GB", "British English"), ("en_US", "US English")]
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling)
monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages)
(fncPath / "nw_en_US.qm").touch()
(fncPath / "project_en_US.json").touch()
CONFIG._nwLangPath = fncPath
prefs = GuiPreferences(nwGUI)
with qtbot.waitExposed(prefs):
@@ -313,7 +318,9 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
with monkeypatch.context() as mp:
mp.setattr(QFontDatabase, "families", lambda *a: ["TestFont"])
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
prefs.buttonBox.button(QtDialogSave).click()
button = prefs.buttonBox.button(QtDialogSave)
assert button is not None
button.click()
assert signal.args == [True, True, True, True]
# Check Settings
@@ -88,7 +88,12 @@ def testDlgProjSettings_SettingsPage(qtbot, monkeypatch, nwGUI, fncPath, projPat
"""Test the settings page of the dialog."""
languages = [("en", "English"), ("de", "German")]
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda *a: languages)
monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages)
(fncPath / "nw_en.qm").touch()
(fncPath / "nw_de.qm").touch()
(fncPath / "project_en.json").touch()
(fncPath / "project_de.json").touch()
CONFIG._nwLangPath = fncPath
# Create new project
buildTestProject(nwGUI, projPath)
+11 -6
View File
@@ -441,6 +441,9 @@ def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
ctxMenu.deleteLater()
# Copy Text
clipboard = QApplication.clipboard()
assert clipboard is not None
ctxMenu = getMenuForPos(docEditor, 31, True)
assert ctxMenu is not None
assert docEditor.textCursor().selectedText() == "text"
@@ -448,14 +451,14 @@ def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
assert actions == [
"Cut", "Copy", "Paste", "Select All", "Select Word", "Select Paragraph"
]
QApplication.clipboard().clear()
clipboard.clear()
ctxMenu.actions()[1].trigger()
assert QApplication.clipboard().text(QClipboard.Mode.Clipboard) == "text"
assert clipboard.text(QClipboard.Mode.Clipboard) == "text"
# Cut Text
QApplication.clipboard().clear()
clipboard.clear()
ctxMenu.actions()[0].trigger()
assert QApplication.clipboard().text(QClipboard.Mode.Clipboard) == "text"
assert clipboard.text(QClipboard.Mode.Clipboard) == "text"
assert "text" not in docEditor.getText()
# Paste Text
@@ -572,7 +575,9 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Select/Cut/Copy/Paste/Undo/Redo
# ===============================
QApplication.clipboard().clear()
clipboard = QApplication.clipboard()
assert clipboard is not None
clipboard.clear()
# Select All
assert docEditor.docAction(nwDocAction.SEL_ALL) is True
@@ -624,7 +629,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
assert newPara[5] == ipsumText[4]
assert newPara[6] == ipsumText[2]
QApplication.clipboard().clear()
clipboard.clear()
# Emphasis/Undo/Redo
# ==================
+7 -6
View File
@@ -91,18 +91,19 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
docViewer.setTextCursor(cursor)
docViewer._makeSelection(QTextCursor.SelectionType.WordUnderCursor)
qClip = QApplication.clipboard()
qClip.clear()
clipboard = QApplication.clipboard()
assert clipboard is not None
clipboard.clear()
# Cut
assert docViewer.docAction(nwDocAction.CUT) is True
assert qClip.text() == "laoreet"
qClip.clear()
assert clipboard.text() == "laoreet"
clipboard.clear()
# Copy
assert docViewer.docAction(nwDocAction.COPY) is True
assert qClip.text() == "laoreet"
qClip.clear()
assert clipboard.text() == "laoreet"
clipboard.clear()
# Select Paragraph
assert docViewer.docAction(nwDocAction.SEL_PARA) is True
+3 -1
View File
@@ -1148,7 +1148,9 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
def getTransformSubMenu(menu: QMenu) -> list[str]:
for action in menu.actions():
if action.text() == "Transform ...":
return [x.text() for x in action.menu().actions() if x.text()]
submenu = action.menu()
assert submenu is not None
return [x.text() for x in submenu.actions() if x.text()]
return []
# Context Menu on Document File Item
+6 -2
View File
@@ -95,7 +95,9 @@ def testToolManuscriptBuild_Main(
assert (fncPath / "TestBuild").with_suffix(nwLabels.BUILD_EXT[fmt]).exists()
lastFmt = fmt
manus._dialogButtonClicked(manus.buttonBox.button(QtDialogClose))
button = manus.buttonBox.button(QtDialogClose)
assert button is not None
manus._dialogButtonClicked(button)
manus.deleteLater()
assert build.lastBuildName == "TestBuild"
@@ -150,5 +152,7 @@ def testToolManuscriptBuild_Main(
assert lastUrl.startswith("file://")
# Finish
manus._dialogButtonClicked(manus.buttonBox.button(QtDialogClose))
button = manus.buttonBox.button(QtDialogClose)
assert button is not None
manus._dialogButtonClicked(button)
# qtbot.stop()
+15 -5
View File
@@ -67,7 +67,9 @@ def testToolManuscript_Init(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
# Build a preview
manus.buildList.clearSelection()
manus.buildList.setCurrentRow(0)
with qtbot.waitSignal(manus.docPreview.document().contentsChanged):
document = manus.docPreview.document()
assert document is not None
with qtbot.waitSignal(document.contentsChanged):
manus.btnPreview.click()
assert manus.docPreview.toPlainText().strip() == allText
@@ -113,7 +115,9 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
bSettings.newSettingsReady.connect(_testNewSettingsReady)
bSettings.buttonBox.button(QtDialogSave).click()
button = bSettings.buttonBox.button(QtDialogSave)
assert button is not None
button.click()
assert isinstance(build, BuildSettings)
assert build.name == "Test Build"
@@ -132,7 +136,9 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
bSettings.newSettingsReady.connect(_testNewSettingsReady)
bSettings.buttonBox.button(QtDialogApply).click() # Should leave the dialog open
button = bSettings.buttonBox.button(QtDialogApply)
assert button is not None
button.click() # Should leave the dialog open
assert isinstance(build, BuildSettings)
assert build.name == "Test Build"
@@ -210,7 +216,9 @@ def testToolManuscript_Features(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
manus._builds.setBuild(build)
manus.buildList.setCurrentRow(0)
with qtbot.waitSignal(manus.docPreview.document().contentsChanged):
document = manus.docPreview.document()
assert document is not None
with qtbot.waitSignal(document.contentsChanged):
manus.btnPreview.click()
assert manus.docPreview.toPlainText().strip() != ""
@@ -304,7 +312,9 @@ def testToolManuscript_Print(monkeypatch, qtbot, nwGUI, projPath):
manus.loadContent()
manus.buildList.setCurrentRow(0)
with qtbot.waitSignal(manus.docPreview.document().contentsChanged):
document = manus.docPreview.document()
assert document is not None
with qtbot.waitSignal(document.contentsChanged):
manus.btnPreview.click()
assert manus.docPreview.toPlainText().strip() != ""
+60 -20
View File
@@ -51,13 +51,19 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
bSettings.loadContent()
# Flip through pages
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 1).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 1)
assert button is not None
button.click()
assert isinstance(bSettings.toolStack.currentWidget(), _FormattingTab)
bSettings.sidebar._group.button(bSettings.OPT_HEADINGS).click()
button = bSettings.sidebar._group.button(bSettings.OPT_HEADINGS)
assert button is not None
button.click()
assert isinstance(bSettings.toolStack.currentWidget(), _HeadingsTab)
bSettings.sidebar._group.button(bSettings.OPT_FILTERS).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FILTERS)
assert button is not None
button.click()
assert isinstance(bSettings.toolStack.currentWidget(), _FilterTab)
# Check dialog buttons
@@ -72,7 +78,9 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
# Capture Apply button
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
bSettings.newSettingsReady.connect(_testNewSettingsReady)
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogApply))
button = bSettings.buttonBox.button(QtDialogApply)
assert button is not None
bSettings._dialogButtonClicked(button)
assert triggered
@@ -81,7 +89,9 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
bSettings.newSettingsReady.connect(_testNewSettingsReady)
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogSave))
button = bSettings.buttonBox.button(QtDialogSave)
assert button is not None
bSettings._dialogButtonClicked(button)
assert triggered
@@ -98,7 +108,9 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
assert triggered
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -129,7 +141,9 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
bSettings.loadContent()
filterTab = bSettings.optTabSelect
bSettings.sidebar._group.button(bSettings.OPT_FILTERS).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FILTERS)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is filterTab
# Check content
@@ -308,7 +322,9 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
]
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -339,7 +355,9 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
bSettings.loadContent()
headTab = bSettings.optTabHeadings
bSettings.sidebar._group.button(bSettings.OPT_HEADINGS).click()
button = bSettings.sidebar._group.button(bSettings.OPT_HEADINGS)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is headTab
# Check initial values
@@ -478,7 +496,9 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
assert build.getBool("headings.hideSection") is True
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -501,7 +521,9 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI):
bSettings.loadContent()
fmtTab = bSettings.optTabFormatting
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 1).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 1)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values
@@ -538,7 +560,9 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI):
assert build.getBool("text.addNoteHeadings") is True
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -563,7 +587,9 @@ def testToolBuildSettings_FormatTextFormat(monkeypatch, qtbot, nwGUI):
bSettings.loadContent()
fmtTab = bSettings.optTabFormatting
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 2).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 2)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values
@@ -610,7 +636,9 @@ def testToolBuildSettings_FormatTextFormat(monkeypatch, qtbot, nwGUI):
assert fmtTab._textFont == font
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -629,7 +657,9 @@ def testToolBuildSettings_FormatFirstLineIndent(monkeypatch, qtbot, nwGUI):
bSettings.loadContent()
fmtTab = bSettings.optTabFormatting
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 3).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 3)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values
@@ -650,7 +680,9 @@ def testToolBuildSettings_FormatFirstLineIndent(monkeypatch, qtbot, nwGUI):
assert build.getBool("format.indentFirstPar") is True
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -674,7 +706,9 @@ def testToolBuildSettings_FormatPageLayout(monkeypatch, qtbot, nwGUI):
bSettings.loadContent()
fmtTab = bSettings.optTabFormatting
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 4).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 4)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values
@@ -704,7 +738,9 @@ def testToolBuildSettings_FormatPageLayout(monkeypatch, qtbot, nwGUI):
assert fmtTab.rightMargin.value() == 1.5
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
@@ -728,7 +764,9 @@ def testToolBuildSettings_FormatOutput(qtbot, nwGUI):
bSettings.loadContent()
fmtTab = bSettings.optTabFormatting
bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 5).click()
button = bSettings.sidebar._group.button(bSettings.OPT_FORMATTING + 5)
assert button is not None
button.click()
assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values
@@ -769,5 +807,7 @@ def testToolBuildSettings_FormatOutput(qtbot, nwGUI):
assert fmtTab.odtPageHeader.text() == nwHeadFmt.DOC_AUTO
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QtDialogClose))
button = bSettings.buttonBox.button(QtDialogClose)
assert button is not None
bSettings._dialogButtonClicked(button)
# qtbot.stop()
+217 -56
View File
@@ -125,14 +125,37 @@ def testToolWritingStats_Export(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
assert sessLog.notesWords.text() == "{:n}".format(275)
assert sessLog.totalWords.text() == "{:n}".format(875)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(300)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(-120)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(-20)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(40)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(-400)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(200)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(300)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-120)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-20)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(40)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-400)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(200)
assert sessLog._saveData(sessLog.FMT_CSV)
assert sessLog._saveData(sessLog.FMT_JSON)
@@ -205,14 +228,37 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
sessLog.populateGUI()
sessLog.listBox.sortByColumn(sessLog.C_TIME, Qt.SortOrder.AscendingOrder)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(300)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(-120)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(-20)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(40)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(-400)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(200)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(300)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-120)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-20)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(40)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-400)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(200)
# No Novel Files
qtbot.mouseClick(sessLog.incNovel, QtMouseLeft)
@@ -222,14 +268,37 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(-100)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(150)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(-60)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(-10)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(20)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(100)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-100)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(150)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-60)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-10)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(20)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(100)
assert jsonData == [
{
@@ -268,14 +337,37 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(-100)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(150)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(-60)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(-10)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(20)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(100)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-100)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(150)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-60)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-10)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(20)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(100)
assert jsonData == [
{
@@ -314,10 +406,21 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(300)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(40)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(200)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(300)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(40)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(200)
assert jsonData == [
{
@@ -344,16 +447,45 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(0)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(300)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(-120)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(-20)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(40)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(-400)
assert sessLog.listBox.topLevelItem(8).text(sessLog.C_COUNT) == "{:n}".format(200)
assert sessLog.listBox.topLevelItem(9).text(sessLog.C_COUNT) == "{:n}".format(0)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(0)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(300)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-120)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-20)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(40)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-400)
item = sessLog.listBox.topLevelItem(8)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(200)
item = sessLog.listBox.topLevelItem(9)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(0)
assert jsonData == [
{
@@ -390,9 +522,15 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
]
# Toggle Idle Time
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_IDLE) == "4 %"
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_IDLE) == "4 %"
qtbot.mouseClick(sessLog.showIdleTime, QtMouseLeft)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_IDLE) == "00:01:10"
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_IDLE) == "00:01:10"
# Group by Day
qtbot.mouseClick(sessLog.groupByDay, QtMouseLeft)
@@ -402,14 +540,37 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
assert sessLog.listBox.topLevelItem(0).text(sessLog.C_COUNT) == "{:n}".format(1)
assert sessLog.listBox.topLevelItem(1).text(sessLog.C_COUNT) == "{:n}".format(-200)
assert sessLog.listBox.topLevelItem(2).text(sessLog.C_COUNT) == "{:n}".format(180)
assert sessLog.listBox.topLevelItem(3).text(sessLog.C_COUNT) == "{:n}".format(-20)
assert sessLog.listBox.topLevelItem(4).text(sessLog.C_COUNT) == "{:n}".format(40)
assert sessLog.listBox.topLevelItem(5).text(sessLog.C_COUNT) == "{:n}".format(-400)
assert sessLog.listBox.topLevelItem(6).text(sessLog.C_COUNT) == "{:n}".format(200)
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(0)
item = sessLog.listBox.topLevelItem(0)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(1)
item = sessLog.listBox.topLevelItem(1)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-200)
item = sessLog.listBox.topLevelItem(2)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(180)
item = sessLog.listBox.topLevelItem(3)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-20)
item = sessLog.listBox.topLevelItem(4)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(40)
item = sessLog.listBox.topLevelItem(5)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(-400)
item = sessLog.listBox.topLevelItem(6)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(200)
item = sessLog.listBox.topLevelItem(7)
assert item is not None
assert item.text(sessLog.C_COUNT) == "{:n}".format(0)
assert jsonData == [
{
+3 -1
View File
@@ -241,5 +241,7 @@ class SimpleDialog(QDialog):
def addWidget(self, widget: QWidget) -> None:
self._widget = widget
self.layout().addWidget(widget)
layout = self.layout()
assert layout is not None
layout.addWidget(widget)
return