QComboBox for QML

QML does not come (yet?) with the equivalent of a QComboBox. As I find a ComboBox quite useful, even for touch UIs, I wrote a simple wrapper / adapter or whatever you want to call it that enables using a QComboBox in QML. Thereby the goal was to integrate the native QComboBox Qt widget as good as possible with QML, i.e., give it the look and feel of QML and make it work with the touch UI. The following pictures show the “QML’ed” QComboBox in action in MeePasswords on Maemo Fremantle and MeeGo / Harmattan respectively.

The C++ code consists of three parts.

The header file (qcomboboxqmladapter.h):

#ifndef QCOMBOBOXQMLADAPTER_H
#define QCOMBOBOXQMLADAPTER_H

#include
#include
#include
#include

#include "entrylistmodel.h"

class CustomComboBox : public QComboBox {
    Q_OBJECT
public:
    explicit CustomComboBox(QWidget *parent = 0) : QComboBox(parent){}

protected:
    void focusInEvent(QFocusEvent *event){ QComboBox::focusInEvent(event); emit focusIn(); }
    void focusOutEvent(QFocusEvent *event){ QComboBox::focusOutEvent(event); emit focusOut(); }

signals:
    void focusIn();
    void focusOut();
};

class QComboBoxQmlAdapter : public QGraphicsProxyWidget
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
    explicit QComboBoxQmlAdapter(QGraphicsItem *parent = 0);
    ~QComboBoxQmlAdapter();

    void setText(const QString text) { m_comboBox->setEditText(text); }
    QString text() { return m_comboBox->currentText(); }

    Q_INVOKABLE void setItems(const QStringList &items);

signals:
    void focusIn();
    void focusOut();

    void textChanged(QString text);

private:
    CustomComboBox *m_comboBox;

};
#endif // QCOMBOBOXQMLADAPTER_H

The *.cpp file (qcomboboxqmladapter.cpp):

#include "qcomboboxqmladapter.h"

QComboBoxQmlAdapter::QComboBoxQmlAdapter(QGraphicsItem *parent) :
    QGraphicsProxyWidget(parent)
{
    m_comboBox = new CustomComboBox(0);
    m_comboBox->setEditable(true);

    QFont font = QFont(m_comboBox->font().family(), 17);
    m_comboBox->setFont(font);
    m_comboBox->setFrame(false);

#ifdef MEEGO_EDITION_HARMATTAN
    m_comboBox->setStyleSheet("QComboBox { border: 3px solid gray; border-radius: 18px; padding: 8px 20px 6px 8px; margin-top: 2px; border-color: lightgray; border-bottom-color: white; background: white; selection-background-color: rgb(70, 140, 250) }"
                              "QComboBox:focus { border-color: rgb(70, 140, 250) }"
                              "QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 40px; border-style: none; padding-right 10px; selection-background-color: rgb(70, 140, 250) }"
                              "QComboBox::down-arrow { image: url(/usr/share/themes/blanco/meegotouch/icons/icon-m-toolbar-down.png); }"
                              "QListView { selection-background-color: rgb(70, 140, 250); }"
                              );
#elif defined(Q_WS_MAEMO_5)
    m_comboBox->setStyleSheet("QComboBox { border: 3px solid gray; border-radius: 18px; padding: 2px 20px 2px 8px; border-color: lightgray; background: white; selection-background-color: rgb(70, 140, 250) }"
                              "QComboBox:focus { border-color: rgb(70, 140, 250) }"
                              "QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 40px; border-style: none; padding-right 10px; selection-background-color: rgb(70, 140, 250) }"
                              "QComboBox::down-arrow { image: url(/usr/share/themes/default/images/ComboBoxButtonNormal.png); }"
                              "QListView { selection-background-color: rgb(70, 140, 250); }"
                              );
#else
    m_comboBox->setStyleSheet("QComboBox { border: 3px solid gray; border-radius: 18px; padding: 8px 20px 8px 8px; border-color: lightgray; background: white; selection-background-color: rgb(70, 140, 250) }"
                              "QComboBox:focus { border-color: rgb(70, 140, 250) }"
                              "QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 40px; border-style: none; padding-right 10px; selection-background-color: rgb(70, 140, 250) }"
                              "QListView { selection-background-color: rgb(70, 140, 250); }"
                              );
#endif

    connect(m_comboBox, SIGNAL(focusIn()), this, SIGNAL(focusIn()));
    connect(m_comboBox, SIGNAL(focusOut()), this, SIGNAL(focusOut()));
    connect(m_comboBox, SIGNAL(currentIndexChanged(QString)), this, SIGNAL(textChanged(QString)));

    setWidget(m_comboBox);
}

QComboBoxQmlAdapter::~QComboBoxQmlAdapter(){
    delete m_comboBox;
}

void QComboBoxQmlAdapter::setItems(const QStringList &items){
    QString temp = m_comboBox->currentText();
    m_comboBox->clear();
    m_comboBox->addItems(items);
    m_comboBox->setEditText(temp);
}

Then our new “QML ComboBox” needs to be registered such that it can be used in QML. This can be done, e.g., in the main.cpp file, as follows:

#include

#include "qcomboboxqmladapter.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    ...
    /*
     * The following code registers the class QComboBoxQmlAdapter as QML
     * element "QComboBox" in the QML plugin "meepasswords" with version 1.0.
     */
    qmlRegisterType("meepasswords", 1, 0, "QComboBox");
    ...
}

Finally, we can use our new QML QComboBox as follows (this code is taken from the MeeGo / Harmattan version of MeePasswords):

...
import meepasswords 1.0
...
    QComboBox{id: categoryInput; width: 0.5 * parent.width; z: 3
        TextInput{ id: textInput }

        onFocusIn: textInput.openSoftwareInputPanel()

        onFocusOut: textInput.closeSoftwareInputPanel()

        Component.onCompleted: {
            categoryInput.setItems(["foo", "bar", "snafu"])
        }
    }
...

In this example the combobox is “filled” with items via a hardcoded list of strings in Component.onCompleted. In a real application one would most probably use something which is more dynamic. Though as an example this should suffice.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

This entry was posted in Qt/QML, Snippets and tagged , , , , , , , . Bookmark the permalink.

2 Responses to QComboBox for QML

  1. Mauro says:

    Hi, congratulations for you code, this project helped me with mine.
    However you let me make a correction, in the main file to register the class instruction should be

    qmlRegisterType &lt QComboBoxQmlAdapter,1 &gt (“meepasswords”, 1, 0, “QComboBox”);

    We must also add the library qmldeclarative.h by a bug in QT4.7

    “” Since qmlRegisterType is listed as related to QDeclarativeEngine it would seem logical to #include in order to be able to use the qmlRegisterType function. However, this is not the case. Instead, you must #include . This is not mentioned in the documentation. I actually had to grep the source code to figure this out! “”

    https://bugreports.qt-project.org/browse/QTBUG-15630

  2. Sergey says:

    QComboBoxQmlAdapter::~QComboBoxQmlAdapter(){
    delete m_comboBox; // — is not necessary, because m_comboBox destroyed as child of QGraphicsProxyWidget (result of setWidget)
    }

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.