--- a/src/eric7/EricWidgets/EricTextEditSearchWidget.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/EricWidgets/EricTextEditSearchWidget.py Wed Jul 13 14:55:47 2022 +0200 @@ -12,8 +12,14 @@ from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QMetaObject, QSize from PyQt6.QtGui import QPalette, QTextDocument, QTextCursor from PyQt6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QCheckBox, - QToolButton, QSizePolicy + QWidget, + QVBoxLayout, + QHBoxLayout, + QLabel, + QComboBox, + QCheckBox, + QToolButton, + QSizePolicy, ) import UI.PixmapCache @@ -23,6 +29,7 @@ """ Class defining the supported text edit types. """ + UNKNOWN = 0 QTEXTEDIT = 1 QTEXTBROWSER = 2 @@ -32,16 +39,17 @@ class EricTextEditSearchWidget(QWidget): """ Class implementing a horizontal search widget for QTextEdit. - + @signal closePressed() emitted to indicate the closing of the widget via the close button """ + closePressed = pyqtSignal() - + def __init__(self, parent=None, widthForHeight=True, enableClose=False): """ Constructor - + @param parent reference to the parent widget @type QWidget @param widthForHeight flag indicating to prefer width for height. @@ -53,50 +61,47 @@ """ super().__init__(parent) self.__setupUi(widthForHeight, enableClose) - + self.__textedit = None self.__texteditType = EricTextEditType.UNKNOWN self.__findBackwards = False - + self.__defaultBaseColor = ( - self.findtextCombo.lineEdit().palette().color( - QPalette.ColorRole.Base) + self.findtextCombo.lineEdit().palette().color(QPalette.ColorRole.Base) ) self.__defaultTextColor = ( - self.findtextCombo.lineEdit().palette().color( - QPalette.ColorRole.Text) + self.findtextCombo.lineEdit().palette().color(QPalette.ColorRole.Text) ) - + self.findHistory = [] - + self.findtextCombo.setCompleter(None) - self.findtextCombo.lineEdit().returnPressed.connect( - self.__findByReturnPressed) - + self.findtextCombo.lineEdit().returnPressed.connect(self.__findByReturnPressed) + self.__setSearchButtons(False) self.infoLabel.hide() - + self.setFocusProxy(self.findtextCombo) - + def __setupUi(self, widthForHeight, enableClose): """ Private method to generate the UI. - + @param widthForHeight flag indicating to prefer width for height @type bool @param enableClose flag indicating to show a close button @type bool """ self.setObjectName("EricTextEditSearchWidget") - + self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) - + # row 1 of widgets self.horizontalLayout1 = QHBoxLayout() self.horizontalLayout1.setObjectName("horizontalLayout1") - + if enableClose: self.closeButton = QToolButton(self) self.closeButton.setIcon(UI.PixmapCache.getIcon("close")) @@ -104,21 +109,21 @@ self.horizontalLayout1.addWidget(self.closeButton) else: self.closeButton = None - + self.label = QLabel(self) self.label.setObjectName("label") self.label.setText(self.tr("Find:")) self.horizontalLayout1.addWidget(self.label) - + self.findtextCombo = QComboBox(self) self.findtextCombo.setEditable(True) self.findtextCombo.lineEdit().setClearButtonEnabled(True) - sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, - QSizePolicy.Policy.Fixed) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( - self.findtextCombo.sizePolicy().hasHeightForWidth()) + self.findtextCombo.sizePolicy().hasHeightForWidth() + ) self.findtextCombo.setSizePolicy(sizePolicy) self.findtextCombo.setMinimumSize(QSize(100, 0)) self.findtextCombo.setEditable(True) @@ -126,63 +131,61 @@ self.findtextCombo.setDuplicatesEnabled(False) self.findtextCombo.setObjectName("findtextCombo") self.horizontalLayout1.addWidget(self.findtextCombo) - + # row 2 (maybe) of widgets self.horizontalLayout2 = QHBoxLayout() self.horizontalLayout2.setObjectName("horizontalLayout2") - + self.caseCheckBox = QCheckBox(self) self.caseCheckBox.setObjectName("caseCheckBox") self.caseCheckBox.setText(self.tr("Match case")) self.horizontalLayout2.addWidget(self.caseCheckBox) - + self.wordCheckBox = QCheckBox(self) self.wordCheckBox.setObjectName("wordCheckBox") self.wordCheckBox.setText(self.tr("Whole word")) self.horizontalLayout2.addWidget(self.wordCheckBox) - + # layout for the navigation buttons self.horizontalLayout3 = QHBoxLayout() self.horizontalLayout3.setSpacing(0) self.horizontalLayout3.setObjectName("horizontalLayout3") - + self.findPrevButton = QToolButton(self) self.findPrevButton.setObjectName("findPrevButton") - self.findPrevButton.setToolTip(self.tr( - "Press to find the previous occurrence")) + self.findPrevButton.setToolTip(self.tr("Press to find the previous occurrence")) self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) self.horizontalLayout3.addWidget(self.findPrevButton) - + self.findNextButton = QToolButton(self) self.findNextButton.setObjectName("findNextButton") - self.findNextButton.setToolTip(self.tr( - "Press to find the next occurrence")) + self.findNextButton.setToolTip(self.tr("Press to find the next occurrence")) self.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) self.horizontalLayout3.addWidget(self.findNextButton) - + self.horizontalLayout2.addLayout(self.horizontalLayout3) - + # info label (in row 2 or 3) self.infoLabel = QLabel(self) self.infoLabel.setText("") self.infoLabel.setObjectName("infoLabel") - + # place everything together self.verticalLayout.addLayout(self.horizontalLayout1) self.__addWidthForHeightLayout(widthForHeight) self.verticalLayout.addWidget(self.infoLabel) - + QMetaObject.connectSlotsByName(self) - + self.setTabOrder(self.findtextCombo, self.caseCheckBox) self.setTabOrder(self.caseCheckBox, self.wordCheckBox) self.setTabOrder(self.wordCheckBox, self.findPrevButton) self.setTabOrder(self.findPrevButton, self.findNextButton) - + def setWidthForHeight(self, widthForHeight): """ Public method to set the 'width for height'. - + @param widthForHeight flag indicating to prefer width @type bool """ @@ -191,11 +194,11 @@ else: self.verticalLayout.takeAt(self.__widthForHeightLayoutIndex) self.__addWidthForHeightLayout(widthForHeight) - + def __addWidthForHeightLayout(self, widthForHeight): """ Private method to set the middle part of the layout. - + @param widthForHeight flag indicating to prefer width @type bool """ @@ -205,13 +208,13 @@ else: self.verticalLayout.insertLayout(1, self.horizontalLayout2) self.__widthForHeightLayoutIndex = 1 - + self.__widthForHeight = widthForHeight - + def attachTextEdit(self, textedit, editType=EricTextEditType.QTEXTEDIT): """ Public method to attach a QTextEdit or QWebEngineView widget. - + @param textedit reference to the edit widget to be attached @type QTextEdit, QTextBrowser or QWebEngineView @param editType type of the attached edit widget @@ -219,39 +222,36 @@ """ if self.__textedit is not None: self.detachTextEdit() - + self.__textedit = textedit self.__texteditType = editType - - self.wordCheckBox.setVisible(editType in ( - EricTextEditType.QTEXTEDIT, EricTextEditType.QTEXTBROWSER - )) + + self.wordCheckBox.setVisible( + editType in (EricTextEditType.QTEXTEDIT, EricTextEditType.QTEXTBROWSER) + ) self.infoLabel.setVisible(editType == EricTextEditType.QWEBENGINEVIEW) if editType == EricTextEditType.QWEBENGINEVIEW: - self.__textedit.page().findTextFinished.connect( - self.__findTextFinished) - + self.__textedit.page().findTextFinished.connect(self.__findTextFinished) + def detachTextEdit(self): """ Public method to detach the current text edit. """ if self.__texteditType == EricTextEditType.QWEBENGINEVIEW: - self.__textedit.page().findTextFinished.disconnect( - self.__findTextFinished) - + self.__textedit.page().findTextFinished.disconnect(self.__findTextFinished) + self.__textedit = None self.__texteditType = EricTextEditType.UNKNOWN - + @pyqtSlot() def activate(self): """ Public slot to activate the widget. """ self.show() - self.findtextCombo.setFocus( - Qt.FocusReason.ActiveWindowFocusReason) + self.findtextCombo.setFocus(Qt.FocusReason.ActiveWindowFocusReason) self.findtextCombo.lineEdit().selectAll() - + @pyqtSlot() def deactivate(self): """ @@ -264,31 +264,31 @@ if self.closeButton is not None: self.hide() self.closePressed.emit() - + @pyqtSlot() def __closeButtonClicked(self): """ Private slot to close the widget. - + Note: The widget is just hidden. """ self.deactivate() - + def keyPressEvent(self, event): """ Protected slot to handle key press events. - + @param event reference to the key press event @type QKeyEvent """ if self.__textedit: key = event.key() modifiers = event.modifiers() - + if key == Qt.Key.Key_Escape: self.deactivate() event.accept() - + elif key == Qt.Key.Key_F3: if modifiers == Qt.KeyboardModifier.NoModifier: # search forward @@ -298,88 +298,88 @@ # search backward self.on_findPrevButton_clicked() event.accept() - + @pyqtSlot(str) def on_findtextCombo_editTextChanged(self, txt): """ Private slot to enable/disable the find buttons. - + @param txt text of the combobox @type str """ self.__setSearchButtons(txt != "") - + if self.__texteditType == EricTextEditType.QWEBENGINEVIEW: self.infoLabel.clear() else: self.infoLabel.hide() self.__setFindtextComboBackground(False) - + def __setSearchButtons(self, enabled): """ Private slot to set the state of the search buttons. - + @param enabled flag indicating the state @type bool """ self.findPrevButton.setEnabled(enabled) self.findNextButton.setEnabled(enabled) - + def __findByReturnPressed(self): """ Private slot to handle the returnPressed signal of the findtext combobox. """ self.__find(self.__findBackwards) - + @pyqtSlot() def on_findPrevButton_clicked(self): """ Private slot to find the previous occurrence. """ self.__find(True) - + @pyqtSlot() def on_findNextButton_clicked(self): """ Private slot to find the next occurrence. """ self.__find(False) - + @pyqtSlot() def findPrev(self): """ Public slot to find the previous occurrence of the current search term. """ self.on_findPrevButton_clicked() - + @pyqtSlot() def findNext(self): """ Public slot to find the next occurrence of the current search term. """ self.on_findNextButton_clicked() - + def __find(self, backwards): """ Private method to search the associated text edit. - + @param backwards flag indicating a backwards search @type bool """ if not self.__textedit: return - + self.infoLabel.clear() if self.__texteditType != EricTextEditType.QWEBENGINEVIEW: self.infoLabel.hide() self.__setFindtextComboBackground(False) - + txt = self.findtextCombo.currentText() if not txt: return self.__findBackwards = backwards - + # This moves any previous occurrence of this statement to the head # of the list and updates the combobox if txt in self.findHistory: @@ -387,32 +387,33 @@ self.findHistory.insert(0, txt) self.findtextCombo.clear() self.findtextCombo.addItems(self.findHistory) - + if self.__texteditType in ( - EricTextEditType.QTEXTBROWSER, EricTextEditType.QTEXTEDIT + EricTextEditType.QTEXTBROWSER, + EricTextEditType.QTEXTEDIT, ): self.__findPrevNextQTextEdit(backwards) elif self.__texteditType == EricTextEditType.QWEBENGINEVIEW: self.__findPrevNextQWebEngineView(backwards) - + def __findPrevNextQTextEdit(self, backwards): """ Private method to to search the associated edit widget of type QTextEdit. - + @param backwards flag indicating a backwards search @type bool """ flags = ( QTextDocument.FindFlag.FindBackward - if backwards else - QTextDocument.FindFlag(0) + if backwards + else QTextDocument.FindFlag(0) ) if self.caseCheckBox.isChecked(): flags |= QTextDocument.FindFlag.FindCaseSensitively if self.wordCheckBox.isChecked(): flags |= QTextDocument.FindFlag.FindWholeWords - + ok = self.__textedit.find(self.findtextCombo.currentText(), flags) if not ok: # wrap around once @@ -426,71 +427,71 @@ cursor.movePosition(moveOp) self.__textedit.setTextCursor(cursor) ok = self.__textedit.find(self.findtextCombo.currentText(), flags) - + if not ok: self.infoLabel.setText( - self.tr("'{0}' was not found.").format( - self.findtextCombo.currentText()) + self.tr("'{0}' was not found.").format(self.findtextCombo.currentText()) ) self.infoLabel.show() self.__setFindtextComboBackground(True) - + def __findPrevNextQWebEngineView(self, backwards): """ Private method to to search the associated edit widget of type QWebEngineView. - + @param backwards flag indicating a backwards search @type bool """ from PyQt6.QtWebEngineCore import QWebEnginePage - + findFlags = QWebEnginePage.FindFlag(0) if self.caseCheckBox.isChecked(): findFlags |= QWebEnginePage.FindFlag.FindCaseSensitively if backwards: findFlags |= QWebEnginePage.FindFlag.FindBackward self.__textedit.findText(self.findtextCombo.currentText(), findFlags) - + def __setFindtextComboBackground(self, error): """ Private slot to change the findtext combo background to indicate errors. - + @param error flag indicating an error condition @type bool """ styleSheet = ( "color: #000000; background-color: #ff6666" - if error else - f"color: {self.__defaultTextColor};" + if error + else f"color: {self.__defaultTextColor};" f" background-color: {self.__defaultBaseColor}" ) self.findtextCombo.setStyleSheet(styleSheet) - + def __findTextFinished(self, result): """ Private slot handling the findTextFinished signal of the web page. - + @param result reference to the QWebEngineFindTextResult object of the last search @type QWebEngineFindTextResult """ if result.numberOfMatches() == 0: self.infoLabel.setText( - self.tr("'{0}' was not found.").format( - self.findtextCombo.currentText()) + self.tr("'{0}' was not found.").format(self.findtextCombo.currentText()) ) self.__setFindtextComboBackground(True) else: - self.infoLabel.setText(self.tr("Match {0} of {1}").format( - result.activeMatch(), result.numberOfMatches()) + self.infoLabel.setText( + self.tr("Match {0} of {1}").format( + result.activeMatch(), result.numberOfMatches() + ) ) - + def showInfo(self, info): """ Public method to show some information in the info label. - + @param info informational text to be shown @type str """