├── Button.qml ├── Filter.qml ├── LineEdit.qml ├── Main.qml ├── PreviewDelegate.qml ├── QmlCompletionBox.qmlproject ├── SuggestionBox.qml ├── Suggestions.qml ├── SuggestionsPreview.qml └── resources └── icons └── clear-text-rtl.png /Button.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.0 2 | 3 | Rectangle { 4 | id: buttonRoot 5 | property bool hovered: false 6 | 7 | width: childrenRect.width 8 | height: childrenRect.height 9 | 10 | signal clicked 11 | 12 | MouseArea { 13 | anchors.fill: parent 14 | acceptedButtons: Qt.LeftButton 15 | hoverEnabled: true 16 | onEntered: buttonRoot.hovered = true 17 | onExited: buttonRoot.hovered = false 18 | 19 | onClicked: { 20 | buttonRoot.clicked() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Filter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.0 2 | 3 | Item { 4 | id: component 5 | property alias model: filterModel 6 | 7 | property QtObject sourceModel: undefined 8 | property string filter: "" 9 | property string property: "" 10 | 11 | Connections { 12 | onFilterChanged: invalidateFilter() 13 | onPropertyChanged: invalidateFilter() 14 | onSourceModelChanged: invalidateFilter() 15 | } 16 | 17 | Component.onCompleted: invalidateFilter() 18 | 19 | ListModel { 20 | id: filterModel 21 | } 22 | 23 | 24 | // filters out all items of source model that does not match filter 25 | function invalidateFilter() { 26 | if (sourceModel === undefined) 27 | return; 28 | 29 | filterModel.clear(); 30 | 31 | if (!isFilteringPropertyOk()) 32 | return 33 | 34 | var length = sourceModel.count 35 | for (var i = 0; i < length; ++i) { 36 | var item = sourceModel.get(i); 37 | if (isAcceptedItem(item)) { 38 | filterModel.append(item) 39 | } 40 | } 41 | } 42 | 43 | 44 | // returns true if item is accepted by filter 45 | function isAcceptedItem(item) { 46 | if (item[this.property] === undefined) 47 | return false 48 | 49 | if (item[this.property].match(this.filter) === null) { 50 | return false 51 | } 52 | 53 | return true 54 | } 55 | 56 | // checks if it has any sence to process invalidating based on property 57 | function isFilteringPropertyOk() { 58 | if(this.property === undefined || this.property === "") { 59 | return false 60 | } 61 | return true 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /LineEdit.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.0 2 | 3 | FocusScope { 4 | // --- properties 5 | property color borderColor: "black" 6 | property color borderColorFocused: "steelblue" 7 | property int borderWidth: 1 8 | property alias borderRadius: borderRect.radius 9 | property alias backgroundColor: borderRect.color 10 | property alias hint: hintComponent 11 | property bool hasClearButton: true 12 | property alias clearButton: clearButtonComponent 13 | property alias textInput: textInputComponent 14 | 15 | // --- signals 16 | signal accepted 17 | 18 | 19 | id: focusScope 20 | 21 | Rectangle { 22 | id: borderRect 23 | anchors.fill: parent 24 | border { 25 | width: focusScope.borderWidth 26 | color: (parent.activeFocus || textInputComponent.activeFocus) ? focusScope.borderColorFocused : focusScope.borderColor 27 | } 28 | radius: 4 29 | color: "white" 30 | } 31 | 32 | Text { 33 | id: hintComponent 34 | anchors.fill: parent; anchors.leftMargin: 4 35 | verticalAlignment: Text.AlignVCenter 36 | text: "Type something..." 37 | color: "gray" 38 | font.italic: true 39 | } 40 | 41 | MouseArea { 42 | anchors.fill: parent 43 | onClicked: { focusScope.focus = true; textInput.openSoftwareInputPanel(); } 44 | } 45 | 46 | TextInput { 47 | id: textInputComponent 48 | anchors { left: parent.left; leftMargin: 4; right: clearButtonComponent.left; rightMargin: 4; verticalCenter: parent.verticalCenter } 49 | focus: true 50 | selectByMouse: true 51 | color: "black" 52 | onAccepted: focusScope.accepted() 53 | } 54 | 55 | Button { 56 | id: clearButtonComponent 57 | opacity: 0 58 | color: "transparent" 59 | 60 | anchors { right: parent.right; rightMargin: 4; verticalCenter: parent.verticalCenter } 61 | 62 | onClicked: { textInput.text = ''; focusScope.focus = true; textInput.openSoftwareInputPanel(); } 63 | 64 | Image { 65 | width: 16 66 | height: 16 67 | anchors.centerIn: parent 68 | source: "resources/icons/clear-text-rtl.png" 69 | } 70 | } 71 | 72 | states: State { 73 | name: "hasText"; when: textInput.text != '' 74 | PropertyChanges { target: hintComponent; opacity: 0 } 75 | PropertyChanges { target: clearButtonComponent; opacity: 1 } 76 | } 77 | 78 | transitions: [ 79 | Transition { 80 | from: ""; to: "hasText" 81 | NumberAnimation { exclude: hintComponent; properties: "opacity" } 82 | }, 83 | Transition { 84 | from: "hasText"; to: "" 85 | NumberAnimation { properties: "opacity" } 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /Main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | 3 | Rectangle { 4 | width: 640 5 | height: 480 6 | color: "black" 7 | 8 | Suggestions { 9 | id: suggestions 10 | } 11 | 12 | Item { 13 | id: contents 14 | width: parent.width - 100 15 | height: parent.height - 100 16 | anchors.centerIn: parent 17 | 18 | LineEdit { 19 | id: inputField 20 | anchors.top: parent.top 21 | anchors.left: parent.left 22 | anchors.right: parent.right 23 | height: 18 24 | 25 | hint.text: "Enter text. It will be completed with lines below" 26 | borderColor: "white" 27 | } 28 | 29 | SuggestionsPreview { 30 | // just to show you what you can type in 31 | model: suggestions 32 | } 33 | 34 | SuggestionBox { 35 | id: suggestionsBox 36 | model: suggestions 37 | width: 200 38 | anchors.top: inputField.bottom 39 | anchors.left: inputField.left 40 | filter: inputField.textInput.text 41 | property: "name" 42 | } 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /PreviewDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | 3 | Item { 4 | id: delegate 5 | property string text 6 | width: parent.width 7 | height: itemRect.height + 2 8 | 9 | Rectangle { 10 | id: itemRect 11 | 12 | height: textComponent.height 13 | width: parent.width - 2 14 | 15 | anchors.verticalCenter: parent.verticalCenter 16 | anchors.horizontalCenter: parent.horizontalCenter 17 | 18 | color: "#333333" 19 | radius: 5 20 | border { 21 | width: 2 22 | color: "gray" 23 | } 24 | 25 | Text { 26 | width: parent.width - 10 27 | anchors.horizontalCenter: parent.horizontalCenter 28 | id: textComponent 29 | color: "white" 30 | text: delegate.text 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /QmlCompletionBox.qmlproject: -------------------------------------------------------------------------------- 1 | /* File generated by Qt Creator, version 2.6.2 */ 2 | 3 | import QmlProject 1.1 4 | 5 | Project { 6 | mainFile: "Main.qml" 7 | 8 | /* Include .qml, .js, and image files from current directory and subdirectories */ 9 | QmlFiles { 10 | directory: "." 11 | } 12 | JavaScriptFiles { 13 | directory: "." 14 | } 15 | ImageFiles { 16 | directory: "." 17 | } 18 | /* List of plugin directories passed to QML runtime */ 19 | // importPaths: [ "../exampleplugin" ] 20 | } 21 | -------------------------------------------------------------------------------- /SuggestionBox.qml: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Jocelyn Turcotte 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Library General Public 6 | License as published by the Free Software Foundation; either 7 | version 2 of the License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | Library General Public License for more details. 13 | 14 | You should have received a copy of the GNU Library General Public License 15 | along with this program; see the file COPYING.LIB. If not, write to 16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | import QtQuick 1.1 21 | 22 | Rectangle { 23 | id: container 24 | 25 | // --- properties 26 | property QtObject model: undefined 27 | property Item delegate 28 | property alias suggestionsModel: filterItem.model 29 | property alias filter: filterItem.filter 30 | property alias property: filterItem.property 31 | signal itemSelected(variant item) 32 | 33 | 34 | // --- behaviours 35 | z: parent.z + 100 36 | visible: filter.length > 0 && suggestionsModel.count > 0 37 | height: visible ? childrenRect.height : 0 38 | Behavior on height { 39 | SpringAnimation { spring: 2; damping: 0.2 } 40 | } 41 | 42 | 43 | // --- defaults 44 | color: "gray" 45 | radius: 5 46 | border { 47 | width: 1 48 | color: "white" 49 | } 50 | 51 | 52 | Filter { 53 | id: filterItem 54 | sourceModel: container.model 55 | } 56 | 57 | 58 | // --- UI 59 | Column { 60 | id: popup 61 | clip: true 62 | height: childrenRect.height 63 | width: parent.width - 6 64 | anchors.centerIn: parent 65 | 66 | 67 | property int selectedIndex 68 | property variant selectedItem: selectedIndex == -1 ? null : model[selectedIndex] 69 | signal suggestionClicked(variant suggestion) 70 | 71 | opacity: container.visible ? 1.0 : 0 72 | Behavior on opacity { 73 | NumberAnimation { } 74 | } 75 | 76 | 77 | Repeater { 78 | id: repeater 79 | model: container.suggestionsModel 80 | delegate: Item { 81 | id: delegateItem 82 | property variant suggestion: model 83 | 84 | height: textComponent.height 85 | width: container.width 86 | 87 | Text { 88 | id: textComponent 89 | color: "white" 90 | text: suggestion.name 91 | width: parent.width - 6 92 | anchors.horizontalCenter: parent.horizontalCenter 93 | } 94 | 95 | MouseArea { 96 | anchors.fill: parent 97 | onClicked: container.itemSelected(delegateItem.suggestion) 98 | } 99 | } 100 | } 101 | } 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /Suggestions.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | 3 | ListModel { 4 | id: completionItems 5 | 6 | ListElement { name: "apple" } 7 | ListElement { name: "apricot" } 8 | ListElement { name: "banana" } 9 | ListElement { name: "orange" } 10 | ListElement { name: "kiwi" } 11 | } 12 | -------------------------------------------------------------------------------- /SuggestionsPreview.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | 3 | Rectangle { 4 | 5 | property alias model: completionsHint.model 6 | 7 | color: "#333333" 8 | radius: 5 9 | border { 10 | width: 1 11 | color: "#333333" 12 | } 13 | 14 | anchors.top: inputField.bottom 15 | anchors.left: parent.left 16 | anchors.right: parent.right 17 | anchors.bottom: parent.bottom 18 | anchors.margins: 10 19 | 20 | ListView { 21 | 22 | anchors.fill: parent 23 | anchors.margins: 5 24 | clip: true 25 | spacing: 4 26 | 27 | id: completionsHint 28 | delegate: PreviewDelegate { 29 | text: model.name 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/icons/clear-text-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dant3/qmlcompletionbox/41eebf2b50ef4ade26c99946eaa36a7bfabafef5/resources/icons/clear-text-rtl.png --------------------------------------------------------------------------------