167 lines
4.9 KiB
QML
167 lines
4.9 KiB
QML
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
|
|
}
|
|
|
|
ListView {
|
|
id: listView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
property bool selectByHover: false
|
|
|
|
clip: true
|
|
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()
|
|
}
|
|
}
|