Improvement on selection + Fix iwth copy from contextmenu

This commit is contained in:
Paul Ebbers 2025-01-11 13:27:22 +01:00
parent 5fbeab5eb3
commit ebbb39ab00
3 changed files with 54 additions and 84 deletions

View File

@ -2,14 +2,14 @@ import FreeCAD as App
import FreeCADGui as Gui import FreeCADGui as Gui
import os import os
from PySide.QtCore import ( from PySide6.QtCore import (
Qt, Qt,
SIGNAL, SIGNAL,
QSize, QSize,
QIdentityProxyModel, QIdentityProxyModel,
QPoint, QPoint,
) )
from PySide.QtWidgets import ( from PySide6.QtWidgets import (
QTabWidget, QTabWidget,
QSlider, QSlider,
QSpinBox, QSpinBox,
@ -28,7 +28,7 @@ from PySide.QtWidgets import (
QApplication, QApplication,
QListWidget, QListWidget,
) )
from PySide.QtGui import ( from PySide6.QtGui import (
QIcon, QIcon,
QPixmap, QPixmap,
QColor, QColor,
@ -91,9 +91,7 @@ class SearchBox(QLineEdit):
self.getItemGroups = getItemGroups self.getItemGroups = getItemGroups
self.getToolTip = getToolTip 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.itemGroups = None # Will be initialized by calling getItemGroups() the first time the search box gains focus, through focusInEvent and refreshItemGroups
self.maxVisibleRows = ( self.maxVisibleRows = maxVisibleRows # TODO: use this to compute the correct height
maxVisibleRows # TODO: use this to compute the correct height
)
# Create proxy model # Create proxy model
self.proxyModel = QIdentityProxyModel() 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. # 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.setWindowFlag(Qt.WindowType.FramelessWindowHint)
self.listView.setSelectionMode(self.listView.SelectionMode.SingleSelection) self.listView.setSelectionMode(self.listView.SelectionMode.SingleSelection)
self.listView.setModel(self.proxyModel) self.listView.setModel(self.proxyModel)
self.listView.setItemDelegate( self.listView.setItemDelegate(getItemDelegate()) # https://stackoverflow.com/a/65930408/324969
getItemDelegate()
) # https://stackoverflow.com/a/65930408/324969
self.listView.setMouseTracking(True) self.listView.setMouseTracking(True)
# make the QListView non-editable # make the QListView non-editable
self.listView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.listView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
@ -122,28 +118,19 @@ class SearchBox(QLineEdit):
self.pendingExtraInfo = None self.pendingExtraInfo = None
self.currentExtraInfo = None self.currentExtraInfo = None
# Connect signals and slots # 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. # 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 # These events and their proxies in the SearchBorLight fixes this
self.listView.mousePressEvent = lambda event: self.proxyMousePressEvent(event) self.listView.mousePressEvent = lambda event: self.proxyMousePressEvent(event)
self.listView.mouseMoveEvent = lambda event: self.proxyMouseMoveEvent(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... # Note: should probably use the eventFilter method instead...
wdgctx = Qt.ShortcutContext.WidgetShortcut wdgctx = Qt.ShortcutContext.WidgetShortcut
QShortcut( QShortcut(QKeySequence(Qt.Key.Key_Down), self, context=wdgctx).activated.connect(self.listDown)
QKeySequence(Qt.Key.Key_Down), self, context=wdgctx QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect(self.listUp)
).activated.connect(self.listDown) QShortcut(QKeySequence(Qt.Key.Key_PageDown), self, context=wdgctx).activated.connect(self.listPageDown)
QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect( QShortcut(QKeySequence(Qt.Key.Key_PageUp), self, context=wdgctx).activated.connect(self.listPageUp)
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. # Home and End do not work, for some reason.
# QShortcut(QKeySequence.MoveToEndOfDocument, self, context = wdgctx).activated.connect(self.listEnd) # 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(Qt.Key.Key_End), self, context = wdgctx).activated.connect(self.listEnd)
# QShortcut(QKeySequence('Home'), self, context = wdgctx).activated.connect(self.listStart) # QShortcut(QKeySequence('Home'), self, context = wdgctx).activated.connect(self.listStart)
QShortcut( QShortcut(QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx).activated.connect(self.listAccept)
QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx QShortcut(QKeySequence(Qt.Key.Key_Return), self, context=wdgctx).activated.connect(self.listAccept)
).activated.connect(self.listAccept) QShortcut(QKeySequence("Ctrl+Return"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
QShortcut( QShortcut(QKeySequence("Ctrl+Enter"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
QKeySequence(Qt.Key.Key_Return), self, context=wdgctx QShortcut(QKeySequence("Ctrl+Space"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
).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( QShortcut(QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx).activated.connect(self.listCancel)
QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx
).activated.connect(self.listCancel)
# Initialize the model with the full list (assuming the text() is empty) # 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 # 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 @staticmethod
def proxyMousePressEvent(self, event): def proxyMousePressEvent(self, event):
if self.listView.underMouse():
self.selectResult(mode=None, index=self.listView.currentIndex()) self.selectResult(mode=None, index=self.listView.currentIndex())
else:
event.ignore()
return return
@staticmethod @staticmethod
def proxyMouseMoveEvent(self, arg__1): 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 return
@staticmethod @staticmethod
@ -194,15 +180,18 @@ class SearchBox(QLineEdit):
@staticmethod @staticmethod
def proxyFocusInEvent(self, qFocusEvent): 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: if self.firstShowList:
mdl = QStandardItemModel() mdl = QStandardItemModel()
mdl.appendRow( mdl.appendRow(
[ [
QStandardItem( QStandardItem(
genericToolIcon, genericToolIcon,
translate( translate("SearchBar", "Please wait, loading results from cache…"),
"SearchBar", "Please wait, loading results from cache…"
),
), ),
QStandardItem("0"), QStandardItem("0"),
QStandardItem("-1"), QStandardItem("-1"),
@ -217,13 +206,20 @@ class SearchBox(QLineEdit):
self.refreshItemGroups() self.refreshItemGroups()
self.showList() self.showList()
super(SearchBoxLight, self).focusInEvent(qFocusEvent) super(SearchBoxLight, self).focusInEvent(qFocusEvent)
return
@staticmethod @staticmethod
def proxyFocusOutEvent(self, qFocusEvent): def proxyFocusOutEvent(self, qFocusEvent):
global globalIgnoreFocusOut global globalIgnoreFocusOut
if not globalIgnoreFocusOut: if not globalIgnoreFocusOut:
self.hideList() self.hideList()
super(SearchBoxLight, self).focusOutEvent(qFocusEvent) # super(SearchBoxLight, self).focusOutEvent(qFocusEvent)
@staticmethod
def proxyLeaveEvent(self, qFocusEvent):
self.clearFocus()
self.hideList()
return
@staticmethod @staticmethod
def movementKey(self, rowUpdate): def movementKey(self, rowUpdate):
@ -247,17 +243,11 @@ class SearchBox(QLineEdit):
@staticmethod @staticmethod
def proxyListPageDown(self): def proxyListPageDown(self):
self.movementKey( self.movementKey(lambda current, nbRows: min(current + max(1, self.maxVisibleRows / 2), nbRows - 1))
lambda current, nbRows: min(
current + max(1, self.maxVisibleRows / 2), nbRows - 1
)
)
@staticmethod @staticmethod
def proxyListPageUp(self): def proxyListPageUp(self):
self.movementKey( self.movementKey(lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0))
lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0)
)
@staticmethod @staticmethod
def proxyListEnd(self): def proxyListEnd(self):
@ -324,7 +314,6 @@ class SearchBox(QLineEdit):
@staticmethod @staticmethod
def selectResult(self, mode: None, index): def selectResult(self, mode: None, index):
groupId = int(index.model().itemData(index.siblingAtColumn(2))[0]) groupId = int(index.model().itemData(index.siblingAtColumn(2))[0])
print(f"Got here, {index}")
self.hideList() self.hideList()
# TODO: allow other options, e.g. some items could act as combinators / cumulative filters # TODO: allow other options, e.g. some items could act as combinators / cumulative filters
self.setText("") self.setText("")
@ -393,9 +382,7 @@ class SearchBox(QLineEdit):
def getScreenPosition(widget): def getScreenPosition(widget):
geo = widget.geometry() geo = widget.geometry()
parent = widget.parent() parent = widget.parent()
parentPos = ( parentPos = getScreenPosition(parent) if parent is not None else QPoint(0, 0)
getScreenPosition(parent) if parent is not None else QPoint(0, 0)
)
return QPoint(geo.x() + parentPos.x(), geo.y() + parentPos.y()) return QPoint(geo.x() + parentPos.x(), geo.y() + parentPos.y())
pos = getScreenPosition(self) pos = getScreenPosition(self)
@ -425,22 +412,6 @@ class SearchBox(QLineEdit):
self.listView.setGeometry(x, y, w, h) self.listView.setGeometry(x, y, w, h)
self.extraInfo.setGeometry(extrax, y, extraw, 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 @staticmethod
def setExtraInfo(self, index): def setExtraInfo(self, index):
if self.currentExtraInfo == (index.row(), index.column(), index.model()): if self.currentExtraInfo == (index.row(), index.column(), index.model()):

View File

@ -6,9 +6,7 @@ from PySide import QtCore
class SearchBoxLight(QtGui.QLineEdit): class SearchBoxLight(QtGui.QLineEdit):
resultSelected = QtCore.Signal(int, int) resultSelected = QtCore.Signal(int, int)
def __init__( def __init__(self, getItemGroups, getToolTip, getItemDelegate, maxVisibleRows=20, parent=None):
self, getItemGroups, getToolTip, getItemDelegate, maxVisibleRows=20, parent=None
):
self.isInitialized = False self.isInitialized = False
# Store arguments # Store arguments
@ -27,9 +25,7 @@ class SearchBoxLight(QtGui.QLineEdit):
self.addAction(ico, QtGui.QLineEdit.LeadingPosition) self.addAction(ico, QtGui.QLineEdit.LeadingPosition)
self.setClearButtonEnabled(True) self.setClearButtonEnabled(True)
self.setPlaceholderText("Search tools, prefs & tree") self.setPlaceholderText("Search tools, prefs & tree")
self.setFixedWidth( self.setFixedWidth(200) # needed to avoid a change of width when the clear button appears/disappears
200
) # needed to avoid a change of width when the clear button appears/disappears
def lazyInit(self): def lazyInit(self):
pass pass
@ -51,6 +47,9 @@ class SearchBoxLight(QtGui.QLineEdit):
def MouseMoveEvent(self, *args, **kwargs): def MouseMoveEvent(self, *args, **kwargs):
return self.proxyMouseMoveEvent(*args, **kwargs) return self.proxyMouseMoveEvent(*args, **kwargs)
def LeaveEvent(self, *args, **kwargs):
return self.proxyLeaveEvent(*args, **kwargs)
def focusInEvent(self, *args, **kwargs): def focusInEvent(self, *args, **kwargs):
return self.proxyFocusInEvent(*args, **kwargs) return self.proxyFocusInEvent(*args, **kwargs)
@ -60,8 +59,8 @@ class SearchBoxLight(QtGui.QLineEdit):
def keyPressEvent(self, *args, **kwargs): def keyPressEvent(self, *args, **kwargs):
return self.proxyKeyPressEvent(*args, **kwargs) return self.proxyKeyPressEvent(*args, **kwargs)
def onSelectionChanged(self, *args, **kwargs): # def onSelectionChanged(self, *args, **kwargs):
return self.proxyOnSelectionChanged(*args, **kwargs) # return self.proxyOnSelectionChanged(*args, **kwargs)
def filterModel(self, *args, **kwargs): def filterModel(self, *args, **kwargs):
return self.proxyFilterModel(*args, **kwargs) return self.proxyFilterModel(*args, **kwargs)

View File

@ -5,7 +5,7 @@
<description>Adds a search bar widget for tools, document objects, and preferences</description> <description>Adds a search bar widget for tools, document objects, and preferences</description>
<version>1.3.0</version> <version>1.3.1</version>
<date>2022-06-01</date> <date>2022-06-01</date>