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 readonly property alias label: label readonly property alias warningLabel: warningLabel readonly property alias edit: edit property bool confirmBtnEnabled: (listView.count > 0) 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 onWidthChanged: { listView.positionViewAtEnd(); } RowLayout { anchors.fill: parent Item { //40 px least space for input Layout.preferredWidth: (listView.contentWidth < (parent.width - 40)) ? listView.contentWidth : (parent.width - 40) Layout.preferredHeight: 44 StatusListView { clip: true id: listView width: parent.width height: 30 anchors.verticalCenter: parent.verticalCenter orientation: ListView.Horizontal spacing: Style.current.halfPadding ScrollBar.horizontal: scrollBar onCountChanged: { positionViewAtEnd(); } } StatusScrollBar { id: scrollBar parent: listView.parent anchors.top: listView.bottom anchors.left: listView.left anchors.right: listView.right policy: ScrollBar.AsNeeded visible: resolveVisibility(policy, listView.width, listView.contentWidth) } } TextInput { id: edit Layout.fillWidth: true Layout.fillHeight: true verticalAlignment: Text.AlignVCenter font.pixelSize: 15 color: Theme.palette.directColor1 clip: true 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 === "" && listView.count > 0) { 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 } } } 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: root.confirmBtnEnabled 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: edit x: (parent.contentWidth - Style.current.halfPadding) 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 } } ColumnLayout { anchors.fill: parent StatusBaseText { visible: root.suggestionsModel.count === 0 text: qsTr("No results found") color: Theme.palette.baseColor1 } StatusListView { id: suggestionsListView visible: root.suggestionsModel.count implicitWidth: contentItem.childrenRect.width implicitHeight: contentItem.childrenRect.height onVisibleChanged: currentIndex = 0 } } } }