Add a context menu entry to set item name from heading (#1614)

This commit is contained in:
Veronica Berglyd Olsen
2023-11-23 20:39:28 +01:00
committed by GitHub
56 changed files with 429 additions and 190 deletions
+147 -113
View File
@@ -83,8 +83,8 @@ class GuiDocEditor(QPlainTextEdit):
"""Gui Widget: Main Document Editor""" """Gui Widget: Main Document Editor"""
MOVE_KEYS = ( MOVE_KEYS = (
Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down, Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up, Qt.Key.Key_Down,
Qt.Key_PageUp, Qt.Key_PageDown Qt.Key.Key_PageUp, Qt.Key.Key_PageDown
) )
# Custom Signals # Custom Signals
@@ -97,6 +97,8 @@ class GuiDocEditor(QPlainTextEdit):
spellCheckStateChanged = pyqtSignal(bool) spellCheckStateChanged = pyqtSignal(bool)
closeDocumentRequest = pyqtSignal() closeDocumentRequest = pyqtSignal()
toggleFocusModeRequest = pyqtSignal() toggleFocusModeRequest = pyqtSignal()
requestProjectItemSelected = pyqtSignal(str, bool)
requestProjectItemRenamed = pyqtSignal(str, str)
def __init__(self, mainGui: GuiMain) -> None: def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -157,28 +159,28 @@ class GuiDocEditor(QPlainTextEdit):
self.docToolBar.requestDocAction.connect(self.docAction) self.docToolBar.requestDocAction.connect(self.docAction)
# Context Menu # Context Menu
self.setContextMenuPolicy(Qt.CustomContextMenu) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self._openContextMenu) self.customContextMenuRequested.connect(self._openContextMenu)
# Editor Settings # Editor Settings
self.setMinimumWidth(CONFIG.pxInt(300)) self.setMinimumWidth(CONFIG.pxInt(300))
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.Shape.NoFrame)
# Custom Shortcuts # Custom Shortcuts
self.keyContext = QShortcut(self) self.keyContext = QShortcut(self)
self.keyContext.setKey("Ctrl+.") self.keyContext.setKey("Ctrl+.")
self.keyContext.setContext(Qt.WidgetShortcut) self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyContext.activated.connect(self._openContextFromCursor) self.keyContext.activated.connect(self._openContextFromCursor)
self.followTag1 = QShortcut(self) self.followTag1 = QShortcut(self)
self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier) self.followTag1.setKey(Qt.Key.Key_Return | Qt.KeyboardModifier.ControlModifier)
self.followTag1.setContext(Qt.WidgetShortcut) self.followTag1.setContext(Qt.ShortcutContext.WidgetShortcut)
self.followTag1.activated.connect(self._processTag) self.followTag1.activated.connect(self._processTag)
self.followTag2 = QShortcut(self) self.followTag2 = QShortcut(self)
self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier) self.followTag2.setKey(Qt.Key.Key_Enter | Qt.KeyboardModifier.ControlModifier)
self.followTag2.setContext(Qt.WidgetShortcut) self.followTag2.setContext(Qt.ShortcutContext.WidgetShortcut)
self.followTag2.activated.connect(self._processTag) self.followTag2.activated.connect(self._processTag)
# Set Up Document Word Counter # Set Up Document Word Counter
@@ -274,14 +276,14 @@ class GuiDocEditor(QPlainTextEdit):
def updateSyntaxColours(self) -> None: def updateSyntaxColours(self) -> None:
"""Update the syntax highlighting theme.""" """Update the syntax highlighting theme."""
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) mainPalette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack)) mainPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) mainPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack)) docPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) docPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -333,25 +335,25 @@ class GuiDocEditor(QPlainTextEdit):
options = QTextOption() options = QTextOption()
if CONFIG.doJustify: if CONFIG.doJustify:
options.setAlignment(Qt.AlignJustify) options.setAlignment(Qt.AlignmentFlag.AlignJustify)
if CONFIG.showTabsNSpaces: if CONFIG.showTabsNSpaces:
options.setFlags(options.flags() | QTextOption.ShowTabsAndSpaces) options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces)
if CONFIG.showLineEndings: if CONFIG.showLineEndings:
options.setFlags(options.flags() | QTextOption.ShowLineAndParagraphSeparators) options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators)
self._qDocument.setDefaultTextOption(options) self._qDocument.setDefaultTextOption(options)
# Scrolling # Scrolling
self.setCenterOnScroll(CONFIG.scrollPastEnd) self.setCenterOnScroll(CONFIG.scrollPastEnd)
if CONFIG.hideVScroll: if CONFIG.hideVScroll:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
if CONFIG.hideHScroll: if CONFIG.hideHScroll:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
# Refresh the tab stops # Refresh the tab stops
self.setTabStopDistance(CONFIG.getTabWidth()) self.setTabStopDistance(CONFIG.getTabWidth())
@@ -370,7 +372,7 @@ class GuiDocEditor(QPlainTextEdit):
return return
def loadText(self, tHandle, tLine=None) -> bool: def loadText(self, tHandle: str, tLine=None) -> bool:
"""Load text from a document into the editor. If we have an I/O """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 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 risk overwriting the file if it exists. This can for instance
@@ -388,7 +390,7 @@ class GuiDocEditor(QPlainTextEdit):
self.clearEditor() self.clearEditor()
return False return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
self._docHandle = tHandle self._docHandle = tHandle
self._allowAutoReplace(False) self._allowAutoReplace(False)
@@ -441,7 +443,7 @@ class GuiDocEditor(QPlainTextEdit):
"""Replace the text of the current document with the provided """Replace the text of the current document with the provided
text. This also clears undo history. text. This also clears undo history.
""" """
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
self.setPlainText(text) self.setPlainText(text)
self.updateDocMargins() self.updateDocMargins()
self.setDocumentChanged(True) self.setDocumentChanged(True)
@@ -676,7 +678,7 @@ class GuiDocEditor(QPlainTextEdit):
""" """
logger.debug("Running spell checker") logger.debug("Running spell checker")
start = time() start = time()
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
self._qDocument.syntaxHighlighter.rehighlight() self._qDocument.syntaxHighlighter.rehighlight()
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
logger.debug("Document highlighted in %.3f ms", 1000*(time() - start)) logger.debug("Document highlighted in %.3f ms", 1000*(time() - start))
@@ -726,9 +728,9 @@ class GuiDocEditor(QPlainTextEdit):
elif action == nwDocAction.D_QUOTE: elif action == nwDocAction.D_QUOTE:
self._wrapSelection(self._typDQuoteO, self._typDQuoteC) self._wrapSelection(self._typDQuoteO, self._typDQuoteC)
elif action == nwDocAction.SEL_ALL: elif action == nwDocAction.SEL_ALL:
self._makeSelection(QTextCursor.Document) self._makeSelection(QTextCursor.SelectionType.Document)
elif action == nwDocAction.SEL_PARA: elif action == nwDocAction.SEL_PARA:
self._makeSelection(QTextCursor.BlockUnderCursor) self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor)
elif action == nwDocAction.BLOCK_H1: elif action == nwDocAction.BLOCK_H1:
self._formatBlock(nwDocAction.BLOCK_H1) self._formatBlock(nwDocAction.BLOCK_H1)
elif action == nwDocAction.BLOCK_H2: elif action == nwDocAction.BLOCK_H2:
@@ -909,17 +911,17 @@ class GuiDocEditor(QPlainTextEdit):
* We also handle automatic scrolling here. * We also handle automatic scrolling here.
""" """
self._lastActive = time() self._lastActive = time()
isReturn = event.key() == Qt.Key_Return isReturn = event.key() == Qt.Key.Key_Return
isReturn |= event.key() == Qt.Key_Enter isReturn |= event.key() == Qt.Key.Key_Enter
if isReturn and self.docSearch.anyFocus(): if isReturn and self.docSearch.anyFocus():
return return
elif event == QKeySequence.Redo: elif event == QKeySequence.StandardKey.Redo:
self.docAction(nwDocAction.REDO) self.docAction(nwDocAction.REDO)
return return
elif event == QKeySequence.Undo: elif event == QKeySequence.StandardKey.Undo:
self.docAction(nwDocAction.UNDO) self.docAction(nwDocAction.UNDO)
return return
elif event == QKeySequence.SelectAll: elif event == QKeySequence.StandardKey.SelectAll:
self.docAction(nwDocAction.SEL_ALL) self.docAction(nwDocAction.SEL_ALL)
return return
@@ -928,7 +930,7 @@ class GuiDocEditor(QPlainTextEdit):
super().keyPressEvent(event) super().keyPressEvent(event)
nPos = self.cursorRect().topLeft().y() nPos = self.cursorRect().topLeft().y()
kMod = event.modifiers() kMod = event.modifiers()
okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier okMod = kMod in (Qt.KeyboardModifier.NoModifier, Qt.KeyboardModifier.ShiftModifier)
okKey = event.key() not in self.MOVE_KEYS okKey = event.key() not in self.MOVE_KEYS
if nPos != cPos and okMod and okKey: if nPos != cPos and okMod and okKey:
mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height() mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
@@ -959,7 +961,7 @@ class GuiDocEditor(QPlainTextEdit):
pressed, check if we're clicking on a tag, and trigger the pressed, check if we're clicking on a tag, and trigger the
follow tag function. follow tag function.
""" """
if qApp.keyboardModifiers() == Qt.ControlModifier: if qApp.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier:
self._processTag(self.cursorForPosition(event.pos())) self._processTag(self.cursorForPosition(event.pos()))
super().mouseReleaseEvent(event) super().mouseReleaseEvent(event)
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
@@ -1065,43 +1067,50 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot("QPoint") @pyqtSlot("QPoint")
def _openContextMenu(self, pos: QPoint) -> None: def _openContextMenu(self, pos: QPoint) -> None:
"""Triggered by right click to open the context menu. Also """Open the editor context menu at a given coordinate."""
triggered by the Ctrl+. shortcut.
"""
uCursor = self.textCursor() uCursor = self.textCursor()
pCursor = self.cursorForPosition(pos) pCursor = self.cursorForPosition(pos)
pBlock = pCursor.block()
ctxMenu = QMenu(self) ctxMenu = QMenu(self)
ctxMenu.setObjectName("ContextMenu")
if pBlock.userState() == GuiDocHighlighter.BLOCK_TITLE:
action = ctxMenu.addAction(self.tr("Set as Document Name"))
action.triggered.connect(lambda: self._emitRenameItem(pBlock))
# Follow # Follow
status = self._processTag(cursor=pCursor, follow=False) status = self._processTag(cursor=pCursor, follow=False)
if status == nwTrinary.POSITIVE: if status == nwTrinary.POSITIVE:
aTag = ctxMenu.addAction(self.tr("Follow Tag")) action = ctxMenu.addAction(self.tr("Follow Tag"))
aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True)) action.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
ctxMenu.addSeparator() ctxMenu.addSeparator()
elif status == nwTrinary.NEGATIVE: elif status == nwTrinary.NEGATIVE:
aTag = ctxMenu.addAction(self.tr("Create Note for Tag")) action = ctxMenu.addAction(self.tr("Create Note for Tag"))
aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True)) action.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
ctxMenu.addSeparator() ctxMenu.addSeparator()
# Cut, Copy and Paste # Cut, Copy and Paste
if uCursor.hasSelection(): if uCursor.hasSelection():
aCut = ctxMenu.addAction(self.tr("Cut")) action = ctxMenu.addAction(self.tr("Cut"))
aCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT)) action.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
aCopy = ctxMenu.addAction(self.tr("Copy")) action = ctxMenu.addAction(self.tr("Copy"))
aCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY)) action.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
aPaste = ctxMenu.addAction(self.tr("Paste")) action = ctxMenu.addAction(self.tr("Paste"))
aPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE)) action.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
ctxMenu.addSeparator() ctxMenu.addSeparator()
# Selections # Selections
aSAll = ctxMenu.addAction(self.tr("Select All")) action = ctxMenu.addAction(self.tr("Select All"))
aSAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL)) action.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
aSWrd = ctxMenu.addAction(self.tr("Select Word")) action = ctxMenu.addAction(self.tr("Select Word"))
aSWrd.triggered.connect(lambda: self._makePosSelection(QTextCursor.WordUnderCursor, pos)) action.triggered.connect(
aSPar = ctxMenu.addAction(self.tr("Select Paragraph")) lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, pos)
aSPar.triggered.connect(lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, pos)) )
action = ctxMenu.addAction(self.tr("Select Paragraph"))
action.triggered.connect(lambda: self._makePosSelection(
QTextCursor.SelectionType.BlockUnderCursor, pos)
)
# Spell Checking # Spell Checking
if SHARED.project.data.spellCheck: if SHARED.project.data.spellCheck:
@@ -1111,21 +1120,23 @@ class GuiDocEditor(QPlainTextEdit):
block = pCursor.block() block = pCursor.block()
sCursor = self.textCursor() sCursor = self.textCursor()
sCursor.setPosition(block.position() + cPos) sCursor.setPosition(block.position() + cPos)
sCursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, cLen) sCursor.movePosition(
QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, cLen
)
if suggest: if suggest:
ctxMenu.addSeparator() ctxMenu.addSeparator()
ctxMenu.addAction(self.tr("Spelling Suggestion(s)")) ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
for option in suggest[:15]: for option in suggest[:15]:
aFix = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}") action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
aFix.triggered.connect( action.triggered.connect(
lambda _, option=option: self._correctWord(sCursor, option) lambda _, option=option: self._correctWord(sCursor, option)
) )
else: else:
ctxMenu.addAction("%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions"))) ctxMenu.addAction("%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")))
ctxMenu.addSeparator() ctxMenu.addSeparator()
aAdd = ctxMenu.addAction(self.tr("Add Word to Dictionary")) action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
aAdd.triggered.connect(lambda: self._addWord(word, block)) action.triggered.connect(lambda: self._addWord(word, block))
# Execute the context menu # Execute the context menu
ctxMenu.exec_(self.viewport().mapToGlobal(pos)) ctxMenu.exec_(self.viewport().mapToGlobal(pos))
@@ -1316,8 +1327,8 @@ class GuiDocEditor(QPlainTextEdit):
else: else:
resIdx = 0 if doLoop else maxIdx resIdx = 0 if doLoop else maxIdx
cursor.setPosition(resS[resIdx], QTextCursor.MoveAnchor) cursor.setPosition(resS[resIdx], QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(resE[resIdx], QTextCursor.KeepAnchor) cursor.setPosition(resE[resIdx], QTextCursor.MoveMode.KeepAnchor)
self.setTextCursor(cursor) self.setTextCursor(cursor)
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
@@ -1343,9 +1354,9 @@ class GuiDocEditor(QPlainTextEdit):
findOpt = QTextDocument.FindFlag(0) findOpt = QTextDocument.FindFlag(0)
if self.docSearch.isCaseSense: if self.docSearch.isCaseSense:
findOpt |= QTextDocument.FindCaseSensitively findOpt |= QTextDocument.FindFlag.FindCaseSensitively
if self.docSearch.isWholeWord: if self.docSearch.isWholeWord:
findOpt |= QTextDocument.FindWholeWords findOpt |= QTextDocument.FindFlag.FindWholeWords
searchFor = self.docSearch.getSearchObject() searchFor = self.docSearch.getSearchObject()
cursor.setPosition(0) cursor.setPosition(0)
@@ -1363,8 +1374,8 @@ class GuiDocEditor(QPlainTextEdit):
break break
if hasSelection: if hasSelection:
cursor.setPosition(origA, QTextCursor.MoveAnchor) cursor.setPosition(origA, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(origB, QTextCursor.KeepAnchor) cursor.setPosition(origB, QTextCursor.MoveMode.KeepAnchor)
else: else:
cursor.setPosition(origA) cursor.setPosition(origA)
@@ -1475,8 +1486,8 @@ class GuiDocEditor(QPlainTextEdit):
if blockS != blockE: if blockS != blockE:
posE = blockS.position() + blockS.length() - 1 posE = blockS.position() + blockS.length() - 1
cursor.clearSelection() cursor.clearSelection()
cursor.setPosition(posS, QTextCursor.MoveAnchor) cursor.setPosition(posS, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(posE, QTextCursor.KeepAnchor) cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
self.setTextCursor(cursor) self.setTextCursor(cursor)
numB = 0 numB = 0
@@ -1578,7 +1589,9 @@ class GuiDocEditor(QPlainTextEdit):
self._allowAutoReplace(False) self._allowAutoReplace(False)
for posC in range(posS, posE+1): for posC in range(posS, posE+1):
cursor.setPosition(posC) cursor.setPosition(posC)
cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2) cursor.movePosition(
QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 2
)
selText = cursor.selectedText() selText = cursor.selectedText()
nS = len(selText) nS = len(selText)
@@ -1598,12 +1611,16 @@ class GuiDocEditor(QPlainTextEdit):
cursor.setPosition(posC) cursor.setPosition(posC)
if pC in closeCheck: if pC in closeCheck:
cursor.beginEditBlock() cursor.beginEditBlock()
cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1) cursor.movePosition(
QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
)
cursor.insertText(oQuote) cursor.insertText(oQuote)
cursor.endEditBlock() cursor.endEditBlock()
else: else:
cursor.beginEditBlock() cursor.beginEditBlock()
cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1) cursor.movePosition(
QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
)
cursor.insertText(cQuote) cursor.insertText(cQuote)
cursor.endEditBlock() cursor.endEditBlock()
@@ -1723,7 +1740,7 @@ class GuiDocEditor(QPlainTextEdit):
# Replace the block text # Replace the block text
cursor.beginEditBlock() cursor.beginEditBlock()
posO = cursor.position() posO = cursor.position()
cursor.select(QTextCursor.BlockUnderCursor) cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
posS = cursor.selectionStart() posS = cursor.selectionStart()
cursor.removeSelectedText() cursor.removeSelectedText()
cursor.setPosition(posS) cursor.setPosition(posS)
@@ -1783,7 +1800,9 @@ class GuiDocEditor(QPlainTextEdit):
cursor.beginEditBlock() cursor.beginEditBlock()
cursor.clearSelection() cursor.clearSelection()
cursor.setPosition(rS) cursor.setPosition(rS)
cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, rE-rS) cursor.movePosition(
QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, rE-rS
)
cursor.insertText(cleanText.rstrip() + "\n") cursor.insertText(cleanText.rstrip() + "\n")
cursor.endEditBlock() cursor.endEditBlock()
@@ -1852,6 +1871,13 @@ class GuiDocEditor(QPlainTextEdit):
return nwTrinary.NEUTRAL return nwTrinary.NEUTRAL
def _emitRenameItem(self, block: QTextBlock) -> None:
"""Emit a signal to request an item be renamed."""
if self._docHandle:
text = block.text().lstrip("#").lstrip("!").strip()
self.requestProjectItemRenamed.emit(self._docHandle, text)
return
def _openContextFromCursor(self) -> None: def _openContextFromCursor(self) -> None:
"""Open the spell check context menu at the cursor.""" """Open the spell check context menu at the cursor."""
self._openContextMenu(self.cursorRect().center()) self._openContextMenu(self.cursorRect().center())
@@ -1943,7 +1969,9 @@ class GuiDocEditor(QPlainTextEdit):
tInsert = tInsert + self._typPadChar tInsert = tInsert + self._typPadChar
if nDelete > 0: if nDelete > 0:
cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, nDelete) cursor.movePosition(
QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, nDelete
)
cursor.insertText(tInsert) cursor.insertText(tInsert)
return return
@@ -1994,8 +2022,8 @@ class GuiDocEditor(QPlainTextEdit):
return cursor return cursor
cursor.clearSelection() cursor.clearSelection()
cursor.setPosition(sPos, QTextCursor.MoveAnchor) cursor.setPosition(sPos, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(ePos, QTextCursor.KeepAnchor) cursor.setPosition(ePos, QTextCursor.MoveMode.KeepAnchor)
self.setTextCursor(cursor) self.setTextCursor(cursor)
@@ -2007,18 +2035,18 @@ class GuiDocEditor(QPlainTextEdit):
cursor.clearSelection() cursor.clearSelection()
cursor.select(mode) cursor.select(mode)
if mode == QTextCursor.WordUnderCursor: if mode == QTextCursor.SelectionType.WordUnderCursor:
cursor = self._autoSelect() cursor = self._autoSelect()
elif mode == QTextCursor.BlockUnderCursor: elif mode == QTextCursor.SelectionType.BlockUnderCursor:
# This selection mode also selects the preceding paragraph # This selection mode also selects the preceding paragraph
# separator, which we want to avoid. # separator, which we want to avoid.
posS = cursor.selectionStart() posS = cursor.selectionStart()
posE = cursor.selectionEnd() posE = cursor.selectionEnd()
selTxt = cursor.selectedText() selTxt = cursor.selectedText()
if selTxt.startswith(nwUnicode.U_PSEP): if selTxt.startswith(nwUnicode.U_PSEP):
cursor.setPosition(posS+1, QTextCursor.MoveAnchor) cursor.setPosition(posS+1, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(posE, QTextCursor.KeepAnchor) cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
self.setTextCursor(cursor) self.setTextCursor(cursor)
@@ -2100,7 +2128,10 @@ class MetaCompleter(QMenu):
def keyPressEvent(self, event: QKeyEvent) -> None: def keyPressEvent(self, event: QKeyEvent) -> None:
"""Capture keypresses and forward most of them to the editor.""" """Capture keypresses and forward most of them to the editor."""
parent = self.parent() parent = self.parent()
if event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_Return, Qt.Key_Enter, Qt.Key_Escape): if event.key() in (
Qt.Key.Key_Up, Qt.Key.Key_Down, Qt.Key.Key_Return,
Qt.Key.Key_Enter, Qt.Key.Key_Escape
):
super().keyPressEvent(event) super().keyPressEvent(event)
elif isinstance(parent, GuiDocEditor): elif isinstance(parent, GuiDocEditor):
parent.keyPressEvent(event) parent.keyPressEvent(event)
@@ -2254,9 +2285,9 @@ class GuiDocToolBar(QWidget):
def updateTheme(self) -> None: def updateTheme(self) -> None:
"""Initialise GUI elements that depend on specific settings.""" """Initialise GUI elements that depend on specific settings."""
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(palette) self.setPalette(palette)
tPx = int(0.8*SHARED.theme.fontPixelSize) tPx = int(0.8*SHARED.theme.fontPixelSize)
@@ -2336,7 +2367,7 @@ class GuiDocEditSearch(QFrame):
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain) self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
self.mainBox = QGridLayout(self) self.mainBox = QGridLayout(self)
self.setLayout(self.mainBox) self.setLayout(self.mainBox)
@@ -2355,7 +2386,7 @@ class GuiDocEditSearch(QFrame):
self.replaceBox.returnPressed.connect(self._doReplace) self.replaceBox.returnPressed.connect(self._doReplace)
self.searchOpt = QToolBar(self) self.searchOpt = QToolBar(self)
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly) self.searchOpt.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.searchOpt.setIconSize(QSize(tPx, tPx)) self.searchOpt.setIconSize(QSize(tPx, tPx))
self.searchOpt.setContentsMargins(0, 0, 0, 0) self.searchOpt.setContentsMargins(0, 0, 0, 0)
@@ -2417,7 +2448,7 @@ class GuiDocEditSearch(QFrame):
bPx = self.searchBox.sizeHint().height() bPx = self.searchBox.sizeHint().height()
self.showReplace = QToolButton(self) self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow) self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
self.showReplace.setCheckable(True) self.showReplace.setCheckable(True)
self.showReplace.toggled.connect(self._doToggleReplace) self.showReplace.toggled.connect(self._doToggleReplace)
@@ -2431,8 +2462,8 @@ class GuiDocEditSearch(QFrame):
self.replaceButton.setToolTip(self.tr("Find and replace in current document")) self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace) self.replaceButton.clicked.connect(self._doReplace)
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft) self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignmentFlag.AlignLeft)
self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignRight) self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignmentFlag.AlignRight)
self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1) self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1)
self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2) self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2)
self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1) self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1)
@@ -2490,18 +2521,18 @@ class GuiDocEditSearch(QFrame):
# only added in Qt 5.13. Otherwise, 5.3 and up supports # only added in Qt 5.13. Otherwise, 5.3 and up supports
# only the QRegExp class. # only the QRegExp class.
if CONFIG.verQtValue >= 0x050d00: if CONFIG.verQtValue >= 0x050d00:
rxOpt = QRegularExpression.UseUnicodePropertiesOption rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
if not self.isCaseSense: if not self.isCaseSense:
rxOpt |= QRegularExpression.CaseInsensitiveOption rxOpt |= QRegularExpression.PatternOption.CaseInsensitiveOption
regEx = QRegularExpression(text, rxOpt) regEx = QRegularExpression(text, rxOpt)
self._alertSearchValid(regEx.isValid()) self._alertSearchValid(regEx.isValid())
return regEx return regEx
else: # pragma: no cover else: # pragma: no cover
# >= 50300 to < 51300 # >= 50300 to < 51300
if self.isCaseSense: if self.isCaseSense:
rxOpt = Qt.CaseSensitive rxOpt = Qt.CaseSensitivity.CaseSensitive
else: else:
rxOpt = Qt.CaseInsensitive rxOpt = Qt.CaseSensitivity.CaseInsensitive
regEx = QRegExp(text, rxOpt) regEx = QRegExp(text, rxOpt)
self._alertSearchValid(regEx.isValid()) self._alertSearchValid(regEx.isValid())
return regEx return regEx
@@ -2637,7 +2668,7 @@ class GuiDocEditSearch(QFrame):
def _doSearch(self) -> None: def _doSearch(self) -> None:
"""Call the search action function for the document editor.""" """Call the search action function for the document editor."""
modKey = qApp.keyboardModifiers() modKey = qApp.keyboardModifiers()
if modKey == Qt.ShiftModifier: if modKey == Qt.KeyboardModifier.ShiftModifier:
self.docEditor.findNext(goBack=True) self.docEditor.findNext(goBack=True)
else: else:
self.docEditor.findNext() self.docEditor.findNext()
@@ -2653,9 +2684,9 @@ class GuiDocEditSearch(QFrame):
def _doToggleReplace(self, state: bool) -> None: def _doToggleReplace(self, state: bool) -> None:
"""Toggle the show/hide of the replace box.""" """Toggle the show/hide of the replace box."""
if state: if state:
self.showReplace.setArrowType(Qt.DownArrow) self.showReplace.setArrowType(Qt.ArrowType.DownArrow)
else: else:
self.showReplace.setArrowType(Qt.RightArrow) self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
self.replaceBox.setVisible(state) self.replaceBox.setVisible(state)
self.replaceButton.setVisible(state) self.replaceButton.setVisible(state)
self.repVisible = state self.repVisible = state
@@ -2708,7 +2739,7 @@ class GuiDocEditSearch(QFrame):
isn't valid. Take the colour from the replace box. isn't valid. Take the colour from the replace box.
""" """
qPalette = self.replaceBox.palette() qPalette = self.replaceBox.palette()
qPalette.setColor(QPalette.Base, self.rxCol[isValid]) qPalette.setColor(QPalette.ColorRole.Base, self.rxCol[isValid])
self.searchBox.setPalette(qPalette) self.searchBox.setPalette(qPalette)
return return
@@ -2749,7 +2780,7 @@ class GuiDocEditHeader(QWidget):
self.itemTitle.setMargin(0) self.itemTitle.setMargin(0)
self.itemTitle.setContentsMargins(0, 0, 0, 0) self.itemTitle.setContentsMargins(0, 0, 0, 0)
self.itemTitle.setAutoFillBackground(True) self.itemTitle.setAutoFillBackground(True)
self.itemTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) self.itemTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
self.itemTitle.setFixedHeight(fPx) self.itemTitle.setFixedHeight(fPx)
lblFont = self.itemTitle.font() lblFont = self.itemTitle.font()
@@ -2761,7 +2792,7 @@ class GuiDocEditHeader(QWidget):
self.tbButton.setContentsMargins(0, 0, 0, 0) self.tbButton.setContentsMargins(0, 0, 0, 0)
self.tbButton.setIconSize(iconSize) self.tbButton.setIconSize(iconSize)
self.tbButton.setFixedSize(fPx, fPx) self.tbButton.setFixedSize(fPx, fPx)
self.tbButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.tbButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.tbButton.setVisible(False) self.tbButton.setVisible(False)
self.tbButton.setToolTip(self.tr("Toggle Tool Bar")) self.tbButton.setToolTip(self.tr("Toggle Tool Bar"))
self.tbButton.clicked.connect(lambda: self.toggleToolBarRequest.emit()) self.tbButton.clicked.connect(lambda: self.toggleToolBarRequest.emit())
@@ -2770,7 +2801,7 @@ class GuiDocEditHeader(QWidget):
self.searchButton.setContentsMargins(0, 0, 0, 0) self.searchButton.setContentsMargins(0, 0, 0, 0)
self.searchButton.setIconSize(iconSize) self.searchButton.setIconSize(iconSize)
self.searchButton.setFixedSize(fPx, fPx) self.searchButton.setFixedSize(fPx, fPx)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.searchButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.searchButton.setVisible(False) self.searchButton.setVisible(False)
self.searchButton.setToolTip(self.tr("Search")) self.searchButton.setToolTip(self.tr("Search"))
self.searchButton.clicked.connect(self.docEditor.toggleSearch) self.searchButton.clicked.connect(self.docEditor.toggleSearch)
@@ -2779,7 +2810,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setContentsMargins(0, 0, 0, 0) self.minmaxButton.setContentsMargins(0, 0, 0, 0)
self.minmaxButton.setIconSize(iconSize) self.minmaxButton.setIconSize(iconSize)
self.minmaxButton.setFixedSize(fPx, fPx) self.minmaxButton.setFixedSize(fPx, fPx)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.minmaxButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.minmaxButton.setVisible(False) self.minmaxButton.setVisible(False)
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode")) self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
self.minmaxButton.clicked.connect(lambda: self.docEditor.toggleFocusModeRequest.emit()) self.minmaxButton.clicked.connect(lambda: self.docEditor.toggleFocusModeRequest.emit())
@@ -2788,7 +2819,7 @@ class GuiDocEditHeader(QWidget):
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(iconSize) self.closeButton.setIconSize(iconSize)
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close")) self.closeButton.setToolTip(self.tr("Close"))
self.closeButton.clicked.connect(self._closeDocument) self.closeButton.clicked.connect(self._closeDocument)
@@ -2846,9 +2877,9 @@ class GuiDocEditHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(palette) self.setPalette(palette)
self.itemTitle.setPalette(palette) self.itemTitle.setPalette(palette)
@@ -2924,7 +2955,8 @@ class GuiDocEditHeader(QWidget):
"""Capture a click on the title and ensure that the item is """Capture a click on the title and ensure that the item is
selected in the project tree. selected in the project tree.
""" """
self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True) if event.button() == Qt.MouseButton.LeftButton:
self.docEditor.requestProjectItemSelected.emit(self._docHandle, True)
return return
# END Class GuiDocEditHeader # END Class GuiDocEditHeader
@@ -2961,11 +2993,13 @@ class GuiDocEditFooter(QWidget):
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
alLeftTop = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
# Status # Status
self.statusIcon = QLabel("") self.statusIcon = QLabel("")
self.statusIcon.setContentsMargins(0, 0, 0, 0) self.statusIcon.setContentsMargins(0, 0, 0, 0)
self.statusIcon.setFixedHeight(self.sPx) self.statusIcon.setFixedHeight(self.sPx)
self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.statusIcon.setAlignment(alLeftTop)
self.statusText = QLabel(self.tr("Status")) self.statusText = QLabel(self.tr("Status"))
self.statusText.setIndent(0) self.statusText.setIndent(0)
@@ -2973,14 +3007,14 @@ class GuiDocEditFooter(QWidget):
self.statusText.setContentsMargins(0, 0, 0, 0) self.statusText.setContentsMargins(0, 0, 0, 0)
self.statusText.setAutoFillBackground(True) self.statusText.setAutoFillBackground(True)
self.statusText.setFixedHeight(fPx) self.statusText.setFixedHeight(fPx)
self.statusText.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.statusText.setAlignment(alLeftTop)
self.statusText.setFont(lblFont) self.statusText.setFont(lblFont)
# Lines # Lines
self.linesIcon = QLabel("") self.linesIcon = QLabel("")
self.linesIcon.setContentsMargins(0, 0, 0, 0) self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(self.sPx) self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.linesIcon.setAlignment(alLeftTop)
self.linesText = QLabel("") self.linesText = QLabel("")
self.linesText.setIndent(0) self.linesText.setIndent(0)
@@ -2988,14 +3022,14 @@ class GuiDocEditFooter(QWidget):
self.linesText.setContentsMargins(0, 0, 0, 0) self.linesText.setContentsMargins(0, 0, 0, 0)
self.linesText.setAutoFillBackground(True) self.linesText.setAutoFillBackground(True)
self.linesText.setFixedHeight(fPx) self.linesText.setFixedHeight(fPx)
self.linesText.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.linesText.setAlignment(alLeftTop)
self.linesText.setFont(lblFont) self.linesText.setFont(lblFont)
# Words # Words
self.wordsIcon = QLabel("") self.wordsIcon = QLabel("")
self.wordsIcon.setContentsMargins(0, 0, 0, 0) self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(self.sPx) self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.wordsIcon.setAlignment(alLeftTop)
self.wordsText = QLabel("") self.wordsText = QLabel("")
self.wordsText.setIndent(0) self.wordsText.setIndent(0)
@@ -3003,7 +3037,7 @@ class GuiDocEditFooter(QWidget):
self.wordsText.setContentsMargins(0, 0, 0, 0) self.wordsText.setContentsMargins(0, 0, 0, 0)
self.wordsText.setAutoFillBackground(True) self.wordsText.setAutoFillBackground(True)
self.wordsText.setFixedHeight(fPx) self.wordsText.setFixedHeight(fPx)
self.wordsText.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.wordsText.setAlignment(alLeftTop)
self.wordsText.setFont(lblFont) self.wordsText.setFont(lblFont)
# Assemble Layout # Assemble Layout
@@ -3051,9 +3085,9 @@ class GuiDocEditFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(palette) self.setPalette(palette)
self.statusText.setPalette(palette) self.statusText.setPalette(palette)
+44 -42
View File
@@ -37,8 +37,8 @@ from PyQt5.QtGui import (
QTextOption QTextOption
) )
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAction, qApp, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser, QAction, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser, QToolButton,
QToolButton, QWidget QWidget, qApp
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -59,6 +59,7 @@ class GuiDocViewer(QTextBrowser):
documentLoaded = pyqtSignal(str) documentLoaded = pyqtSignal(str)
loadDocumentTagRequest = pyqtSignal(str, Enum) loadDocumentTagRequest = pyqtSignal(str, Enum)
togglePanelVisibility = pyqtSignal() togglePanelVisibility = pyqtSignal()
requestProjectItemSelected = pyqtSignal(str, bool)
def __init__(self, mainGui: GuiMain) -> None: def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -75,8 +76,8 @@ class GuiDocViewer(QTextBrowser):
self.setMinimumWidth(CONFIG.pxInt(300)) self.setMinimumWidth(CONFIG.pxInt(300))
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
self.setOpenExternalLinks(False) self.setOpenExternalLinks(False)
self.setFocusPolicy(Qt.StrongFocus) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.Shape.NoFrame)
# Document Header and Footer # Document Header and Footer
self.docHeader = GuiDocViewHeader(self) self.docHeader = GuiDocViewHeader(self)
@@ -92,7 +93,7 @@ class GuiDocViewer(QTextBrowser):
self.installEventFilter(self.wheelEventFilter) self.installEventFilter(self.wheelEventFilter)
# Context Menu # Context Menu
self.setContextMenuPolicy(Qt.CustomContextMenu) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self._openContextMenu) self.customContextMenuRequested.connect(self._openContextMenu)
self.initViewer() self.initViewer()
@@ -148,14 +149,14 @@ class GuiDocViewer(QTextBrowser):
# Set the widget colours to match syntax theme # Set the widget colours to match syntax theme
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) mainPalette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack)) mainPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) mainPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack)) docPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) docPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -165,19 +166,19 @@ class GuiDocViewer(QTextBrowser):
self.document().setDocumentMargin(0) self.document().setDocumentMargin(0)
theOpt = QTextOption() theOpt = QTextOption()
if CONFIG.doJustify: if CONFIG.doJustify:
theOpt.setAlignment(Qt.AlignJustify) theOpt.setAlignment(Qt.AlignmentFlag.AlignJustify)
self.document().setDefaultTextOption(theOpt) self.document().setDefaultTextOption(theOpt)
# Scroll bars # Scroll bars
if CONFIG.hideVScroll: if CONFIG.hideVScroll:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
if CONFIG.hideHScroll: if CONFIG.hideHScroll:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
# Refresh the tab stops # Refresh the tab stops
self.setTabStopDistance(CONFIG.getTabWidth()) self.setTabStopDistance(CONFIG.getTabWidth())
@@ -196,7 +197,7 @@ class GuiDocViewer(QTextBrowser):
return False return False
logger.debug("Generating preview for item '%s'", tHandle) logger.debug("Generating preview for item '%s'", tHandle)
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
sPos = self.verticalScrollBar().value() sPos = self.verticalScrollBar().value()
aDoc = ToHtml(SHARED.project) aDoc = ToHtml(SHARED.project)
@@ -272,9 +273,9 @@ class GuiDocViewer(QTextBrowser):
elif action == nwDocAction.COPY: elif action == nwDocAction.COPY:
self.copy() self.copy()
elif action == nwDocAction.SEL_ALL: elif action == nwDocAction.SEL_ALL:
self._makeSelection(QTextCursor.Document) self._makeSelection(QTextCursor.SelectionType.Document)
elif action == nwDocAction.SEL_PARA: elif action == nwDocAction.SEL_PARA:
self._makeSelection(QTextCursor.BlockUnderCursor) self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor)
else: else:
logger.debug("Unknown or unsupported document action '%s'", str(action)) logger.debug("Unknown or unsupported document action '%s'", str(action))
return False return False
@@ -392,13 +393,13 @@ class GuiDocViewer(QTextBrowser):
mnuSelWord = QAction(self.tr("Select Word"), mnuContext) mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
mnuSelWord.triggered.connect( mnuSelWord.triggered.connect(
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, point) lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, point)
) )
mnuContext.addAction(mnuSelWord) mnuContext.addAction(mnuSelWord)
mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext) mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
mnuSelPara.triggered.connect( mnuSelPara.triggered.connect(
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, point) lambda: self._makePosSelection(QTextCursor.SelectionType.BlockUnderCursor, point)
) )
mnuContext.addAction(mnuSelPara) mnuContext.addAction(mnuSelPara)
@@ -419,9 +420,9 @@ class GuiDocViewer(QTextBrowser):
def mouseReleaseEvent(self, event: QMouseEvent) -> None: def mouseReleaseEvent(self, event: QMouseEvent) -> None:
"""Capture mouse click events on the document.""" """Capture mouse click events on the document."""
if event.button() == Qt.BackButton: if event.button() == Qt.MouseButton.BackButton:
self.navBackward() self.navBackward()
elif event.button() == Qt.ForwardButton: elif event.button() == Qt.MouseButton.ForwardButton:
self.navForward() self.navForward()
else: else:
super().mouseReleaseEvent(event) super().mouseReleaseEvent(event)
@@ -437,15 +438,15 @@ class GuiDocViewer(QTextBrowser):
cursor.clearSelection() cursor.clearSelection()
cursor.select(selType) cursor.select(selType)
if selType == QTextCursor.BlockUnderCursor: if selType == QTextCursor.SelectionType.BlockUnderCursor:
# This selection mode also selects the preceding paragraph # This selection mode also selects the preceding paragraph
# separator, which we want to avoid. # separator, which we want to avoid.
posS = cursor.selectionStart() posS = cursor.selectionStart()
posE = cursor.selectionEnd() posE = cursor.selectionEnd()
selTxt = cursor.selectedText() selTxt = cursor.selectedText()
if selTxt.startswith(nwUnicode.U_PSEP): if selTxt.startswith(nwUnicode.U_PSEP):
cursor.setPosition(posS+1, QTextCursor.MoveAnchor) cursor.setPosition(posS+1, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(posE, QTextCursor.KeepAnchor) cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
self.setTextCursor(cursor) self.setTextCursor(cursor)
@@ -663,7 +664,7 @@ class GuiDocViewHeader(QWidget):
self.docTitle.setMargin(0) self.docTitle.setMargin(0)
self.docTitle.setContentsMargins(0, 0, 0, 0) self.docTitle.setContentsMargins(0, 0, 0, 0)
self.docTitle.setAutoFillBackground(True) self.docTitle.setAutoFillBackground(True)
self.docTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) self.docTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
self.docTitle.setFixedHeight(fPx) self.docTitle.setFixedHeight(fPx)
lblFont = self.docTitle.font() lblFont = self.docTitle.font()
@@ -675,7 +676,7 @@ class GuiDocViewHeader(QWidget):
self.backButton.setContentsMargins(0, 0, 0, 0) self.backButton.setContentsMargins(0, 0, 0, 0)
self.backButton.setIconSize(QSize(fPx, fPx)) self.backButton.setIconSize(QSize(fPx, fPx))
self.backButton.setFixedSize(fPx, fPx) self.backButton.setFixedSize(fPx, fPx)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.backButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.backButton.setVisible(False) self.backButton.setVisible(False)
self.backButton.setToolTip(self.tr("Go Backward")) self.backButton.setToolTip(self.tr("Go Backward"))
self.backButton.clicked.connect(self.docViewer.navBackward) self.backButton.clicked.connect(self.docViewer.navBackward)
@@ -684,7 +685,7 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.setContentsMargins(0, 0, 0, 0) self.forwardButton.setContentsMargins(0, 0, 0, 0)
self.forwardButton.setIconSize(QSize(fPx, fPx)) self.forwardButton.setIconSize(QSize(fPx, fPx))
self.forwardButton.setFixedSize(fPx, fPx) self.forwardButton.setFixedSize(fPx, fPx)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.forwardButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.forwardButton.setVisible(False) self.forwardButton.setVisible(False)
self.forwardButton.setToolTip(self.tr("Go Forward")) self.forwardButton.setToolTip(self.tr("Go Forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward) self.forwardButton.clicked.connect(self.docViewer.navForward)
@@ -693,7 +694,7 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setContentsMargins(0, 0, 0, 0) self.refreshButton.setContentsMargins(0, 0, 0, 0)
self.refreshButton.setIconSize(QSize(fPx, fPx)) self.refreshButton.setIconSize(QSize(fPx, fPx))
self.refreshButton.setFixedSize(fPx, fPx) self.refreshButton.setFixedSize(fPx, fPx)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.refreshButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.refreshButton.setVisible(False) self.refreshButton.setVisible(False)
self.refreshButton.setToolTip(self.tr("Reload")) self.refreshButton.setToolTip(self.tr("Reload"))
self.refreshButton.clicked.connect(self._refreshDocument) self.refreshButton.clicked.connect(self._refreshDocument)
@@ -702,7 +703,7 @@ class GuiDocViewHeader(QWidget):
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx)) self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close")) self.closeButton.setToolTip(self.tr("Close"))
self.closeButton.clicked.connect(self._closeDocument) self.closeButton.clicked.connect(self._closeDocument)
@@ -761,9 +762,9 @@ class GuiDocViewHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(palette) self.setPalette(palette)
self.docTitle.setPalette(palette) self.docTitle.setPalette(palette)
return return
@@ -834,7 +835,8 @@ class GuiDocViewHeader(QWidget):
"""Capture a click on the title and ensure that the item is """Capture a click on the title and ensure that the item is
selected in the project tree. selected in the project tree.
""" """
self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True) if event.button() == Qt.MouseButton.LeftButton:
self.docViewer.requestProjectItemSelected.emit(self._docHandle, True)
return return
# END Class GuiDocViewHeader # END Class GuiDocViewHeader
@@ -868,7 +870,7 @@ class GuiDocViewFooter(QWidget):
# Show/Hide Details # Show/Hide Details
self.showHide = QToolButton(self) self.showHide = QToolButton(self)
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showHide.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.showHide.setIconSize(QSize(fPx, fPx)) self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(lambda: self.docViewer.togglePanelVisibility.emit()) self.showHide.clicked.connect(lambda: self.docViewer.togglePanelVisibility.emit())
@@ -878,7 +880,7 @@ class GuiDocViewFooter(QWidget):
self.showComments = QToolButton(self) self.showComments = QToolButton(self)
self.showComments.setCheckable(True) self.showComments.setCheckable(True)
self.showComments.setChecked(CONFIG.viewComments) self.showComments.setChecked(CONFIG.viewComments)
self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showComments.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.showComments.setIconSize(QSize(fPx, fPx)) self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx)) self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments) self.showComments.toggled.connect(self._doToggleComments)
@@ -888,7 +890,7 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis = QToolButton(self) self.showSynopsis = QToolButton(self)
self.showSynopsis.setCheckable(True) self.showSynopsis.setCheckable(True)
self.showSynopsis.setChecked(CONFIG.viewSynopsis) self.showSynopsis.setChecked(CONFIG.viewSynopsis)
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showSynopsis.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
self.showSynopsis.setIconSize(QSize(fPx, fPx)) self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx)) self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis) self.showSynopsis.toggled.connect(self._doToggleSynopsis)
@@ -902,7 +904,7 @@ class GuiDocViewFooter(QWidget):
self.lblComments.setContentsMargins(0, 0, 0, 0) self.lblComments.setContentsMargins(0, 0, 0, 0)
self.lblComments.setAutoFillBackground(True) self.lblComments.setAutoFillBackground(True)
self.lblComments.setFixedHeight(fPx) self.lblComments.setFixedHeight(fPx)
self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblComments.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.lblSynopsis = QLabel(self.tr("Synopsis")) self.lblSynopsis = QLabel(self.tr("Synopsis"))
self.lblSynopsis.setBuddy(self.showSynopsis) self.lblSynopsis.setBuddy(self.showSynopsis)
@@ -911,7 +913,7 @@ class GuiDocViewFooter(QWidget):
self.lblSynopsis.setContentsMargins(0, 0, 0, 0) self.lblSynopsis.setContentsMargins(0, 0, 0, 0)
self.lblSynopsis.setAutoFillBackground(True) self.lblSynopsis.setAutoFillBackground(True)
self.lblSynopsis.setFixedHeight(fPx) self.lblSynopsis.setFixedHeight(fPx)
self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblSynopsis.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize) lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
@@ -977,9 +979,9 @@ class GuiDocViewFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack)) palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText)) palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
self.setPalette(palette) self.setPalette(palette)
self.lblComments.setPalette(palette) self.lblComments.setPalette(palette)
self.lblSynopsis.setPalette(palette) self.lblSynopsis.setPalette(palette)
+22 -17
View File
@@ -138,7 +138,6 @@ class GuiProjectView(QWidget):
self.emptyTrash = self.projTree.emptyTrash self.emptyTrash = self.projTree.emptyTrash
self.requestDeleteItem = self.projTree.requestDeleteItem self.requestDeleteItem = self.projTree.requestDeleteItem
self.getSelectedHandle = self.projTree.getSelectedHandle self.getSelectedHandle = self.projTree.getSelectedHandle
self.setSelectedHandle = self.projTree.setSelectedHandle
self.changedSince = self.projTree.changedSince self.changedSince = self.projTree.changedSince
self.createNewNote = self.projTree.createNewNote self.createNewNote = self.projTree.createNewNote
@@ -191,17 +190,26 @@ class GuiProjectView(QWidget):
"""Check if the project tree has focus.""" """Check if the project tree has focus."""
return self.projTree.hasFocus() return self.projTree.hasFocus()
def renameTreeItem(self, tHandle: str | None = None) -> bool: ##
# Public Slots
##
@pyqtSlot(str, str)
def renameTreeItem(self, tHandle: str | None = None, name: str = "") -> None:
"""External request to rename an item or the currently selected """External request to rename an item or the currently selected
item. This is triggered by the global menu or keyboard shortcut. item. This is triggered by the global menu or keyboard shortcut.
""" """
if tHandle is None: if tHandle is None:
tHandle = self.projTree.getSelectedHandle() tHandle = self.projTree.getSelectedHandle()
return self.projTree.renameTreeItem(tHandle) if tHandle else False if tHandle:
self.projTree.renameTreeItem(tHandle, name=name)
return
## @pyqtSlot(str, bool)
# Public Slots def setSelectedHandle(self, tHandle: str, doScroll: bool = False) -> None:
## """Select an item and optionally scroll it into view."""
self.projTree.setSelectedHandle(tHandle, doScroll=doScroll)
return
@pyqtSlot(str) @pyqtSlot(str)
def updateItemValues(self, tHandle: str) -> None: def updateItemValues(self, tHandle: str) -> None:
@@ -761,19 +769,16 @@ class GuiProjectTree(QTreeWidget):
self.setCurrentItem(tItem.child(0)) self.setCurrentItem(tItem.child(0))
return return
def renameTreeItem(self, tHandle: str) -> bool: def renameTreeItem(self, tHandle: str, name: str = "") -> None:
"""Open a dialog to edit the label of an item.""" """Open a dialog to edit the label of an item."""
tItem = SHARED.project.tree[tHandle] tItem = SHARED.project.tree[tHandle]
if tItem is None: if tItem:
return False newLabel, dlgOk = GuiEditLabel.getLabel(self, text=name or tItem.itemName)
if dlgOk:
newLabel, dlgOk = GuiEditLabel.getLabel(self, text=tItem.itemName) tItem.setName(newLabel)
if dlgOk: self.setTreeItemValues(tHandle)
tItem.setName(newLabel) self._alertTreeChange(tHandle, flush=False)
self.setTreeItemValues(tHandle) return
self._alertTreeChange(tHandle, flush=False)
return True
def saveTreeOrder(self) -> None: def saveTreeOrder(self) -> None:
"""Build a list of the items in the project tree and send them """Build a list of the items in the project tree and send them
+3
View File
@@ -284,10 +284,13 @@ class GuiMain(QMainWindow):
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState) self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
self.docEditor.closeDocumentRequest.connect(self.closeDocEditor) self.docEditor.closeDocumentRequest.connect(self.closeDocEditor)
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode) self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle) self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
self.docViewer.loadDocumentTagRequest.connect(self._followTag) self.docViewer.loadDocumentTagRequest.connect(self._followTag)
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility) self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
self.docViewer.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
self.docViewerPanel.loadDocumentTagRequest.connect(self._followTag) self.docViewerPanel.loadDocumentTagRequest.connect(self._followTag)
self.docViewerPanel.openDocumentRequest.connect(self._openDocument) self.docViewerPanel.openDocumentRequest.connect(self._openDocument)
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import time import time
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import uuid import uuid
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import uuid import uuid
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import copy import copy
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+3 -1
View File
@@ -18,8 +18,8 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtWidgets import QMessageBox
import pytest import pytest
from shutil import copyfile from shutil import copyfile
@@ -28,6 +28,8 @@ from zipfile import ZipFile
from mocked import causeOSError from mocked import causeOSError
from tools import C, cmpFiles, buildTestProject, XML_IGNORE from tools import C, cmpFiles, buildTestProject, XML_IGNORE
from PyQt5.QtWidgets import QMessageBox
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemClass from novelwriter.enum import nwItemClass
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
import zipfile import zipfile
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+2 -1
View File
@@ -18,11 +18,12 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtCore import QEvent, QObject, QPoint, Qt
import pytest import pytest
from PyQt5.QtGui import QKeyEvent, QWheelEvent from PyQt5.QtGui import QKeyEvent, QWheelEvent
from PyQt5.QtCore import QEvent, QObject, QPoint, Qt
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
from novelwriter.extensions.wheeleventfilter import WheelEventFilter from novelwriter.extensions.wheeleventfilter import WheelEventFilter
+146 -2
View File
@@ -18,21 +18,23 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
from tools import C, buildTestProject from tools import C, buildTestProject
from mocked import causeOSError from mocked import causeOSError
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption from PyQt5.QtGui import QClipboard, QTextBlock, QTextCursor, QTextOption
from PyQt5.QtCore import QThreadPool, Qt from PyQt5.QtCore import QThreadPool, Qt
from PyQt5.QtWidgets import QAction, qApp from PyQt5.QtWidgets import QAction, QMenu, qApp
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout, nwTrinary, nwWidget from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout, nwTrinary, nwWidget
from novelwriter.constants import nwKeyWords, nwUnicode from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.core.index import countWords from novelwriter.core.index import countWords
from novelwriter.gui.doceditor import GuiDocEditor, GuiDocToolBar from novelwriter.gui.doceditor import GuiDocEditor, GuiDocToolBar
from novelwriter.dialogs.editlabel import GuiEditLabel
KEY_DELAY = 1 KEY_DELAY = 1
@@ -209,6 +211,148 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
# END Test testGuiEditor_MetaData # END Test testGuiEditor_MetaData
@pytest.mark.gui
def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
"""Test the editor context menu."""
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
buildTestProject(nwGUI, projPath)
assert nwGUI.openDocument(C.hSceneDoc) is True
docEditor = nwGUI.docEditor
sceneItem = SHARED.project.tree[C.hSceneDoc]
assert sceneItem is not None
def getMenuForPos(pos: int, select: bool = False) -> QMenu | None:
nonlocal docEditor
cursor = docEditor.textCursor()
cursor.setPosition(pos)
if select:
cursor.select(QTextCursor.SelectionType.WordUnderCursor)
docEditor.setTextCursor(cursor)
docEditor._openContextMenu(docEditor.cursorRect().center())
for obj in docEditor.children():
if isinstance(obj, QMenu) and obj.objectName() == "ContextMenu":
return obj
return None
docText = (
"### A Scene\n\n"
"@pov: Jane\n"
"Some text ..."
)
docEditor.setPlainText(docText)
assert docEditor.getText() == docText
# Rename Item from Heading
ctxMenu = getMenuForPos(1)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Set as Document Name", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda a, text: (text, True))
assert sceneItem.itemName == "New Scene"
ctxMenu.actions()[0].trigger()
assert sceneItem.itemName == "A Scene"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Create Character
ctxMenu = getMenuForPos(21)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Create Note for Tag", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[0].trigger()
janeItem = SHARED.project.tree["0000000000010"]
assert janeItem is not None
assert janeItem.itemName == "Jane"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Follow Character Tag
ctxMenu = getMenuForPos(21)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Follow Tag", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[0].trigger()
assert nwGUI.docViewer.docHandle == "0000000000010"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select Word
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[3].trigger()
assert docEditor.textCursor().selectedText() == "text"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select Paragraph
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[4].trigger()
assert docEditor.textCursor().selectedText() == "Some text ..."
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select All
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[2].trigger()
assert docEditor.textCursor().selectedText() == docEditor.document().toRawText()
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Copy Text
ctxMenu = getMenuForPos(31, True)
assert ctxMenu is not None
assert docEditor.textCursor().selectedText() == "text"
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Cut", "Copy", "Paste", "Select All", "Select Word", "Select Paragraph"
]
qApp.clipboard().clear()
ctxMenu.actions()[1].trigger()
assert qApp.clipboard().text(QClipboard.Mode.Clipboard) == "text"
# Cut Text
qApp.clipboard().clear()
ctxMenu.actions()[0].trigger()
assert qApp.clipboard().text(QClipboard.Mode.Clipboard) == "text"
assert "text" not in docEditor.getText()
# Paste Text
ctxMenu.actions()[2].trigger()
assert docEditor.getText() == docText
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# qtbot.stop()
# END Test testGuiEditor_ContextMenu
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd): def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
"""Test the document actions. This is not an extensive test of the """Test the document actions. This is not an extensive test of the
+8 -4
View File
@@ -18,13 +18,14 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
from mocked import causeException from mocked import causeException
from PyQt5.QtGui import QTextCursor from PyQt5.QtGui import QMouseEvent, QTextCursor
from PyQt5.QtCore import Qt, QUrl from PyQt5.QtCore import QEvent, QPoint, Qt, QUrl
from PyQt5.QtWidgets import QMenu, qApp, QAction from PyQt5.QtWidgets import QMenu, qApp, QAction
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -58,7 +59,10 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
assert nwGUI.projView.projTree.getSelectedHandle() is None assert nwGUI.projView.projTree.getSelectedHandle() is None
# Re-select via header click # Re-select via header click
docViewer.docHeader.mousePressEvent(None) # type: ignore button = Qt.MouseButton.LeftButton
modifier = Qt.KeyboardModifier.NoModifier
event = QMouseEvent(QEvent.MouseButtonPress, QPoint(), button, button, modifier)
docViewer.docHeader.mousePressEvent(event)
assert nwGUI.projView.projTree.getSelectedHandle() == "88243afbe5ed8" assert nwGUI.projView.projTree.getSelectedHandle() == "88243afbe5ed8"
# Reload the text # Reload the text
@@ -127,7 +131,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
assert docViewer.docAction(nwDocAction.COPY) is False assert docViewer.docAction(nwDocAction.COPY) is False
# Open again via menu # Open again via menu
assert nwGUI.projView.setSelectedHandle("88243afbe5ed8") assert nwGUI.projView.projTree.setSelectedHandle("88243afbe5ed8")
nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger) nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger)
# Open context menu # Open context menu
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
+3 -2
View File
@@ -18,15 +18,16 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
from tools import C, writeFile, buildTestProject
from PyQt5.QtGui import QTextCursor, QTextBlock from PyQt5.QtGui import QTextCursor, QTextBlock
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
from tools import C, writeFile, buildTestProject
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert from novelwriter.enum import nwDocAction, nwDocInsert
from novelwriter.constants import nwKeyWords, nwUnicode from novelwriter.constants import nwKeyWords, nwUnicode
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import time import time
import pytest import pytest
+3 -6
View File
@@ -156,12 +156,9 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
# Rename plot folder # Rename plot folder
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("Stuff", True)) mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("Stuff", True))
projTree.renameTreeItem(C.hPlotRoot) is True projTree.renameTreeItem(C.hPlotRoot)
assert project.tree[C.hPlotRoot].itemName == "Stuff" # type: ignore assert project.tree[C.hPlotRoot].itemName == "Stuff" # type: ignore
# Rename invalid folder
projTree.renameTreeItem("0000000000000") is False
# Other Checks # Other Checks
# ============ # ============
@@ -1095,12 +1092,12 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("FooBar", True)) mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("FooBar", True))
projTree.clearSelection() projTree.clearSelection()
assert SHARED.project.tree[C.hChapterDoc].itemName == "New Chapter" # type: ignore assert SHARED.project.tree[C.hChapterDoc].itemName == "New Chapter" # type: ignore
assert projView.renameTreeItem(C.hChapterDoc) is True projView.renameTreeItem(C.hChapterDoc)
assert SHARED.project.tree[C.hChapterDoc].itemName == "FooBar" # type: ignore assert SHARED.project.tree[C.hChapterDoc].itemName == "FooBar" # type: ignore
projTree.setSelectedHandle(C.hSceneDoc) projTree.setSelectedHandle(C.hSceneDoc)
assert SHARED.project.tree[C.hSceneDoc].itemName == "New Scene" # type: ignore assert SHARED.project.tree[C.hSceneDoc].itemName == "New Scene" # type: ignore
assert projView.renameTreeItem() is True projView.renameTreeItem()
assert SHARED.project.tree[C.hSceneDoc].itemName == "FooBar" # type: ignore assert SHARED.project.tree[C.hSceneDoc].itemName == "FooBar" # type: ignore
# Check Crash Resistance # Check Crash Resistance
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import time import time
import pytest import pytest
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
import enchant import enchant
+1
View File
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
+3 -2
View File
@@ -18,9 +18,8 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices
import pytest import pytest
from pathlib import Path from pathlib import Path
@@ -28,6 +27,8 @@ from pytestqt.qtbot import QtBot
from tools import buildTestProject from tools import buildTestProject
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QDialogButtonBox, QFileDialog, QListWidgetItem, QMessageBox from PyQt5.QtWidgets import QDialogButtonBox, QFileDialog, QListWidgetItem, QMessageBox
from novelwriter.enum import nwBuildFmt from novelwriter.enum import nwBuildFmt
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import sys import sys
import pytest import pytest
@@ -18,6 +18,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import pytest import pytest