import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.14 import QtGraphicalEffects 1.0 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import utils 1.0 Popup { id: root width: 400 height: 300 property alias model: listView.model // delegate interface has to be fulfilled property Component delegate: Item { property var modelData property bool isCurrentItem function filterAccepts(searchText) { return true } } property string searchBoxPlaceholder: qsTr("Search...") signal selected(int index, var modelData) background: Rectangle { radius: Style.current.radius color: Style.current.background border.color: Style.current.border layer.enabled: true layer.effect: DropShadow { verticalOffset: 3 radius: 8 samples: 15 fast: true cached: true color: "#22000000" } } ColumnLayout { anchors.fill: parent StatusInput { id: searchBox Layout.fillWidth: true leftPadding: 0 rightPadding: 0 input.placeholderText: root.searchBoxPlaceholder input.icon: StatusIconSettings { width: 24 height: 24 name: "search" color: Theme.palette.baseColor1 } function goToNextAvailableIndex(up) { var currentIndex = listView.currentIndex for (var i = 0; i < listView.count; i++) { currentIndex = up ? (currentIndex === 0 ? listView.count - 1 : currentIndex - 1) : (currentIndex === listView.count - 1 ? 0 : currentIndex + 1) listView.currentIndex = currentIndex if (listView.currentItem.visible) { return } } listView.currentIndex = 0 } Keys.onReleased: { listView.selectByHover = false if (event.key === Qt.Key_Down) { searchBox.goToNextAvailableIndex(false) } if (event.key === Qt.Key_Up) { searchBox.goToNextAvailableIndex(true) } if (event.key === Qt.Key_Escape) { return root.close() } if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { return root.selected(listView.currentIndex, listView.currentItem.myData) } if (!listView.currentItem.visible) { goToNextAvailableIndex(false) } } onTextChanged: if (text === "") listView.currentIndex = 0 } StatusListView { id: listView Layout.fillWidth: true Layout.fillHeight: true property bool selectByHover: false highlightMoveDuration: 200 delegate: Item { id: delegateItem property var myData: typeof modelData === "undefined" ? model : modelData width: listView.width height: visible ? delegateLoader.height : 0 Loader { id: delegateLoader width: parent.width sourceComponent: root.delegate onLoaded: { item.modelData = delegateItem.myData item.isCurrentItem = Qt.binding(() => delegateItem.ListView.isCurrentItem) delegateItem.visible = Qt.binding(() => item.filterAccepts(searchBox.text)) } } MouseArea { anchors.fill: parent hoverEnabled: true onClicked: (mouse) => { listView.currentIndex = index root.selected(index, delegateItem.myData) mouse.accepted = false } onContainsMouseChanged: if (containsMouse) listView.currentIndex = index cursorShape: Qt.PointingHandCursor } } Loader { anchors.fill: parent active: !listView.selectByHover sourceComponent: MouseArea { hoverEnabled: true onPositionChanged: listView.selectByHover = true } } } } onAboutToShow: { listView.currentIndex = 0 listView.selectByHover = false searchBox.text = "" searchBox.input.edit.forceActiveFocus() } }