Creating QML Controls From Scratch

Chris Cortopassi


A seasoned engineer with 10+ years experience, Chris has expertise in desktop and embedded systems in larger, real-time multi-threaded applications, as well as experience using Qt-based software for complex projects within the motor control, telecommunications and civil engineering industries. 

By Chris Cortopassi | Sunday, March 12, 2017

Continuing our QML Controls from Scratch series, this time we'll implement a CheckBox. We'll also get RadioButton almost for free. CheckBox is similar to Button with the exception that it holds checked/unchecked state in the checked property. All QML properties have an associated *Changed signal, so the checkedChanged() signal (useful for C++) causes onCheckedChanged to run when the property checked changes.

CheckBox also serves as a radio button if a client sets radio: true, which is all RadioButton.qml does. To get multiple radio buttons to act together in a group, put a RadioButton in a ListView delegate with currentIndex indicating the currently selected radio button (see test.qml below).

Since we're not using Image, we used a unicode check ✓ (hexadecimal 2713) glyph to render the check mark as Text, but you might want to replace with an Image .png asset from your designer. The RadioButton dot is implemented with a Rectangle.

Take a look:

CheckBox.qml

import QtQuick 2.0

Item { // size controlled by height
    id: root
    
// public    
    property string text:    'text'
    property bool   checked: false; // onCheckedChanged: print('onCheckedChanged', checked)
    
    signal clicked(bool checked);   // onClicked: print('onClicked', checked) // only runs via user interaction whereas checkedChanged runs via programmatic changes to checked as well

// private
    property real padding: 0.1    // around rectangle: percent of root.height
    property bool radio:   false  // false: check box, true: radio button
    
    width: 250;  height: 50  // default size
    //width: rectangle.width + 3 * padding * root.height + text.width;  height: 50 // width expands with text width
    opacity: enabled? 1: 0.3 // disabled state
      
    Rectangle { // check box (or circle for radio button)
        id: rectangle
        
        height: root.height * (1 - 2 * padding);  width: height // square
        x: padding * root.height
        anchors.verticalCenter: parent.verticalCenter
        border.width: 0.05 * root.height
        radius: (radio? 0.5: 0.2) * height
        
        Text { // check
            visible: checked  &&  !radio
            anchors.centerIn: parent
            text: '\u2713' // http://www.fileformat.info/info/unicode/char/2713/index.htm
            font.pixelSize: parent.height
        }
        
        Rectangle { // radio dot
            visible: checked  &&  radio
            color: 'black'
            width: 0.5 * parent.width;  height: width // square
            anchors.centerIn: parent
            radius: 0.5 * width // circle
        }
    }

    Text {
        id: text
        
        text: root.text
        anchors {left: rectangle.right;  verticalCenter: rectangle.verticalCenter;  margins: padding * root.height}
        font.pixelSize: 0.5 * root.height
    }
    
    MouseArea {
        anchors.fill: parent
        onPressed:  parent.opacity = 0.5 // down state
        onReleased: parent.opacity = 1
        onCanceled: parent.opacity = 1
        onClicked: {
            if(!radio)  checked = !checked // clients of radio button must set checked to keep them all in sync
            root.clicked(checked) // emit
        }
    }
}

RadioButton.qml

import QtQuick 2.0

CheckBox {
    radio: true
}

test.qml

CheckBox {            
    text:             'CheckBox'
    checked:          true
    onCheckedChanged: print('CheckBox onCheckedChanged', checked)
}
        
ListView { // RadioButton
    width: 250;  height: 50 * count
    
    model:[{text: 'RadioButton 0'}, {text: 'RadioButton 1'}]
    
    delegate: RadioButton {
        text:             modelData.text
        checked:          currentIndex == index // equality
        onClicked:        currentIndex =  index // assignment
        onCheckedChanged: print('RadioButton onCheckedChanged', index, checked)
    }
}

Summary

In this post, we created CheckBox and RadioButton. Next time we'll create Switch. The source code can be downloaded here.


Tags:  QML

Have a question or add to the conversation: Log in Register