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