summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/controls/ComboBox.qml296
-rw-r--r--src/controls/Styles/Base/ComboBoxStyle.qml50
-rw-r--r--src/controls/Styles/Desktop/ComboBoxStyle.qml5
3 files changed, 342 insertions, 9 deletions
diff --git a/src/controls/ComboBox.qml b/src/controls/ComboBox.qml
index 4af49c145..74645db6d 100644
--- a/src/controls/ComboBox.qml
+++ b/src/controls/ComboBox.qml
@@ -58,10 +58,11 @@ import QtQuick.Controls.Private 1.0
}
\endqml
- Example 2:
+ In this example we are demonstrating how to use a ListModel with a combo box.
\qml
ComboBox {
+ currentIndex: 2
model: ListModel {
id: cbItems
ListElement { text: "Banana"; color: "Yellow" }
@@ -73,6 +74,31 @@ import QtQuick.Controls.Private 1.0
}
\endqml
+ You can make a combo box editable by setting the \l editable property. An editable combo box will
+ autocomplete its text based on what is available in the model.
+
+ In the next example we demonstrate how you can append content to an editable combo box by
+ reacting to the \l accepted signal. Note that you have to explicitly prevent duplicates.
+
+ \qml
+ ComboBox {
+ editable: true
+ model: ListModel {
+ id: model
+ ListElement { text: "Banana"; color: "Yellow" }
+ ListElement { text: "Apple"; color: "Green" }
+ ListElement { text: "Coconut"; color: "Brown" }
+ }
+ onAccepted: {
+ if (editableCombo.find(currentText) === -1) {
+ model.append({text: editText})
+ currentIndex = editableCombo.find(editText)
+ }
+ }
+ }
+ \endqml
+
+
You can create a custom appearance for a ComboBox by
assigning a \l ComboBoxStyle.
*/
@@ -97,7 +123,19 @@ Control {
\note Since \c currentText depends on \c currentIndex, there's no way to ensure \c currentText
will be up to date whenever a \c onCurrentIndexChanged handler is called.
*/
- readonly property alias currentText: popup.selectedText
+ readonly property alias currentText: popup.currentText
+
+ /*! This property holds whether the combo box can be edited by the user.
+ The default value is \c false.
+ \since QtQuick.Controls 1.1
+ */
+ property bool editable: false
+
+ /*! \qmlproperty string ComboBox::editText
+ \since QtQuick.Controls 1.1
+ This property specifies text being manipulated by the user for an editable combo box.
+ */
+ property alias editText: input.text
/*! This property specifies whether the combobox should gain active focus when pressed.
The default value is \c false. */
@@ -114,6 +152,115 @@ Control {
*/
readonly property alias hovered: mouseArea.containsMouse
+ /*! \qmlproperty int ComboBox::count
+ \since QtQuick.Controls 1.1
+ This property holds the number of items in the combo box.
+ */
+ readonly property alias count: popupItems.count
+
+ /*! Returns the text for a given \a index.
+ If an invalid index is provided, \c null is returned
+ \since QtQuick.Controls 1.1
+ */
+ function textAt (index) {
+ if (index >= count || index < 0)
+ return null;
+ return popupItems.objectAt(index).text;
+ }
+
+ /*! Finds and returns the index of a given \a text
+ If no match is found, \c -1 is returned. The search is case sensitive.
+ \since QtQuick.Controls 1.1
+ */
+ function find (text) {
+ return input.find(text, Qt.MatchExactly)
+ }
+
+ /*!
+ \qmlproperty Validator ComboBox::validator
+ \since QtQuick.Controls 1.1
+
+ Allows you to set a text validator for an editable ComboBox.
+ When a validator is set,
+ the text field will only accept input which leaves the text property in
+ an intermediate state. The accepted signal will only be sent
+ if the text is in an acceptable state when enter is pressed.
+
+ Currently supported validators are \l{QtQuick2::IntValidator},
+ \l{QtQuick2::DoubleValidator}, and \l{QtQuick2::RegExpValidator}. An
+ example of using validators is shown below, which allows input of
+ integers between 11 and 31 into the text field:
+
+ \note This property is only applied when \l editable is \c true
+
+ \qml
+ import QtQuick 2.1
+ import QtQuick.Controls 1.1
+
+ ComboBox {
+ editable: true
+ model: 10
+ validator: IntValidator {bottom: 0; top: 10;}
+ focus: true
+ }
+ \endqml
+
+ \sa acceptableInput, accepted, editable
+ */
+ property alias validator: input.validator
+
+ /*!
+ \qmlproperty bool ComboBox::acceptableInput
+ \since QtQuick.Controls 1.1
+
+ Returns \c true if the combo box contains acceptable
+ text in the editable text field.
+
+ If a validator was set, this property will return \c
+ true if the current text satisfies the validator or mask as
+ a final string (not as an intermediate string).
+
+ \sa validator, accepted
+
+ */
+ readonly property alias acceptableInput: input.acceptableInput
+
+ /*!
+ \qmlsignal ComboBox::accepted()
+ \since QtQuick.Controls 1.1
+
+ This signal is emitted when the Return or Enter key is pressed on an
+ \l editable combo box. If the confirmed string is not currently in the model,
+ the currentIndex will be set to -1 and the \l currentText will be updated
+ accordingly.
+
+ \note If there is a \l validator set on the combobox,
+ the signal will only be emitted if the input is in an acceptable state.
+ */
+ signal accepted
+
+ /*!
+ \qmlsignal ComboBox::activated(int index)
+ \since QtQuick.Controls 1.1
+
+ \a index is the triggered model index or -1 if a new string is accepted
+
+ This signal is similar to currentIndex changed, but will only
+ be emitted if the combo box index was changed by the user and not
+ when set programatically.
+ */
+ signal activated(int index)
+
+ /*!
+ \qmlmethod ComboBox::selectAll()
+ \since QtQuick.Controls 1.1
+
+ Causes all \l editText to be selected.
+ */
+ function selectAll() {
+ input.selectAll()
+ }
+
/*! \internal */
property var __popup: popup
@@ -142,6 +289,118 @@ Control {
popup.resolveTextValue(textRole)
}
+ Keys.onPressed: {
+ // Perform one-character based lookup for non-editable combo box
+ if (!editable && event.text.length > 0) {
+ var index = input.find(event.text, Qt.MatchStartsWith);
+ if (index >= 0 && index !== currentIndex) {
+ currentIndex = index;
+ activated(currentIndex);
+ }
+ }
+ }
+
+ TextInput {
+ id: input
+
+ visible: editable
+ enabled: editable
+ focus: true
+ clip: contentWidth > width
+ text: currentText
+
+ anchors.fill: parent
+ anchors.leftMargin: 8
+ anchors.rightMargin: 24
+ verticalAlignment: Text.AlignVCenter
+
+ renderType: Text.NativeRendering
+ selectByMouse: true
+ selectionColor: syspal.highlight
+ selectedTextColor: syspal.highlightedText
+ onAccepted: {
+ var idx = input.find(editText)
+ if (idx > -1) {
+ var string = textAt(idx);
+ if (string.length === editText.length) {
+ currentIndex = idx;
+ editText = string;
+ }
+ } else {
+ currentIndex = -1;
+ popup.currentText = editText;
+ }
+ comboBox.accepted();
+ }
+
+ SystemPalette { id: syspal }
+
+ property bool blockUpdate: false
+ property string prevText: null
+
+ function find (text, searchType) {
+ for (var i = 0 ; i < popupItems.count ; ++i) {
+ var currentString = popupItems.objectAt(i).text
+ if (searchType === Qt.MatchExactly) {
+ if (text === currentString)
+ return i;
+ } else if (searchType === Qt.CaseSensitive) {
+ if (currentString.indexOf(text) === 0)
+ return i;
+ } else if (currentString.toLowerCase().indexOf(text.toLowerCase()) === 0) {
+ return i
+ }
+ }
+ return -1;
+ }
+
+ // Finds first entry and shortest entry. Used by editable combo
+ function tryComplete (inputText) {
+ var candidate = "";
+ var shortestString = "";
+ for (var i = 0 ; i < popupItems.count ; ++i) {
+ var currentString = popupItems.objectAt(i).text;
+
+ if (currentString.toLowerCase().indexOf(inputText.toLowerCase()) === 0) {
+ if (candidate.length) { // Find smallest possible match
+ var cmp = 0;
+
+ // We try to complete the shortest string that matches our search
+ if (currentString.length < candidate.length)
+ candidate = currentString
+
+ while (cmp < Math.min(currentString.length, shortestString.length)
+ && shortestString[cmp].toLowerCase() === currentString[cmp].toLowerCase())
+ cmp++;
+ shortestString = shortestString.substring(0, cmp);
+ } else { // First match, select as current index and find other matches
+ candidate = currentString;
+ shortestString = currentString;
+ }
+ }
+ }
+
+ if (candidate.length)
+ return inputText + candidate.substring(inputText.length, candidate.length);
+ return inputText;
+ }
+
+ property bool allowComplete: false
+ Keys.onPressed: allowComplete = (event.key !== Qt.Key_Backspace && event.key !== Qt.Key_Delete);
+
+ onTextChanged: {
+ if (editable && !blockUpdate && allowComplete) {
+ var completed = input.tryComplete(text)
+ if (completed.length > text.length) {
+ var oldtext = input.text;
+ input.text = completed;
+ input.select(text.length, oldtext.length);
+ }
+ }
+ prevText = text
+ }
+ }
+
onTextRoleChanged: popup.resolveTextValue(textRole)
Menu {
@@ -150,6 +409,9 @@ Control {
style: isPopup ? __style.__popupStyle : __style.__dropDownStyle
+ property string currentText: selectedText
+ onSelectedTextChanged: if (selectedText) popup.currentText = selectedText
+
readonly property string selectedText: items[__selectedIndex] ? items[__selectedIndex].text : ""
property string textRole: ""
@@ -170,8 +432,12 @@ Control {
MenuItem {
text: popup.textRole === '' ?
modelData :
- ((popup.__modelIsArray ? modelData[popup.textRole] : model[popup.textRole]) || '')
-
+ ((popup.__modelIsArray ? modelData[popup.textRole] : model[popup.textRole]) || '')
+ onTriggered: {
+ if (index !== currentIndex)
+ activated(index)
+ comboBox.editText = text
+ }
checkable: true
exclusiveGroup: eg
}
@@ -236,6 +502,24 @@ Control {
if (!popup.popupVisible)
popup.show()
}
- Keys.onUpPressed: { if (currentIndex > 0) currentIndex-- }
- Keys.onDownPressed: { if (currentIndex < popupItems.count - 1) currentIndex++ }
+
+ Keys.onUpPressed: {
+ input.blockUpdate = true
+ if (currentIndex > 0) {
+ currentIndex--;
+ input.text = popup.currentText;
+ activated(currentIndex);
+ }
+ input.blockUpdate = false;
+ }
+
+ Keys.onDownPressed: {
+ input.blockUpdate = true;
+ if (currentIndex < popupItems.count - 1) {
+ currentIndex++;
+ input.text = popup.currentText;
+ activated(currentIndex);
+ }
+ input.blockUpdate = false;
+ }
}
diff --git a/src/controls/Styles/Base/ComboBoxStyle.qml b/src/controls/Styles/Base/ComboBoxStyle.qml
index 5a0ef9377..2011b219f 100644
--- a/src/controls/Styles/Base/ComboBoxStyle.qml
+++ b/src/controls/Styles/Base/ComboBoxStyle.qml
@@ -99,6 +99,44 @@ Style {
}
}
+ /*! \internal */
+ property Component __editor: Item {
+ implicitWidth: 100
+ implicitHeight: 25
+ clip: true
+ BorderImage {
+ anchors.fill: parent
+ anchors.rightMargin: -2
+ source: "images/editbox.png"
+ border.left: 4
+ border.right: 4
+ border.top: 4
+ border.bottom: 4
+ BorderImage {
+ anchors.fill: parent
+ anchors.margins: -1
+ anchors.topMargin: -2
+ anchors.rightMargin: 0
+ anchors.bottomMargin: 1
+ source: "images/focusframe.png"
+ visible: control.activeFocus
+ border.left: 4
+ border.right: 4
+ border.top: 4
+ border.bottom: 4
+ }
+ }
+ Rectangle {
+ color: "#aaa"
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 3
+ anchors.topMargin: 1
+ width: 1
+ }
+ }
+
/*! This defines the label of the button. */
property Component label: Item {
implicitWidth: textitem.implicitWidth + 20
@@ -118,18 +156,28 @@ Style {
property bool popup: false
anchors.centerIn: parent
anchors.fill: parent
- implicitWidth: Math.max(labelLoader.implicitWidth + padding.left + padding.right, backgroundLoader.implicitWidth)
+ implicitWidth: 125
implicitHeight: Math.max(labelLoader.implicitHeight + padding.top + padding.bottom, backgroundLoader.implicitHeight)
Loader {
id: backgroundLoader
anchors.fill: parent
sourceComponent: background
+
+ }
+
+ Loader {
+ id: editorLoader
+ anchors.fill: parent
+ anchors.rightMargin: 20
+ anchors.bottomMargin: -1
+ sourceComponent: control.editable ? __editor : null
}
Loader {
id: labelLoader
sourceComponent: label
+ visible: !control.editable
anchors.fill: parent
anchors.leftMargin: padding.left
anchors.topMargin: padding.top
diff --git a/src/controls/Styles/Desktop/ComboBoxStyle.qml b/src/controls/Styles/Desktop/ComboBoxStyle.qml
index 91a8e82c6..e6495cebc 100644
--- a/src/controls/Styles/Desktop/ComboBoxStyle.qml
+++ b/src/controls/Styles/Desktop/ComboBoxStyle.qml
@@ -47,7 +47,7 @@ Style {
property Component panel: Item {
property bool popup: !!styleItem.styleHint("comboboxpopup")
- implicitWidth: 115
+ implicitWidth: 125
implicitHeight: styleItem.implicitHeight
anchors.fill: parent
StyleItem {
@@ -70,7 +70,8 @@ Style {
hints: control.styleHints
properties: {
- "popup": control.__popup
+ "popup": control.__popup,
+ "editable" : control.editable
}
}
}