Add result count to search and rewrite scroll past end feature (#946)
* Restore colours to dark theme search-replace icon * Implement a working search result counter * Disable scroll past end feature and clean up search/replace and update test * Re-implement scroll past end feature as a static distance * Stop the search box from resizing on every search
This commit is contained in:
committed by
GitHub
parent
b0f939af32
commit
0401b8556d
@@ -26,9 +26,14 @@
|
||||
id="defs877" />
|
||||
<path
|
||||
id="path871"
|
||||
style="fill:#aeaeae;fill-opacity:1;stroke-width:1.19007"
|
||||
d="m 10.330622,1.9999997 c -4.5924663,0 -8.3306193,3.738153 -8.3306193,8.3306193 0,1.391929 0.3467825,2.702587 0.9532665,3.857242 0.084308,-0.108411 0.1590865,-0.228838 0.2534294,-0.323181 L 4.7482003,12.323178 C 4.5237338,11.697056 4.378519,11.032991 4.3785189,10.330619 c 0,-3.2810129 2.6710902,-5.949778 5.9521031,-5.949778 0.702303,0 1.36649,0.1429324 1.992559,0.3673563 L 14.141363,2.9300158 C 12.997884,2.3384803 11.704084,1.9999997 10.330622,1.9999997 Z m 5.635898,0.4766332 c -0.396657,0 -0.792437,0.1507259 -1.095095,0.4533829 L 3.57173,14.229712 c -0.3026571,0.302657 -0.5759103,0.751428 -0.7742385,1.227621 -0.1983282,0.47826 -0.3208556,0.99063 -0.3208556,1.418274 v 4.647756 h 4.6477558 c 0.427645,0 0.9376894,-0.122526 1.4159495,-0.320855 0.4782601,-0.198328 0.9272894,-0.471583 1.2299463,-0.774239 L 21.069984,9.1285734 c 0.302656,-0.302657 0.453382,-0.7017958 0.453382,-1.0974191 0,-0.3966562 -0.150726,-0.7924369 -0.453382,-1.0950939 L 17.063939,2.9300158 C 16.761281,2.6273588 16.362143,2.4766329 15.96652,2.4766329 Z m 0,2.2785396 3.275981,3.2759818 -1.334572,1.3368983 -3.275982,-3.2759819 z m 0,0.7300627 -0.60451,0.6045104 2.545919,2.5482442 0.460357,-0.4626829 C 18.332278,8.0413114 18.299136,7.9066594 18.256684,7.7754 Z M 13.901884,6.8221334 15.173682,8.0939305 6.6082326,16.661704 5.3364357,15.387582 Z m 2.004185,2.0041848 1.271796,1.2717968 -8.5654475,8.565448 -1.2741221,-1.271796 z m 0.353406,1.0834691 c 0.0083,0.1189097 0.01733,0.2373427 0.01861,0.3580557 l 0.169728,-0.169728 z m 2.059986,2.6993717 -5.710299,5.710299 c 0.105352,-0.03032 0.217183,-0.04484 0.320855,-0.07906 l 0.892816,0.89049 1.799581,1.797261 0.07208,0.07208 0.07672,0.06743 C 16.486443,21.66984 17.375591,22 18.275281,22 c 2.054056,0 3.724716,-1.671795 3.724716,-3.72704 0,-0.999657 -0.390465,-1.936781 -1.099745,-2.638921 l -1.129969,-1.174145 -0.639387,-0.639386 -0.890489,-0.892815 c 0.03403,-0.103138 0.04885,-0.213757 0.07906,-0.318531 z m -11.7600542,2.28784 -0.4929085,0.492908 0.5417343,0.541734 0.57196,-0.571959 C 6.960254,15.221393 6.7588057,15.062002 6.5594068,14.896999 Z m 2.7179722,1.285747 -1.209021,1.209021 0.5417345,0.541734 1.6577535,-1.655429 c -0.3372847,-0.0036 -0.668706,-0.03764 -0.990467,-0.09533 z m -4.5710293,0.06743 c 0.010333,-0.02065 3.0644031,3.034179 3.0644031,3.034179 C 7.43814,19.422771 7.2039295,19.456408 7.1243917,19.456408 H 5.5759147 L 4.5435968,18.424084 v -1.548477 c 0,-0.07953 0.033633,-0.315548 0.1627529,-0.625436 z" />
|
||||
<g
|
||||
id="g1022"
|
||||
style="display:none;opacity:1;stroke:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
style="display:inline;fill:#6699cc;fill-opacity:1;stroke-width:1.19007"
|
||||
d="m 10.330622,1.9999997 c -4.5924663,0 -8.3306193,3.738153 -8.3306193,8.3306193 0,1.391929 0.3467825,2.702587 0.9532665,3.857242 0.084308,-0.108411 0.1590865,-0.228838 0.2534294,-0.323181 L 4.7482003,12.323178 C 4.5237338,11.697056 4.378519,11.032991 4.3785189,10.330619 c 0,-3.2810129 2.6710902,-5.949778 5.9521031,-5.949778 0.702303,0 1.36649,0.1429324 1.992559,0.3673563 L 14.141363,2.9300158 C 12.997884,2.3384803 11.704084,1.9999997 10.330622,1.9999997 Z m 5.635898,3.4852355 -0.60451,0.6045104 2.545919,2.5482442 0.460357,-0.4626829 C 18.332278,8.0413114 18.299136,7.9066594 18.256684,7.7754 Z m 0.292955,4.4245521 c 0.0083,0.1189097 0.01733,0.2373427 0.01861,0.3580557 l 0.169728,-0.169728 z m 2.059986,2.6993717 -5.710299,5.710299 c 0.105352,-0.03032 0.217183,-0.04484 0.320855,-0.07906 l 0.892816,0.89049 1.799581,1.797261 0.07208,0.07208 0.07672,0.06743 C 16.486443,21.66984 17.375591,22 18.275281,22 c 2.054056,0 3.724716,-1.671795 3.724716,-3.72704 0,-0.999657 -0.390465,-1.936781 -1.099745,-2.638921 l -1.129969,-1.174145 -0.639387,-0.639386 -0.890489,-0.892815 c 0.03403,-0.103138 0.04885,-0.213757 0.07906,-0.318531 z m -11.7600542,2.28784 -0.4929085,0.492908 0.5417343,0.541734 0.57196,-0.571959 C 6.960254,15.221393 6.7588057,15.062002 6.5594068,14.896999 Z m 2.7179722,1.285747 -1.209021,1.209021 0.5417345,0.541734 1.6577535,-1.655429 c -0.3372847,-0.0036 -0.668706,-0.03764 -0.990467,-0.09533 z" />
|
||||
<path
|
||||
d="m 15.96652,2.4766329 c -0.396657,0 -0.792437,0.1507259 -1.095095,0.4533829 L 3.57173,14.229712 c -0.3026571,0.302657 -0.5759103,0.751428 -0.7742385,1.227621 -0.1983282,0.47826 -0.3208556,0.99063 -0.3208556,1.418274 v 4.647756 h 4.6477558 c 0.427645,0 0.9376894,-0.122526 1.4159495,-0.320855 0.4782601,-0.198328 0.9272894,-0.471583 1.2299463,-0.774239 L 21.069984,9.1285734 c 0.302656,-0.302657 0.453382,-0.7017958 0.453382,-1.0974191 0,-0.3966562 -0.150726,-0.7924369 -0.453382,-1.0950939 L 17.063939,2.9300158 C 16.761281,2.6273588 16.362143,2.4766329 15.96652,2.4766329 Z m 0,2.2785396 3.275981,3.2759818 -1.334572,1.3368983 -3.275982,-3.2759819 z M 13.901884,6.8221334 15.173682,8.0939305 6.6082326,16.661704 5.3364357,15.387582 Z m 2.004185,2.0041848 1.271796,1.2717968 -8.5654475,8.565448 -1.2741221,-1.271796 z M 4.7063497,16.250176 c 0.010333,-0.02065 3.0644031,3.034179 3.0644031,3.034179 C 7.43814,19.422771 7.2039295,19.456408 7.1243917,19.456408 H 5.5759147 L 4.5435968,18.424084 v -1.548477 c 0,-0.07953 0.033633,-0.315548 0.1627529,-0.625436 z"
|
||||
style="display:inline;fill:#99cc99;fill-opacity:1;stroke-width:1.19007"
|
||||
id="path15590" />
|
||||
<path
|
||||
id="rect19553"
|
||||
d="m 2.4766359,18.937425 v 2.585938 h 2.5605469 z"
|
||||
style="display:inline;opacity:0.95;fill:#0a0a0a;fill-opacity:1;stroke:none;stroke-width:1.88249397;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -137,7 +137,7 @@ class Config:
|
||||
self.doReplaceDash = True # Replace multiple hyphens with dashes
|
||||
self.doReplaceDots = True # Replace three dots with ellipsis
|
||||
|
||||
self.scrollPastEnd = True # Allow scrolling past end of document
|
||||
self.scrollPastEnd = 25 # Number of lines to scroll past end of document
|
||||
self.autoScroll = False # Typewriter-like scrolling
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
|
||||
@@ -479,7 +479,7 @@ class Config:
|
||||
self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote)
|
||||
self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash)
|
||||
self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots)
|
||||
self.scrollPastEnd = theConf.rdBool(cnfSec, "scrollpastend", self.scrollPastEnd)
|
||||
self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd)
|
||||
self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
|
||||
self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos)
|
||||
self.fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", self.fmtSingleQuotes)
|
||||
|
||||
@@ -761,12 +761,16 @@ class GuiPreferencesEditor(QWidget):
|
||||
self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
|
||||
|
||||
# Scroll Past End
|
||||
self.scrollPastEnd = QSwitch()
|
||||
self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd)
|
||||
self.scrollPastEnd = QSpinBox(self)
|
||||
self.scrollPastEnd.setMinimum(0)
|
||||
self.scrollPastEnd.setMaximum(100)
|
||||
self.scrollPastEnd.setSingleStep(1)
|
||||
self.scrollPastEnd.setValue(int(self.mainConf.scrollPastEnd))
|
||||
self.mainForm.addRow(
|
||||
self.tr("Scroll past end of the document"),
|
||||
self.scrollPastEnd,
|
||||
self.tr("Also improves typewriter scrolling for short documents.")
|
||||
self.tr("Set to 0 to disable this feature."),
|
||||
theUnit=self.tr("lines")
|
||||
)
|
||||
|
||||
# Typewriter Scrolling
|
||||
@@ -809,7 +813,7 @@ class GuiPreferencesEditor(QWidget):
|
||||
self.mainConf.showLineEndings = self.showLineEndings.isChecked()
|
||||
|
||||
# Scroll Behaviour
|
||||
self.mainConf.scrollPastEnd = self.scrollPastEnd.isChecked()
|
||||
self.mainConf.scrollPastEnd = self.scrollPastEnd.value()
|
||||
self.mainConf.autoScroll = self.autoScroll.isChecked()
|
||||
self.mainConf.autoScrollPos = self.autoScrollPos.value()
|
||||
|
||||
|
||||
+121
-55
@@ -29,6 +29,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import bisect
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
@@ -39,8 +40,8 @@ from PyQt5.QtCore import (
|
||||
QPointF, QObject, QRunnable, QPropertyAnimation
|
||||
)
|
||||
from PyQt5.QtGui import (
|
||||
QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette,
|
||||
QTextDocument, QCursor, QPixmap
|
||||
QFontMetrics, QTextCursor, QTextOption, QKeySequence, QFont, QColor,
|
||||
QPalette, QTextDocument, QCursor, QPixmap
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QWidget, QLabel,
|
||||
@@ -381,10 +382,17 @@ class GuiDocEditor(QTextEdit):
|
||||
else:
|
||||
self.setCursorLine(tLine)
|
||||
|
||||
if self.mainConf.scrollPastEnd > 0:
|
||||
fSize = QFontMetrics(self.font()).lineSpacing()
|
||||
docFrame = self.document().rootFrame().frameFormat()
|
||||
docFrame.setBottomMargin(round(self.mainConf.scrollPastEnd * fSize))
|
||||
self.document().rootFrame().setFrameFormat(docFrame)
|
||||
|
||||
self.docFooter.updateLineCount()
|
||||
self._docHeaders = self.theIndex.getHandleHeaders(self._docHandle)
|
||||
|
||||
qApp.processEvents()
|
||||
self.document().clearUndoRedoStacks()
|
||||
self.setDocumentChanged(False)
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
@@ -546,17 +554,6 @@ class GuiDocEditor(QTextEdit):
|
||||
lM = max(cM, fH)
|
||||
self.setViewportMargins(tM, uM, tM, lM)
|
||||
|
||||
tmpDocChanged = self._docChanged
|
||||
if self.mainConf.scrollPastEnd:
|
||||
docFrame = self.document().rootFrame().frameFormat()
|
||||
docFrame.setBottomMargin(max(0, 0.9*(wH - uM - lM - 4*tB)))
|
||||
self.document().rootFrame().setFrameFormat(docFrame)
|
||||
|
||||
# This is needed as the setFrameFormat function itself will
|
||||
# trigger the contetsChanged signal which sets _docChanged, so we
|
||||
# set it back to whatever it was before.
|
||||
self.setDocumentChanged(tmpDocChanged)
|
||||
|
||||
return
|
||||
|
||||
def updateDocInfo(self, tHandle):
|
||||
@@ -1322,6 +1319,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self._queuePos = None
|
||||
else:
|
||||
logger.verbose("Denied cursor move to %d > %d", self._queuePos, thePos)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
@@ -1329,75 +1327,123 @@ class GuiDocEditor(QTextEdit):
|
||||
##
|
||||
|
||||
def beginSearch(self):
|
||||
"""Sets the selected text as the search text for the search bar.
|
||||
"""Set the selected text as the search text for the search bar.
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if theCursor.hasSelection():
|
||||
self.docSearch.setSearchText(theCursor.selectedText())
|
||||
else:
|
||||
self.docSearch.setSearchText(None)
|
||||
self.updateDocMargins()
|
||||
resS, _ = self.findAllOccurences()
|
||||
self.docSearch.setResultCount(None, len(resS))
|
||||
return
|
||||
|
||||
def beginReplace(self):
|
||||
"""Opens the replace line of the search bar and sets the find
|
||||
text if a selection has been made, and resets the replace text.
|
||||
"""Initialise the search box and reset the replace text box.
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if theCursor.hasSelection():
|
||||
self.docSearch.setSearchText(theCursor.selectedText())
|
||||
else:
|
||||
self.docSearch.setSearchText(None)
|
||||
self.beginSearch()
|
||||
self.docSearch.setReplaceText("")
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
def findNext(self, goBack=False):
|
||||
"""Searches for the next or previous occurrence of the search
|
||||
bar text in the document. Wraps around if not found and loop is
|
||||
enabled, or continues to next file if next file is enabled.
|
||||
"""Search for the next or previous occurrence of the search bar
|
||||
text in the document. Wrap around if not found and loop is
|
||||
enabled, or continue to next file if next file is enabled.
|
||||
"""
|
||||
if not self.anyFocus():
|
||||
logger.debug("Editor does not have focus")
|
||||
return False
|
||||
return
|
||||
|
||||
if not self.docSearch.isVisible():
|
||||
self.beginSearch()
|
||||
return
|
||||
|
||||
findOpt = QTextDocument.FindFlag(0)
|
||||
resS, resE = self.findAllOccurences()
|
||||
if len(resS) == 0:
|
||||
self.docSearch.setResultCount(0, 0)
|
||||
self._lastFind = None
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
self._docHandle, wrapAround=self.docSearch.doLoop
|
||||
)
|
||||
self.beginSearch()
|
||||
return
|
||||
|
||||
theCursor = self.textCursor()
|
||||
resIdx = bisect.bisect_left(resS, theCursor.position())
|
||||
|
||||
doLoop = self.docSearch.doLoop
|
||||
maxIdx = len(resS) - 1
|
||||
|
||||
if goBack:
|
||||
findOpt |= QTextDocument.FindBackward
|
||||
resIdx -= 2
|
||||
|
||||
if resIdx < 0:
|
||||
resIdx = maxIdx if doLoop else 0
|
||||
|
||||
if resIdx > maxIdx:
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
self._docHandle, wrapAround=self.docSearch.doLoop
|
||||
)
|
||||
self.beginSearch()
|
||||
return
|
||||
else:
|
||||
resIdx = 0 if doLoop else maxIdx
|
||||
|
||||
theCursor.setPosition(resS[resIdx], QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(resE[resIdx], QTextCursor.KeepAnchor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
self.docSearch.setResultCount(resIdx + 1, len(resS))
|
||||
self._lastFind = (resS[resIdx], resE[resIdx])
|
||||
|
||||
return
|
||||
|
||||
def findAllOccurences(self):
|
||||
"""Create a list of all search results of the current search in
|
||||
the document.
|
||||
"""
|
||||
resS = []
|
||||
resE = []
|
||||
theCursor = self.textCursor()
|
||||
hasSelection = theCursor.hasSelection()
|
||||
if hasSelection:
|
||||
origA = theCursor.selectionStart()
|
||||
origB = theCursor.selectionEnd()
|
||||
else:
|
||||
origA = theCursor.position()
|
||||
|
||||
findOpt = QTextDocument.FindFlag(0)
|
||||
if self.docSearch.isCaseSense:
|
||||
findOpt |= QTextDocument.FindCaseSensitively
|
||||
if self.docSearch.isWholeWord:
|
||||
findOpt |= QTextDocument.FindWholeWords
|
||||
|
||||
searchFor = self.docSearch.getSearchObject()
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
if not wasFound:
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
self._docHandle, wrapAround=self.docSearch.doLoop
|
||||
)
|
||||
elif self.docSearch.doLoop:
|
||||
theCursor = self.textCursor()
|
||||
theCursor.movePosition(
|
||||
QTextCursor.End if goBack else QTextCursor.Start
|
||||
)
|
||||
self.setTextCursor(theCursor)
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
theCursor.setPosition(0)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
if wasFound:
|
||||
while self.find(searchFor, findOpt):
|
||||
theCursor = self.textCursor()
|
||||
self._lastFind = (theCursor.selectionStart(), theCursor.selectionEnd())
|
||||
resS.append(theCursor.selectionStart())
|
||||
resE.append(theCursor.selectionEnd())
|
||||
|
||||
return
|
||||
if hasSelection:
|
||||
theCursor.setPosition(origA, QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(origB, QTextCursor.KeepAnchor)
|
||||
else:
|
||||
theCursor.setPosition(origA)
|
||||
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
return resS, resE
|
||||
|
||||
def replaceNext(self):
|
||||
"""Searches for the next occurrence of the search bar text in
|
||||
the document and replaces it with the replace text. Calls search
|
||||
next automatically when done.
|
||||
"""Search for the next occurrence of the search bar text in the
|
||||
document and replace it with the replace text. Call search next
|
||||
automatically when done.
|
||||
"""
|
||||
if not self.anyFocus():
|
||||
logger.debug("Editor does not have focus")
|
||||
@@ -2131,8 +2177,8 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
mPx = self.mainConf.pxInt(6)
|
||||
tPx = int(0.8*self.theTheme.fontPixelSize)
|
||||
boxFont = self.theTheme.guiFont
|
||||
boxFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
self.boxFont = self.theTheme.guiFont
|
||||
self.boxFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.setAutoFillBackground(True)
|
||||
@@ -2143,13 +2189,14 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
# Text Boxes
|
||||
# ==========
|
||||
|
||||
self.searchBox = QLineEdit(self)
|
||||
self.searchBox.setFont(boxFont)
|
||||
self.searchBox.setFont(self.boxFont)
|
||||
self.searchBox.setPlaceholderText(self.tr("Search"))
|
||||
self.searchBox.returnPressed.connect(self._doSearch)
|
||||
|
||||
self.replaceBox = QLineEdit(self)
|
||||
self.replaceBox.setFont(boxFont)
|
||||
self.replaceBox.setFont(self.boxFont)
|
||||
self.replaceBox.setPlaceholderText(self.tr("Replace"))
|
||||
self.replaceBox.returnPressed.connect(self._doReplace)
|
||||
|
||||
@@ -2160,9 +2207,13 @@ class GuiDocEditSearch(QFrame):
|
||||
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
|
||||
|
||||
self.searchLabel = QLabel(self.tr("Search"))
|
||||
self.searchLabel.setFont(boxFont)
|
||||
self.searchLabel.setFont(self.boxFont)
|
||||
self.searchLabel.setIndent(self.mainConf.pxInt(6))
|
||||
|
||||
self.resultLabel = QLabel("?/?")
|
||||
self.resultLabel.setFont(self.boxFont)
|
||||
self.resultLabel.setMinimumWidth(self.theTheme.getTextWidth("?/?", self.boxFont))
|
||||
|
||||
self.toggleCase = QAction(self.tr("Case Sensitive"), self)
|
||||
self.toggleCase.setToolTip(self.tr("Match case"))
|
||||
self.toggleCase.setIcon(self.theTheme.getIcon("search_case"))
|
||||
@@ -2223,6 +2274,7 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
# Buttons
|
||||
# =======
|
||||
|
||||
bPx = self.searchBox.sizeHint().height()
|
||||
|
||||
self.showReplace = QToolButton(self)
|
||||
@@ -2243,18 +2295,20 @@ class GuiDocEditSearch(QFrame):
|
||||
self.replaceButton.clicked.connect(self._doReplace)
|
||||
|
||||
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
|
||||
self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 2, Qt.AlignRight)
|
||||
self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignRight)
|
||||
self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1)
|
||||
self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2)
|
||||
self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1)
|
||||
self.mainBox.addWidget(self.resultLabel, 1, 4, 1, 1)
|
||||
self.mainBox.addWidget(self.replaceBox, 2, 1, 1, 2)
|
||||
self.mainBox.addWidget(self.replaceButton, 2, 3, 1, 1)
|
||||
|
||||
self.mainBox.setColumnStretch(0, 1)
|
||||
self.mainBox.setColumnStretch(0, 0)
|
||||
self.mainBox.setColumnStretch(1, 0)
|
||||
self.mainBox.setColumnStretch(2, 0)
|
||||
self.mainBox.setColumnStretch(2, 1)
|
||||
self.mainBox.setColumnStretch(3, 0)
|
||||
self.mainBox.setColumnStretch(4, 0)
|
||||
self.mainBox.setColumnStretch(5, 0)
|
||||
self.mainBox.setSpacing(self.mainConf.pxInt(2))
|
||||
self.mainBox.setContentsMargins(mPx, mPx, mPx, mPx)
|
||||
|
||||
@@ -2349,6 +2403,18 @@ class GuiDocEditSearch(QFrame):
|
||||
self.replaceBox.setText(theText)
|
||||
return True
|
||||
|
||||
def setResultCount(self, currRes, resCount):
|
||||
"""Set the count values for the current search.
|
||||
"""
|
||||
currRes = "?" if currRes is None else currRes
|
||||
resCount = "?" if resCount is None else resCount
|
||||
minWidth = self.theTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont)
|
||||
self.resultLabel.setText(f"{currRes}/{resCount}")
|
||||
self.resultLabel.setMinimumWidth(minWidth)
|
||||
self.adjustSize()
|
||||
self.docEditor.updateDocMargins()
|
||||
return
|
||||
|
||||
def getSearchObject(self):
|
||||
"""Return the current search text either as text or as a regular
|
||||
expression object.
|
||||
|
||||
@@ -42,7 +42,7 @@ repsquotes = True
|
||||
repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
scrollpastend = True
|
||||
scrollpastend = 25
|
||||
autoscroll = False
|
||||
autoscrollpos = 30
|
||||
fmtsinglequote = ‘, ’
|
||||
|
||||
@@ -42,7 +42,7 @@ repsquotes = True
|
||||
repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
scrollpastend = False
|
||||
scrollpastend = 0
|
||||
autoscroll = True
|
||||
autoscrollpos = 30
|
||||
fmtsinglequote = ‘, ’
|
||||
|
||||
@@ -169,16 +169,14 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
||||
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
|
||||
assert tabEditor.showLineEndings.isChecked()
|
||||
|
||||
qtbot.wait(keyDelay)
|
||||
assert tabEditor.scrollPastEnd.isChecked()
|
||||
qtbot.mouseClick(tabEditor.scrollPastEnd, Qt.LeftButton)
|
||||
assert not tabEditor.scrollPastEnd.isChecked()
|
||||
|
||||
qtbot.wait(keyDelay)
|
||||
assert not tabEditor.autoScroll.isChecked()
|
||||
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
|
||||
assert tabEditor.autoScroll.isChecked()
|
||||
|
||||
qtbot.wait(keyDelay)
|
||||
tabEditor.scrollPastEnd.setValue(0)
|
||||
|
||||
qtbot.wait(keyDelay)
|
||||
tabEditor.bigDocLimit.setValue(500)
|
||||
|
||||
|
||||
@@ -1297,135 +1297,151 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
theCursor = nwGUI.docEditor.textCursor()
|
||||
assert theCursor.selectedText() == "est"
|
||||
|
||||
# Activate Search
|
||||
# Activate search
|
||||
nwGUI.mainMenu.aFind.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.isVisible()
|
||||
assert nwGUI.docEditor.docSearch.getSearchText() == "est"
|
||||
|
||||
# Find Next by Enter
|
||||
# Find next by enter key
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
|
||||
qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
|
||||
|
||||
# Find Next by Button
|
||||
# Find next by button
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
|
||||
|
||||
# Activate Loop Search
|
||||
# Activate loop search
|
||||
nwGUI.docEditor.docSearch.toggleLoop.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleLoop.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.doLoop
|
||||
assert nwGUI.docEditor.docSearch.doLoop is True
|
||||
|
||||
# Find Next by Menu Search > Find Next
|
||||
# Find next by menu Search > Find Next
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
|
||||
# Close Search
|
||||
# Close search
|
||||
nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.isVisible() is False
|
||||
assert nwGUI.docEditor.setCursorPosition(15)
|
||||
|
||||
# Toggle Search Again with Header Button
|
||||
# Toggle search again with header button
|
||||
qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert nwGUI.docEditor.docSearch.setSearchText("")
|
||||
assert nwGUI.docEditor.docSearch.isVisible() is True
|
||||
|
||||
# Enable RegEx Search
|
||||
# Search for non-existing
|
||||
assert nwGUI.docEditor.setCursorPosition(0)
|
||||
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
||||
|
||||
# Enable RegEx search
|
||||
nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleRegEx.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.isRegEx
|
||||
assert nwGUI.docEditor.docSearch.isRegEx is True
|
||||
|
||||
# Set Invalid RegEx
|
||||
# Set invalid RegEx
|
||||
assert nwGUI.docEditor.setCursorPosition(0)
|
||||
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
||||
|
||||
# Set Valid RegEx
|
||||
# Set valid RegEx
|
||||
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
|
||||
# Find Next and then Prev
|
||||
# Find next and then prev
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 309) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
|
||||
# Make RegEx Case Sensitive
|
||||
# Make RegEx case sensitive
|
||||
nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleCase.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.isCaseSense
|
||||
assert nwGUI.docEditor.docSearch.isCaseSense is True
|
||||
|
||||
# Find Next (One Result)
|
||||
# Find next/prev (one result)
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
|
||||
# Trigger Replace
|
||||
# Trigger replace
|
||||
nwGUI.mainMenu.aReplace.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.setReplaceText("foo")
|
||||
|
||||
# Disable RegEx Case Sensitive
|
||||
# Disable RegEx case sensitive
|
||||
nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger)
|
||||
assert not nwGUI.docEditor.docSearch.toggleCase.isChecked()
|
||||
assert not nwGUI.docEditor.docSearch.isCaseSense
|
||||
assert nwGUI.docEditor.docSearch.isCaseSense is False
|
||||
|
||||
# Toggle Replace Preserve Case
|
||||
# Toggle replace preserve case
|
||||
nwGUI.docEditor.docSearch.toggleMatchCap.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleMatchCap.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.doMatchCap
|
||||
assert nwGUI.docEditor.docSearch.doMatchCap is True
|
||||
|
||||
# Replace "Sus" with "Foo" via Menu
|
||||
# Replace "Sus" with "Foo" via menu
|
||||
assert nwGUI.docEditor.setCursorPosition(590)
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aReplaceNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[608:619] == "Foopendisse"
|
||||
|
||||
# Find Next to Loop File
|
||||
# Find next/prev to loop file
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1790) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
|
||||
# Replace "sus" with "foo" via Replace Button
|
||||
# Replace "sus" with "foo" via replace button
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay)
|
||||
assert nwGUI.docEditor.getText()[205:213] == "foocipit"
|
||||
|
||||
# Revert Last Two Replaces
|
||||
# Revert last two replaces
|
||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO)
|
||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO)
|
||||
assert nwGUI.docEditor.getText() == origText
|
||||
|
||||
# Disable RegEx Search
|
||||
# Disable RegEx search
|
||||
nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger)
|
||||
assert not nwGUI.docEditor.docSearch.toggleRegEx.isChecked()
|
||||
assert not nwGUI.docEditor.docSearch.isRegEx
|
||||
assert nwGUI.docEditor.docSearch.isRegEx is False
|
||||
|
||||
# Close Search and Select "est" Again
|
||||
# Close search and select "est" again
|
||||
nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.setCursorPosition(630)
|
||||
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
|
||||
theCursor = nwGUI.docEditor.textCursor()
|
||||
assert theCursor.selectedText() == "est"
|
||||
|
||||
# Activate Search Again
|
||||
# Activate search again
|
||||
nwGUI.mainMenu.aFind.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.isVisible()
|
||||
assert nwGUI.docEditor.docSearch.getSearchText() == "est"
|
||||
|
||||
# Enable Full Word Search
|
||||
# Enable full word search
|
||||
nwGUI.docEditor.docSearch.toggleWord.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleWord.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.isWholeWord
|
||||
assert nwGUI.docEditor.docSearch.isWholeWord is True
|
||||
|
||||
# Only One Match
|
||||
# Only one match
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
|
||||
# Enable Next Doc Search
|
||||
# Enable next doc search
|
||||
nwGUI.docEditor.docSearch.toggleProject.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docSearch.toggleProject.isChecked()
|
||||
assert nwGUI.docEditor.docSearch.doNextFile
|
||||
assert nwGUI.docEditor.docSearch.doNextFile is True
|
||||
|
||||
# Next Match
|
||||
# Next match
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() == "2426c6f0ca922" # Next document
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
@@ -1433,6 +1449,16 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1127) < 3
|
||||
|
||||
# Next doc, no match
|
||||
assert nwGUI.docEditor.docSearch.doNextFile is True
|
||||
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() != "2426c6f0ca922"
|
||||
assert nwGUI.docEditor.docHandle() == "04468803b92e1"
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() != "04468803b92e1"
|
||||
assert nwGUI.docEditor.docHandle() == "7a992350f3eb6"
|
||||
|
||||
# Toggle Replace
|
||||
nwGUI.docEditor.beginReplace()
|
||||
|
||||
@@ -1441,16 +1467,16 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: True)
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False)
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False)
|
||||
assert not nwGUI.docEditor.focusNextPrevChild(True)
|
||||
assert nwGUI.docEditor.focusNextPrevChild(True) is False
|
||||
|
||||
monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: False)
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False)
|
||||
assert nwGUI.docEditor.focusNextPrevChild(True)
|
||||
assert nwGUI.docEditor.focusNextPrevChild(True) is True
|
||||
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False)
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True)
|
||||
assert nwGUI.docEditor.focusNextPrevChild(True)
|
||||
assert nwGUI.docEditor.focusNextPrevChild(True) is True
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
|
||||
|
||||
@@ -199,7 +199,6 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
|
||||
# Change some settings
|
||||
nwGUI.mainConf.hideHScroll = True
|
||||
nwGUI.mainConf.hideVScroll = True
|
||||
nwGUI.mainConf.scrollPastEnd = True
|
||||
nwGUI.mainConf.autoScrollPos = 80
|
||||
nwGUI.mainConf.autoScroll = True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user