From ebbb39ab00b28fe935260fd8fa7c7f1f8a875810 Mon Sep 17 00:00:00 2001 From: Paul Ebbers Date: Sat, 11 Jan 2025 13:27:22 +0100 Subject: [PATCH] Improvement on selection + Fix iwth copy from contextmenu --- SearchBox.py | 121 ++++++++++++++++++---------------------------- SearchBoxLight.py | 15 +++--- package.xml | 2 +- 3 files changed, 54 insertions(+), 84 deletions(-) diff --git a/SearchBox.py b/SearchBox.py index b5633b1..ca52129 100644 --- a/SearchBox.py +++ b/SearchBox.py @@ -2,14 +2,14 @@ import FreeCAD as App import FreeCADGui as Gui import os -from PySide.QtCore import ( +from PySide6.QtCore import ( Qt, SIGNAL, QSize, QIdentityProxyModel, QPoint, ) -from PySide.QtWidgets import ( +from PySide6.QtWidgets import ( QTabWidget, QSlider, QSpinBox, @@ -28,7 +28,7 @@ from PySide.QtWidgets import ( QApplication, QListWidget, ) -from PySide.QtGui import ( +from PySide6.QtGui import ( QIcon, QPixmap, QColor, @@ -91,9 +91,7 @@ class SearchBox(QLineEdit): self.getItemGroups = getItemGroups self.getToolTip = getToolTip self.itemGroups = None # Will be initialized by calling getItemGroups() the first time the search box gains focus, through focusInEvent and refreshItemGroups - self.maxVisibleRows = ( - maxVisibleRows # TODO: use this to compute the correct height - ) + self.maxVisibleRows = maxVisibleRows # TODO: use this to compute the correct height # Create proxy model self.proxyModel = QIdentityProxyModel() # Filtered model to which items are manually added. Store it as a property of the object instead of a local variable, to prevent grbage collection. @@ -105,9 +103,7 @@ class SearchBox(QLineEdit): self.listView.setWindowFlag(Qt.WindowType.FramelessWindowHint) self.listView.setSelectionMode(self.listView.SelectionMode.SingleSelection) self.listView.setModel(self.proxyModel) - self.listView.setItemDelegate( - getItemDelegate() - ) # https://stackoverflow.com/a/65930408/324969 + self.listView.setItemDelegate(getItemDelegate()) # https://stackoverflow.com/a/65930408/324969 self.listView.setMouseTracking(True) # make the QListView non-editable self.listView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) @@ -122,28 +118,19 @@ class SearchBox(QLineEdit): self.pendingExtraInfo = None self.currentExtraInfo = None # Connect signals and slots - self.listView.clicked.connect(lambda x: self.selectResult("select", x)) - self.listView.selectionModel().selectionChanged.connect(self.onSelectionChanged) # Add custom mouse events. On windows the click events were not working for Searcbar versions 1.2.x and older. # These events and their proxies in the SearchBorLight fixes this self.listView.mousePressEvent = lambda event: self.proxyMousePressEvent(event) self.listView.mouseMoveEvent = lambda event: self.proxyMouseMoveEvent(event) + self.extraInfo.leaveEvent = lambda event: self.proxyLeaveEvent(event) # Note: should probably use the eventFilter method instead... wdgctx = Qt.ShortcutContext.WidgetShortcut - QShortcut( - QKeySequence(Qt.Key.Key_Down), self, context=wdgctx - ).activated.connect(self.listDown) - QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect( - self.listUp - ) - QShortcut( - QKeySequence(Qt.Key.Key_PageDown), self, context=wdgctx - ).activated.connect(self.listPageDown) - QShortcut( - QKeySequence(Qt.Key.Key_PageUp), self, context=wdgctx - ).activated.connect(self.listPageUp) + QShortcut(QKeySequence(Qt.Key.Key_Down), self, context=wdgctx).activated.connect(self.listDown) + QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect(self.listUp) + QShortcut(QKeySequence(Qt.Key.Key_PageDown), self, context=wdgctx).activated.connect(self.listPageDown) + QShortcut(QKeySequence(Qt.Key.Key_PageUp), self, context=wdgctx).activated.connect(self.listPageUp) # Home and End do not work, for some reason. # QShortcut(QKeySequence.MoveToEndOfDocument, self, context = wdgctx).activated.connect(self.listEnd) @@ -151,25 +138,13 @@ class SearchBox(QLineEdit): # QShortcut(QKeySequence(Qt.Key.Key_End), self, context = wdgctx).activated.connect(self.listEnd) # QShortcut(QKeySequence('Home'), self, context = wdgctx).activated.connect(self.listStart) - QShortcut( - QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx - ).activated.connect(self.listAccept) - QShortcut( - QKeySequence(Qt.Key.Key_Return), self, context=wdgctx - ).activated.connect(self.listAccept) - QShortcut(QKeySequence("Ctrl+Return"), self, context=wdgctx).activated.connect( - self.listAcceptToggle - ) - QShortcut(QKeySequence("Ctrl+Enter"), self, context=wdgctx).activated.connect( - self.listAcceptToggle - ) - QShortcut(QKeySequence("Ctrl+Space"), self, context=wdgctx).activated.connect( - self.listAcceptToggle - ) + QShortcut(QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx).activated.connect(self.listAccept) + QShortcut(QKeySequence(Qt.Key.Key_Return), self, context=wdgctx).activated.connect(self.listAccept) + QShortcut(QKeySequence("Ctrl+Return"), self, context=wdgctx).activated.connect(self.listAcceptToggle) + QShortcut(QKeySequence("Ctrl+Enter"), self, context=wdgctx).activated.connect(self.listAcceptToggle) + QShortcut(QKeySequence("Ctrl+Space"), self, context=wdgctx).activated.connect(self.listAcceptToggle) - QShortcut( - QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx - ).activated.connect(self.listCancel) + QShortcut(QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx).activated.connect(self.listCancel) # Initialize the model with the full list (assuming the text() is empty) # self.proxyFilterModel(self.text()) # This is done by refreshItemGroups on focusInEvent, because the initial loading from cache can take time @@ -179,12 +154,23 @@ class SearchBox(QLineEdit): @staticmethod def proxyMousePressEvent(self, event): - self.selectResult(mode=None, index=self.listView.currentIndex()) + if self.listView.underMouse(): + self.selectResult(mode=None, index=self.listView.currentIndex()) + else: + event.ignore() return @staticmethod def proxyMouseMoveEvent(self, arg__1): - self.listView.setCurrentIndex(self.listView.indexAt(arg__1.pos())) + index = self.listView.indexAt(arg__1.pos()) + self.listView.setCurrentIndex(index) + + self.setExtraInfo(index) + # Poor attempt to circumvent a glitch where the extra info pane stays visible after pressing Return + if not self.listView.isHidden(): + self.showExtraInfo() + if self.listView.isHidden(): + self.hideExtraInfo() return @staticmethod @@ -194,15 +180,18 @@ class SearchBox(QLineEdit): @staticmethod def proxyFocusInEvent(self, qFocusEvent): + # if the extrainfo is under the cursor, don't focus but only show the list + if self.extraInfo.underMouse(): + self.showList() + qFocusEvent.ignore() + return if self.firstShowList: mdl = QStandardItemModel() mdl.appendRow( [ QStandardItem( genericToolIcon, - translate( - "SearchBar", "Please wait, loading results from cache…" - ), + translate("SearchBar", "Please wait, loading results from cache…"), ), QStandardItem("0"), QStandardItem("-1"), @@ -217,13 +206,20 @@ class SearchBox(QLineEdit): self.refreshItemGroups() self.showList() super(SearchBoxLight, self).focusInEvent(qFocusEvent) + return @staticmethod def proxyFocusOutEvent(self, qFocusEvent): global globalIgnoreFocusOut if not globalIgnoreFocusOut: self.hideList() - super(SearchBoxLight, self).focusOutEvent(qFocusEvent) + # super(SearchBoxLight, self).focusOutEvent(qFocusEvent) + + @staticmethod + def proxyLeaveEvent(self, qFocusEvent): + self.clearFocus() + self.hideList() + return @staticmethod def movementKey(self, rowUpdate): @@ -247,17 +243,11 @@ class SearchBox(QLineEdit): @staticmethod def proxyListPageDown(self): - self.movementKey( - lambda current, nbRows: min( - current + max(1, self.maxVisibleRows / 2), nbRows - 1 - ) - ) + self.movementKey(lambda current, nbRows: min(current + max(1, self.maxVisibleRows / 2), nbRows - 1)) @staticmethod def proxyListPageUp(self): - self.movementKey( - lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0) - ) + self.movementKey(lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0)) @staticmethod def proxyListEnd(self): @@ -324,7 +314,6 @@ class SearchBox(QLineEdit): @staticmethod def selectResult(self, mode: None, index): groupId = int(index.model().itemData(index.siblingAtColumn(2))[0]) - print(f"Got here, {index}") self.hideList() # TODO: allow other options, e.g. some items could act as combinators / cumulative filters self.setText("") @@ -393,9 +382,7 @@ class SearchBox(QLineEdit): def getScreenPosition(widget): geo = widget.geometry() parent = widget.parent() - parentPos = ( - getScreenPosition(parent) if parent is not None else QPoint(0, 0) - ) + parentPos = getScreenPosition(parent) if parent is not None else QPoint(0, 0) return QPoint(geo.x() + parentPos.x(), geo.y() + parentPos.y()) pos = getScreenPosition(self) @@ -425,22 +412,6 @@ class SearchBox(QLineEdit): self.listView.setGeometry(x, y, w, h) self.extraInfo.setGeometry(extrax, y, extraw, h) - @staticmethod - def proxyOnSelectionChanged(self, selected, deselected): - # The list has .setSelectionMode(QAbstractItemView.SingleSelection), - # so there is always at most one index in selected.indexes() and at most one - # index in deselected.indexes() - selected = selected.indexes() - deselected = deselected.indexes() - if len(selected) > 0: - index = selected[0] - self.setExtraInfo(index) - # Poor attempt to circumvent a glitch where the extra info pane stays visible after pressing Return - if not self.listView.isHidden(): - self.showExtraInfo() - elif len(deselected) > 0: - self.hideExtraInfo() - @staticmethod def setExtraInfo(self, index): if self.currentExtraInfo == (index.row(), index.column(), index.model()): diff --git a/SearchBoxLight.py b/SearchBoxLight.py index 90c4a23..0fd8310 100644 --- a/SearchBoxLight.py +++ b/SearchBoxLight.py @@ -6,9 +6,7 @@ from PySide import QtCore class SearchBoxLight(QtGui.QLineEdit): resultSelected = QtCore.Signal(int, int) - def __init__( - self, getItemGroups, getToolTip, getItemDelegate, maxVisibleRows=20, parent=None - ): + def __init__(self, getItemGroups, getToolTip, getItemDelegate, maxVisibleRows=20, parent=None): self.isInitialized = False # Store arguments @@ -27,9 +25,7 @@ class SearchBoxLight(QtGui.QLineEdit): self.addAction(ico, QtGui.QLineEdit.LeadingPosition) self.setClearButtonEnabled(True) self.setPlaceholderText("Search tools, prefs & tree") - self.setFixedWidth( - 200 - ) # needed to avoid a change of width when the clear button appears/disappears + self.setFixedWidth(200) # needed to avoid a change of width when the clear button appears/disappears def lazyInit(self): pass @@ -51,6 +47,9 @@ class SearchBoxLight(QtGui.QLineEdit): def MouseMoveEvent(self, *args, **kwargs): return self.proxyMouseMoveEvent(*args, **kwargs) + def LeaveEvent(self, *args, **kwargs): + return self.proxyLeaveEvent(*args, **kwargs) + def focusInEvent(self, *args, **kwargs): return self.proxyFocusInEvent(*args, **kwargs) @@ -60,8 +59,8 @@ class SearchBoxLight(QtGui.QLineEdit): def keyPressEvent(self, *args, **kwargs): return self.proxyKeyPressEvent(*args, **kwargs) - def onSelectionChanged(self, *args, **kwargs): - return self.proxyOnSelectionChanged(*args, **kwargs) + # def onSelectionChanged(self, *args, **kwargs): + # return self.proxyOnSelectionChanged(*args, **kwargs) def filterModel(self, *args, **kwargs): return self.proxyFilterModel(*args, **kwargs) diff --git a/package.xml b/package.xml index 5d7f553..5bebba0 100644 --- a/package.xml +++ b/package.xml @@ -5,7 +5,7 @@ Adds a search bar widget for tools, document objects, and preferences - 1.3.0 + 1.3.1 2022-06-01