--- a/eric7/E5Gui/EricTextEditSearchWidget.py Sat May 22 19:52:34 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,378 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a horizontal search widget for QTextEdit. -""" - -import enum - -from PyQt6.QtCore import pyqtSlot, Qt, QMetaObject, QSize -from PyQt6.QtGui import QPalette, QBrush, QColor, QTextDocument, QTextCursor -from PyQt6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QCheckBox, - QToolButton, QSizePolicy -) - -import UI.PixmapCache - - -class EricTextEditType(enum.Enum): - """ - Class defining the supported text edit types. - """ - UNKNOWN = 0 - QTEXTEDIT = 1 - QTEXTBROWSER = 2 - QWEBENGINEVIEW = 3 - - -class EricTextEditSearchWidget(QWidget): - """ - Class implementing a horizontal search widget for QTextEdit. - """ - def __init__(self, parent=None, widthForHeight=True): - """ - Constructor - - @param parent reference to the parent widget - @type QWidget - @param widthForHeight flag indicating to prefer width for height. - If this parameter is False, some widgets are shown in a third - line. - @type bool - """ - super().__init__(parent) - self.__setupUi(widthForHeight) - - self.__textedit = None - self.__texteditType = EricTextEditType.UNKNOWN - self.__findBackwards = True - - self.__defaultBaseColor = ( - self.findtextCombo.lineEdit().palette().color( - QPalette.ColorRole.Base) - ) - self.__defaultTextColor = ( - self.findtextCombo.lineEdit().palette().color( - QPalette.ColorRole.Text) - ) - - self.findHistory = [] - - self.findtextCombo.setCompleter(None) - self.findtextCombo.lineEdit().returnPressed.connect( - self.__findByReturnPressed) - - self.__setSearchButtons(False) - self.infoLabel.hide() - - self.setFocusProxy(self.findtextCombo) - - def __setupUi(self, widthForHeight): - """ - Private method to generate the UI. - - @param widthForHeight flag indicating to prefer width for height - @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") - - 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.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( - self.findtextCombo.sizePolicy().hasHeightForWidth()) - self.findtextCombo.setSizePolicy(sizePolicy) - self.findtextCombo.setMinimumSize(QSize(100, 0)) - self.findtextCombo.setEditable(True) - self.findtextCombo.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) - 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.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.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 - """ - if self.__widthForHeight: - self.horizontalLayout1.takeAt(self.__widthForHeightLayoutIndex) - 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 - """ - if widthForHeight: - self.horizontalLayout1.addLayout(self.horizontalLayout2) - self.__widthForHeightLayoutIndex = 2 - 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 widget. - - @param textedit reference to the edit widget to be attached - @type QTextEdit, QWebEngineView or QWebView - @param editType type of the attached edit widget - @type EricTextEditType - """ - self.__textedit = textedit - self.__texteditType = editType - - self.wordCheckBox.setVisible(editType == "QTextEdit") - - def keyPressEvent(self, event): - """ - Protected slot to handle key press events. - - @param event reference to the key press event (QKeyEvent) - """ - if self.__textedit and event.key() == Qt.Key.Key_Escape: - self.__textedit.setFocus(Qt.FocusReason.ActiveWindowFocusReason) - event.accept() - - @pyqtSlot(str) - def on_findtextCombo_editTextChanged(self, txt): - """ - Private slot to enable/disable the find buttons. - - @param txt text of the combobox (string) - """ - self.__setSearchButtons(txt != "") - - 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 (boolean) - """ - 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) - - def __find(self, backwards): - """ - Private method to search the associated text edit. - - @param backwards flag indicating a backwards search (boolean) - """ - if not self.__textedit: - return - - self.infoLabel.clear() - 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: - self.findHistory.remove(txt) - self.findHistory.insert(0, txt) - self.findtextCombo.clear() - self.findtextCombo.addItems(self.findHistory) - - if self.__texteditType in ( - EricTextEditType.QTEXTBROWSER, EricTextEditType.QTEXTEDIT - ): - ok = self.__findPrevNextQTextEdit(backwards) - self.__findNextPrevCallback(ok) - 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 - @return flag indicating the search result - @rtype bool - """ - flags = ( - QTextDocument.FindFlag.FindBackward - 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 - cursor = self.__textedit.textCursor() - if backwards: - moveOp = QTextCursor.MoveOperation.End - # move to end of document - else: - moveOp = QTextCursor.MoveOperation.Start - # move to start of document - cursor.movePosition(moveOp) - self.__textedit.setTextCursor(cursor) - ok = self.__textedit.find(self.findtextCombo.currentText(), flags) - - return ok - - 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.QtWebEngineWidgets 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, self.__findNextPrevCallback) - - def __findNextPrevCallback(self, found): - """ - Private method to process the result of the last search. - - @param found flag indicating if the last search succeeded - @type bool - """ - if not found: - txt = self.findtextCombo.currentText() - self.infoLabel.setText( - self.tr("'{0}' was not found.").format(txt)) - self.infoLabel.show() - self.__setFindtextComboBackground(True) - - def __setFindtextComboBackground(self, error): - """ - Private slot to change the findtext combo background to indicate - errors. - - @param error flag indicating an error condition (boolean) - """ - le = self.findtextCombo.lineEdit() - p = le.palette() - if error: - p.setBrush(QPalette.ColorRole.Base, QBrush(QColor("#FF6666"))) - p.setBrush(QPalette.ColorRole.Text, QBrush(QColor("#000000"))) - else: - p.setBrush(QPalette.ColorRole.Base, self.__defaultBaseColor) - p.setBrush(QPalette.ColorRole.Text, self.__defaultTextColor) - le.setPalette(p) - le.update()