18

How to make a QML button toggle that follows / controls any Boolean property

 3 years ago
source link: https://www.codesd.com/item/how-to-make-a-qml-button-toggle-that-follows-controls-any-boolean-property.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to make a QML button toggle that follows / controls any Boolean property

advertisements

My QML/QtQuick exercise for today is to make a little ToggleButton widget that I can instantiate to monitor the state of a specified boolean QML property, and that also toggles that property's state when I click on the ToggleButton.

So far I have this for my ToggleButton component:

// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4

Button {
    property bool isActive: false

    onClicked: {
        isActive = !isActive;
    }

    style: ButtonStyle {
        background: Rectangle {
            border.width: control.activeFocus ? 2 : 1
            border.color: "black"
            radius: 4
            color:  isActive ? "red" : "gray";
        }
    }
}

.... and here is my little test harness that I use to see whether it works the way I want it to, or not:

// Contents of main.qml
import QtQuick 2.6
import QtQuick.Window 2.2

Window {
   visible: true
   width: 360
   height: 360

   Rectangle {
      property bool lighten: false;

      id:blueRect
      x: 32; y:32; width:64; height:64
      color: lighten ? "lightBlue" : "blue";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   Rectangle {
      property bool lighten: false;

      id:greenRect
      x:192; y:32; width:64; height:64
      color: lighten ? "lightGreen" : "green";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   ToggleButton {
      x:32; y:128
      text: "Bright Blue Rect"
      isActive: blueRect.lighten
   }

   ToggleButton {
      x:192; y:128
      text: "Bright Green Rect"
      isActive: greenRect.lighten
   }
}

You can run this by saving the code to ToggleButton.qml and main.qml (respectively) and then running "qmlscene main.qml".

Note that if you click on the blue or green rectangles, it works as expected; the boolean "lighten" property of the Rectangle object is toggled on and off, causing the Rectangle to change color, and the associated ToggleButton also reacts appropriately (by turning itself red when the "lighten" property is true, and gray when the "lighten" property is false).

So far, so good, but if you then click on the ToggleButton itself, the binding is broken: that is, clicking on the ToggleButton causes the ToggleButton to turn red/gray as expected, but the rectangle's color doesn't follow suit, and after doing that, clicking on the rectangle no longer causes the ToggleButton's state to change, either.

My question is, what is the trick to doing this properly, so that I always have a bidirectional correspondence between the ToggleButton and the property it is tracking? (Note that the ideal solution would add as little code as possible to main.qml, since I'd like this functionality to be encapsulated inside ToggleButton.qml, to minimize the amount of complexity exposed to the rest of my QML app)


The reason this does not work is that you are overwriting the binding with a fixed value. By manually assigning a value in you onClicked you overwrite the binding with a value.

The problem is: QML does not support 2way bindings right now. There are, however, some tricks to create one. See http://imaginativethinking.ca/bi-directional-data-binding-qt-quick/

Note: Instead of using the isActive property like this, why not use the checked state of the button? (From the documentation this way the binding won't break, even if you click the button:

// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4

Button {
    //use the "checked" property instead of your own "isActive"

    checkable: true

    style: ButtonStyle {
        background: Rectangle {
            border.width: control.activeFocus ? 2 : 1
            border.color: "black"
            radius: 4
            color:  checked? "red" : "gray";
        }
    }
}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK