QtQuick2 - QML reusable item focus changes when selecting/clicking outside/inside of root item

1.4k views Asked by At

How can I implement focus changing state into reusable QML component when I clicked or selected another item outside component body to call any event (highlight text, show/hide rectangle and etc). There is code I trying to implement highlight line under CheckBox text when item have focus, and hide that when focus is lost. Please help me to understand more effective way to implement it. Here is the source of element:

BreezeQuickCheckbox.qml

import QtQuick 2.4

Item {
    id: root
    property BreezeQuickPalette palette: BreezeQuickPalette
    property bool checked: false
    property string caption: "Checkbox"
    property int fontSize: 18
    implicitHeight: 48
    implicitWidth: bodyText.width + 48
    Rectangle{
        id: body
        width: 32
        height: 32
        anchors {
            verticalCenter: parent.verticalCenter
            left: parent.left
            leftMargin: 8
            rightMargin: 8
        }
        radius: 2
        border{
            width: 1
            color: palette.iconGrey
        }
        color: "transparent"
    }
    Rectangle{
        id: check
        opacity: 1
        radius: 1
        color: palette.focusColor
        anchors {
            verticalCenter: body.verticalCenter
        }
        anchors.fill: body
        anchors.margins: 4
        transform: Rotation {
            id: checkRotation
            origin.x: check.width/2
            origin.y: check.height/2
            axis {
                x: 1
                y: 1
                z: 0
            }
            angle: 90
            Behavior on angle {
                NumberAnimation {
                    duration: 150
                    easing.type: Easing.InOutQuad
                }
            }
        }
        smooth: true
    }
    Text {
        id: bodyText
        anchors {
            verticalCenter: parent.verticalCenter
            left: body.right
        }
        color: palette.normalText
        text: caption
        anchors.leftMargin: 8
        font.pointSize: fontSize
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
    Rectangle {
        id: highlightLine
        anchors{
            horizontalCenter: bodyText.horizontalCenter
            top: bodyText.bottom
            topMargin: -2
        }
        width: bodyText.width
        height: 2
        color: palette.focusColor
    }
    MouseArea{
        id: bodyArea
        anchors.fill: parent
        hoverEnabled: true
        onEntered: {
            if (checked == false){
                body.border.color = palette.focusColor
            } else if (checked == true){
                body.border.color = palette.lightPlasmaBlue
                check.color = palette.lightPlasmaBlue
            }
        }
        onExited: {
            if (checked == false){
                body.border.color = palette.iconGrey
            }
            if (checked == true){
                body.border.color = palette.focusColor
                check.color = palette.focusColor
            }
        }
        onClicked: {
            if (checked == false){
                checked = true
                body.border.color = palette.lightPlasmaBlue
                checkRotation.angle = 0
            } else if (checked == true){
                checked = false
                body.border.color = palette.focusColor
                checkRotation.angle = 90
            }
        }
    }
    onCheckedChanged: {
        if (checked == true){
            body.border.color = palette.focusColor
            if (bodyArea.containsMouse) {
                check.color = palette.lightPlasmaBlue
                checkRotation.angle = 0
            } else if (!bodyArea.containsMouse){
                check.color = palette.focusColor
                checkRotation.angle = 0
            }
        } else if (checked == false){
            body.border.color = palette.iconGrey
            checkRotation.angle = 90
        }
    }
}

You can see this snippet where actual line is declared:

    Rectangle {
        id: highlightLine
        anchors{
            horizontalCenter: bodyText.horizontalCenter
            top: bodyText.bottom
            topMargin: -2
        }
        width: bodyText.width
        height: 2
        color: palette.focusColor
    }

Here is the palette code, if you are interested in it - it's subset of components with it's own palette:

BreezeQuickPalette.qml

import QtQuick 2.4

QtObject {
    id: palette
    property string theme: "light"


    readonly property color normalText: if (theme == "light"){
                                      charcoalGrey
                                  } else if (theme == "dark"){
                                      cardboardGrey
                                  }
    readonly property color inactiveText: asphalt
    readonly property color activeText: plasmaBlue
    readonly property color linkText: seaBlue
    readonly property color negativeText: iconRed
    readonly property color neutralText: bewareOrange
    readonly property color positiveText: deepGreen
    readonly property color focusColor: plasmaBlue
    readonly property color hoverColor: plasmaBlue
    readonly property color normalBackground: if (theme == "light"){
                                      cardboardGrey
                                  } else if (theme == "dark"){
                                                  charcoalGrey
                                              }

    readonly property color alternateBackground: if (theme == "light"){
                                      steel
                                  } else if (theme == "dark"){
                                                     shadeBlack
                                                 }

    readonly property color paperWhite: "#fcfcfc"
    readonly property color cardboardGrey: "#eff0f1"
    readonly property color iconGrey: "#4d4d4d"
    readonly property color charcoalGrey: "#31363b"
    readonly property color shadeBlack: "#232629"
    readonly property color plasmaBlue: "#3daee9"
    readonly property color iconRed: "#da4453"
    readonly property color dangerRed: "#ed1515"
    readonly property color iconOrange: "#f47750"
    readonly property color bewareOrange: "#f67400"
    readonly property color iconYellow: "#fdbc4b"
    readonly property color sunbeamYellow: "#c9ce3b"
    readonly property color mellowTurquoise: "#1cdc9a"
    readonly property color iconGreen: "#2ecc71"
    readonly property color verdantGreen: "#11d116"
    readonly property color iconBlue: "#1d99f3"
    readonly property color seaBlue: "#2980b9"
    readonly property color skyBlue: "#3498db"
    readonly property color turquoise: "#1abc9c"
    readonly property color deepGreen: "#27ae60"
    readonly property color deepTurquoise: "#16a085"
    readonly property color peach: "#e74c3f"
    readonly property color lightMaroon: "#c0392b"
    readonly property color deepYellow: "#f39c1f"
    readonly property color purple: "#9b59b6"
    readonly property color deepPurple: "#8e44ad"
    readonly property color steelBlue: "#34495e"
    readonly property color charcoal: "#2c3e50"
    readonly property color asphalt: "#7f8c8d"
    readonly property color steel: "#bdc3c7"
    readonly property color grey: "#95a5a6"
    readonly property color lightPlasmaBlue: "#63beed"
}

Please help me to understand how can I make this Rectangle visible or hidden once I clicked or selected something outside/inside of root item. This is what I'm trying to see when reusable CheckBox is focused, and hide underline when focus gone and passed to another component or even clicked outside of element.

enter image description here

1

There are 1 answers

2
mcchu On BEST ANSWER

Bind highlightLine.visible to root.activeFocus so the highlight only shows when the checkbox gains active focus:

Rectangle {
    id: highlightLine
    //...
    visible: root.activeFocus
}

The active focus can be set in many ways. For example, when user clicked the checkbox:

MouseArea{
    id: bodyArea
    onClicked: {
        //...
        root.forceActiveFocus(); //set root's active focus when mouse clicked
    }
}

And you can write a simple program to verify the result:

Row {
   BreezeQuickCheckbox {}
   BreezeQuickCheckbox {}
   BreezeQuickCheckbox {}
}

For more information about focus, check Keyboard focus in Qt Quick.