Improve the word counter logic of the editor

This commit is contained in:
Veronica Berglyd Olsen
2024-03-03 15:39:08 +01:00
parent ef9e84f2ce
commit 79513bcbe5
2 changed files with 54 additions and 93 deletions
+53 -87
View File
@@ -1200,19 +1200,13 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot(int, int, int) @pyqtSlot(int, int, int)
def _updateDocCounts(self, cCount: int, wCount: int, pCount: int) -> None: def _updateDocCounts(self, cCount: int, wCount: int, pCount: int) -> None:
"""Process the word counter's finished signal.""" """Process the word counter's finished signal."""
if self._docHandle is None or self._nwItem is None: if self._docHandle and self._nwItem:
return logger.debug("Updating word count")
self._nwItem.setCharCount(cCount)
logger.debug("Updating word count") self._nwItem.setWordCount(wCount)
self._nwItem.setParaCount(pCount)
self._nwItem.setCharCount(cCount) self.docCountsChanged.emit(self._docHandle, cCount, wCount, pCount)
self._nwItem.setWordCount(wCount) self.docFooter.updateWordCount(wCount, False)
self._nwItem.setParaCount(pCount)
# Must not be emitted if docHandle is None!
self.docCountsChanged.emit(self._docHandle, cCount, wCount, pCount)
self.docFooter.updateCounts()
return return
@pyqtSlot() @pyqtSlot()
@@ -1223,11 +1217,9 @@ class GuiDocEditor(QPlainTextEdit):
if self.textCursor().hasSelection(): if self.textCursor().hasSelection():
if not self.wcTimerSel.isActive(): if not self.wcTimerSel.isActive():
self.wcTimerSel.start() self.wcTimerSel.start()
self.docFooter.setHasSelection(True)
else: else:
self.wcTimerSel.stop() self.wcTimerSel.stop()
self.docFooter.setHasSelection(False) self.docFooter.updateWordCount(0, False)
self.docFooter.updateCounts()
return return
@pyqtSlot() @pyqtSlot()
@@ -1247,13 +1239,10 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot(int, int, int) @pyqtSlot(int, int, int)
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 is None or self._nwItem is None: if self._docHandle and self._nwItem:
return logger.debug("User selected %d words", wCount)
self.docFooter.updateWordCount(wCount, True)
logger.debug("User selected %d words", wCount) self.wcTimerSel.stop()
self.docFooter.updateCounts(wCount=wCount, cCount=cCount)
self.wcTimerSel.stop()
return return
@pyqtSlot() @pyqtSlot()
@@ -2818,6 +2807,7 @@ class GuiDocEditHeader(QWidget):
self._docHandle = None self._docHandle = None
fPx = int(0.9*SHARED.theme.fontPixelSize) fPx = int(0.9*SHARED.theme.fontPixelSize)
mPx = CONFIG.pxInt(8)
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
iconSize = QSize(fPx, fPx) iconSize = QSize(fPx, fPx)
@@ -2825,8 +2815,7 @@ class GuiDocEditHeader(QWidget):
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
# Title Label # Title Label
self.itemTitle = QLabel() self.itemTitle = QLabel("", self)
self.itemTitle.setText("")
self.itemTitle.setIndent(0) self.itemTitle.setIndent(0)
self.itemTitle.setMargin(0) self.itemTitle.setMargin(0)
self.itemTitle.setContentsMargins(0, 0, 0, 0) self.itemTitle.setContentsMargins(0, 0, 0, 0)
@@ -2883,14 +2872,14 @@ class GuiDocEditHeader(QWidget):
self.outerBox.addWidget(self.itemTitle, 1) self.outerBox.addWidget(self.itemTitle, 1)
self.outerBox.addWidget(self.minmaxButton, 0) self.outerBox.addWidget(self.minmaxButton, 0)
self.outerBox.addWidget(self.closeButton, 0) self.outerBox.addWidget(self.closeButton, 0)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
# Fix Margins and Size # Fix Margins and Size
# This is needed for high DPI systems. See issue #499. # This is needed for high DPI systems. See issue #499.
cM = CONFIG.pxInt(8)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.outerBox.setContentsMargins(cM, cM, cM, cM) self.setMinimumHeight(fPx + 2*mPx)
self.setMinimumHeight(fPx + 2*cM)
self.updateTheme() self.updateTheme()
@@ -3026,19 +3015,23 @@ class GuiDocEditFooter(QWidget):
logger.debug("Create: GuiDocEditFooter") logger.debug("Create: GuiDocEditFooter")
self._tItem = None self._tItem = None
self._docHandle = None self._docHandle = None
self._docSelection = False iPx = round(0.9*SHARED.theme.baseIconSize)
self.sPx = int(round(0.9*SHARED.theme.baseIconSize))
fPx = int(0.9*SHARED.theme.fontPixelSize) fPx = int(0.9*SHARED.theme.fontPixelSize)
mPx = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(4) bSp = CONFIG.pxInt(4)
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize) lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
# Cached Translations
self._trLineCount = self.tr("Line: {0} ({1})")
self._trWordCount = self.tr("Words: {0} ({1})")
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)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
@@ -3048,7 +3041,7 @@ class GuiDocEditFooter(QWidget):
# Status # Status
self.statusIcon = QLabel("", self) self.statusIcon = QLabel("", self)
self.statusIcon.setContentsMargins(0, 0, 0, 0) self.statusIcon.setContentsMargins(0, 0, 0, 0)
self.statusIcon.setFixedHeight(self.sPx) self.statusIcon.setFixedHeight(iPx)
self.statusIcon.setAlignment(alLeftTop) self.statusIcon.setAlignment(alLeftTop)
self.statusText = QLabel(self.tr("Status")) self.statusText = QLabel(self.tr("Status"))
@@ -3063,7 +3056,7 @@ class GuiDocEditFooter(QWidget):
# Lines # Lines
self.linesIcon = QLabel("", self) self.linesIcon = QLabel("", self)
self.linesIcon.setContentsMargins(0, 0, 0, 0) self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(self.sPx) self.linesIcon.setFixedHeight(iPx)
self.linesIcon.setAlignment(alLeftTop) self.linesIcon.setAlignment(alLeftTop)
self.linesText = QLabel("", self) self.linesText = QLabel("", self)
@@ -3078,7 +3071,7 @@ class GuiDocEditFooter(QWidget):
# Words # Words
self.wordsIcon = QLabel("", self) self.wordsIcon = QLabel("", self)
self.wordsIcon.setContentsMargins(0, 0, 0, 0) self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(self.sPx) self.wordsIcon.setFixedHeight(iPx)
self.wordsIcon.setAlignment(alLeftTop) self.wordsIcon.setAlignment(alLeftTop)
self.wordsText = QLabel("", self) self.wordsText = QLabel("", self)
@@ -3101,18 +3094,20 @@ class GuiDocEditFooter(QWidget):
self.outerBox.addSpacing(hSp) self.outerBox.addSpacing(hSp)
self.outerBox.addWidget(self.wordsIcon) self.outerBox.addWidget(self.wordsIcon)
self.outerBox.addWidget(self.wordsText) self.outerBox.addWidget(self.wordsText)
self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
# Fix Margins and Size # Fix Margins and Size
# This is needed for high DPI systems. See issue #499. # This is needed for high DPI systems. See issue #499.
cM = CONFIG.pxInt(8)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.outerBox.setContentsMargins(cM, cM, cM, cM) self.setMinimumHeight(fPx + 2*mPx)
self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours # Fix the Colours
self.updateTheme() self.updateTheme()
self.updateCounts()
# Initialise Info
self.updateWordCount(0, False)
logger.debug("Ready: GuiDocEditFooter") logger.debug("Ready: GuiDocEditFooter")
@@ -3124,8 +3119,9 @@ class GuiDocEditFooter(QWidget):
def updateTheme(self) -> None: def updateTheme(self) -> None:
"""Update theme elements.""" """Update theme elements."""
self.linesIcon.setPixmap(SHARED.theme.getPixmap("status_lines", (self.sPx, self.sPx))) iPx = round(0.9*SHARED.theme.baseIconSize)
self.wordsIcon.setPixmap(SHARED.theme.getPixmap("status_stats", (self.sPx, self.sPx))) self.linesIcon.setPixmap(SHARED.theme.getPixmap("status_lines", (iPx, iPx)))
self.wordsIcon.setPixmap(SHARED.theme.getPixmap("status_stats", (iPx, iPx)))
self.matchColours() self.matchColours()
return return
@@ -3154,27 +3150,20 @@ class GuiDocEditFooter(QWidget):
else: else:
self._tItem = SHARED.project.tree[self._docHandle] self._tItem = SHARED.project.tree[self._docHandle]
self.setHasSelection(False)
self.updateInfo() self.updateInfo()
self.updateCounts() self.updateWordCount(0, False)
return return
def setHasSelection(self, hasSelection: bool) -> None:
"""Toggle the word counter mode between full count and selection
count mode.
"""
self._docSelection = hasSelection
return
def updateInfo(self) -> None: def updateInfo(self) -> None:
"""Update the content of text labels.""" """Update the content of text labels."""
if self._tItem is None: if self._tItem is None:
sIcon = QPixmap() sIcon = QPixmap()
sText = "" sText = ""
else: else:
iPx = round(0.9*SHARED.theme.baseIconSize)
status, icon = self._tItem.getImportStatus(incIcon=True) status, icon = self._tItem.getImportStatus(incIcon=True)
sIcon = icon.pixmap(self.sPx, self.sPx) sIcon = icon.pixmap(iPx, iPx)
sText = f"{status} / {self._tItem.describeMe()}" sText = f"{status} / {self._tItem.describeMe()}"
self.statusIcon.setPixmap(sIcon) self.statusIcon.setPixmap(sIcon)
@@ -3183,49 +3172,26 @@ class GuiDocEditFooter(QWidget):
return return
def updateLineCount(self, cursor: QTextCursor) -> None: def updateLineCount(self, cursor: QTextCursor) -> None:
"""Update the line counter.""" """Update the line and document position counter."""
cPos = cursor.position() + 1 cPos = cursor.position() + 1
cLine = cursor.blockNumber() + 1
cCount = max(cursor.document().characterCount(), 1) cCount = max(cursor.document().characterCount(), 1)
iLine = cursor.blockNumber() + 1
iDist = 100*cPos//cCount
self.linesText.setText( self.linesText.setText(
self.tr("Line: {0} ({1})").format(f"{iLine:n}", f"{iDist:d} %") self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %")
)
self.linesText.setToolTip(
self.tr("Document size is {0} bytes").format(f"{cCount:n}")
) )
return return
def updateCounts(self, wCount: int | None = None, cCount: int | None = None) -> None: def updateWordCount(self, wCount: int, selection: bool) -> None:
"""Select which word count display mode to use.""" """Update word counter information."""
if self._docSelection: if selection and wCount:
self._updateSelectionWordCounts(wCount, cCount) wText = self._trSelectCount.format(f"{wCount:n}")
elif self._tItem:
wCount = self._tItem.wordCount
wDiff = wCount - self._tItem.initCount
wText = self._trWordCount.format(f"{wCount:n}", f"{wDiff:+n}")
else: else:
self._updateWordCounts() wText = self._trWordCount.format("0", "+0")
return self.wordsText.setText(wText)
##
# Internal Functions
##
def _updateWordCounts(self) -> None:
"""Update the word count for the whole document."""
wCount = self._tItem.wordCount if self._tItem else 0
wDiff = wCount - self._tItem.initCount if self._tItem else 0
self.wordsText.setText(
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
)
return
def _updateSelectionWordCounts(self, wCount: int | None, cCount: int | None) -> None:
"""Update the word count for a selection."""
if wCount and cCount:
self.wordsText.setText(
self.tr("Words: {0} selected").format(f"{wCount:n}")
)
self.wordsText.setToolTip(
self.tr("Character count: {0}").format(f"{cCount:n}")
)
return return
# END Class GuiDocEditFooter # END Class GuiDocEditFooter
+1 -6
View File
@@ -1698,17 +1698,12 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
assert SHARED.project.tree[C.hSceneDoc]._paraCount == pC # type: ignore assert SHARED.project.tree[C.hSceneDoc]._paraCount == pC # type: ignore
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})" assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
# Select all text # Select all text and run the selection word counter
assert nwGUI.docEditor.docFooter._docSelection is False
nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
assert nwGUI.docEditor.docFooter._docSelection is True
# Run the selection word counter
nwGUI.docEditor._runSelCounter() nwGUI.docEditor._runSelCounter()
assert threadPool.objectID() == id(nwGUI.docEditor.wCounterSel) assert threadPool.objectID() == id(nwGUI.docEditor.wCounterSel)
nwGUI.docEditor.wCounterSel.run() nwGUI.docEditor.wCounterSel.run()
# nwGUI.docEditor._updateSelCounts(cC, wC, pC)
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected" assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
# qtbot.stop() # qtbot.stop()