Improvement on selection + Fix iwth copy from contextmenu
This commit is contained in:
parent
5fbeab5eb3
commit
ebbb39ab00
119
SearchBox.py
119
SearchBox.py
|
@ -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()):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user