Reputation: 55
I'm building a custom QML ComboBox with checkboxes. It displays all well, but I am not able to control the popup closing event.
I would like the combobox popup to stay open, so I could check multiple items. And close only if I click outside my parent or hit escape. Currently it closes as soon as I check one item.
I am using the CheckDelegate so I could override the combobox popup appearance. However it doesn't wait allow me to check multiple items in one go.
Here is my sample code for the custom combobox
import QtQuick 2.7
import QtQuick.Controls 2.1
ComboBox {
id: control
property alias combo_box_model: control.model
property string combo_box_displayText: control.displayText
property var combo_box_height
model: combo_box_model
delegate: CheckDelegate {
id: checkbox_control
width: control.width
contentItem: Text {
leftPadding: checkbox_control.indicator.width + control.leftPadding
text: modelData
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: control.highlightedIndex === index
// checked: combo_box_model.isChecked(index)
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.leftPadding
anchors.verticalCenter: parent.verticalCenter
radius: 3
color: "transparent"
border.color: checkbox_control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: checkbox_control.down ? "#17a81a" : "#21be2b"
visible: checkbox_control.checked
}
}
// onClicked: {
// combo_box_model.setChecked(index, checked)
// }
}
contentItem: Text {
leftPadding: 0
rightPadding: control.indicator.width + control.spacing
text: control.displayText
font: control.font
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
popup: Popup {
id: checkbox_popup
y: control.height - 1
width: control.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: combo_box_height ? combo_box_height : contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
}
}
For the combobox popup (checkbox_popup), I tried setting the closePolicy to NoAutoClose, but no luck.
So I feel somewhere in the CheckDelegate I need to catch the close event or so and handle it. But not sure exactly how or am I missing something ? Quite a newbie as far as QML is concerned.
Upvotes: 4
Views: 3256
Reputation: 24386
I agree with Mark here: ComboBox
is meant to display a single selected item when it is closed, so having multiple selections doesn't make sense.
But I also agree that it's fun to try it anyway. :D Here's a way that exploits the fact that ComboBox
relies on the delegate being an AbstractButton
:
import QtQuick 2.6
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
ComboBox {
id: comboBox
model: ListModel {
ListElement {
name: "A"
checked: false
}
ListElement {
name: "B"
checked: false
}
ListElement {
name: "C"
checked: false
}
}
delegate: Item {
width: parent.width
implicitHeight: checkDelegate.implicitHeight
CheckDelegate {
id: checkDelegate
width: parent.width
text: model.name
highlighted: comboBox.highlightedIndex === index
checked: model.checked
onCheckedChanged: model.checked = checked
}
}
}
}
Upvotes: 3
Reputation: 3020
This seems quite a complicated override of the Combobox delegate, and maybe not the right approach.
CheckDelegate inherits ItemDelegate, which is responsible for the very behaviour you are trying to avoid (closing the popup after a single click). If you override the delegate with a different item, e.g. Rectangle, MouseArea, or Item, then the autoclosing behaviour would go away.
Also, the combobox is designed to have one currentItem selected at once. It usually returns a single currentIndex when the currentItem is changed. To alter this behaviour I feel is an uphill struggle, and you might be better simply using a popup with a listview, rather than a Combobox.
However, your challenge looked fun, so here is one way of fixing your combobox:
ComboBox {
id: control
property bool forceOpen: false
model: ["alpha", "beta", "gamma"]
delegate: CheckDelegate {
id: checkbox_control
width: control.width
contentItem: Text {
leftPadding: checkbox_control.indicator.width + control.leftPadding
text: modelData
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: control.highlightedIndex === index
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.leftPadding
anchors.verticalCenter: parent.verticalCenter
radius: 3
color: "transparent"
border.color: checkbox_control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: checkbox_control.down ? "#17a81a" : "#21be2b"
visible: checkbox_control.checked
}
}
}
popup: Popup {
id: checkbox_popup
y: control.height - 1
width: control.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
onClosed: if (control.forceOpen) open()
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
border.width: !control.editable && control.visualFocus ? 2 : 0
visible: !control.flat || control.down
MouseArea {
anchors.fill: parent
onClicked: {
if (control.popup.visible) {
control.forceOpen = false
control.popup.close()
} else {
control.forceOpen = true
control.popup.open()
}
}
}
}
}
Upvotes: 1