I have made custom Button in QML and I want it to be the base item for all buttons in my project. What I want is to derive (like in OOP) buttons from it to change functionality. At first, every derived button should have its own onClicked response.
Is this possible in QML and if it is, how?
It's possible.
Here is an example of my BaseButton type. When BaseButton is clicked it emits a custom signal, in my code it's sgnClicked.
BaseButton.qml
import QtQuick 2.0
Item {
id:idButton
signal sgnClicked()
signal sgnClickMaintained()
signal sgnEntered()
signal sgnExited()
signal sgnReleased()
signal sgnPressed()
signal sgnCanceled()
property alias label: idText
property alias text: idText.text
property string iconSourceUp:""
property string iconSourceDown:""
property string iconSourceDisabled:""
property alias backgroundWidth: background.width
property alias backgroundHeight: background.height
property alias backgroundRect: backgroundRect
property alias hover: touchArea.enabled
width: 100
height: 20
Text{
id:idText
//text: "Button"
color:"white"
anchors.centerIn: parent
font.pointSize: 12
}
//if maintained at each 300 ms resend signal
Timer{
id:timer
running: false
repeat: true
interval: 300
onTriggered: sgnClickMaintained()
}
Image{
id:background
z: -1
width: parent.width
height: parent.height
source:iconSourceUp
visible: source.toString() !== ""
}
Gradient {
id:idGradient
GradientStop { position: 0 ; color: touchArea.pressed ? "#ccc" : "#eee" }
GradientStop { position: 1 ; color: touchArea.pressed ? "#aaa" : "#ccc" }
}
Rectangle {
id: backgroundRect
width: parent.width
height: parent.height
border.color: "#888"
color: enabled ?"":"lightgrey"
radius: 4
z: -1
visible: !background.visible
gradient: enabled ?idGradient:null
}
MouseArea{
id: touchArea
anchors.fill: parent
hoverEnabled: true
onCanceled: idButton.sgnCanceled()
onClicked:
{
idButton.sgnClicked()
}
onEntered: idButton.sgnEntered()
onExited: idButton.sgnExited()
onPressAndHold: {
timer.restart()
idButton.sgnClickMaintained()
}
onReleased:{
idButton.state = "up"
timer.stop()
idButton.sgnReleased()
}
onPressed:
{
idButton.state = "down"
idButton.sgnPressed()
}
}
onEnabledChanged: {
if(enabled === false)
{
idButton.state = "disabled"
timer.stop()
}
else{
idButton.state = "up"
}
}
states: [
State {
name: "down"
PropertyChanges {
target: background
source: iconSourceDown
}
},
State {
name: "disabled"
PropertyChanges {
target: background
source: iconSourceDisabled
}
},
State {
name: "up"
PropertyChanges {
target: background
source: iconSourceUp
}
}
]
Component.onCompleted:{
if(enabled)
{
state = "up"
}
else
{
state = "disabled"
}
}
}
To make a new Button inheriting from BaseButton you should instantiante the BaseButton as root item and in the slot onSgnClicked write what you want your button should do when clicked.
CustomButton.qml
import QtQuick 2.0
BaseButton {
width: 100
height: 50
onSgnClicked: {
//do something
}
}
Related
Here I have a rectangle that is the parent and a child rectangle. The child rectangle has a MouseArea that allows you to swipe the child rectangle. Now, I want to have a button on the parent rectangle (under the child rectangle and MouseArea) and I want to click that button even if the child rectangle is covering the button. Here is an example"
import QtQuick 2.15
import QtQuick.Window 2.0
import QtQuick.Controls 2.15
Window {
visible: true
width: 800
height: 800
Rectangle {
id: root
anchors.fill: parent
color: "yellow"
Button {
id: button1
x: 314
y: 182
text: qsTr("Button")
onClicked: console.log("Hello")
}
Rectangle {
id: panel
width: parent.width
height: parent.height * 0.8
radius: 20
color: "orange"
opacity: 0.2
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: panel
drag.minimumY: 0
drag.maximumY: 0
drag.minimumX: -panel.width
drag.maximumX: 0
onReleased: {
//if the panel is swiped more than 30% it will hide
//else it will go back to the original position
//this makes a pretty nice effect :)
if (panel.x < -panel.width * 0.3) {
//we need to make sure that a state change happens to
//fire the transition animation
root.state = "show"
root.state = "hide"
}
else {
root.state = "hide"
root.state = "show"
}
}
}
}
Rectangle {
id: button
width: 45
height: width
radius: 5
color: "lightblue"
anchors.bottom: parent.bottom
anchors.left: parent.left
MouseArea {
anchors.fill: parent
onClicked: {
root.state = root.state === "show" ? "hide" : "show"
}
}
}
state: "show"
states: [
State {
name: "hide"
PropertyChanges { target: panel; x: -panel.width }
},
State {
name: "show"
PropertyChanges { target: panel; x: 0 }
}
]
transitions: Transition {
NumberAnimation {
target: panel
property: "x"
duration: 1000
easing.type: Easing.OutCubic
}
}
}
}
How can I click on the button without swiping the child rectangle?
First you need to detect in mouseArea if drag is active or not, and if drag is not active then detect if point where mouse press event is triggered is also inside the button1 below the panel. You can do that by using Item's mapToItem method. If that is the case then you can set button1 pressed visualization manually on.
Then when released event is triggered you detect if you are still inside the button1 and emit special signal e.g. buttonBelowClicked. That signal needs to be connected to button1 clicked signal via signal chaining.
Note that you need to reset button1 pressed visualization always in mouseArea onReleased because you might have started panel dragging from the top of the button1 and button shows pressed visualization but then dragging gets enabled...
Window {
visible: true
width: 800
height: 800
Rectangle {
id: root
anchors.fill: parent
color: "yellow"
Button {
id: button1
x: 314
y: 182
text: qsTr("Button")
onClicked: console.log("Hello")
Component.onCompleted: mouseArea.buttonBelowClicked.connect(clicked)
}
Rectangle {
id: panel
width: parent.width
height: parent.height * 0.8
radius: 20
color: "orange"
opacity: 0.2
MouseArea {
id: mouseArea
signal buttonBelowClicked
anchors.fill: parent
drag.target: panel
drag.minimumY: 0
drag.maximumY: 0
drag.minimumX: -panel.width
drag.maximumX: 0
onPressed: {
if (!drag.active) {
if (isPointInsideButton1(mouse.x, mouse.y)) {
button1.down = true
}
}
}
onReleased: {
if (!drag.active) {
if (isPointInsideButton1(mouse.x, mouse.y)) {
buttonBelowClicked()
}
} else {
if (panel.x < -panel.width * 0.3) {
root.state = "show"
root.state = "hide"
}
else {
root.state = "hide"
root.state = "show"
}
}
button1.down = undefined
}
function isPointInsideButton1(x, y) {
const mapped = panel.mapToItem(button1, x, y)
if (button1.contains(Qt.point(mapped.x, mapped.y))) {
return true
}
return false
}
}
}
Rectangle {
id: button
width: 45
height: width
radius: 5
color: "lightblue"
anchors.bottom: parent.bottom
anchors.left: parent.left
MouseArea {
anchors.fill: parent
onClicked: {
root.state = root.state === "show" ? "hide" : "show"
}
}
}
state: "show"
states: [
State {
name: "hide"
PropertyChanges { target: panel; x: -panel.width }
},
State {
name: "show"
PropertyChanges { target: panel; x: 0 }
}
]
transitions: Transition {
NumberAnimation {
target: panel
property: "x"
duration: 1000
easing.type: Easing.OutCubic
}
}
}
}
the simplest solution is to place button1 below panel. Like this
Rectangle {
id: panel
width: parent.width
height: parent.height * 0.8
radius: 20
color: "orange"
opacity: 0.2
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: panel
drag.minimumY: 0
drag.maximumY: 0
drag.minimumX: -panel.width
drag.maximumX: 0
onReleased: {
//if the panel is swiped more than 30% it will hide
//else it will go back to the original position
//this makes a pretty nice effect :)
if (panel.x < -panel.width * 0.3) {
//we need to make sure that a state change happens to
//fire the transition animation
root.state = "show"
root.state = "hide"
}
else {
root.state = "hide"
root.state = "show"
}
}
}
}
Button {
id: button1
x: 314
y: 182
text: qsTr("Button")
onClicked: console.log("Hello")
}
i have created a custom button in qml, which i want to focus on pressing 'tab', i mean "jump to it on pressing tab if its on top of queue" by default qml has this functionality on button itself and some of other controls too but what about new components which does not exist,i saw a "FocusScope" control of qml but there is no documentation of using it and i am not sure how can i implement it, here is my control :
import QtQuick 2.4
Item {
id: button
width: innerText.width + 10
height: 30
property alias text: innerText.text;
property alias font: innerText.font;
property color color: "#00171f"
property color hoverColor: "#00395f"
property color pressColor: "#3E65FF"
property int fontSize: 12
property int borderWidth: 0
property int borderRadius: 2
property bool highlighted : true
onEnabledChanged: state = ""
signal clicked
property var background
Rectangle {
id: rectangleButton
anchors.fill: button
radius: borderRadius
color: button.enabled ? button.color : "grey"
border.width: borderWidth
border.color: "black"
Text {
id: innerText
font.pointSize: fontSize
font.family: "B Nazanin"
color: "white"
anchors.centerIn: rectangleButton
}
}
//change the color of the button in differen button states
states: [
State {
name: "Hovering"
PropertyChanges {
target: rectangleButton
color: hoverColor
}
},
State {
name: "Pressed"
PropertyChanges {
target: rectangleButton
color: pressColor
}
}
]
//define transmission for the states
transitions: [
Transition {
from: ""; to: "Hovering"
ColorAnimation { duration: 200 }
},
Transition {
from: "*"; to: "Pressed"
ColorAnimation { duration: 10 }
},
Transition {
from: "*"
to: ""
ColorAnimation { duration: 200 }
}
]
//Mouse area to react on click events
MouseArea {
hoverEnabled: true
anchors.fill: button
onEntered: { button.state='Hovering'}
onExited: { button.state=''}
onClicked: { button.clicked();}
onPressed: { button.state="Pressed" }
onReleased: {
if (containsMouse)
button.state="Hovering";
else
button.state="";
}
}
}
It looks like you are simply looking for the activeFocusOnTab property:
Item {
id: button
activeFocusOnTab: true
// ...
}
in Qml there is auto key navigation for already known components like checkbox,textfield,button and etc , i have my custom component which is a item or rectangle and i want same functionality for it,without writing
KeyNavigation.tab: componentid
here is one of my custom controls :
Rectangle {
signal clicked
property alias font : icoText.font.family
property alias icon : icoText.text
property alias size : icoText.font.pixelSize
property alias toolTip : tooltipText.text
property string colorEnter :"#0481ff"
property string colorExit :"#00171f"
id: root
implicitWidth: 50
implicitHeight: 50
//width: childrenRect.width
radius: 0
//height: childrenRect.height
color: colorExit
state: "default"
Text {
id: icoText
text: ""
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 25
font.family: "fontawesome"
visible: text!= ""
color: "white"
}
ToolTip {
id:tooltipText
text: ""
delay: 500
timeout: 2000
visible: mouseArea.containsMouse && text!=""
font.family: "B Nazanin"
contentItem: Text {
text: tooltipText.text
font: tooltipText.font
color: "white"
}
background: Rectangle {
color: "#cc000000"
border.color: "black"
}
}
InnerShadow {
id:shadow
anchors.fill: icoText
radius: 1.0
samples: 17
horizontalOffset: 1
color: colorExit
source: icoText
visible: false
}
MouseArea{
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: root.color = colorEnter
onExited: root.color = root.state == "transparent"? "transparent" : root.colorExit
onPressed: {
shadow.visible = true
}
onReleased: {
shadow.visible = false
}
onClicked: {
root.clicked()
}
}
states: [
State {
name: "transparent"
PropertyChanges {
target: root
color:"transparent"
}
PropertyChanges {
target: icoText
color:colorExit
}
},
State{
name: "default"
PropertyChanges {
target: root
color:colorExit
}
PropertyChanges {
target: icoText
color:"white"
}
}
]
}
which will be inside a page like this :
Item{
myControl{
}
myControl{
}
}
this component by default does not loop through pressing tab what should i do?
i already tried this without success, i think this should be inside FocusScope but cause of poor documentation i did not get a simple example for this
set activeFocusOnTab on parent and focus:true in child you want to get focus
Rectangle{
activeFocusOnTab: true
Control{
focus: true
}
}
Focusable Qml Components (Focus My Control On Tab)
For my experience key navigation only works with native components like checkbox, textfield, button, etc.
To work arround this problem I used a fake native component hided with the same size of my custom component like the next example:
Rectangle {
id: myCustomComponent1
width: 100
height: 100
color: red
Button {
id: buttonFake1
text: "My Accessible text Component 1"
width: parent.width
height: parent.height
opacity: 0 // hide the fake component
Accessible.role: Accessible.defaultButton
Accessible.name: text
KeyNavigation.tab: buttonFake2
onClicked: {
console.log(index)
}
onFocusChanged: {
if(focus === true){
// Here do what you want with your custom component
// For example, change color, size, ...
Do_what_you_Want()
// And then set back the focus at the fake native component
// to key navigation keeps working from the same component
buttonFake1.focus = true
}
}
}
}
Rectangle {
id: myCustomComponent2
width: 100
height: 100
color: green
Button {
id: buttonFake2
text: "My Accessible text Component 2"
width: parent.width
height: parent.height
opacity: 0 // hide the fake component
Accessible.role: Accessible.defaultButton
Accessible.name: text
KeyNavigation.tab: buttonFake1
onClicked: {
console.log(index)
}
onFocusChanged: {
if(focus === true){
// Here do what you want with your custom component
// For example, change color, size, ...
Do_what_you_Want()
// And then set back the focus at the fake native component
// to key navigation keeps working from the same component
buttonFake2.focus = true
}
}
}
}
I want connect one signal from QObject to various pages, loaded by the "Loader" qml element. My problem similar Dead QML elements receiving signals? but loaded items destroyed before calling the "onDestruction" method.
For example below, if switch from page1 to page2 in console writed:
"QML: Loading status: 1 Item: QDeclarativeRectangle(0x8dcd408, "page2")
QML Item: Loaded QDeclarativeRectangle(0x8dcd408, "page2") 1
qrc:/page1.qml:12: TypeError: Result of expression 'parent' [null] is not an object.
qrc:/page1.qml:15: ReferenceError: Can't find variable: page1text"
every second. So there can't disconnect from signal because parent object is destroyed.
How to handle signals from QObject (root) in loaded items? or How to disconnect signal from unloaded page?
main.qml
import QtQuick 1.1
Rectangle {
id: root
objectName: "root"
width: 360
height: 360
state: "page1"
color: "white"
Item {
width: parent.width
height: parent.height/2
anchors.top: parent.top
Loader {
id: pageLoader
objectName: "pageLoader"
anchors.fill: parent
anchors.centerIn: parent
signal textMsg(variant params)
onStatusChanged: console.log("QML: Loading status: ", status, " Item: ", item)
onLoaded: { console.log("QML Item: Loaded",item,status); }
}
}
states: [
State {
name: "page1"
PropertyChanges { target: pageLoader; source: "qrc:/page1.qml"}
}
,State {
name: "page2"
PropertyChanges { target: pageLoader; source: "qrc:/page2.qml"}
}
]
Timer {
// simulate signals from QObject
interval: 1000; running: true; repeat: true
onTriggered: pageLoader.textMsg({"msg2page1":"test","msg2page2":"test"})
}
Rectangle {
anchors.left: parent.left
anchors.bottom: parent.bottom
width: parent.width/2
height: parent.height/2
border {
color: "black"
width: 1
}
color: "yellow"
Text{
anchors.fill: parent
anchors.centerIn: parent
text: "Set Page 1"
}
MouseArea {
anchors.fill: parent
onClicked: {
root.state = "page1";
}
}
}
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: parent.width/2
height: parent.height/2
border {
color: "black"
width: 1
}
color: "red"
Text{
anchors.fill: parent
anchors.centerIn: parent
text: "Set Page 2"
}
MouseArea {
anchors.fill: parent
onClicked: {
root.state = "page2";
}
}
}
}
page1.qml
import QtQuick 1.1
Rectangle {
id: page1
objectName: "page1"
color: "yellow"
Component.onCompleted: {
parent.textMsg.connect(msgHandler);
}
Component.onDestruction: {
parent.textMsg.disconnect(msgHandler);
}
function msgHandler(params) {
page1text.text += " "+params.msg2page1;
}
Text {
id: page1text
anchors.fill: parent
wrapMode: Text.WordWrap
text: "page1"
}
}
page2.qml
import QtQuick 1.1
Rectangle {
id: page2
objectName: "page2"
color: "red"
}
That's nicely described in Loader documenation. It reads:
Any signals emitted from the loaded item can be received using the Connections element.
There is also an example, I copy it below for the sake of clarity:
// Application.qml
import QtQuick 1.0
Item {
width: 100; height: 100
Loader {
id: myLoader
source: "MyItem.qml"
}
Connections {
target: myLoader.item
onMessage: console.log(msg)
}
}
// MyItem.qml
import QtQuick 1.0
Rectangle {
id: myItem
signal message(string msg)
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: myItem.message("clicked!")
}
}
Clearly, if item is destroyed, any signal handlers are ignored until the target is recreated again.
My answer is: Don't use the "Loader", create child object by JS and destroy it as no needed, for example:
main.qml
import QtQuick 1.1
import "qrc:/pageloader.js" as Pageloader
Rectangle {
id: root
objectName: "root"
width: 360
height: 360
state: "page1"
color: "white"
signal textMsg (variant params)
states: [
State {
name: "page1"
StateChangeScript{ script: Pageloader.createPageObject("qrc:/page1.qml");}
}
,State {
name: "page2"
StateChangeScript{ script: Pageloader.createPageObject("qrc:/page2.qml");}
}
]
Timer {
// simulate signals from QObject
interval: 1000; running: true; repeat: true
onTriggered: textMsg({"msg2page1":"test","msg2page2":"test"})
}
Rectangle {
anchors.left: parent.left
anchors.bottom: parent.bottom
width: parent.width/2
height: parent.height/2
border {
color: "black"
width: 1
}
color: "yellow"
Text{
anchors.fill: parent
anchors.centerIn: parent
text: "Set Page 1"
}
MouseArea {
anchors.fill: parent
onClicked: {
root.state = "page1";
}
}
}
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: parent.width/2
height: parent.height/2
border {
color: "black"
width: 1
}
color: "red"
Text{
anchors.fill: parent
anchors.centerIn: parent
text: "Set Page 2"
}
MouseArea {
anchors.fill: parent
onClicked: {
root.state = "page2";
}
}
}
}
pageloader.js
var component;
var sprite;
function createPageObject(path) {
if(sprite){
console.log("sprite.destroy() ",typeof sprite);
sprite.destroy();
console.log("component.destroy() ",typeof component);
component.destroy();
}
component = Qt.createComponent(path);
if (component.status === Component.Ready)
finishCreation();
else
component.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (component.status == Component.Ready) {
sprite = component.createObject(root);
if (sprite == null) {
// Error Handling
console.log("Error creating object");
}
} else{
if (component.status === Component.Error) {
// Error Handling
console.log("Error loading component:", component.errorString());
}else{
console.log("Component status changed:", component.status);
}
}
}
page1.qml and page2.qml not changed.
I got it. My setup:
qml file to display ListViews
Several qml files defining Listviews, each takes a different column of different SQL tables. The model comes from C++
So here is the shortened code:
Dialog {
id: dialog
width: 1000; height: 400
property Component listViewItem
signal newDatabaseEntry( string text ) [1]
contentItem: Rectangle {
[...]
TextInputWithButton { [3]
id: newRecords
onInputAccepted: { newDatabaseEntry( text ) } [1]
}
}
[...]
Loader {
id: listViewPlaceholder
anchors.fill: parent
sourceComponent: dialog.listViewItem
onLoaded: {
if( typeof listViewPlaceholder.item.insertRecord === "function" )
// newRecords.inputAccepted.connect( listViewPlaceholder.item.insertRecord ) [1]
dialog.newDatabaseEntry.connect( listViewPlaceholder.item.insertRecord ) [2]
}
The above code is the general view of ListViews. The signal roundtrip [1] is necessary, otherwise no data is passed. How to chain signals is described here:
http://doc.qt.io/qt-5/qtqml-syntax-signals.html#connecting-signals-to-methods-and-signals
The input button [3] delivers the confirmed data to be inserted into the db.
A ListView passed to the above function looks like this:
DialogSqlSingleColumnEdit {
listViewItem: ListView {
function insertRecord( text ) {
console.log( "done:" + text )
sqlModel.insertRecord( text )
}
[...]
The insertRecord is called forwards the text to the sql-C++ model.
I want to implement the following scenario in QML.
Here is a sample/simplified delegate for ListView element:
Component {
Item {
id: container
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
container.ListView.view.currentIndex = index
container.forceActiveFocus();
}
onEntered: {
actionList.state = "SHOW";
myItem.state = "HOVER"
}
onExited: {
actionList.state = "HIDE";
myItem.state = "NORMAL"
}
Rectangle {
id: myItem
color: "gray"
anchors.fill: parent
Row {
id: actionList
spacing: 5; anchors.fill: parent
Image {
id: helpAction
source: "" //Some image address
width: 16; height: 16; fillMode: Image.PreserveAspectFit
states: [
State {
name: "NORMAL"
PropertyChanges { target: helpAction; opacity: 0.7 }
},
State {
name: "HOVER"
PropertyChanges { target: helpAction; opacity: 1.0 }
}
]
MouseArea {
hoverEnabled: true
anchors.fill: parent
onEntered: {
parent.state = "HOVER";
}
onExited: {
parent.state = "NORMAL";
}
}
states: [
State {
name: "SHOW"
PropertyChanges { target: actionList; visible: false }
},
State {
name: "HIDE"
PropertyChanges { target: actionList; visible: true }
}
]
}
//Other action buttons...
states: [
// `NORMAL` and `HOVER` states definition here...
]
}
}
}
}
But I have a problem with MouseArea.
Inner MouseArea (actionButton) does not work properly for entered event. When mouse enters on action button, outer MouseArea fires exited event.
Is there any mistake in my code? More generally, how can I implement such a scenario in QML?
I was faced by this same problem, and came across the answer in the QtQuick 5.0 documentation for MouseArea. The answer to this is actually quite simple.
If you want to include child mouse hover events in your parent MouseArea, make you child MouseArea a child of the parent MouseArea:
MouseArea {
id: parent
MouseArea {
id: child
}
}
Since I have a custom Widget type that would be used as the parent view, I ended up with the default property being the children of the MouseArea:
Item {
default property alias children: mouseArea.data
MouseArea {
id: mouseArea
}
}
Iv'e tried a few things but it does not seem possible to hover over two MouseArea simultaneously. The preventStealing and propagateComposedEvents seem to only work when you have a click event. But from the inner MouseArea you can trigger the entered() signal of the other one. Something like this:
import QtQuick 2.1
Rectangle {
width: 500
height: 500
Rectangle {
width:300
height: 300
color: "red"
MouseArea {
id: big
anchors.fill: parent
hoverEnabled:true
onEntered: {
console.log("ENTERED BIG mousearea");
}
onExited: {
console.log("EXITED BIG mousearea");
}
}
Rectangle {
anchors.centerIn: parent
height: 100
width: 100
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled:true
onEntered: {
console.log("ENTERED small mousearea");
big.entered();
}
onExited: {
console.log("EXITED small mousearea");
big.exited();
}
}
}
}
}
The issue is that the exited() signal from the containing MouseArea will be called before calling the entered() back again. So you might need to "delay" the change of state in exited() just to make sure you really want to hide your action buttons. Another solution would be to save the current mouse position and hide the buttons ONLY if exited() is called with the mouse on one of its border.
make states for each state of the elements in the View then you can use things like if statements or case statements to change these properties In Other words, Try not to set your elements up to work on MouseArea but on properties And set the Elements properties to work on the set properties I hope that this helps if not here is example:
EDIT I added the color to be transparent. if there is no mouse what so ever. If I was using a Image I would use opacity then add a bunch of Behaviors also But this is a working
example
import QtQuick 2.0
Rectangle {
width: 360
height: 360
property string state1:"OutMouse"
property string state2: "OutMouse"
property string state3: "OutMouse"
property string state4: "OutMouse"
Rectangle{
id:blueRec
width: parent.width
height: parent.height / 6
color: state1 === "InMouse" ? "blue" : "green"
MouseArea{
anchors.fill: blueRec
hoverEnabled: true
onEntered: state1 = "InMouse"
onExited: {
if (state1 === state2 || state3 || state4){
state1 = "InMouse"
}
if(state1 !== state2 || state3 || state4)
{
state1 = "OutMouse"
}
}
}
Text {
text: state1=== "InMouse"? qsTr("foo") :"bar"
anchors.centerIn: blueRec
}
Row{
width: parent.width
height: parent.height / 4
spacing: 2
anchors{
left: parent.left
verticalCenter: blueRec.verticalCenter
leftMargin: blueRec.width / 12
}
Rectangle{
id: rec1
height: parent.height;
width: height
color: {
if ( state3 === "InMouse")
return "gray"
if (state1 === "OutMouse")
return "transparent"
else
return "white"}
MouseArea{
id: rec1M
anchors.fill: parent
hoverEnabled: true
onEntered:{
state1 = "InMouse"
state2 = "InMouse"
}
onExited: state2 = "OutMouse"
}
}
Rectangle{
id: rec2
height: parent.height ;
width: height
color: {
if (state3 === "InMouse")
return "gray"
if (state1 === "OutMouse")
return "transparent"
else
return "white"
}
MouseArea{
id: rec2M
anchors.fill: parent
hoverEnabled: true
onEntered:{
state1 = "InMouse"
state3 = "InMouse"
}
onExited: state3 = "OutMouse"
}
}
Rectangle{
id: rec3
height: parent.height;
width: height
color:{
if (state4 === "InMouse")
return "gray"
if (state1 === "OutMouse")
return "transparent"
else
return "white"
}
MouseArea{
id: rec3M
anchors.fill: parent
hoverEnabled: true
onEntered:{
state4 = "InMouse"
state1 = "InMouse"
}
onExited: state4 = "OutMouse"
}
}
}
}
}
Try this:
add a signal to the inner area that's emitted on mouse enter.
Connect the signal to the outer area.
The signal causes the outer area to enter the hovered state.
Mouse exit on both will still cancel hover state. As you move the mouse off the controls it should work correctly without any extra code