import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.14 import QtGraphicalEffects 1.0 import StatusQ.Core 0.1 import StatusQ.Controls 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 import StatusQ.Popups.Dialog 0.1 import utils 1.0 Item { id: root property alias model: listView.model property alias delegate: listView.delegate property alias suggestionsModel: suggestionsListView.model property alias suggestionsDelegate: suggestionsListView.delegate property size suggestionsDelegateSize: Qt.size(344, 64) readonly property alias label: label readonly property alias warningLabel: warningLabel readonly property alias edit: edit signal confirmed() signal rejected() signal enterKeyPressed() signal upKeyPressed() signal downKeyPressed() signal entryAccepted(var suggestionsDelegate) signal entryRemoved(var delegate) signal textPasted(string text) implicitWidth: mainLayout.implicitWidth implicitHeight: mainLayout.implicitHeight RowLayout { id: mainLayout anchors.fill: parent spacing: Style.current.padding Rectangle { Layout.fillWidth: true Layout.preferredHeight: 44 Layout.alignment: Qt.AlignVCenter Layout.leftMargin: Style.current.halfPadding color: Theme.palette.baseColor2 radius: Style.current.radius RowLayout { anchors.fill: parent spacing: Style.current.halfPadding StatusBaseText { id: label Layout.leftMargin: Style.current.padding Layout.alignment: Qt.AlignVCenter visible: text !== "" font.pixelSize: 15 color: Theme.palette.baseColor1 } Item { Layout.fillWidth: true Layout.fillHeight: true StatusScrollView { id: scrollView function positionViewAtEnd() { if (scrollView.contentWidth > scrollView.width) { scrollView.contentX = scrollView.contentWidth - scrollView.width } else { scrollView.contentX = 0 } } anchors.fill: parent padding: 0 onContentWidthChanged: positionViewAtEnd() onWidthChanged: positionViewAtEnd() RowLayout { height: scrollView.height StatusListView { id: listView Layout.fillWidth: true Layout.preferredHeight: 30 implicitWidth: contentWidth orientation: ListView.Horizontal spacing: Style.current.halfPadding } TextInput { id: edit Layout.minimumWidth: 4 Layout.fillHeight: true verticalAlignment: Text.AlignVCenter font.pixelSize: 15 color: Theme.palette.directColor1 selectByMouse: true selectionColor: Theme.palette.primaryColor2 selectedTextColor: color cursorDelegate: Rectangle { color: Theme.palette.primaryColor1 implicitWidth: 2 radius: 1 visible: edit.cursorVisible SequentialAnimation on visible { loops: Animation.Infinite running: edit.cursorVisible PropertyAnimation { to: false; duration: 600; } PropertyAnimation { to: true; duration: 600; } } } Keys.onPressed: { if (event.matches(StandardKey.Paste)) { event.accepted = true const previousText = text; const previousSelectedText = selectedText; paste() if (previousText === "" || previousSelectedText.length === previousText.length) root.textPasted(text) return; } if (suggestionsDialog.visible) { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { root.entryAccepted(suggestionsListView.itemAtIndex(suggestionsListView.currentIndex)) } else if (event.key === Qt.Key_Up) { suggestionsListView.decrementCurrentIndex() } else if (event.key === Qt.Key_Down) { suggestionsListView.incrementCurrentIndex() } } else { if (event.key === Qt.Key_Backspace && edit.text === "") { root.entryRemoved(listView.itemAtIndex(listView.count - 1)) } else if (event.key === Qt.Key_Return || event.key === Qt.Enter) { root.enterKeyPressed() } else if (event.key === Qt.Key_Escape) { root.rejected() } else if (event.key === Qt.Key_Up) { root.upKeyPressed() } else if (event.key === Qt.Key_Down) { root.downKeyPressed() } } } } // ensure edit cursor is visible Item { Layout.fillHeight: true implicitWidth: 1 } } ScrollBar.horizontal: StatusScrollBar { id: scrollBar parent: scrollView.parent anchors.top: scrollView.bottom anchors.left: scrollView.left anchors.right: scrollView.right policy: ScrollBar.AsNeeded visible: resolveVisibility(policy, scrollView.width, scrollView.contentWidth) } } } StatusBaseText { id: warningLabel Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.rightMargin: Style.current.padding visible: text !== "" font.pixelSize: 10 color: Theme.palette.dangerColor1 } } MouseArea { anchors.fill: parent propagateComposedEvents: true onPressed: { edit.forceActiveFocus() mouse.accepted = false } } } StatusButton { objectName: "inlineSelectorConfirmButton" Layout.alignment: Qt.AlignVCenter enabled: (listView.count > 0) text: qsTr("Confirm") onClicked: root.confirmed() } StatusButton { Layout.alignment: Qt.AlignVCenter text: qsTr("Cancel") type: StatusBaseButton.Type.Danger onClicked: root.rejected() } } Popup { id: suggestionsDialog parent: scrollView x: Math.min(parent.width, parent.contentWidth) y: parent.height + Style.current.halfPadding visible: edit.text !== "" padding: Style.current.halfPadding background: StatusDialogBackground { id: bg layer.enabled: true layer.effect: DropShadow { source: bg horizontalOffset: 0 verticalOffset: 4 radius: 12 samples: 25 spread: 0.2 color: Theme.palette.dropShadow } } height: suggestionsListView.count ? Math.min(400, suggestionsListView.count * suggestionsDelegateSize.height + 2 * padding) : noResultsFoundText.height + 2 * padding width: suggestionsDelegateSize.width ColumnLayout { anchors.fill: parent StatusBaseText { id: noResultsFoundText Layout.fillWidth: true visible: root.suggestionsModel.count === 0 text: qsTr("No results found") color: Theme.palette.baseColor1 } StatusListView { id: suggestionsListView Layout.fillWidth: true Layout.fillHeight: true visible: root.suggestionsModel.count highlightMoveDuration: 0 highlightMoveVelocity: -1 verticalScrollBar { visible: contentHeight > height policy: ScrollBar.AlwaysOn } onVisibleChanged: currentIndex = 0 onCountChanged: currentIndex = 0 } } } }