From 916039b42059fa03bdf44cdd4468fbe0fd37aa39 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 16:27:30 +0200 Subject: [PATCH] Allow display of character count in editor footer and status bar --- novelwriter/constants.py | 4 +++ novelwriter/core/item.py | 49 +++++++++++++++++++----------------- novelwriter/gui/doceditor.py | 48 +++++++++++++++++++++-------------- novelwriter/gui/statusbar.py | 20 ++++++++++----- novelwriter/guimain.py | 18 ++++++++++--- 5 files changed, 87 insertions(+), 52 deletions(-) diff --git a/novelwriter/constants.py b/novelwriter/constants.py index 16c5a082..c20b9c47 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -352,6 +352,10 @@ class nwLabels: nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"), nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"), } + STATS_DISPLAY: Final[dict[str, str]] = { + nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters: {0} ({1})"), + nwStats.WORDS: QT_TRANSLATE_NOOP("Stats", "Words: {0} ({1})"), + } BUILD_FMT: Final[dict[nwBuildFmt, str]] = { nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"), nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"), diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 1dd17e47..e8f067a9 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -53,10 +53,10 @@ class NWItem: """ __slots__ = ( - "_active", "_charCount", "_class", "_cursorPos", "_expanded", - "_handle", "_heading", "_import", "_initCount", "_layout", "_name", + "_active", "_charCount", "_charInit", "_class", "_cursorPos", + "_expanded", "_handle", "_heading", "_import", "_layout", "_name", "_order", "_paraCount", "_parent", "_project", "_root", "_status", - "_type", "_wordCount", + "_type", "_wordCount", "_wordInit", ) def __init__(self, project: NWProject, handle: str) -> None: @@ -81,7 +81,8 @@ class NWItem: self._wordCount = 0 # Current word count self._paraCount = 0 # Current paragraph count self._cursorPos = 0 # Last cursor position - self._initCount = 0 # Initial word count + self._wordInit = 0 # Initial character count + self._charInit = 0 # Initial word count return @@ -170,7 +171,7 @@ class NWItem: @property def initCount(self) -> int: - return self._initCount + return self._wordInit if CONFIG.useCharCount else self._charInit @property def cursorPos(self) -> int: @@ -261,7 +262,8 @@ class NWItem: self._paraCount = 0 self._cursorPos = 0 - self._initCount = self._wordCount + self._wordInit = self._charCount + self._charInit = self._wordCount return True @@ -269,23 +271,24 @@ class NWItem: def duplicate(cls, source: NWItem, handle: str) -> NWItem: """Make a copy of an item.""" new = cls(source._project, handle) - new._name = source._name - new._parent = source._parent - new._root = source._root - new._order = source._order - new._type = source._type - new._class = source._class - new._layout = source._layout - new._status = source._status - new._import = source._import - new._active = source._active - new._expanded = source._expanded - new._heading = source._heading - new._charCount = source._charCount - new._wordCount = source._wordCount - new._paraCount = source._paraCount - new._cursorPos = source._cursorPos - new._initCount = source._initCount + new._name = source._name + new._parent = source._parent + new._root = source._root + new._order = source._order + new._type = source._type + new._class = source._class + new._layout = source._layout + new._status = source._status + new._import = source._import + new._active = source._active + new._expanded = source._expanded + new._heading = source._heading + new._charCount = source._charCount + new._wordCount = source._wordCount + new._paraCount = source._paraCount + new._cursorPos = source._cursorPos + new._wordInit = source._wordInit + new._charInit = source._charInit return new ## diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 208db194..adbab414 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -55,7 +55,9 @@ from novelwriter import CONFIG, SHARED from novelwriter.common import ( decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase ) -from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode +from novelwriter.constants import ( + nwConst, nwKeyWords, nwLabels, nwShortcode, nwStats, nwUnicode, trStats +) from novelwriter.core.document import NWDocument from novelwriter.enum import ( nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass, @@ -321,6 +323,7 @@ class GuiDocEditor(QPlainTextEdit): """ # Auto-Replace self._autoReplace.initSettings() + self.docFooter.initSettings() # Reload spell check and dictionaries SHARED.updateSpellCheckLanguage() @@ -1233,7 +1236,8 @@ class GuiDocEditor(QPlainTextEdit): """Process the word counter's finished signal.""" if self._docHandle and self._nwItem: logger.debug("Updating word count") - needsRefresh = wCount != self._nwItem.wordCount + mCount = cCount if CONFIG.useCharCount else wCount + needsRefresh = mCount != self._nwItem.mainCount self._nwItem.setCharCount(cCount) self._nwItem.setWordCount(wCount) self._nwItem.setParaCount(pCount) @@ -1241,7 +1245,7 @@ class GuiDocEditor(QPlainTextEdit): self._nwItem.notifyToRefresh() if not self.textCursor().hasSelection(): # Selection counter should take precedence (#2155) - self.docFooter.updateWordCount(wCount, False) + self.docFooter.updateMainCount(mCount, False) return @pyqtSlot() @@ -1254,7 +1258,7 @@ class GuiDocEditor(QPlainTextEdit): self._timerSel.start() else: self._timerSel.stop() - self.docFooter.updateWordCount(0, False) + self.docFooter.updateMainCount(0, False) return @pyqtSlot() @@ -1271,8 +1275,7 @@ class GuiDocEditor(QPlainTextEdit): def _updateSelCounts(self, cCount: int, wCount: int, pCount: int) -> None: """Update the counts on the counter's finished signal.""" if self._docHandle and self._nwItem: - logger.debug("User selected %d words", wCount) - self.docFooter.updateWordCount(wCount, True) + self.docFooter.updateMainCount(cCount if CONFIG.useCharCount else wCount, True) self._timerSel.stop() return @@ -3045,9 +3048,9 @@ class GuiDocEditFooter(QWidget): fPx = int(0.9*SHARED.theme.fontPixelSize) # Cached Translations + self.initSettings() self._trLineCount = self.tr("Line: {0} ({1})") - self._trWordCount = self.tr("Words: {0} ({1})") - self._trSelectCount = self.tr("Words: {0} selected") + self._trSelectCount = self.tr("Selected: {0}") # Main Widget Settings self.setContentsMargins(0, 0, 0, 0) @@ -3108,7 +3111,7 @@ class GuiDocEditFooter(QWidget): self.updateTheme() # Initialise Info - self.updateWordCount(0, False) + self.updateMainCount(0, False) logger.debug("Ready: GuiDocEditFooter") @@ -3118,6 +3121,13 @@ class GuiDocEditFooter(QWidget): # Methods ## + def initSettings(self) -> None: + """Apply user settings.""" + self._trMainCount = trStats(nwLabels.STATS_DISPLAY[ + nwStats.CHARS if CONFIG.useCharCount else nwStats.WORDS + ]) + return + def updateFont(self) -> None: """Update the font settings.""" self.setFont(SHARED.theme.guiFont) @@ -3162,7 +3172,7 @@ class GuiDocEditFooter(QWidget): self._tItem = SHARED.project.tree[self._docHandle] self.updateInfo() - self.updateWordCount(0, False) + self.updateMainCount(0, False) return @@ -3193,15 +3203,15 @@ class GuiDocEditFooter(QWidget): ) return - def updateWordCount(self, wCount: int, selection: bool) -> None: - """Update word counter information.""" - if selection and wCount: - wText = self._trSelectCount.format(f"{wCount:n}") + def updateMainCount(self, count: int, selection: bool) -> None: + """Update main counter information.""" + if selection and count: + text = self._trSelectCount.format(f"{count:n}") elif self._tItem: - wCount = self._tItem.wordCount - wDiff = wCount - self._tItem.initCount - wText = self._trWordCount.format(f"{wCount:n}", f"{wDiff:+n}") + count = self._tItem.mainCount + diff = count - self._tItem.initCount + text = self._trMainCount.format(f"{count:n}", f"{diff:+n}") else: - wText = self._trWordCount.format("0", "+0") - self.wordsText.setText(wText) + text = self._trMainCount.format("0", "+0") + self.wordsText.setText(text) return diff --git a/novelwriter/gui/statusbar.py b/novelwriter/gui/statusbar.py index 4f84b8c3..3239fd4a 100644 --- a/novelwriter/gui/statusbar.py +++ b/novelwriter/gui/statusbar.py @@ -33,7 +33,7 @@ from PyQt6.QtWidgets import QApplication, QLabel, QStatusBar, QWidget from novelwriter import CONFIG, SHARED from novelwriter.common import formatTime -from novelwriter.constants import nwConst +from novelwriter.constants import nwConst, nwLabels, nwStats, trStats from novelwriter.extensions.modified import NClickableLabel from novelwriter.extensions.statusled import StatusLED @@ -108,11 +108,22 @@ class GuiMainStatus(QStatusBar): logger.debug("Ready: GuiMainStatus") + self.initSettings() self.updateTheme() self.clearStatus() return + def initSettings(self) -> None: + """Apply user settings.""" + if CONFIG.useCharCount: + self._trStatsCount = trStats(nwLabels.STATS_DISPLAY[nwStats.CHARS]) + self._trStatsTip = self.tr("Total character count (session change)") + else: + self._trStatsCount = trStats(nwLabels.STATS_DISPLAY[nwStats.WORDS]) + self._trStatsTip = self.tr("Total word count (session change)") + return + def clearStatus(self) -> None: """Reset all widgets on the status bar to default values.""" self.setRefTime(-1.0) @@ -173,11 +184,8 @@ class GuiMainStatus(QStatusBar): def setProjectStats(self, pWC: int, sWC: int) -> None: """Update the current project statistics.""" - self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}")) - if CONFIG.incNotesWCount: - self.statsText.setToolTip(self.tr("Project word count (session change)")) - else: - self.statsText.setToolTip(self.tr("Novel word count (session change)")) + self.statsText.setText(self._trStatsCount.format(f"{pWC:n}", f"{sWC:+n}")) + self.statsText.setToolTip(self._trStatsTip) return def updateTime(self, idleTime: float = 0.0) -> None: diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index f946a32a..a12383fe 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -1077,6 +1077,7 @@ class GuiMain(QMainWindow): self.projView.initSettings() self.novelView.initSettings() self.outlineView.initSettings() + self.mainStatus.initSettings() # Force update of word count self._lastTotalCount = 0 @@ -1265,11 +1266,20 @@ class GuiMain(QMainWindow): SHARED.project.updateCounts() if CONFIG.incNotesWCount: - iTotal = sum(SHARED.project.data.initCounts[:2]) - cTotal = sum(SHARED.project.data.currCounts[:2]) + if CONFIG.useCharCount: + iTotal = sum(SHARED.project.data.initCounts[2:]) + cTotal = sum(SHARED.project.data.currCounts[2:]) + else: + iTotal = sum(SHARED.project.data.initCounts[:2]) + cTotal = sum(SHARED.project.data.currCounts[:2]) else: - iTotal = SHARED.project.data.initCounts[0] - cTotal = SHARED.project.data.currCounts[0] + if CONFIG.useCharCount: + iTotal = SHARED.project.data.initCounts[2] + cTotal = SHARED.project.data.currCounts[2] + else: + iTotal = SHARED.project.data.initCounts[0] + cTotal = SHARED.project.data.currCounts[0] + self.mainStatus.setProjectStats(cTotal, cTotal - iTotal) return