Merge branch 'main' into primary_count

This commit is contained in:
Veronica Berglyd Olsen
2025-05-12 19:41:33 +02:00
42 changed files with 490 additions and 278 deletions
+3 -3
View File
@@ -25,15 +25,15 @@ text = 86, 88, 105
link = 9, 161, 237
headertext = 45, 174, 88
headertag = 45, 174, 88, 160
emphasis = 19, 187, 183
dialog = 207, 156, 0
emphasis = 247, 103, 187
dialog = 9, 161, 237
altdialog = 207, 156, 0
note = 120, 187, 185
hidden = 145, 148, 162
shortcode = 247, 103, 187
keyword = 9, 161, 237
tag = 45, 174, 88
value = 9, 161, 237
value = 207, 156, 0
optional = 207, 156, 0
spellcheckline = 255, 92, 87
errorline = 45, 174, 88
+48
View File
@@ -0,0 +1,48 @@
[Main]
name = Snazzy Light
author = Veronica Berglyd Olsen (adaptation)
credit = Florian Reuschel (color theme)
url = https://github.com/loilo/vscode-snazzy-light
license = MIT License
licenseurl = https://github.com/loilo/vscode-snazzy-light/blob/master/LICENSE
[Icons]
default = 86, 88, 105
faded = 84, 85, 84
red = 255, 92, 87
orange = 245, 185, 0
yellow = 207, 156, 0
green = 45, 174, 88
aqua = 19, 187, 183
blue = 9, 161, 237
purple = 247, 103, 187
[Project]
root = 9, 161, 237
folder = 207, 156, 0
file = 84, 85, 84
title = 45, 174, 88
chapter = 255, 92, 87
scene = 9, 161, 237
note = 207, 156, 0
[Palette]
window = 243, 244, 245
windowtext = 86, 88, 105
base = 250, 251, 252
alternatebase = 234, 234, 235
text = 86, 88, 105
tooltipbase = 245, 233, 194
tooltiptext = 86, 88, 105
button = 250, 251, 252
buttontext = 86, 88, 105
brighttext = 255, 255, 255
highlight = 9, 161, 237
highlightedtext = 255, 255, 255
link = 9, 161, 237
linkvisited = 9, 161, 237
[GUI]
helptext = 9, 161, 237
fadedtext = 84, 85, 84
errortext = 255, 92, 87
+10 -1
View File
@@ -40,7 +40,7 @@ from PyQt6.QtCore import QCoreApplication, QMimeData, QUrl
from PyQt6.QtGui import QAction, QDesktopServices, QFont, QFontDatabase, QFontInfo
from PyQt6.QtWidgets import QMenu, QMenuBar, QWidget
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
from novelwriter.constants import nwConst, nwLabels, nwQuotes, nwUnicode, trConst
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
@@ -298,6 +298,15 @@ def uniqueCompact(text: str) -> str:
return "".join(sorted(set(compact(text))))
def processDialogSymbols(symbols: str) -> str:
"""Process dialogue line symbols."""
result = ""
for c in uniqueCompact(symbols):
if c in nwQuotes.ALLOWED:
result += c
return result
def elide(text: str, length: int) -> str:
"""Elide a piece of text to a maximum length."""
if len(text) > (cut := max(4, length)):
+41 -19
View File
@@ -34,17 +34,17 @@ from time import time
from typing import TYPE_CHECKING, Final
from PyQt6.QtCore import (
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
QLocale, QStandardPaths, QSysInfo, QTranslator
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QDate,
QDateTime, QLibraryInfo, QLocale, QStandardPaths, QSysInfo, QTranslator
)
from PyQt6.QtGui import QFont, QFontDatabase, QFontMetrics
from PyQt6.QtWidgets import QApplication
from novelwriter.common import (
NWConfigParser, checkInt, checkPath, describeFont, fontMatcher,
formatTimeStamp
formatTimeStamp, processDialogSymbols, simplified
)
from novelwriter.constants import nwFiles, nwHtmlUnicode, nwUnicode
from novelwriter.constants import nwFiles, nwHtmlUnicode, nwQuotes, nwUnicode
from novelwriter.error import formatException, logException
if TYPE_CHECKING:
@@ -65,16 +65,16 @@ class Config:
__slots__ = (
"_appPath", "_appRoot", "_backPath", "_backupPath", "_confPath", "_dLocale", "_dShortDate",
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "_manuals",
"_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths", "_recentProjects",
"_splash", "allowOpenDial", "altDialogClose", "altDialogOpen", "appHandle", "appName",
"askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj", "autoScroll",
"autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine", "dialogStyle",
"doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash", "doReplaceDots",
"doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose", "fmtDQuoteOpen",
"fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose", "fmtSQuoteOpen",
"focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme", "hasEnchant",
"hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "_lastAuthor",
"_manuals", "_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths",
"_recentProjects", "_splash", "allowOpenDial", "altDialogClose", "altDialogOpen",
"appHandle", "appName", "askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj",
"autoScroll", "autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine",
"dialogStyle", "doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash",
"doReplaceDots", "doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose",
"fmtDQuoteOpen", "fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose",
"fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme",
"hasEnchant", "hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
"iconColDocs", "iconColTree", "iconTheme", "incNotesWCount", "isDebug", "kernelVer",
"lastNotes", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog",
"nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
@@ -149,6 +149,7 @@ class Config:
self._recentProjects = RecentProjects(self)
self._recentPaths = RecentPaths(self)
self._lastAuthor = ""
# General GUI Settings
self.guiLocale = self._qLocale.name()
@@ -321,6 +322,11 @@ class Config:
def recentProjects(self) -> RecentProjects:
return self._recentProjects
@property
def lastAuthor(self) -> str:
"""Return the last author name used."""
return simplified(self._lastAuthor)
##
# Getters
##
@@ -336,6 +342,11 @@ class Config:
# Setters
##
def setLastAuthor(self, value: str) -> None:
"""Set tle last used author name."""
self._lastAuthor = simplified(value)
return
def setMainWinSize(self, width: int, height: int) -> None:
"""Set the size of the main window, but only if the change is
larger than 5 pixels. The OS window manager will sometimes
@@ -467,11 +478,16 @@ class Config:
def localDate(self, value: datetime) -> str:
"""Return a localised date format."""
return self._dLocale.toString(value, self._dShortDate)
# Explicitly convert the date first, see bug #2325
return self._dLocale.toString(QDate(value.year, value.month, value.day), self._dShortDate)
def localDateTime(self, value: datetime) -> str:
"""Return a localised datetime format."""
return self._dLocale.toString(value, self._dShortDateTime)
# Explicitly convert the datetime first, see bug #2325
return self._dLocale.toString(
QDateTime(value.year, value.month, value.day, value.hour, value.minute, value.second),
self._dShortDateTime,
)
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
"""List localisation files in the i18n folder. The default GUI
@@ -640,6 +656,7 @@ class Config:
self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
self.askBeforeExit = conf.rdBool(sec, "askbeforeexit", self.askBeforeExit)
self._lastAuthor = conf.rdStr(sec, "lastauthor", self._lastAuthor)
# Editor
sec = "Editor"
@@ -675,9 +692,9 @@ class Config:
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
self.dialogStyle = conf.rdInt(sec, "dialogstyle", self.dialogStyle)
self.allowOpenDial = conf.rdBool(sec, "allowopendial", self.allowOpenDial)
self.dialogLine = conf.rdStr(sec, "dialogline", self.dialogLine)
self.narratorBreak = conf.rdStr(sec, "narratorbreak", self.narratorBreak)
self.narratorDialog = conf.rdStr(sec, "narratordialog", self.narratorDialog)
dialogLine = conf.rdStr(sec, "dialogline", self.dialogLine)
narratorBreak = conf.rdStr(sec, "narratorbreak", self.narratorBreak)
narratorDialog = conf.rdStr(sec, "narratordialog", self.narratorDialog)
self.altDialogOpen = conf.rdStr(sec, "altdialogopen", self.altDialogOpen)
self.altDialogClose = conf.rdStr(sec, "altdialogclose", self.altDialogClose)
self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
@@ -716,6 +733,10 @@ class Config:
logger.info("Using straight double quotes, so disabling auto-replace")
self.doReplaceDQuote = False
self.dialogLine = processDialogSymbols(dialogLine)
self.narratorBreak = narratorBreak if narratorBreak in nwQuotes.DASHES else ""
self.narratorDialog = narratorDialog if narratorDialog in nwQuotes.DASHES else ""
return True
def saveConfig(self) -> bool:
@@ -760,6 +781,7 @@ class Config:
"backuponclose": str(self.backupOnClose),
"askbeforebackup": str(self.askBeforeBackup),
"askbeforeexit": str(self.askBeforeExit),
"lastauthor": str(self._lastAuthor),
}
conf["Editor"] = {
+13
View File
@@ -505,6 +505,19 @@ class nwQuotes:
"\u300f": QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
}
DASHES: Final[dict[str, str]] = {
"": QT_TRANSLATE_NOOP("Constant", "None"),
"\u2013": QT_TRANSLATE_NOOP("Constant", "Short dash"),
"\u2014": QT_TRANSLATE_NOOP("Constant", "Long dash"),
"\u2015": QT_TRANSLATE_NOOP("Constant", "Horizontal bar"),
}
ALLOWED: Final[list[str]] = [
"\u0027", "\u0022", "\u2018", "\u2019", "\u201a", "\u201b", "\u201c", "\u201d", "\u201e",
"\u201f", "\u2e42", "\u2039", "\u203a", "\u00ab", "\u00bb", "\u300c", "\u300d", "\u300e",
"\u300f", "\u2013", "\u2014", "\u2015",
]
class nwUnicode:
"""Supported unicode character constants and their HTML equivalents."""
+41 -34
View File
@@ -383,10 +383,12 @@ class ProjectBuilder:
"""Build or copy a project from a data dictionary."""
if isinstance(data, dict):
path = data.get("path", None) or None
if author := data.get("author"):
CONFIG.setLastAuthor(author)
if isinstance(path, str | Path):
self._path = Path(path).resolve()
if data.get("sample"):
return self._extractSampleProject(self._path)
return self._extractSampleProject(self._path, data)
elif data.get("template"):
return self._copyProject(self._path, data)
else:
@@ -416,20 +418,21 @@ class ProjectBuilder:
self._path = project.storage.storagePath
lblNewProject = self.tr("New Project")
lblTitlePage = self.tr("Title Page")
trName = self.tr("New Project")
trAuthor = self.tr("Author Name")
trTitlePage = self.tr("Title Page")
# Settings
project.data.setUuid(None)
project.data.setName(data.get("name", lblNewProject))
project.data.setAuthor(data.get("author", ""))
project.data.setName(data.get("name", trName))
project.data.setAuthor(data.get("author", trAuthor))
project.data.setLanguage(CONFIG.guiLocale)
project.setDefaultStatusImport()
project.session.startSession()
# Add Root Folders
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
hTitlePage = project.newFile(trTitlePage, hNovelRoot)
# Generate Title Page
aDoc = project.storage.getDocument(hTitlePage)
@@ -446,25 +449,21 @@ class ProjectBuilder:
"\n"
">> {count}: [field:{field}] <<\n"
).format(
author=project.data.author or "None",
address=self.tr("Address"),
title=project.data.name or "None",
author=project.data.author or trAuthor,
address=self.tr("Address Line"),
title=project.data.name or trName,
by=self.tr("By"),
count=self.tr("Word Count"),
field=nwStats.WORDS_TEXT,
))
# Create a project structure based on selected root folders
# and a number of chapters and scenes selected in the
# wizard's custom page.
# Create chapters and scenes
numChapters = data.get("chapters", 0)
numScenes = data.get("scenes", 0)
chSynop = self.tr("Summary of the chapter.")
scSynop = self.tr("Summary of the scene.")
bfNote = self.tr("A short description.")
trChSynop = self.tr("Summary of the chapter.")
trScSynop = self.tr("Summary of the scene.")
trNoteDesc = self.tr("A short description.")
# Create chapters
if numChapters > 0:
@@ -472,7 +471,7 @@ class ProjectBuilder:
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
cHandle = project.newFile(chTitle, hNovelRoot)
aDoc = project.storage.getDocument(cHandle)
aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {chSynop}\n\n")
aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {trChSynop}\n\n")
# Create chapter scenes
if numScenes > 0 and cHandle:
@@ -480,7 +479,7 @@ class ProjectBuilder:
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {trScSynop}\n\n")
# Create scenes (no chapters)
elif numScenes > 0:
@@ -488,7 +487,7 @@ class ProjectBuilder:
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = project.newFile(scTitle, hNovelRoot)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {trScSynop}\n\n")
# Create notes folders
noteTitles = {
@@ -508,7 +507,7 @@ class ProjectBuilder:
aDoc.writeDocument(
f"# {noteTitles[newRoot]}\n\n"
f"@tag: {ntTag}\n\n"
f"%Short: {bfNote}\n\n"
f"%Short: {trNoteDesc}\n\n"
)
# Also add the archive and trash folders
@@ -563,23 +562,11 @@ class ProjectBuilder:
return False
# Open the copied project and update settings
project = NWProject()
project.openProject(dstPath)
project.data.setUuid("") # Creates a fresh uuid
project.data.setName(data.get("name", "None"))
project.data.setAuthor(data.get("author", ""))
project.data.setSpellCheck(True)
project.data.setSpellLang(None)
project.data.setDoBackup(True)
project.data.setSaveCount(0)
project.data.setAutoCount(0)
project.data.setEditTime(0)
project.saveProject()
project.closeProject()
self._resetProject(dstPath, data)
return True
def _extractSampleProject(self, path: Path) -> bool:
def _extractSampleProject(self, path: Path, data: dict) -> bool:
"""Make a copy of the sample project by extracting the
sample.zip file to the new path.
"""
@@ -593,6 +580,7 @@ class ProjectBuilder:
if (sample := CONFIG.assetPath("sample.zip")).is_file():
try:
shutil.unpack_archive(sample, path)
self._resetProject(path, data)
except Exception as exc:
SHARED.error(self.tr("Failed to create a new example project."), exc=exc)
return False
@@ -605,3 +593,22 @@ class ProjectBuilder:
return False
return True
def _resetProject(self, path: Path, data: dict) -> None:
"""Open a project and reset/update its settings."""
project = NWProject()
project.openProject(path)
project.data.setUuid("") # Creates a fresh uuid
if name := data.get("name", ""):
project.data.setName(name)
if author := data.get("author", ""):
project.data.setAuthor(author)
project.data.setSpellCheck(True)
project.data.setSpellLang(None)
project.data.setDoBackup(True)
project.data.setSaveCount(0)
project.data.setAutoCount(0)
project.data.setEditTime(0)
project.saveProject()
project.closeProject()
return
+6 -2
View File
@@ -53,8 +53,10 @@ C_COUNT_ICON = 0x0100 | Qt.ItemDataRole.DecorationRole
C_COUNT_ALIGN = 0x0100 | Qt.ItemDataRole.TextAlignmentRole
C_ACTIVE_ICON = 0x0200 | Qt.ItemDataRole.DecorationRole
C_ACTIVE_TIP = 0x0200 | Qt.ItemDataRole.ToolTipRole
C_ACTIVE_ACCESS = 0x0200 | Qt.ItemDataRole.AccessibleTextRole
C_STATUS_ICON = 0x0300 | Qt.ItemDataRole.DecorationRole
C_STATUS_TIP = 0x0300 | Qt.ItemDataRole.ToolTipRole
C_STATUS_ACCESS = 0x0300 | Qt.ItemDataRole.AccessibleTextRole
NODE_FLAGS = Qt.ItemFlag.ItemIsEnabled
NODE_FLAGS |= Qt.ItemFlag.ItemIsSelectable
@@ -150,13 +152,15 @@ class ProjectNode:
# Active
aText, aIcon = self._item.getActiveStatus()
self._cache[C_ACTIVE_TIP] = aText
self._cache[C_ACTIVE_ICON] = aIcon
self._cache[C_ACTIVE_TIP] = aText
self._cache[C_ACTIVE_ACCESS] = aText
# Status
sText, sIcon = self._item.getImportStatus()
self._cache[C_STATUS_TIP] = sText
self._cache[C_STATUS_ICON] = sIcon
self._cache[C_STATUS_TIP] = sText
self._cache[C_STATUS_ACCESS] = sText
return
+2
View File
@@ -46,6 +46,7 @@ R_TEXT = Qt.ItemDataRole.DisplayRole
R_ICON = Qt.ItemDataRole.DecorationRole
R_ALIGN = Qt.ItemDataRole.TextAlignmentRole
R_TIP = Qt.ItemDataRole.ToolTipRole
R_ACCESS = Qt.ItemDataRole.AccessibleTextRole
R_HANDLE = 0xff01
R_KEY = 0xff02
@@ -217,6 +218,7 @@ class NovelModel(QAbstractTableModel):
text = ", ".join(refs)
data[C_FACTOR*2 | R_TEXT] = text
data[C_FACTOR*2 | R_TIP] = f"<b>{self._extraLabel}:</b> {text}"
data[C_FACTOR*2 | R_ACCESS] = f"{self._extraLabel}: {text}"
data[C_FACTOR*3 | R_ICON] = self._more
data[R_HANDLE] = handle
data[R_KEY] = key
+8 -8
View File
@@ -483,14 +483,14 @@ class NWProject:
def setDefaultStatusImport(self) -> None:
"""Set the default status and importance values."""
self._data.itemStatus.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
self._data.itemStatus.add(None, self.tr("Note"), (200, 50, 0), "SQUARE", 0)
self._data.itemStatus.add(None, self.tr("Draft"), (200, 150, 0), "SQUARE", 0)
self._data.itemStatus.add(None, self.tr("Finished"), (50, 200, 0), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Minor"), (200, 50, 0), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Major"), (200, 150, 0), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Main"), (50, 200, 0), "SQUARE", 0)
self._data.itemStatus.add(None, self.tr("New"), (120, 120, 120), "STAR", 0)
self._data.itemStatus.add(None, self.tr("Note"), (205, 171, 143), "TRIANGLE", 0)
self._data.itemStatus.add(None, self.tr("Draft"), (143, 240, 164), "CIRCLE_T", 0)
self._data.itemStatus.add(None, self.tr("Finished"), (249, 240, 107), "STAR", 0)
self._data.itemImport.add(None, self.tr("New"), (120, 120, 120), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Minor"), (220, 138, 221), "BLOCK_2", 0)
self._data.itemImport.add(None, self.tr("Major"), (220, 138, 221), "BLOCK_3", 0)
self._data.itemImport.add(None, self.tr("Main"), (220, 138, 221), "BLOCK_4", 0)
return
def setProjectLang(self, language: str | None) -> None:
+2 -1
View File
@@ -73,8 +73,9 @@ class GuiDocMerge(NDialog):
self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
# Merge Options
self.trashLabel = QLabel(self.tr("Move merged items to Trash"), self)
self.trashSwitch = NSwitch(self, height=iPx)
self.trashLabel = QLabel(self.tr("Move merged items to Trash"), self)
self.trashLabel.setBuddy(self.trashSwitch)
self.optBox = QGridLayout()
self.optBox.addWidget(self.trashLabel, 0, 0)
+6 -3
View File
@@ -90,16 +90,19 @@ class GuiDocSplit(NDialog):
self.splitLevel.currentIndexChanged.connect(self._reloadList)
# Split Options
self.folderLabel = QLabel(self.tr("Split into a new folder"), self)
self.folderSwitch = NSwitch(self, height=iPx)
self.folderSwitch.setChecked(intoFolder)
self.folderLabel = QLabel(self.tr("Split into a new folder"), self)
self.folderLabel.setBuddy(self.folderSwitch)
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"), self)
self.hierarchySwitch = NSwitch(self, height=iPx)
self.hierarchySwitch.setChecked(docHierarchy)
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"), self)
self.hierarchyLabel.setBuddy(self.hierarchySwitch)
self.trashLabel = QLabel(self.tr("Move split document to Trash"), self)
self.trashSwitch = NSwitch(self, height=iPx)
self.trashLabel = QLabel(self.tr("Move split document to Trash"), self)
self.trashLabel.setBuddy(self.trashSwitch)
self.optBox = QGridLayout()
self.optBox.addWidget(self.folderLabel, 0, 0)
+11 -8
View File
@@ -43,11 +43,14 @@ class GuiEditLabel(NDialog):
self.setWindowTitle(self.tr("Item Label"))
# Item Label
self.labelValue = QLineEdit(self)
self.labelValue.setMinimumWidth(220)
self.labelValue.setMaxLength(200)
self.labelValue.setText(text)
self.labelValue.selectAll()
self.edtValue = QLineEdit(self)
self.edtValue.setMinimumWidth(220)
self.edtValue.setMaxLength(200)
self.edtValue.setText(text)
self.edtValue.selectAll()
self.lblValue = QLabel(self.tr("Label"), self)
self.lblValue.setBuddy(self.lblValue)
# Buttons
self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
@@ -56,8 +59,8 @@ class GuiEditLabel(NDialog):
# Assemble
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(QLabel(self.tr("Label"), self), 0)
self.innerBox.addWidget(self.labelValue, 1)
self.innerBox.addWidget(self.lblValue, 0)
self.innerBox.addWidget(self.edtValue, 1)
self.innerBox.setSpacing(12)
self.outerBox = QVBoxLayout()
@@ -77,7 +80,7 @@ class GuiEditLabel(NDialog):
@property
def itemLabel(self) -> str:
return self.labelValue.text()
return self.edtValue.text()
@classmethod
def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
+16 -16
View File
@@ -34,9 +34,9 @@ from PyQt6.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import compact, describeFont, uniqueCompact
from novelwriter.common import compact, describeFont, processDialogSymbols, uniqueCompact
from novelwriter.config import DEF_GUI, DEF_ICONS, DEF_SYNTAX, DEF_TREECOL
from novelwriter.constants import nwLabels, nwUnicode, trConst
from novelwriter.constants import nwLabels, nwQuotes, nwUnicode, trConst
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.configlayout import NColorLabel, NScrollableForm
from novelwriter.extensions.modified import (
@@ -80,6 +80,7 @@ class GuiPreferences(NDialog):
# SideBar
self.sidebar = NPagedSideBar(self)
self.sidebar.setLabelColor(SHARED.theme.helpText)
self.sidebar.setAccessibleName(self.titleLabel.text())
self.sidebar.buttonClicked.connect(self._sidebarClicked)
# Form
@@ -644,21 +645,20 @@ class GuiPreferences(NDialog):
self.tr("Lines starting with any of these symbols are dialogue.")
)
self.narratorBreak = QLineEdit(self)
self.narratorBreak.setMaxLength(1)
self.narratorBreak.setFixedWidth(boxFixed)
self.narratorBreak.setAlignment(QtAlignCenter)
self.narratorBreak.setText(CONFIG.narratorBreak)
self.narratorBreak = NComboBox(self)
self.narratorDialog = NComboBox(self)
for key, value in nwQuotes.DASHES.items():
label = trConst(value)
self.narratorBreak.addItem(label, key)
self.narratorDialog.addItem(label, key)
self.narratorBreak.setCurrentData(CONFIG.narratorBreak, "")
self.narratorDialog.setCurrentData(CONFIG.narratorDialog, "")
self.mainForm.addRow(
self.tr("Narrator break symbol"), self.narratorBreak,
self.tr("Symbol to indicate a narrator break in dialogue.")
)
self.narratorDialog = QLineEdit(self)
self.narratorDialog.setMaxLength(1)
self.narratorDialog.setFixedWidth(boxFixed)
self.narratorDialog.setAlignment(QtAlignCenter)
self.narratorDialog.setText(CONFIG.narratorDialog)
self.mainForm.addRow(
self.tr("Alternating dialogue/narration symbol"), self.narratorDialog,
self.tr("Alternates dialogue highlighting within any paragraph.")
@@ -1045,9 +1045,9 @@ class GuiPreferences(NDialog):
# Text Highlighting
dialogueStyle = self.dialogStyle.currentData()
allowOpenDial = self.allowOpenDial.isChecked()
dialogueLine = uniqueCompact(self.dialogLine.text())
narratorBreak = self.narratorBreak.text().strip()
narratorDialog = self.narratorDialog.text().strip()
dialogueLine = processDialogSymbols(self.dialogLine.text())
narratorBreak = self.narratorBreak.currentData()
narratorDialog = self.narratorDialog.currentData()
altDialogOpen = compact(self.altDialogOpen.text())
altDialogClose = compact(self.altDialogClose.text())
highlightEmph = self.highlightEmph.isChecked()
+3
View File
@@ -86,6 +86,7 @@ class GuiProjectSettings(NDialog):
# SideBar
self.sidebar = NPagedSideBar(self)
self.sidebar.setLabelColor(SHARED.theme.helpText)
self.sidebar.setAccessibleName(self.titleLabel.text())
self.sidebar.addButton(self.tr("Settings"), self.PAGE_SETTINGS)
self.sidebar.addButton(self.tr("Status"), self.PAGE_STATUS)
self.sidebar.addButton(self.tr("Importance"), self.PAGE_IMPORT)
@@ -349,6 +350,7 @@ class _StatusPage(NFixedPage):
self.listBox.setHeaderLabels([self.tr("Label"), self.tr("Usage")])
self.listBox.setColumnWidth(self.C_LABEL, wCol0)
self.listBox.setIndentation(0)
self.listBox.setAccessibleName(pageLabel)
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
@@ -686,6 +688,7 @@ class _ReplacePage(NFixedPage):
self.listBox.setHeaderLabels([self.tr("Keyword"), self.tr("Replace With")])
self.listBox.setColumnWidth(self.C_KEY, wCol0)
self.listBox.setIndentation(0)
self.listBox.setAccessibleName(self.pageTitle.text())
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
+6 -2
View File
@@ -211,7 +211,8 @@ class NScrollableForm(QScrollArea):
else:
qWidget = widget
qLabel = QLabel(label or "", self)
text = label or ""
qLabel = QLabel(text, self)
qLabel.setIndent(self._indent)
qLabel.setBuddy(qWidget)
@@ -227,6 +228,7 @@ class NScrollableForm(QScrollArea):
row.addLayout(labelBox, stretch[0])
if editable:
self._editable[editable] = qHelp
text = f"{text}: {helpText}"
else:
row.addWidget(qLabel, stretch[0])
@@ -235,6 +237,7 @@ class NScrollableForm(QScrollArea):
box.addWidget(qWidget, 1)
box.addWidget(QLabel(unit, self), 0)
row.addLayout(box, stretch[1])
text = f"{text} Unit: {unit}"
elif isinstance(button, QAbstractButton):
box = QHBoxLayout()
box.addWidget(qWidget, 1)
@@ -243,10 +246,11 @@ class NScrollableForm(QScrollArea):
else:
row.addWidget(qWidget, stretch[1])
self._first = False
self._layout.addLayout(row)
if label:
self._index[label.strip()] = qWidget
self._first = False
qLabel.setAccessibleName(text)
return
+16 -15
View File
@@ -23,12 +23,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty # pyright: ignore
from PyQt6.QtGui import QEnterEvent, QMouseEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt6.QtCore import QPropertyAnimation, Qt, pyqtProperty, pyqtSlot # pyright: ignore
from PyQt6.QtGui import QEnterEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt6.QtWidgets import QAbstractButton, QWidget
from novelwriter import SHARED
from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixed
from novelwriter.types import QtNoPen, QtPaintAntiAlias, QtSizeFixed
class NSwitch(QAbstractButton):
@@ -50,6 +50,8 @@ class NSwitch(QAbstractButton):
self.setFixedHeight(self._xH)
self._offset = self._xR
self.clicked.connect(self._onClick)
return
##
@@ -94,7 +96,7 @@ class NSwitch(QAbstractButton):
painter.setRenderHint(QtPaintAntiAlias, True)
painter.setOpacity(1.0 if self.isEnabled() else 0.5)
painter.setPen(palette.mid().color())
painter.setPen(palette.highlight().color() if self.hasFocus() else palette.mid().color())
painter.setBrush(palette.highlight() if self.isChecked() else palette.alternateBase())
painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
@@ -106,19 +108,18 @@ class NSwitch(QAbstractButton):
return
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
"""Animate the switch on mouse release."""
super().mouseReleaseEvent(event)
if event.button() == QtMouseLeft:
anim = QPropertyAnimation(self, b"offset", self)
anim.setDuration(120)
anim.setStartValue(self._offset)
anim.setEndValue((self._xW - self._xR) if self.isChecked() else self._xR)
anim.start()
return
def enterEvent(self, event: QEnterEvent) -> None:
"""Change the cursor when hovering the button."""
self.setCursor(Qt.CursorShape.PointingHandCursor)
super().enterEvent(event)
return
@pyqtSlot(bool)
def _onClick(self, checked: bool) -> None:
"""Animate the toggle action."""
anim = QPropertyAnimation(self, b"offset", self)
anim.setDuration(120)
anim.setStartValue(self._offset)
anim.setEndValue((self._xW - self._xR) if checked else self._xR)
anim.start()
return
+1
View File
@@ -100,6 +100,7 @@ class NSwitchBox(QScrollArea):
switch.toggled.connect(lambda state: self._emitSwitchSignal(identifier, state))
self._content.addWidget(switch, self._index, 2, QtAlignRight)
label.setBuddy(switch)
self._widgets.append(switch)
self._bumpIndex()
+1
View File
@@ -299,6 +299,7 @@ class GuiNovelToolBar(QWidget):
self.novelValue.setHandle(rootHandle)
SHARED.project.data.setLastHandle(rootHandle, "novel")
self.novelView.setCurrentNovel(rootHandle)
self.novelView.novelTree.setAccessibleName(self.novelValue.currentText())
return
def setLastColType(self, colType: nwNovelExtra, doRefresh: bool = True) -> None:
+1
View File
@@ -254,6 +254,7 @@ class GuiProjectToolBar(QWidget):
self.viewLabel.setFont(SHARED.theme.guiFontB)
self.viewLabel.setContentsMargins(0, 0, 0, 0)
self.viewLabel.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
self.projTree.setAccessibleName(self.viewLabel.text())
# Quick Links
self.mQuick = QMenu(self)
+1
View File
@@ -125,6 +125,7 @@ class GuiProjectSearch(QWidget):
self.searchResult.setAllColumnsShowFocus(True)
self.searchResult.itemDoubleClicked.connect(self._searchResultDoubleClicked)
self.searchResult.itemSelectionChanged.connect(self._searchResultSelected)
self.searchResult.setAccessibleName(self.viewLabel.text())
if header := self.searchResult.header():
header.setStretchLastSection(False)
+2 -1
View File
@@ -75,8 +75,9 @@ class GuiLipsum(NDialog):
self.paraCount.setMaximum(100)
self.paraCount.setValue(5)
self.randLabel = QLabel(self.tr("Randomise order"), self)
self.randSwitch = NSwitch(self)
self.randLabel = QLabel(self.tr("Randomise order"), self)
self.randLabel.setBuddy(self.randSwitch)
self.formBox = QGridLayout()
self.formBox.addWidget(self.headLabel, 0, 0, 1, 2, QtAlignLeft)
+34 -13
View File
@@ -104,6 +104,7 @@ class GuiBuildSettings(NToolDialog):
# SideBar
self.sidebar = NPagedSideBar(self)
self.sidebar.setLabelColor(SHARED.theme.helpText)
self.sidebar.setAccessibleName(self.titleLabel.text())
self.sidebar.addLabel(self.tr("General"))
self.sidebar.addButton(self.tr("Selection"), self.OPT_FILTERS)
@@ -564,9 +565,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtPart.setReadOnly(True)
self.btnPart = NIconToolButton(self, iSz, "edit", "green")
self.btnPart.clicked.connect(qtLambda(self._editHeading, self.EDIT_TITLE))
self.swtPart = NSwitch(self, height=iPx)
self.hdePart = QLabel(trHide, self)
self.hdePart.setIndent(6)
self.swtPart = NSwitch(self, height=iPx)
self.hdePart.setBuddy(self.swtPart)
self.formatBox.addWidget(self.lblPart, 0, 0)
self.formatBox.addWidget(self.fmtPart, 0, 1)
@@ -580,9 +582,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtChapter.setReadOnly(True)
self.btnChapter = NIconToolButton(self, iSz, "edit", "green")
self.btnChapter.clicked.connect(qtLambda(self._editHeading, self.EDIT_CHAPTER))
self.swtChapter = NSwitch(self, height=iPx)
self.hdeChapter = QLabel(trHide, self)
self.hdeChapter.setIndent(6)
self.swtChapter = NSwitch(self, height=iPx)
self.hdeChapter.setBuddy(self.swtChapter)
self.formatBox.addWidget(self.lblChapter, 1, 0)
self.formatBox.addWidget(self.fmtChapter, 1, 1)
@@ -596,9 +599,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = NIconToolButton(self, iSz, "edit", "green")
self.btnUnnumbered.clicked.connect(qtLambda(self._editHeading, self.EDIT_UNNUM))
self.swtUnnumbered = NSwitch(self, height=iPx)
self.hdeUnnumbered = QLabel(trHide, self)
self.hdeUnnumbered.setIndent(6)
self.swtUnnumbered = NSwitch(self, height=iPx)
self.hdeUnnumbered.setBuddy(self.swtUnnumbered)
self.formatBox.addWidget(self.lblUnnumbered, 2, 0)
self.formatBox.addWidget(self.fmtUnnumbered, 2, 1)
@@ -612,9 +616,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtScene.setReadOnly(True)
self.btnScene = NIconToolButton(self, iSz, "edit", "green")
self.btnScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_SCENE))
self.swtScene = NSwitch(self, height=iPx)
self.hdeScene = QLabel(trHide, self)
self.hdeScene.setIndent(6)
self.swtScene = NSwitch(self, height=iPx)
self.hdeScene.setBuddy(self.swtScene)
self.formatBox.addWidget(self.lblScene, 3, 0)
self.formatBox.addWidget(self.fmtScene, 3, 1)
@@ -628,9 +633,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtAScene.setReadOnly(True)
self.btnAScene = NIconToolButton(self, iSz, "edit", "green")
self.btnAScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_HSCENE))
self.swtAScene = NSwitch(self, height=iPx)
self.hdeAScene = QLabel(trHide, self)
self.hdeAScene.setIndent(6)
self.swtAScene = NSwitch(self, height=iPx)
self.hdeAScene.setBuddy(self.swtAScene)
self.formatBox.addWidget(self.lblAScene, 4, 0)
self.formatBox.addWidget(self.fmtAScene, 4, 1)
@@ -644,9 +650,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtSection.setReadOnly(True)
self.btnSection = NIconToolButton(self, iSz, "edit", "green")
self.btnSection.clicked.connect(qtLambda(self._editHeading, self.EDIT_SECTION))
self.swtSection = NSwitch(self, height=iPx)
self.hdeSection = QLabel(trHide, self)
self.hdeSection.setIndent(6)
self.swtSection = NSwitch(self, height=iPx)
self.hdeSection.setBuddy(self.swtSection)
self.formatBox.addWidget(self.lblSection, 5, 0)
self.formatBox.addWidget(self.fmtSection, 5, 1)
@@ -704,44 +711,59 @@ class _HeadingsTab(NScrollablePage):
# Layout Matrix
# =============
trCentre = self.tr("Centre")
trBreak = self.tr("Page Break")
self.layoutMatrix = QGridLayout()
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)
self.layoutMatrix.addWidget(QLabel(trCentre, self), 0, 1)
self.layoutMatrix.addWidget(QLabel(trBreak, self), 0, 2)
# Title Layout
self.lblTitle = QLabel(self._build.getLabel("headings.styleTitle"), self)
trLabel = self._build.getLabel("headings.styleTitle")
self.lblTitle = QLabel(trLabel, self)
self.centerTitle = NSwitch(self, height=iPx)
self.centerTitle.setAccessibleName(f"{trLabel}: {trCentre}")
self.breakTitle = NSwitch(self, height=iPx)
self.breakTitle.setAccessibleName(f"{trLabel}: {trBreak}")
self.layoutMatrix.addWidget(self.lblTitle, 1, 0)
self.layoutMatrix.addWidget(self.centerTitle, 1, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.breakTitle, 1, 2, QtAlignCenter)
# Partition Layout
self.lblPart = QLabel(self._build.getLabel("headings.stylePart"), self)
trLabel = self._build.getLabel("headings.stylePart")
self.lblPart = QLabel(trLabel, self)
self.centerPart = NSwitch(self, height=iPx)
self.centerPart.setAccessibleName(f"{trLabel}: {trCentre}")
self.breakPart = NSwitch(self, height=iPx)
self.breakPart.setAccessibleName(f"{trLabel}: {trBreak}")
self.layoutMatrix.addWidget(self.lblPart, 2, 0)
self.layoutMatrix.addWidget(self.centerPart, 2, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.breakPart, 2, 2, QtAlignCenter)
# Chapter Layout
self.lblChapter = QLabel(self._build.getLabel("headings.styleChapter"), self)
trLabel = self._build.getLabel("headings.styleChapter")
self.lblChapter = QLabel(trLabel, self)
self.centerChapter = NSwitch(self, height=iPx)
self.centerChapter.setAccessibleName(f"{trLabel}: {trCentre}")
self.breakChapter = NSwitch(self, height=iPx)
self.breakChapter.setAccessibleName(f"{trLabel}: {trBreak}")
self.layoutMatrix.addWidget(self.lblChapter, 3, 0)
self.layoutMatrix.addWidget(self.centerChapter, 3, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.breakChapter, 3, 2, QtAlignCenter)
# Scene Layout
self.lblScene = QLabel(self._build.getLabel("headings.styleScene"), self)
trLabel = self._build.getLabel("headings.styleScene")
self.lblScene = QLabel(trLabel, self)
self.centerScene = NSwitch(self, height=iPx)
self.centerScene.setAccessibleName(f"{trLabel}: {trCentre}")
self.breakScene = NSwitch(self, height=iPx)
self.breakScene.setAccessibleName(f"{trLabel}: {trBreak}")
self.layoutMatrix.addWidget(self.lblScene, 4, 0)
self.layoutMatrix.addWidget(self.centerScene, 4, 1, QtAlignCenter)
@@ -979,7 +1001,6 @@ class _FormattingTab(NScrollableForm):
# Note Headings
self.addNoteHead = NSwitch(self, height=iPx)
self.addRow(self._build.getLabel("text.addNoteHeadings"), self.addNoteHead)
# Text Format
+7 -6
View File
@@ -86,6 +86,7 @@ class GuiNovelDetails(NNonBlockingDialog):
# SideBar
self.sidebar = NPagedSideBar(self)
self.sidebar.setLabelColor(SHARED.theme.helpText)
self.sidebar.setAccessibleName(self.titleLabel.text())
self.sidebar.addButton(self.tr("Overview"), self.PAGE_OVERVIEW)
self.sidebar.addButton(self.tr("Contents"), self.PAGE_CONTENTS)
self.sidebar.setSelected(self.PAGE_OVERVIEW)
@@ -356,16 +357,14 @@ class _ContentsPage(NFixedPage):
countFrom = options.getInt("GuiNovelDetails", "countFrom", 1)
clearDouble = options.getBool("GuiNovelDetails", "clearDouble", True)
self.wpLabel = QLabel(self.tr("Words per page"), self)
self.wpValue = QSpinBox(self)
self.wpValue.setMinimum(10)
self.wpValue.setMaximum(1000)
self.wpValue.setSingleStep(10)
self.wpValue.setValue(wordsPerPage)
self.wpValue.valueChanged.connect(self._populateTree)
self.poLabel = QLabel(self.tr("First page offset"), self)
self.wpLabel = QLabel(self.tr("Words per page"), self)
self.wpLabel.setBuddy(self.wpValue)
self.poValue = QSpinBox(self)
self.poValue.setMinimum(1)
@@ -373,12 +372,14 @@ class _ContentsPage(NFixedPage):
self.poValue.setSingleStep(1)
self.poValue.setValue(countFrom)
self.poValue.valueChanged.connect(self._populateTree)
self.dblLabel = QLabel(self.tr("Chapters on odd pages"), self)
self.poLabel = QLabel(self.tr("First page offset"), self)
self.poLabel.setBuddy(self.poValue)
self.dblValue = NSwitch(self, height=iPx)
self.dblValue.setChecked(clearDouble)
self.dblValue.clicked.connect(self._populateTree)
self.dblLabel = QLabel(self.tr("Chapters on odd pages"), self)
self.dblLabel.setBuddy(self.dblValue)
self.optionsBox = QGridLayout()
self.optionsBox.addWidget(self.wpLabel, 0, 0)
+1
View File
@@ -565,6 +565,7 @@ class _NewProjectForm(QWidget):
self.projAuthor = QLineEdit(self)
self.projAuthor.setMaxLength(200)
self.projAuthor.setPlaceholderText(self.tr("Optional"))
self.projAuthor.setText(CONFIG.lastAuthor)
# Project Path
self.projPath = QLineEdit(self)
+67 -44
View File
@@ -183,59 +183,82 @@ class GuiWritingStats(NToolDialog):
# Filter Options
iPx = SHARED.theme.baseIconHeight
self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self)
self.filterForm.setRowStretch(6, 1)
self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterBox.setLayout(self.filterForm)
self.incNovel = NSwitch(self, height=iPx)
self.incNovel.setChecked(
# Include Novel Files
self.swtIncNovel = NSwitch(self, height=iPx)
self.swtIncNovel.setChecked(
pOptions.getBool("GuiWritingStats", "incNovel", True)
)
self.incNovel.clicked.connect(self._updateListBox)
self.swtIncNovel.clicked.connect(self._updateListBox)
self.lblIncNovel = QLabel(self.tr("Count novel files"), self)
self.lblIncNovel.setBuddy(self.swtIncNovel)
self.incNotes = NSwitch(self, height=iPx)
self.incNotes.setChecked(
self.filterForm.addWidget(self.lblIncNovel, 0, 0)
self.filterForm.addWidget(self.swtIncNovel, 0, 1)
# Include Note Files
self.swtIncNotes = NSwitch(self, height=iPx)
self.swtIncNotes.setChecked(
pOptions.getBool("GuiWritingStats", "incNotes", True)
)
self.incNotes.clicked.connect(self._updateListBox)
self.swtIncNotes.clicked.connect(self._updateListBox)
self.lblIncNotes = QLabel(self.tr("Count note files"), self)
self.lblIncNotes.setBuddy(self.swtIncNotes)
self.hideZeros = NSwitch(self, height=iPx)
self.hideZeros.setChecked(
self.filterForm.addWidget(self.lblIncNotes, 1, 0)
self.filterForm.addWidget(self.swtIncNotes, 1, 1)
# Hide Zero Counts
self.swtHideZeros = NSwitch(self, height=iPx)
self.swtHideZeros.setChecked(
pOptions.getBool("GuiWritingStats", "hideZeros", True)
)
self.hideZeros.clicked.connect(self._updateListBox)
self.swtHideZeros.clicked.connect(self._updateListBox)
self.lblHideZeros = QLabel(self.tr("Hide zero word count"), self)
self.lblHideZeros.setBuddy(self.swtHideZeros)
self.hideNegative = NSwitch(self, height=iPx)
self.hideNegative.setChecked(
self.filterForm.addWidget(self.lblHideZeros, 2, 0)
self.filterForm.addWidget(self.swtHideZeros, 2, 1)
# Hide Negative Counts
self.swtHideNegative = NSwitch(self, height=iPx)
self.swtHideNegative.setChecked(
pOptions.getBool("GuiWritingStats", "hideNegative", False)
)
self.hideNegative.clicked.connect(self._updateListBox)
self.swtHideNegative.clicked.connect(self._updateListBox)
self.lblHideNegative = QLabel(self.tr("Hide negative word count"), self)
self.lblHideNegative.setBuddy(self.swtHideNegative)
self.groupByDay = NSwitch(self, height=iPx)
self.groupByDay.setChecked(
self.filterForm.addWidget(self.lblHideNegative, 3, 0)
self.filterForm.addWidget(self.swtHideNegative, 3, 1)
# Group Entries
self.swtGroupByDay = NSwitch(self, height=iPx)
self.swtGroupByDay.setChecked(
pOptions.getBool("GuiWritingStats", "groupByDay", False)
)
self.groupByDay.clicked.connect(self._updateListBox)
self.swtGroupByDay.clicked.connect(self._updateListBox)
self.lblGroupByDay = QLabel(self.tr("Group entries by day"), self)
self.lblGroupByDay.setBuddy(self.swtGroupByDay)
self.showIdleTime = NSwitch(self, height=iPx)
self.showIdleTime.setChecked(
self.filterForm.addWidget(self.lblGroupByDay, 4, 0)
self.filterForm.addWidget(self.swtGroupByDay, 4, 1)
# Show Idle
self.swtShowIdleTime = NSwitch(self, height=iPx)
self.swtShowIdleTime.setChecked(
pOptions.getBool("GuiWritingStats", "showIdleTime", False)
)
self.showIdleTime.clicked.connect(self._updateListBox)
self.swtShowIdleTime.clicked.connect(self._updateListBox)
self.lblShowIdleTime = QLabel(self.tr("Show idle time"), self)
self.lblShowIdleTime.setBuddy(self.swtShowIdleTime)
self.filterForm.addWidget(QLabel(self.tr("Count novel files"), self), 0, 0)
self.filterForm.addWidget(QLabel(self.tr("Count note files"), self), 1, 0)
self.filterForm.addWidget(QLabel(self.tr("Hide zero word count"), self), 2, 0)
self.filterForm.addWidget(QLabel(self.tr("Hide negative word count"), self), 3, 0)
self.filterForm.addWidget(QLabel(self.tr("Group entries by day"), self), 4, 0)
self.filterForm.addWidget(QLabel(self.tr("Show idle time"), self), 5, 0)
self.filterForm.addWidget(self.incNovel, 0, 1)
self.filterForm.addWidget(self.incNotes, 1, 1)
self.filterForm.addWidget(self.hideZeros, 2, 1)
self.filterForm.addWidget(self.hideNegative, 3, 1)
self.filterForm.addWidget(self.groupByDay, 4, 1)
self.filterForm.addWidget(self.showIdleTime, 5, 1)
self.filterForm.setRowStretch(6, 1)
self.filterForm.addWidget(self.lblShowIdleTime, 5, 0)
self.filterForm.addWidget(self.swtShowIdleTime, 5, 1)
# Settings
self.histMax = QSpinBox(self)
@@ -325,12 +348,12 @@ class GuiWritingStats(NToolDialog):
sortCol = self.listBox.sortColumn()
sortOrder = header.sortIndicatorOrder() if header else 0
incNovel = self.incNovel.isChecked()
incNotes = self.incNotes.isChecked()
hideZeros = self.hideZeros.isChecked()
hideNegative = self.hideNegative.isChecked()
groupByDay = self.groupByDay.isChecked()
showIdleTime = self.showIdleTime.isChecked()
incNovel = self.swtIncNovel.isChecked()
incNotes = self.swtIncNotes.isChecked()
hideZeros = self.swtHideZeros.isChecked()
hideNegative = self.swtHideNegative.isChecked()
groupByDay = self.swtGroupByDay.isChecked()
showIdleTime = self.swtShowIdleTime.isChecked()
histMax = self.histMax.value()
logger.debug("Saving State: GuiWritingStats")
@@ -487,11 +510,11 @@ class GuiWritingStats(NToolDialog):
self.listBox.clear()
self.timeFilter = 0.0
incNovel = self.incNovel.isChecked()
incNotes = self.incNotes.isChecked()
hideZeros = self.hideZeros.isChecked()
hideNegative = self.hideNegative.isChecked()
groupByDay = self.groupByDay.isChecked()
incNovel = self.swtIncNovel.isChecked()
incNotes = self.swtIncNotes.isChecked()
hideZeros = self.swtHideZeros.isChecked()
hideNegative = self.swtHideNegative.isChecked()
groupByDay = self.swtGroupByDay.isChecked()
histMax = self.histMax.value()
# Group the data
@@ -563,7 +586,7 @@ class GuiWritingStats(NToolDialog):
# Populate the list
mTrans = Qt.TransformationMode.FastTransformation
mAspect = Qt.AspectRatioMode.IgnoreAspectRatio
showIdleTime = self.showIdleTime.isChecked()
showIdleTime = self.swtShowIdleTime.isChecked()
for _, sStart, sDiff, nWords, _, _, sIdle in self.filterData:
if showIdleTime:
+2 -1
View File
@@ -1,5 +1,5 @@
[Meta]
timestamp = 2025-02-22 19:57:47
timestamp = 2025-05-11 13:28:11
[Main]
font =
@@ -31,6 +31,7 @@ backuppath =
backuponclose = False
askbeforebackup = True
askbeforeexit = True
lastauthor =
[Editor]
textfont =
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="7" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="5" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="12" novelWords="10" notesWords="6" novelChars="45" notesChars="22">
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="6" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="6" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="10" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="10" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="16" novelWords="9" notesWords="0" novelChars="40" notesChars="0">
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="16" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="16" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="3" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="3" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="19" novelWords="28" notesWords="0" novelChars="129" notesChars="0">
@@ -1,6 +1,6 @@
Jane Doe
Address 1
Address 2
Address Line 1
Address Line 2
@@ -17,7 +17,7 @@ Test Project A
**By Jane Doe**
Word Count: 11
Word Count: 13
# Chapter 1
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="15" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="15" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="7" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="22" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
@@ -1,6 +1,6 @@
Jane Doe
Address 1
Address 2
Address Line 1
Address Line 2
@@ -17,7 +17,7 @@ Test Project B
**By Jane Doe**
Word Count: 11
Word Count: 13
* * *
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="9" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="9" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="7" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="16" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="5" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="7" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="12" novelWords="173" notesWords="27" novelChars="1007" notesChars="133">
@@ -16,16 +16,16 @@
</lastHandle>
<autoReplace />
<status>
<entry key="s000000" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
<entry key="s000000" count="5" red="120" green="120" blue="120" shape="STAR">New</entry>
<entry key="s000001" count="0" red="205" green="171" blue="143" shape="TRIANGLE">Note</entry>
<entry key="s000002" count="0" red="143" green="240" blue="164" shape="CIRCLE_T">Draft</entry>
<entry key="s000003" count="0" red="249" green="240" blue="107" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="3" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
<entry key="i000004" count="3" red="120" green="120" blue="120" shape="SQUARE">New</entry>
<entry key="i000005" count="0" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="8" novelWords="9" notesWords="0" novelChars="40" notesChars="0">
+11 -2
View File
@@ -37,8 +37,9 @@ from novelwriter.common import (
formatFileFilter, formatInt, formatTime, formatTimeStamp, formatVersion,
fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, isItemLayout,
isItemType, isListInstance, isTitleTag, jsonEncode, makeFileNameSafe,
minmax, numberToRoman, openExternalPath, readTextFile, simplified,
transferCase, uniqueCompact, xmlElement, xmlIndent, xmlSubElem, yesNo
minmax, numberToRoman, openExternalPath, processDialogSymbols,
readTextFile, simplified, transferCase, uniqueCompact, xmlElement,
xmlIndent, xmlSubElem, yesNo
)
from tests.mocked import causeOSError
@@ -377,6 +378,14 @@ def testBaseCommon_uniqueCompact():
assert uniqueCompact("3 2 1") == "123"
@pytest.mark.base
def testBaseCommon_processDialogSymbols():
"""Test the processDialogSymbols function."""
assert processDialogSymbols("abc") == ""
assert processDialogSymbols("\u00ab\u00ab\u00bb\u00bb") == "\u00ab\u00bb"
assert processDialogSymbols("-\u2013\u2014\u2015") == "\u2013\u2014\u2015"
@pytest.mark.base
def testBaseCommon_elide():
"""Test the elide function."""
+14
View File
@@ -20,6 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import datetime
import json
import sys
@@ -189,6 +190,14 @@ def testBaseConfig_Localisation(fncPath, tstPaths):
languages = tstConf.listLanguages(tstConf.LANG_NW)
assert languages == [("en_GB", "British English"), ("fr", "Français")]
# Date Formats
# Checks for bug #2325
ts = datetime.datetime.fromtimestamp(1746370775)
CONFIG._dShortDate = "dd/MM/yyyy"
CONFIG._dShortDateTime = "dd/MM/yyyy HH:mm"
assert CONFIG.localDate(ts) == ts.strftime("%d/%m/%Y")
assert CONFIG.localDateTime(ts) == ts.strftime("%d/%m/%Y %H:%M")
@pytest.mark.base
def testBaseConfig_Methods(fncPath):
@@ -233,6 +242,11 @@ def testBaseConfig_Methods(fncPath):
# Recent Projects
assert isinstance(tstConf.recentProjects, RecentProjects)
# Last Author
assert CONFIG.lastAuthor == ""
CONFIG.setLastAuthor(" Jane Doe ")
assert CONFIG.lastAuthor == "Jane Doe"
@pytest.mark.base
def testBaseConfig_SettersGetters(fncPath):
+10 -1
View File
@@ -23,11 +23,12 @@ from __future__ import annotations
import pytest
from PyQt6.QtCore import QMimeData, QModelIndex, Qt
from PyQt6.QtTest import QAbstractItemModelTester
from novelwriter.common import decodeMimeHandles
from novelwriter.constants import nwConst
from novelwriter.core.item import NWItem
from novelwriter.core.itemmodel import INV_ROOT, NODE_FLAGS, ProjectNode
from novelwriter.core.itemmodel import INV_ROOT, NODE_FLAGS, ProjectModel, ProjectNode
from novelwriter.core.project import NWProject
from novelwriter.enum import nwItemLayout, nwItemType
@@ -253,6 +254,14 @@ def testCoreItemModel_ProjectNode_Data(mockGUI, mockRnd, fncPath):
assert scene.flags() == NODE_FLAGS | Qt.ItemFlag.ItemIsDragEnabled
@pytest.mark.core
def testCoreItemModel_ProjectModel_ModelTest(mockGUI):
"""Run the Qt model tester on the model."""
project = NWProject()
model = ProjectModel(project.tree)
QAbstractItemModelTester(model)
@pytest.mark.core
def testCoreItemModel_ProjectModel_Interface(mockGUI, mockRnd, fncPath):
"""Test the model interface for the project model."""
+8
View File
@@ -23,6 +23,7 @@ from __future__ import annotations
import pytest
from PyQt6.QtCore import QModelIndex, Qt
from PyQt6.QtTest import QAbstractItemModelTester
from novelwriter.core.indexdata import IndexHeading
from novelwriter.core.novelmodel import NovelModel
@@ -32,6 +33,13 @@ from novelwriter.enum import nwNovelExtra
from tests.tools import C, buildTestProject
@pytest.mark.core
def testCoreNovelModel_ModelTest(nwGUI):
"""Run the Qt model tester on the model."""
model = NovelModel()
QAbstractItemModelTester(model)
@pytest.mark.core
def testCoreNovelModel_Interface(nwGUI, fncPath, mockRnd):
"""Test the novel model interface."""
+2 -2
View File
@@ -269,8 +269,8 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
prefs.dialogStyle.setCurrentData(3, 0)
prefs.allowOpenDial.setChecked(False)
prefs.dialogLine.setText("")
prefs.narratorBreak.setText("")
prefs.narratorDialog.setText("")
prefs.narratorBreak.setCurrentData("", "")
prefs.narratorDialog.setCurrentData("", "")
prefs.altDialogOpen.setText("<")
prefs.altDialogClose.setText(">")
prefs.highlightEmph.setChecked(False)
+15 -15
View File
@@ -214,18 +214,18 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
assert update[0][0] == C.sNew
assert update[0][1].name == "New"
assert update[0][1].color == QColor(100, 100, 100)
assert update[0][1].shape == nwStatusShape.SQUARE
assert update[0][1].color == QColor(120, 120, 120)
assert update[0][1].shape == nwStatusShape.STAR
assert update[1][0] == C.sDraft
assert update[1][1].name == "Draft"
assert update[1][1].color == QColor(200, 150, 0)
assert update[1][1].shape == nwStatusShape.SQUARE
assert update[1][1].color == QColor(143, 240, 164)
assert update[1][1].shape == nwStatusShape.CIRCLE_T
assert update[2][0] == C.sFinished
assert update[2][1].name == "Finished"
assert update[2][1].color == QColor(50, 200, 0)
assert update[2][1].shape == nwStatusShape.SQUARE
assert update[2][1].color == QColor(249, 240, 107)
assert update[2][1].shape == nwStatusShape.STAR
assert update[3][0] is None
assert update[3][1].name == "Final"
@@ -289,18 +289,18 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
assert update[0][0] == C.iNew
assert update[0][1].name == "New"
assert update[0][1].color == QColor(100, 100, 100)
assert update[0][1].color == QColor(120, 120, 120)
assert update[0][1].shape == nwStatusShape.SQUARE
assert update[1][0] == C.iMajor
assert update[1][1].name == "Major"
assert update[1][1].color == QColor(200, 150, 0)
assert update[1][1].shape == nwStatusShape.SQUARE
assert update[1][1].color == QColor(220, 138, 221)
assert update[1][1].shape == nwStatusShape.BLOCK_3
assert update[2][0] == C.iMain
assert update[2][1].name == "Main"
assert update[2][1].color == QColor(50, 200, 0)
assert update[2][1].shape == nwStatusShape.SQUARE
assert update[2][1].color == QColor(220, 138, 221)
assert update[2][1].shape == nwStatusShape.BLOCK_4
assert update[3][0] is None
assert update[3][1].name == "Final"
@@ -354,10 +354,10 @@ def testDlgProjSettings_StatusImportExport(qtbot, monkeypatch, nwGUI, projPath,
assert expFile.is_file() is True
assert expFile.read_text().split() == [
"SQUARE,#646464,New",
"SQUARE,#c83200,Note",
"SQUARE,#c89600,Draft",
"SQUARE,#32c800,Finished",
"STAR,#787878,New",
"TRIANGLE,#cdab8f,Note",
"CIRCLE_T,#8ff0a4,Draft",
"STAR,#f9f06b,Finished",
]
# Import Error
+9 -9
View File
@@ -261,7 +261,7 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
assert item.text(sessLog.C_COUNT) == f"{200:n}"
# No Novel Files
qtbot.mouseClick(sessLog.incNovel, QtMouseLeft)
qtbot.mouseClick(sessLog.swtIncNovel, QtMouseLeft)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = tstPaths.tmpDir / "sessionStats.json"
@@ -329,8 +329,8 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
]
# No Note Files
qtbot.mouseClick(sessLog.incNovel, QtMouseLeft)
qtbot.mouseClick(sessLog.incNotes, QtMouseLeft)
qtbot.mouseClick(sessLog.swtIncNovel, QtMouseLeft)
qtbot.mouseClick(sessLog.swtIncNotes, QtMouseLeft)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = tstPaths.tmpDir / "sessionStats.json"
@@ -398,8 +398,8 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
]
# No Negative Entries
qtbot.mouseClick(sessLog.incNotes, QtMouseLeft)
qtbot.mouseClick(sessLog.hideNegative, QtMouseLeft)
qtbot.mouseClick(sessLog.swtIncNotes, QtMouseLeft)
qtbot.mouseClick(sessLog.swtHideNegative, QtMouseLeft)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = tstPaths.tmpDir / "sessionStats.json"
@@ -439,8 +439,8 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
]
# Un-hide Zero Entries
qtbot.mouseClick(sessLog.hideNegative, QtMouseLeft)
qtbot.mouseClick(sessLog.hideZeros, QtMouseLeft)
qtbot.mouseClick(sessLog.swtHideNegative, QtMouseLeft)
qtbot.mouseClick(sessLog.swtHideZeros, QtMouseLeft)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = tstPaths.tmpDir / "sessionStats.json"
@@ -526,14 +526,14 @@ def testToolWritingStats_Filters(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
assert item is not None
assert item.text(sessLog.C_IDLE) == "4 %"
qtbot.mouseClick(sessLog.showIdleTime, QtMouseLeft)
qtbot.mouseClick(sessLog.swtShowIdleTime, QtMouseLeft)
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)
qtbot.mouseClick(sessLog.swtGroupByDay, QtMouseLeft)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = tstPaths.tmpDir / "sessionStats.json"