diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 101859b8..2b427935 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -219,7 +219,7 @@ class GuiDocEditor(QPlainTextEdit): self.changeFocusState = self.docHeader.changeFocusState # Finalise - self.updateSyntaxColours() + self.updateSyntaxColors() self.initEditor() logger.debug("Ready: GuiDocEditor") @@ -287,7 +287,7 @@ class GuiDocEditor(QPlainTextEdit): self.docToolBar.updateTheme() return - def updateSyntaxColours(self) -> None: + def updateSyntaxColors(self) -> None: """Update the syntax highlighting theme.""" syntax = SHARED.theme.syntaxTheme @@ -304,7 +304,7 @@ class GuiDocEditor(QPlainTextEdit): viewport.setPalette(palette) self.docHeader.matchColours() - self.docFooter.matchColours() + self.docFooter.matchColors() return @@ -339,14 +339,12 @@ class GuiDocEditor(QPlainTextEdit): # Also set the document text options for the document text flow options = QTextOption() - if CONFIG.doJustify: options.setAlignment(QtAlignJustify) if CONFIG.showTabsNSpaces: options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces) if CONFIG.showLineEndings: options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators) - self._qDocument.setDefaultTextOption(options) # Scrolling @@ -379,19 +377,19 @@ class GuiDocEditor(QPlainTextEdit): """Load text from a document into the editor. If we have an I/O error, we must handle this and clear the editor so that we don't risk overwriting the file if it exists. This can for instance - happen of the file contains binary elements or an encoding that + happen if the file contains binary elements or an encoding that novelWriter does not support. If loading is successful, or the document is new (empty string), we set up the editor for editing the file. """ self._nwDocument = SHARED.project.storage.getDocument(tHandle) self._nwItem = self._nwDocument.nwItem - if not ((nwItem := self._nwItem) and nwItem.itemType == nwItemType.FILE): + if not (self._nwItem and self._nwItem.itemType == nwItemType.FILE): logger.debug("Requested item '%s' is not a document", tHandle) self.clearEditor() return False - if (docText := self._nwDocument.readDocument()) is None: + if (text := self._nwDocument.readDocument()) is None: # There was an I/O error self.clearEditor() return False @@ -400,7 +398,7 @@ class GuiDocEditor(QPlainTextEdit): self._docHandle = tHandle self._allowAutoReplace(False) - self._qDocument.setTextContent(docText, tHandle) + self._qDocument.setTextContent(text, tHandle) self._allowAutoReplace(True) QApplication.processEvents() @@ -415,7 +413,7 @@ class GuiDocEditor(QPlainTextEdit): if isinstance(tLine, int): self.setCursorLine(tLine) else: - self.setCursorPosition(nwItem.cursorPos) + self.setCursorPosition(self._nwItem.cursorPos) self.docHeader.setHandle(tHandle) self.docFooter.setHandle(tHandle) @@ -438,7 +436,7 @@ class GuiDocEditor(QPlainTextEdit): # Finalise QApplication.restoreOverrideCursor() self.updateStatusMessage.emit( - self.tr("Opened Document: {0}").format(nwItem.itemName) + self.tr("Opened Document: {0}").format(self._nwItem.itemName) ) return True @@ -469,19 +467,17 @@ class GuiDocEditor(QPlainTextEdit): ) return False - docText = self.getText() - cC, wC, pC = standardCounter(docText) + text = self.getText() + cC, wC, pC = standardCounter(text) self._updateDocCounts(cC, wC, pC) - if not self._nwDocument.writeDocument(docText): + if not self._nwDocument.writeDocument(text): saveOk = False - if self._nwDocument.hashError: - msgYes = SHARED.question(self.tr( - "This document has been changed outside of novelWriter " - "while it was open. Overwrite the file on disk?" - )) - if msgYes: - saveOk = self._nwDocument.writeDocument(docText, forceWrite=True) + if self._nwDocument.hashError and SHARED.question(self.tr( + "This document has been changed outside of novelWriter " + "while it was open. Overwrite the file on disk?" + )): + saveOk = self._nwDocument.writeDocument(text, forceWrite=True) if not saveOk: SHARED.error( @@ -494,10 +490,8 @@ class GuiDocEditor(QPlainTextEdit): self.setDocumentChanged(False) self.docTextChanged.emit(self._docHandle, self._lastEdit) - oldHeader = self._nwItem.mainHeading oldCount = SHARED.project.index.getHandleHeaderCount(tHandle) - SHARED.project.index.scanText(tHandle, docText) - newHeader = self._nwItem.mainHeading + SHARED.project.index.scanText(tHandle, text) newCount = SHARED.project.index.getHandleHeaderCount(tHandle) if self._nwItem.itemClass == nwItemClass.NOVEL: @@ -506,9 +500,6 @@ class GuiDocEditor(QPlainTextEdit): else: self.novelStructureChanged.emit() - if oldHeader != newHeader: - self.docFooter.updateInfo() - # Update the status bar self.updateStatusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName)) @@ -589,7 +580,7 @@ class GuiDocEditor(QPlainTextEdit): QTextDocument->toRawText instead of toPlainText. The former preserves non-breaking spaces, the latter does not. We still want to get rid of paragraph and line separators though. - See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText + See: https://doc.qt.io/qt-6/qtextdocument.html#toPlainText """ text = self._qDocument.toRawText() text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators @@ -627,10 +618,9 @@ class GuiDocEditor(QPlainTextEdit): def setCursorPosition(self, position: int) -> None: """Move the cursor to a given position in the document.""" - nChars = self._qDocument.characterCount() - if nChars > 1 and isinstance(position, int): + if (chars := self._qDocument.characterCount()) > 1 and isinstance(position, int): cursor = self.textCursor() - cursor.setPosition(minmax(position, 0, nChars-1)) + cursor.setPosition(minmax(position, 0, chars-1)) self.setTextCursor(cursor) self.centerCursor() return @@ -693,12 +683,10 @@ class GuiDocEditor(QPlainTextEdit): def spellCheckDocument(self) -> None: """Rerun the highlighter to update spell checking status of the - currently loaded text. The fastest way to do this, at least as - of Qt 5.13, is to clear the text and put it back. This clears - the undo stack, so we only do it for big documents. + currently loaded text. """ - logger.debug("Running spell checker") start = time() + logger.debug("Running spell checker") QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor)) self._qDocument.syntaxHighlighter.rehighlight() QApplication.restoreOverrideCursor() @@ -939,9 +927,7 @@ class GuiDocEditor(QPlainTextEdit): * We also handle automatic scrolling here. """ self._lastActive = time() - isReturn = event.key() == Qt.Key.Key_Return - isReturn |= event.key() == Qt.Key.Key_Enter - if isReturn and self.docSearch.anyFocus(): + if self.docSearch.anyFocus() and event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): return elif event == QKeySequence.StandardKey.Redo: self.docAction(nwDocAction.REDO) @@ -1094,7 +1080,7 @@ class GuiDocEditor(QPlainTextEdit): if (block := self._qDocument.findBlock(pos)).isValid(): text = block.text() if text.startswith("@") and added + removed == 1: - # Only run on single keypresses, otherwise it will trigger + # Only run on single character changes, or it will trigger # at unwanted times when other changes are made to the document cursor = self.textCursor() bPos = cursor.positionInBlock() @@ -1106,10 +1092,10 @@ class GuiDocEditor(QPlainTextEdit): else: self._completer.setVisible(False) - if self._doReplace and added == 1: - cursor = self.textCursor() - if self._autoReplace.process(text, cursor): - self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block()) + if self._doReplace and added == 1: + cursor = self.textCursor() + if self._autoReplace.process(text, cursor): + self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block()) return @@ -1123,11 +1109,10 @@ class GuiDocEditor(QPlainTextEdit): def _insertCompletion(self, pos: int, length: int, text: str) -> None: """Insert choice from the completer menu.""" cursor = self.textCursor() - block = cursor.block() - if block.isValid(): - pos += block.position() - cursor.setPosition(pos, QtMoveAnchor) - cursor.setPosition(pos + length, QtKeepAnchor) + if (block := cursor.block()).isValid(): + check = pos + block.position() + cursor.setPosition(check, QtMoveAnchor) + cursor.setPosition(check + length, QtKeepAnchor) cursor.insertText(text) self._completer.hide() return @@ -1220,7 +1205,8 @@ class GuiDocEditor(QPlainTextEdit): # Execute the context menu if viewport := self.viewport(): ctxMenu.exec(viewport.mapToGlobal(pos)) - ctxMenu.deleteLater() + + ctxMenu.setParent(None) return @@ -1277,15 +1263,11 @@ class GuiDocEditor(QPlainTextEdit): @pyqtSlot() def _runSelCounter(self) -> None: """Update the selection word count.""" - if self._docHandle is None: - return - - if self._wCounterSel.isRunning(): - logger.debug("Selection word counter is busy") - return - - SHARED.runInThreadPool(self._wCounterSel) - + if self._docHandle: + if self._wCounterSel.isRunning(): + logger.debug("Selection word counter is busy") + return + SHARED.runInThreadPool(self._wCounterSel) return @pyqtSlot(int, int, int) @@ -1565,8 +1547,10 @@ class GuiDocEditor(QPlainTextEdit): return - def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None, - select: _SelectAction = _SelectAction.NO_DECISION) -> None: + def _wrapSelection( + self, before: str, after: str | None = None, pos: int | None = None, + select: _SelectAction = _SelectAction.NO_DECISION + ) -> None: """Wrap the selected text in whatever is in tBefore and tAfter. If there is no selection, the autoSelect setting decides the action. AutoSelect will select the word under the cursor before @@ -1928,8 +1912,9 @@ class GuiDocEditor(QPlainTextEdit): self._qDocument.syntaxHighlighter.rehighlightBlock(block) return - def _processTag(self, cursor: QTextCursor | None = None, - follow: bool = True, create: bool = False) -> nwTrinary: + def _processTag( + self, cursor: QTextCursor | None = None, follow: bool = True, create: bool = False + ) -> nwTrinary: """Activated by Ctrl+Enter. Checks that we're in a block starting with '@'. We then find the tag under the cursor and check that it is not the tag itself. If all this is fine, we @@ -2163,13 +2148,10 @@ class BackgroundWordCounter(QRunnable): def __init__(self, docEditor: GuiDocEditor, forSelection: bool = False) -> None: super().__init__() - self._docEditor = docEditor self._forSelection = forSelection self._isRunning = False - self.signals = BackgroundWordCounterSignals() - return def isRunning(self) -> bool: @@ -3079,42 +3061,30 @@ class GuiDocEditFooter(QWidget): # Status self.statusIcon = QLabel("", self) - self.statusIcon.setContentsMargins(0, 0, 0, 0) self.statusIcon.setFixedHeight(iPx) self.statusIcon.setAlignment(QtAlignLeftTop) - self.statusText = QLabel(self.tr("Status"), self) - self.statusText.setIndent(0) - self.statusText.setMargin(0) - self.statusText.setContentsMargins(0, 0, 0, 0) + self.statusText = QLabel("", self) self.statusText.setAutoFillBackground(True) self.statusText.setFixedHeight(fPx) self.statusText.setAlignment(QtAlignLeftTop) # Lines self.linesIcon = QLabel("", self) - self.linesIcon.setContentsMargins(0, 0, 0, 0) self.linesIcon.setFixedHeight(iPx) self.linesIcon.setAlignment(QtAlignLeftTop) self.linesText = QLabel("", self) - self.linesText.setIndent(0) - self.linesText.setMargin(0) - self.linesText.setContentsMargins(0, 0, 0, 0) self.linesText.setAutoFillBackground(True) self.linesText.setFixedHeight(fPx) self.linesText.setAlignment(QtAlignLeftTop) # Words self.wordsIcon = QLabel("", self) - self.wordsIcon.setContentsMargins(0, 0, 0, 0) self.wordsIcon.setFixedHeight(iPx) self.wordsIcon.setAlignment(QtAlignLeftTop) self.wordsText = QLabel("", self) - self.wordsText.setIndent(0) - self.wordsText.setMargin(0) - self.wordsText.setContentsMargins(0, 0, 0, 0) self.wordsText.setAutoFillBackground(True) self.wordsText.setFixedHeight(fPx) self.wordsText.setAlignment(QtAlignLeftTop) @@ -3167,10 +3137,10 @@ class GuiDocEditFooter(QWidget): iPx = round(0.9*SHARED.theme.baseIconHeight) self.linesIcon.setPixmap(SHARED.theme.getPixmap("lines", (iPx, iPx))) self.wordsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx))) - self.matchColours() + self.matchColors() return - def matchColours(self) -> None: + def matchColors(self) -> None: """Update the colours of the widget to match those of the syntax theme rather than the main GUI. """