Reputation: 73081
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)
Upvotes: 2
Views: 8304
Reputation: 7146
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";
}
}
}
Upvotes: 5
Reputation: 73081
It looks like one way to solve this problem is to have the ToggleButton declare its state using an alias-property, rather than a regular property. That way there is only the one external property (since the ToggleButton's internal property is really just an alias to the external one), and therefore no bidirectional binding is necessary. Here's an updated version of the QML code that works as expected:
ToggleButton.qml:
// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
Button {
onClicked: {
isActive = !isActive;
}
style: ButtonStyle {
background: Rectangle {
border.width: control.activeFocus ? 2 : 1
border.color: "black"
radius: 4
color: isActive ? "red" : "gray";
}
}
}
main.qml:
// 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"
property alias isActive: blueRect.lighten
}
ToggleButton {
x:192; y:128
text: "Bright Green Rect"
property alias isActive: greenRect.lighten
}
}
Upvotes: 1