Search code examples
qmloverlapmousearea

QML: Problems with mousearea overlapping


I have a QML application and problems with MouseAreas.

In a small test app, there is a red rectangle and when mouse enters this rect, a grey menu appears below (created with a Loader).

This grey menu must be open while mouse is over the red rectangle or the menu. For this purpose, I have 2 MouseAreas, 1 over the red rect and 1 over the menu. Both are 'hoverEnabled' and with 'enter' and 'exit' I control 'hoverDialog' and 'hoverTopZone'.

When both are false, it means that the mouse is out, so I close the menu (using a signal, the Loader gets inactive).

The timer is required since when passing from 'mouseAreaTopZone' to 'mouseAreaDialog' there is just a moment with 'hoverDialog' and 'hoverTopZone' are both false. Fixed with the timer.

In the middle of the menu there is a green rect, and (only) when mouse is over there, a yellow rect must be visible.

There is my problem. I have a MouseArea inside the green rect, but the yellow rect is not visible when required.

If I move 'rectGreen' below 'mouseAreaTopZone' and 'mouseAreaDialog' (that is, at the end of the file) I get the yellow rect visible when the mouse is over green rect, since its mouse area is then 'topmost'

BUT in this case, the menu dialog is closed, since when the mouse enters the MouseArea inside green rect, hoverDialog and hoverTopZone are false...

I hope U can understand my problem... Here is my code:

Test.qml

import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Window 2.0

Item {
    width: 800
    height: 800

    Rectangle{
        id: rect
        anchors { top: parent.top; topMargin: 100; horizontalCenter: parent.horizontalCenter }
        height: 50; width: 50
        color: "red"

        MouseArea {
            anchors.fill: parent
            hoverEnabled: true
            onEntered: loader_dialog.active = true
        }
    }

    Loader {
        id: loader_dialog
        anchors { top: rect.bottom; horizontalCenter: rect.horizontalCenter}
        active: false
        sourceComponent: TestMenu {
            onClose: loader_dialog.active = false;
        }
    }
}

TestMenu.qml

import QtQuick 2.0

Rectangle {
    id: id_dialog

    signal close()

    width: 400
    height: 600

    color: "lightgrey"

    property bool hoverDialog: false
    property bool hoverTopZone: false

    function update() {
        if (!hoverDialog && !hoverTopZone)
            timer.start();
    }

    function check() {
        if (!hoverDialog && !hoverTopZone)
        {
            console.log("close");
            id_dialog.close();
        }
    }

    Timer {
        id: timer
        interval: 100
        running: false
        repeat: false
        onTriggered: check();
    }

    Rectangle {
        id: rectGreen
        width: 200; height: 100
        anchors.centerIn: parent
        color: "green"

        Rectangle {
            id: rectYellow
            anchors.centerIn: parent
            width: 50; height: 50
            color: "yellow"
            visible: false
        }

        MouseArea {
            anchors.fill: parent

            hoverEnabled: true
            onEntered: { rectYellow.visible = true; }
            onExited: { rectYellow.visible = false }
        }
    }

    MouseArea {
        id: mouseAreaTopZone
        anchors { bottom: parent.top; horizontalCenter: parent.horizontalCenter}
        width: 50; height: 50

        hoverEnabled: true
        onEntered: { hoverTopZone = true; id_dialog.update(); }
        onExited:  { hoverTopZone = false; id_dialog.update(); }
    }

    MouseArea {
        id: mouseAreaDialog
        anchors.fill: parent

        hoverEnabled: true
        onEntered: { hoverDialog = true; id_dialog.update(); }
        onExited: { hoverDialog = false; id_dialog.update(); }
    }
}

Thanks in advance, Diego


Solution

  • Thanks Mark Ch for your help.

    I need to close the dialog when the mouse exits, so I think I can not use 'Popup' control...

    I solved the problem. Using only one variable to know if the mouse is over my dialog ('m_iNumHovered'), I add a reference every time I enter in a Mouse Area, and I decrease it when I exit. The key was to add/remove a reference in the MouseArea over the green rectangle, to keep it 'm_iNumHovered=true' (dialog visible)

    New code for TestMenu.qml:

    import QtQuick 2.0
    
    Rectangle {
        id: id_dialog
    
        signal close()
    
        width: 400
        height: 600
    
        color: "lightgrey"
    
        property int m_iNumHovered: 0
        onM_iNumHoveredChanged: update();
    
        function update() {
            if (m_iNumHovered == 0)
                timer.start();
        }
    
        function check() {
            if (m_iNumHovered == 0)
                id_dialog.close();
        }
    
        Timer {
            id: timer
            interval: 100
            running: false
            repeat: false
            onTriggered: check();
        }
    
        MouseArea {
            id: mouseAreaTopZone
            anchors { bottom: parent.top; horizontalCenter: parent.horizontalCenter}
            width: 50; height: 50
    
            hoverEnabled: true
            onEntered: m_iNumHovered++;
            onExited: m_iNumHovered--;
        }
    
        MouseArea {
            id: mouseAreaDialog
            anchors.fill: parent
    
            hoverEnabled: true
            onEntered: m_iNumHovered++;
            onExited: m_iNumHovered--;
        }
    
        Rectangle {
            id: rectGreen
            width: 200; height: 100
            anchors.centerIn: parent
            color: "green"
    
            Rectangle {
                id: rectYellow
                anchors.centerIn: parent
                width: 50; height: 50
                color: "yellow"
                visible: false
            }
    
            MouseArea {
                anchors.fill: parent
    
                hoverEnabled: true
                onEntered: { m_iNumHovered++; rectYellow.visible = true; }
                onExited: { m_iNumHovered--; rectYellow.visible = false }
           }
        }
    }