Allow display of character count in editor footer and status bar
This commit is contained in:
@@ -352,6 +352,10 @@ class nwLabels:
|
|||||||
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
||||||
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
|
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]] = {
|
BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
|
||||||
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
||||||
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
||||||
|
|||||||
+26
-23
@@ -53,10 +53,10 @@ class NWItem:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_active", "_charCount", "_class", "_cursorPos", "_expanded",
|
"_active", "_charCount", "_charInit", "_class", "_cursorPos",
|
||||||
"_handle", "_heading", "_import", "_initCount", "_layout", "_name",
|
"_expanded", "_handle", "_heading", "_import", "_layout", "_name",
|
||||||
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
||||||
"_type", "_wordCount",
|
"_type", "_wordCount", "_wordInit",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject, handle: str) -> None:
|
def __init__(self, project: NWProject, handle: str) -> None:
|
||||||
@@ -81,7 +81,8 @@ class NWItem:
|
|||||||
self._wordCount = 0 # Current word count
|
self._wordCount = 0 # Current word count
|
||||||
self._paraCount = 0 # Current paragraph count
|
self._paraCount = 0 # Current paragraph count
|
||||||
self._cursorPos = 0 # Last cursor position
|
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
|
return
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ class NWItem:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def initCount(self) -> int:
|
def initCount(self) -> int:
|
||||||
return self._initCount
|
return self._wordInit if CONFIG.useCharCount else self._charInit
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cursorPos(self) -> int:
|
def cursorPos(self) -> int:
|
||||||
@@ -261,7 +262,8 @@ class NWItem:
|
|||||||
self._paraCount = 0
|
self._paraCount = 0
|
||||||
self._cursorPos = 0
|
self._cursorPos = 0
|
||||||
|
|
||||||
self._initCount = self._wordCount
|
self._wordInit = self._charCount
|
||||||
|
self._charInit = self._wordCount
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -269,23 +271,24 @@ class NWItem:
|
|||||||
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
||||||
"""Make a copy of an item."""
|
"""Make a copy of an item."""
|
||||||
new = cls(source._project, handle)
|
new = cls(source._project, handle)
|
||||||
new._name = source._name
|
new._name = source._name
|
||||||
new._parent = source._parent
|
new._parent = source._parent
|
||||||
new._root = source._root
|
new._root = source._root
|
||||||
new._order = source._order
|
new._order = source._order
|
||||||
new._type = source._type
|
new._type = source._type
|
||||||
new._class = source._class
|
new._class = source._class
|
||||||
new._layout = source._layout
|
new._layout = source._layout
|
||||||
new._status = source._status
|
new._status = source._status
|
||||||
new._import = source._import
|
new._import = source._import
|
||||||
new._active = source._active
|
new._active = source._active
|
||||||
new._expanded = source._expanded
|
new._expanded = source._expanded
|
||||||
new._heading = source._heading
|
new._heading = source._heading
|
||||||
new._charCount = source._charCount
|
new._charCount = source._charCount
|
||||||
new._wordCount = source._wordCount
|
new._wordCount = source._wordCount
|
||||||
new._paraCount = source._paraCount
|
new._paraCount = source._paraCount
|
||||||
new._cursorPos = source._cursorPos
|
new._cursorPos = source._cursorPos
|
||||||
new._initCount = source._initCount
|
new._wordInit = source._wordInit
|
||||||
|
new._charInit = source._charInit
|
||||||
return new
|
return new
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ from novelwriter import CONFIG, SHARED
|
|||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase
|
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.core.document import NWDocument
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
|
nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
|
||||||
@@ -321,6 +323,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
"""
|
"""
|
||||||
# Auto-Replace
|
# Auto-Replace
|
||||||
self._autoReplace.initSettings()
|
self._autoReplace.initSettings()
|
||||||
|
self.docFooter.initSettings()
|
||||||
|
|
||||||
# Reload spell check and dictionaries
|
# Reload spell check and dictionaries
|
||||||
SHARED.updateSpellCheckLanguage()
|
SHARED.updateSpellCheckLanguage()
|
||||||
@@ -1233,7 +1236,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
"""Process the word counter's finished signal."""
|
"""Process the word counter's finished signal."""
|
||||||
if self._docHandle and self._nwItem:
|
if self._docHandle and self._nwItem:
|
||||||
logger.debug("Updating word count")
|
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.setCharCount(cCount)
|
||||||
self._nwItem.setWordCount(wCount)
|
self._nwItem.setWordCount(wCount)
|
||||||
self._nwItem.setParaCount(pCount)
|
self._nwItem.setParaCount(pCount)
|
||||||
@@ -1241,7 +1245,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self._nwItem.notifyToRefresh()
|
self._nwItem.notifyToRefresh()
|
||||||
if not self.textCursor().hasSelection():
|
if not self.textCursor().hasSelection():
|
||||||
# Selection counter should take precedence (#2155)
|
# Selection counter should take precedence (#2155)
|
||||||
self.docFooter.updateWordCount(wCount, False)
|
self.docFooter.updateMainCount(mCount, False)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -1254,7 +1258,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self._timerSel.start()
|
self._timerSel.start()
|
||||||
else:
|
else:
|
||||||
self._timerSel.stop()
|
self._timerSel.stop()
|
||||||
self.docFooter.updateWordCount(0, False)
|
self.docFooter.updateMainCount(0, False)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -1271,8 +1275,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
def _updateSelCounts(self, cCount: int, wCount: int, pCount: int) -> None:
|
def _updateSelCounts(self, cCount: int, wCount: int, pCount: int) -> None:
|
||||||
"""Update the counts on the counter's finished signal."""
|
"""Update the counts on the counter's finished signal."""
|
||||||
if self._docHandle and self._nwItem:
|
if self._docHandle and self._nwItem:
|
||||||
logger.debug("User selected %d words", wCount)
|
self.docFooter.updateMainCount(cCount if CONFIG.useCharCount else wCount, True)
|
||||||
self.docFooter.updateWordCount(wCount, True)
|
|
||||||
self._timerSel.stop()
|
self._timerSel.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3045,9 +3048,9 @@ class GuiDocEditFooter(QWidget):
|
|||||||
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
||||||
|
|
||||||
# Cached Translations
|
# Cached Translations
|
||||||
|
self.initSettings()
|
||||||
self._trLineCount = self.tr("Line: {0} ({1})")
|
self._trLineCount = self.tr("Line: {0} ({1})")
|
||||||
self._trWordCount = self.tr("Words: {0} ({1})")
|
self._trSelectCount = self.tr("Selected: {0}")
|
||||||
self._trSelectCount = self.tr("Words: {0} selected")
|
|
||||||
|
|
||||||
# Main Widget Settings
|
# Main Widget Settings
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -3108,7 +3111,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
# Initialise Info
|
# Initialise Info
|
||||||
self.updateWordCount(0, False)
|
self.updateMainCount(0, False)
|
||||||
|
|
||||||
logger.debug("Ready: GuiDocEditFooter")
|
logger.debug("Ready: GuiDocEditFooter")
|
||||||
|
|
||||||
@@ -3118,6 +3121,13 @@ class GuiDocEditFooter(QWidget):
|
|||||||
# Methods
|
# 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:
|
def updateFont(self) -> None:
|
||||||
"""Update the font settings."""
|
"""Update the font settings."""
|
||||||
self.setFont(SHARED.theme.guiFont)
|
self.setFont(SHARED.theme.guiFont)
|
||||||
@@ -3162,7 +3172,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self._tItem = SHARED.project.tree[self._docHandle]
|
self._tItem = SHARED.project.tree[self._docHandle]
|
||||||
|
|
||||||
self.updateInfo()
|
self.updateInfo()
|
||||||
self.updateWordCount(0, False)
|
self.updateMainCount(0, False)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3193,15 +3203,15 @@ class GuiDocEditFooter(QWidget):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateWordCount(self, wCount: int, selection: bool) -> None:
|
def updateMainCount(self, count: int, selection: bool) -> None:
|
||||||
"""Update word counter information."""
|
"""Update main counter information."""
|
||||||
if selection and wCount:
|
if selection and count:
|
||||||
wText = self._trSelectCount.format(f"{wCount:n}")
|
text = self._trSelectCount.format(f"{count:n}")
|
||||||
elif self._tItem:
|
elif self._tItem:
|
||||||
wCount = self._tItem.wordCount
|
count = self._tItem.mainCount
|
||||||
wDiff = wCount - self._tItem.initCount
|
diff = count - self._tItem.initCount
|
||||||
wText = self._trWordCount.format(f"{wCount:n}", f"{wDiff:+n}")
|
text = self._trMainCount.format(f"{count:n}", f"{diff:+n}")
|
||||||
else:
|
else:
|
||||||
wText = self._trWordCount.format("0", "+0")
|
text = self._trMainCount.format("0", "+0")
|
||||||
self.wordsText.setText(wText)
|
self.wordsText.setText(text)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from PyQt6.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
|
|||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import formatTime
|
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.modified import NClickableLabel
|
||||||
from novelwriter.extensions.statusled import StatusLED
|
from novelwriter.extensions.statusled import StatusLED
|
||||||
|
|
||||||
@@ -108,11 +108,22 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
logger.debug("Ready: GuiMainStatus")
|
logger.debug("Ready: GuiMainStatus")
|
||||||
|
|
||||||
|
self.initSettings()
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
|
|
||||||
return
|
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:
|
def clearStatus(self) -> None:
|
||||||
"""Reset all widgets on the status bar to default values."""
|
"""Reset all widgets on the status bar to default values."""
|
||||||
self.setRefTime(-1.0)
|
self.setRefTime(-1.0)
|
||||||
@@ -173,11 +184,8 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
def setProjectStats(self, pWC: int, sWC: int) -> None:
|
def setProjectStats(self, pWC: int, sWC: int) -> None:
|
||||||
"""Update the current project statistics."""
|
"""Update the current project statistics."""
|
||||||
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
|
self.statsText.setText(self._trStatsCount.format(f"{pWC:n}", f"{sWC:+n}"))
|
||||||
if CONFIG.incNotesWCount:
|
self.statsText.setToolTip(self._trStatsTip)
|
||||||
self.statsText.setToolTip(self.tr("Project word count (session change)"))
|
|
||||||
else:
|
|
||||||
self.statsText.setToolTip(self.tr("Novel word count (session change)"))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateTime(self, idleTime: float = 0.0) -> None:
|
def updateTime(self, idleTime: float = 0.0) -> None:
|
||||||
|
|||||||
+14
-4
@@ -1077,6 +1077,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.projView.initSettings()
|
self.projView.initSettings()
|
||||||
self.novelView.initSettings()
|
self.novelView.initSettings()
|
||||||
self.outlineView.initSettings()
|
self.outlineView.initSettings()
|
||||||
|
self.mainStatus.initSettings()
|
||||||
|
|
||||||
# Force update of word count
|
# Force update of word count
|
||||||
self._lastTotalCount = 0
|
self._lastTotalCount = 0
|
||||||
@@ -1265,11 +1266,20 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
SHARED.project.updateCounts()
|
SHARED.project.updateCounts()
|
||||||
if CONFIG.incNotesWCount:
|
if CONFIG.incNotesWCount:
|
||||||
iTotal = sum(SHARED.project.data.initCounts[:2])
|
if CONFIG.useCharCount:
|
||||||
cTotal = sum(SHARED.project.data.currCounts[:2])
|
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:
|
else:
|
||||||
iTotal = SHARED.project.data.initCounts[0]
|
if CONFIG.useCharCount:
|
||||||
cTotal = SHARED.project.data.currCounts[0]
|
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)
|
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user