Improve persistence of document search button state (#1795)

This commit is contained in:
Veronica Berglyd Olsen
2024-04-04 23:33:24 +02:00
committed by GitHub
3 changed files with 66 additions and 86 deletions
+53 -73
View File
@@ -214,6 +214,10 @@ class GuiDocEditor(QPlainTextEdit):
self.wheelEventFilter = WheelEventFilter(self) self.wheelEventFilter = WheelEventFilter(self)
self.installEventFilter(self.wheelEventFilter) self.installEventFilter(self.wheelEventFilter)
# Function Mapping
self.closeSearch = self.docSearch.closeSearch
self.searchVisible = self.docSearch.isVisible
# Finalise # Finalise
self.updateSyntaxColours() self.updateSyntaxColours()
self.initEditor() self.initEditor()
@@ -925,11 +929,6 @@ class GuiDocEditor(QPlainTextEdit):
return True return True
def closeSearch(self) -> bool:
"""Close the search box."""
self.docSearch.closeSearch()
return self.docSearch.isVisible()
## ##
# Document Events and Maintenance # Document Events and Maintenance
## ##
@@ -985,7 +984,7 @@ class GuiDocEditor(QPlainTextEdit):
if self.hasFocus(): if self.hasFocus():
return False return False
elif self.docSearch.isVisible(): elif self.docSearch.isVisible():
return self.docSearch.cycleFocus(next) return self.docSearch.cycleFocus()
return True return True
def mouseReleaseEvent(self, event: QMouseEvent) -> None: def mouseReleaseEvent(self, event: QMouseEvent) -> None:
@@ -1036,8 +1035,8 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot() @pyqtSlot()
def toggleSearch(self) -> None: def toggleSearch(self) -> None:
"""Toggle the visibility of the search box.""" """Toggle the visibility of the search box."""
if self.docSearch.isVisible(): if self.searchVisible():
self.docSearch.closeSearch() self.closeSearch()
else: else:
self.beginSearch() self.beginSearch()
return return
@@ -1319,9 +1318,9 @@ class GuiDocEditor(QPlainTextEdit):
if len(resS) == 0 and self._docHandle: if len(resS) == 0 and self._docHandle:
self.docSearch.setResultCount(0, 0) self.docSearch.setResultCount(0, 0)
self._lastFind = None self._lastFind = None
if self.docSearch.doNextFile and not goBack: if CONFIG.searchNextFile and not goBack:
self.mainGui.openNextDocument( self.mainGui.openNextDocument(
self._docHandle, wrapAround=self.docSearch.doLoop self._docHandle, wrapAround=CONFIG.searchLoop
) )
self.beginSearch() self.beginSearch()
self.setFocus() self.setFocus()
@@ -1330,7 +1329,7 @@ class GuiDocEditor(QPlainTextEdit):
cursor = self.textCursor() cursor = self.textCursor()
resIdx = bisect.bisect_left(resS, cursor.position()) resIdx = bisect.bisect_left(resS, cursor.position())
doLoop = self.docSearch.doLoop doLoop = CONFIG.searchLoop
maxIdx = len(resS) - 1 maxIdx = len(resS) - 1
if goBack: if goBack:
@@ -1340,9 +1339,9 @@ class GuiDocEditor(QPlainTextEdit):
resIdx = maxIdx if doLoop else 0 resIdx = maxIdx if doLoop else 0
if resIdx > maxIdx and self._docHandle: if resIdx > maxIdx and self._docHandle:
if self.docSearch.doNextFile and not goBack: if CONFIG.searchNextFile and not goBack:
self.mainGui.openNextDocument( self.mainGui.openNextDocument(
self._docHandle, wrapAround=self.docSearch.doLoop self._docHandle, wrapAround=CONFIG.searchLoop
) )
self.beginSearch() self.beginSearch()
self.setFocus() self.setFocus()
@@ -1375,9 +1374,9 @@ class GuiDocEditor(QPlainTextEdit):
origB = cursor.position() origB = cursor.position()
findOpt = QTextDocument.FindFlag(0) findOpt = QTextDocument.FindFlag(0)
if self.docSearch.isCaseSense: if CONFIG.searchCase:
findOpt |= QTextDocument.FindFlag.FindCaseSensitively findOpt |= QTextDocument.FindFlag.FindCaseSensitively
if self.docSearch.isWholeWord: if CONFIG.searchWord:
findOpt |= QTextDocument.FindFlag.FindWholeWords findOpt |= QTextDocument.FindFlag.FindWholeWords
searchFor = self.docSearch.getSearchObject() searchFor = self.docSearch.getSearchObject()
@@ -1446,7 +1445,7 @@ class GuiDocEditor(QPlainTextEdit):
searchFor = self.docSearch.searchText searchFor = self.docSearch.searchText
replWith = self.docSearch.replaceText replWith = self.docSearch.replaceText
if self.docSearch.doMatchCap: if CONFIG.searchMatchCap:
replWith = transferCase(cursor.selectedText(), replWith) replWith = transferCase(cursor.selectedText(), replWith)
# Make sure the selected text was selected by an actual find # Make sure the selected text was selected by an actual find
@@ -2391,14 +2390,6 @@ class GuiDocEditSearch(QFrame):
self.docEditor = docEditor self.docEditor = docEditor
self.repVisible = False
self.isCaseSense = CONFIG.searchCase
self.isWholeWord = CONFIG.searchWord
self.isRegEx = CONFIG.searchRegEx
self.doLoop = CONFIG.searchLoop
self.doNextFile = CONFIG.searchNextFile
self.doMatchCap = CONFIG.searchMatchCap
iSz = SHARED.theme.baseIconSize iSz = SHARED.theme.baseIconSize
mPx = CONFIG.pxInt(6) mPx = CONFIG.pxInt(6)
@@ -2440,31 +2431,31 @@ class GuiDocEditSearch(QFrame):
self.toggleCase = QAction(self.tr("Case Sensitive"), self) self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setCheckable(True) self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense) self.toggleCase.setChecked(CONFIG.searchCase)
self.toggleCase.toggled.connect(self._doToggleCase) self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase) self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction(self.tr("Whole Words Only"), self) self.toggleWord = QAction(self.tr("Whole Words Only"), self)
self.toggleWord.setCheckable(True) self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord) self.toggleWord.setChecked(CONFIG.searchWord)
self.toggleWord.toggled.connect(self._doToggleWord) self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord) self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self) self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setCheckable(True) self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx) self.toggleRegEx.setChecked(CONFIG.searchRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx) self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx) self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction(self.tr("Loop Search"), self) self.toggleLoop = QAction(self.tr("Loop Search"), self)
self.toggleLoop.setCheckable(True) self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop) self.toggleLoop.setChecked(CONFIG.searchLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop) self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop) self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction(self.tr("Search Next File"), self) self.toggleProject = QAction(self.tr("Search Next File"), self)
self.toggleProject.setCheckable(True) self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile) self.toggleProject.setChecked(CONFIG.searchNextFile)
self.toggleProject.toggled.connect(self._doToggleProject) self.toggleProject.toggled.connect(self._doToggleProject)
self.searchOpt.addAction(self.toggleProject) self.searchOpt.addAction(self.toggleProject)
@@ -2472,14 +2463,14 @@ class GuiDocEditSearch(QFrame):
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self) self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
self.toggleMatchCap.setCheckable(True) self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap) self.toggleMatchCap.setChecked(CONFIG.searchMatchCap)
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap) self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
self.searchOpt.addAction(self.toggleMatchCap) self.searchOpt.addAction(self.toggleMatchCap)
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.cancelSearch = QAction(self.tr("Close Search"), self) self.cancelSearch = QAction(self.tr("Close Search"), self)
self.cancelSearch.triggered.connect(self._doClose) self.cancelSearch.triggered.connect(self.closeSearch)
self.searchOpt.addAction(self.cancelSearch) self.searchOpt.addAction(self.cancelSearch)
# Buttons # Buttons
@@ -2550,20 +2541,20 @@ class GuiDocEditSearch(QFrame):
expression object. expression object.
""" """
text = self.searchBox.text() text = self.searchBox.text()
if self.isRegEx: if CONFIG.searchRegEx:
# Using the Unicode-capable QRegularExpression class was # Using the Unicode-capable QRegularExpression class was
# 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.PatternOption.UseUnicodePropertiesOption rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
if not self.isCaseSense: if not CONFIG.searchCase:
rxOpt |= QRegularExpression.PatternOption.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 CONFIG.searchCase:
rxOpt = Qt.CaseSensitivity.CaseSensitive rxOpt = Qt.CaseSensitivity.CaseSensitive
else: else:
rxOpt = Qt.CaseSensitivity.CaseInsensitive rxOpt = Qt.CaseSensitivity.CaseInsensitive
@@ -2587,7 +2578,7 @@ class GuiDocEditSearch(QFrame):
self.searchBox.setText(text) self.searchBox.setText(text)
self.searchBox.setFocus() self.searchBox.setFocus()
self.searchBox.selectAll() self.searchBox.selectAll()
if self.isRegEx: if CONFIG.searchRegEx:
self._alertSearchValid(True) self._alertSearchValid(True)
return return
@@ -2657,49 +2648,39 @@ class GuiDocEditSearch(QFrame):
return return
def closeSearch(self) -> None: def cycleFocus(self) -> bool:
"""Close the search box."""
CONFIG.searchCase = self.isCaseSense
CONFIG.searchWord = self.isWholeWord
CONFIG.searchRegEx = self.isRegEx
CONFIG.searchLoop = self.doLoop
CONFIG.searchNextFile = self.doNextFile
CONFIG.searchMatchCap = self.doMatchCap
self.showReplace.setChecked(False)
self.setVisible(False)
self.docEditor.updateDocMargins()
self.docEditor.setFocus()
return
def cycleFocus(self, next: bool) -> bool:
"""The tab key just alternates focus between the two input """The tab key just alternates focus between the two input
boxes, if the replace box is visible. boxes, if the replace box is visible.
""" """
if self.replaceBox.isVisible(): if self.searchBox.hasFocus():
if self.searchBox.hasFocus(): self.replaceBox.setFocus()
self.replaceBox.setFocus() return True
return True elif self.replaceBox.hasFocus():
elif self.replaceBox.hasFocus(): self.searchBox.setFocus()
self.searchBox.setFocus() return True
return True
return False return False
def anyFocus(self) -> bool: def anyFocus(self) -> bool:
"""Return True if any of the input boxes have focus.""" """Return True if any of the input boxes have focus."""
return self.searchBox.hasFocus() | self.replaceBox.hasFocus() return self.searchBox.hasFocus() or self.replaceBox.hasFocus()
##
# Public Slots
##
@pyqtSlot()
def closeSearch(self) -> None:
"""Close the search box."""
self.showReplace.setChecked(False)
self.setVisible(False)
self.docEditor.updateDocMargins()
self.docEditor.setFocus()
return
## ##
# Private Slots # Private Slots
## ##
@pyqtSlot()
def _doClose(self) -> None:
"""Hide the search/replace bar."""
self.closeSearch()
return
@pyqtSlot() @pyqtSlot()
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."""
@@ -2717,7 +2698,6 @@ class GuiDocEditSearch(QFrame):
"""Toggle the show/hide of the replace box.""" """Toggle the show/hide of the replace box."""
self.replaceBox.setVisible(state) self.replaceBox.setVisible(state)
self.replaceButton.setVisible(state) self.replaceButton.setVisible(state)
self.repVisible = state
self.adjustSize() self.adjustSize()
self.docEditor.updateDocMargins() self.docEditor.updateDocMargins()
return return
@@ -2725,37 +2705,37 @@ class GuiDocEditSearch(QFrame):
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleCase(self, state: bool) -> None: def _doToggleCase(self, state: bool) -> None:
"""Enable/disable case sensitive mode.""" """Enable/disable case sensitive mode."""
self.isCaseSense = state CONFIG.searchCase = state
return return
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleWord(self, state: bool) -> None: def _doToggleWord(self, state: bool) -> None:
"""Enable/disable whole word search mode.""" """Enable/disable whole word search mode."""
self.isWholeWord = state CONFIG.searchWord = state
return return
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleRegEx(self, state: bool) -> None: def _doToggleRegEx(self, state: bool) -> None:
"""Enable/disable regular expression search mode.""" """Enable/disable regular expression search mode."""
self.isRegEx = state CONFIG.searchRegEx = state
return return
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleLoop(self, state: bool) -> None: def _doToggleLoop(self, state: bool) -> None:
"""Enable/disable looping the search.""" """Enable/disable looping the search."""
self.doLoop = state CONFIG.searchLoop = state
return return
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleProject(self, state: bool) -> None: def _doToggleProject(self, state: bool) -> None:
"""Enable/disable continuing search in next project file.""" """Enable/disable continuing search in next project file."""
self.doNextFile = state CONFIG.searchNextFile = state
return return
@pyqtSlot(bool) @pyqtSlot(bool)
def _doToggleMatchCap(self, state: bool) -> None: def _doToggleMatchCap(self, state: bool) -> None:
"""Enable/disable preserving capitalisation when replacing.""" """Enable/disable preserving capitalisation when replacing."""
self.doMatchCap = state CONFIG.searchMatchCap = state
return return
## ##
+1 -1
View File
@@ -1270,7 +1270,7 @@ class GuiMain(QMainWindow):
@pyqtSlot() @pyqtSlot()
def _keyPressEscape(self) -> None: def _keyPressEscape(self) -> None:
"""Process an escape keypress in the main window.""" """Process an escape keypress in the main window."""
if self.docEditor.docSearch.isVisible(): if self.docEditor.searchVisible():
self.docEditor.closeSearch() self.docEditor.closeSearch()
elif SHARED.focusMode: elif SHARED.focusMode:
SHARED.setFocusMode(False) SHARED.setFocusMode(False)
+12 -12
View File
@@ -1794,7 +1794,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Activate loop search # Activate loop search
docSearch.toggleLoop.activate(QAction.Trigger) docSearch.toggleLoop.activate(QAction.Trigger)
assert docSearch.toggleLoop.isChecked() assert docSearch.toggleLoop.isChecked()
assert docSearch.doLoop is True assert CONFIG.searchLoop is True
# Find next by menu Search > Find Next # Find next by menu Search > Find Next
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
@@ -1819,7 +1819,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Enable RegEx search # Enable RegEx search
docSearch.toggleRegEx.activate(QAction.Trigger) docSearch.toggleRegEx.activate(QAction.Trigger)
assert docSearch.toggleRegEx.isChecked() assert docSearch.toggleRegEx.isChecked()
assert docSearch.isRegEx is True assert CONFIG.searchRegEx is True
# Set invalid RegEx # Set invalid RegEx
docEditor.setCursorPosition(0) docEditor.setCursorPosition(0)
@@ -1848,7 +1848,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Make RegEx case sensitive # Make RegEx case sensitive
docSearch.toggleCase.activate(QAction.Trigger) docSearch.toggleCase.activate(QAction.Trigger)
assert docSearch.toggleCase.isChecked() assert docSearch.toggleCase.isChecked()
assert docSearch.isCaseSense is True assert CONFIG.searchCase is True
# Find next/prev (one result) # Find next/prev (one result)
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
@@ -1865,12 +1865,12 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Disable RegEx case sensitive # Disable RegEx case sensitive
docSearch.toggleCase.activate(QAction.Trigger) docSearch.toggleCase.activate(QAction.Trigger)
assert docSearch.toggleCase.isChecked() is False assert docSearch.toggleCase.isChecked() is False
assert docSearch.isCaseSense is False assert CONFIG.searchCase is False
# Toggle replace preserve case # Toggle replace preserve case
docSearch.toggleMatchCap.activate(QAction.Trigger) docSearch.toggleMatchCap.activate(QAction.Trigger)
assert docSearch.toggleMatchCap.isChecked() assert docSearch.toggleMatchCap.isChecked()
assert docSearch.doMatchCap is True assert CONFIG.searchMatchCap is True
# Replace "Sus" with "Foo" via menu # Replace "Sus" with "Foo" via menu
docEditor.setCursorPosition(605) docEditor.setCursorPosition(605)
@@ -1898,7 +1898,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Disable RegEx search # Disable RegEx search
docSearch.toggleRegEx.activate(QAction.Trigger) docSearch.toggleRegEx.activate(QAction.Trigger)
assert not docSearch.toggleRegEx.isChecked() assert not docSearch.toggleRegEx.isChecked()
assert docSearch.isRegEx is False assert CONFIG.searchRegEx is False
# Close search and select "est" again # Close search and select "est" again
docSearch.cancelSearch.activate(QAction.Trigger) docSearch.cancelSearch.activate(QAction.Trigger)
@@ -1915,7 +1915,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Enable full word search # Enable full word search
docSearch.toggleWord.activate(QAction.Trigger) docSearch.toggleWord.activate(QAction.Trigger)
assert docSearch.toggleWord.isChecked() assert docSearch.toggleWord.isChecked()
assert docSearch.isWholeWord is True assert CONFIG.searchWord is True
# Only one match # Only one match
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
@@ -1926,7 +1926,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
# Enable next doc search # Enable next doc search
docSearch.toggleProject.activate(QAction.Trigger) docSearch.toggleProject.activate(QAction.Trigger)
assert docSearch.toggleProject.isChecked() assert docSearch.toggleProject.isChecked()
assert docSearch.doNextFile is True assert CONFIG.searchNextFile is True
# Next match # Next match
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
@@ -1937,7 +1937,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
assert abs(docEditor.getCursorPosition() - 1127) < 3 assert abs(docEditor.getCursorPosition() - 1127) < 3
# Next doc, no match # Next doc, no match
assert docSearch.doNextFile is True assert CONFIG.searchNextFile is True
docSearch.setSearchText("abcdef") docSearch.setSearchText("abcdef")
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
assert docEditor.docHandle != "2426c6f0ca922" assert docEditor.docHandle != "2426c6f0ca922"
@@ -1956,21 +1956,21 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
mp.setattr(docSearch.searchBox, "hasFocus", lambda: False) mp.setattr(docSearch.searchBox, "hasFocus", lambda: False)
mp.setattr(docSearch.replaceBox, "hasFocus", lambda: False) mp.setattr(docSearch.replaceBox, "hasFocus", lambda: False)
assert docEditor.focusNextPrevChild(True) is False assert docEditor.focusNextPrevChild(True) is False
assert docSearch.cycleFocus(True) is False assert docSearch.cycleFocus() is False
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(docEditor, "hasFocus", lambda: False) mp.setattr(docEditor, "hasFocus", lambda: False)
mp.setattr(docSearch.searchBox, "hasFocus", lambda: True) mp.setattr(docSearch.searchBox, "hasFocus", lambda: True)
mp.setattr(docSearch.replaceBox, "hasFocus", lambda: False) mp.setattr(docSearch.replaceBox, "hasFocus", lambda: False)
assert docEditor.focusNextPrevChild(True) is True assert docEditor.focusNextPrevChild(True) is True
assert docSearch.cycleFocus(True) is True assert docSearch.cycleFocus() is True
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(docEditor, "hasFocus", lambda: False) mp.setattr(docEditor, "hasFocus", lambda: False)
mp.setattr(docSearch.searchBox, "hasFocus", lambda: False) mp.setattr(docSearch.searchBox, "hasFocus", lambda: False)
mp.setattr(docSearch.replaceBox, "hasFocus", lambda: True) mp.setattr(docSearch.replaceBox, "hasFocus", lambda: True)
assert docEditor.focusNextPrevChild(True) is True assert docEditor.focusNextPrevChild(True) is True
assert docSearch.cycleFocus(True) is True assert docSearch.cycleFocus() is True
docSearch.closeSearch() docSearch.closeSearch()
assert docSearch.isVisible() is False assert docSearch.isVisible() is False
assert docEditor.focusNextPrevChild(True) is True assert docEditor.focusNextPrevChild(True) is True