pappachino
pappachino

Reputation: 55

Control closing of popup for QML ComboBox with checkboxes

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

Answers (2)

Mitch
Mitch

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

Mark Ch
Mark Ch

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

Related Questions