QML: Problems with mousearea overlapping - qml

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

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 }
}
}
}

Related

QML ListView delegate with Component Row does't render correctly

And now I have a problem with my qml script. here's the simple code:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: root
anchors.fill: parent
color: "green"
SystemPalette { id: activePalette }
ColumnLayout {
id: rightPanel
Layout.fillHeight: true
Layout.fillWidth: true
Layout.rightMargin: 10
anchors.fill: parent
Component.onCompleted: {
console.log(this, width, parent, parent.width)
}
ListView {
spacing: 2
Layout.fillHeight: true
Layout.fillWidth: true
model: ListModel {
ListElement {
name: "nihao"
value: "1"
}
ListElement {
name: "fds"
value: "2"
}
ListElement {
name: "fdssd"
value: "4"
}
}
delegate: Component {
// Rectangle {
// color: "yellow"
// height: 40
// width: parent.width
Row {
anchors.fill: parent
spacing: 2
height: 40
width: 200
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: name
}
}
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: value
}
}
}
//}
}
}
}
}
}
The code does't display correctly when I use qmlscene to render it, it even render nothing if the ListModel is too long.
But, if I uncomment out the "Rectangle" code in delegate component, it works well. So I'm comfused with the difference between Reactangle and Row for that they are all inherited from Item. And what should be placed into the delegate component as its direct child?

How to animate scroll in a QML ScrollView?

How can I animate scrolling in a QML ScrollView?
I've tried a Behavior on the contentItem.contentY, but that isn't working.
With Qt Quick Controls 1
You just have to animate the value changes on the property flickableItem.contentY.
A quick example:
Item {
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
Button {
id: btn
onClicked: scroll.scrollTo(scroll.flickableItem.contentY + 100)
}
ScrollView {
id: scroll
function scrollTo(y) {
scrollAnimation.to = y
scrollAnimation.start()
}
NumberAnimation on flickableItem.contentY {
id: scrollAnimation
duration: 1000
}
contentItem: Column {
Repeater {
model: 30
Rectangle {
width: 100; height: 40
border.width: 1
color: "yellow"
}
}
}
}
}
}
When you click on the button, it will scroll by 100 px with a smooth jump.
With Qt Quick Controls 2
The flickableItem.contentY isn't available anymore. The simpliest way to do the same thing in Qt Quick Controls 2 is to animate the position of the ScrollBar.
Notice that the position of QScrollBar is in percent (expressed between 0 and 1), not in pixels.
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ScrollView {
id: scroll
width: 200
height: 200
clip: true
function scrollTo(y) {
scrollAnimation.to = y
scrollAnimation.start()
}
ScrollBar.vertical: ScrollBar {
id: test
parent: scroll
x: scroll.mirrored ? 0 : scroll.width - width
y: scroll.topPadding
height: scroll.availableHeight
active: scroll.ScrollBar.horizontal.active
policy: ScrollBar.AlwaysOn
NumberAnimation on position {
id: scrollAnimation
duration: 1000
}
}
ListView {
model: 20
delegate: ItemDelegate {
text: "Item " + index
}
}
}
Button {
id: btn
anchors.top: scroll.bottom
onClicked: scroll.scrollTo(test.position + 0.1)
}
}
When you click on the button, it will scroll by 10% of the height.

Rectangle width is ignored, only works anchors.fill: parent

In my QML/Qt app, I have following Rectangle:
import QtQuick 2.5
import QtQuick.Layouts 1.1
Rectangle
{
id: uePlaceSelector
signal ueSignalPlaceSelected
signal ueSignalPlaceSelectionAborted
property string ueSelectedPlaceName: ""
radius: 16
border.color: "#4682b4"
border.width: 1
antialiasing: true
//FIXME how to set width same as UePlaceSwitcher's width?
anchors.fill: parent
gradient: Gradient
{
GradientStop
{
position: 0
color: "#636363"
} // GradientStop
GradientStop
{
position: 1
color: "#303030"
} // GradientStop
} // Gradient
ColumnLayout
{
antialiasing: true
layoutDirection: Qt.LeftToRight
spacing: 8
anchors.fill: parent
RowLayout
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 8
Rectangle
{
id: uePlaceSelectorNavigatorBack
Layout.preferredWidth: 32
Layout.preferredHeight: 32
color: "transparent"
SequentialAnimation
{
id: uePlaceSelectorNavigatorBackPressAnimation
loops: 1
running: false
NumberAnimation
{
target: uePlaceSelectorNavigatorBack
property: "opacity"
to: 0
duration: 150
} // NumberAnimation
NumberAnimation
{
target: uePlaceSelectorNavigatorBack
property: "opacity"
to: 1
duration: 150
} // NumberAnimation
} // SequentialAnimation
Image
{
anchors.fill: parent
asynchronous: true
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
fillMode: Image.PreserveAspectFit
smooth: true
source: "qrc:///ueIcons/icons/ueArrowLeft.png"
} // Image
MouseArea
{
anchors.fill: parent
onClicked:
{
uePlaceSelectorNavigatorBackPressAnimation.running=true;
ueSignalPlaceSelectionAborted()
} // onClicked
} // MouseAres
} // Rectangle
} // RowLayout
GridView
{
id: uePlacesGridView
antialiasing: true
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 8
Layout.alignment: Qt.AlignHCenter|Qt.AlignBottom
flow: GridView.FlowLeftToRight
layoutDirection: Qt.LeftToRight
clip: true
cellWidth: parent.width
cellHeight: 128
highlightFollowsCurrentItem: true
highlightRangeMode: GridView.StrictlyEnforceRange
snapMode: GridView.SnapToRow
Component.onCompleted:
{
model=uePlacesModel
}
delegate: UeButton
{
id: uePlacesModelDelegate
width: uePlacesGridView.cellWidth-16
height: uePlacesGridView.cellHeight-16
ueText: model.ueRoleName
ueSoundOn: false
onUeSignalButtonClicked:
{
ueSelectedPlaceName=uePlacesGridView.model.get(index).ueRoleName;
ueSignalPlaceSelected()
} // onUeSignalButtonClicked:
} // delegate
} // GridView
} // ColumnLayout
} // Rectangle
Now, this Rectangle is at startup hidden and disabled. But in my ApplicationWindow I have Toolbar with customs Buttons and when the user presses one of them, this Rectangle pops up via OpacityAnimator. However its size is full screen because of anchors.fill: parent. Its parent is ApplicationWindow, becuase it is declared inside it. But, how do I assign its width, because
Component.onCompleted:
{
ueLoggedUserPlaceSelector.width=uePlaceSwitcher.width
} // Component.onCompleted
inside main.qml does not work. Why?
Replace
anchors.fill: parent
with
width: uePlaceSwitcher.width
height: uePlaceSwitcher.height
You should provide height with width or the rectangle will not be visible.

MouseArea events on a Listview Component in QtQuick2

We are working on a touchscreen device and we are facing an issue with the following code:
import QtQuick 2.1
import QtQuick.Window 2.0
Window {
visible: true
id: box
width: 360
height: 360
ListView {
id: list
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
model: 30
delegate: data
clip: true
focus: true
smooth: true
}
Component{
id: data
Item{
id: item
width: list.width
height: 30
clip: true; smooth: true; visible: true
property string cellColor: getCellColor(index)
Rectangle{
id: condData_item_line
width: parent.width
height: parent.height
color: cellColor
clip: true; smooth: true
Text{
text: index
anchors.centerIn: parent
}
MouseArea{
anchors.fill: parent
onClicked: {
console.log("click", index, mouse.x, mouse.y)
}
onPressed : {
console.log("press", index, mouse.x, mouse.y)
}
onReleased: {
console.log("release", index, mouse.x, mouse.y)
}
onCanceled: {
console.log("cancel")
}
}
}
}
}
function getCellColor(index){
var color;
if(index % 2 == 0){
color = "white";
}else{
color = "blue";
}
return color;
}
}
Whereas we defined handles for the clicked and released events they are never called. Is this normal? If yes, how should we code the event handling so that clicked and released events are fired on the Rectangle components?
Additional info: With this code, touching the first item (index:0), clicked and released events are fired properly. But touching the other items we only get canceled events.

How to highlight the clicked (by mouse) element of a delegate w.r.t FolderListModel?

import QtQuick 2.0
import Qt.labs.folderlistmodel 2.0
Item
{
Component {
id: highlight
Rectangle {
id: rooot
width: 180; height: 20
color: ListView.isCurrentItem ? "black" : "red"; radius: 5
y: list.currentItem.y
Behavior on y {
SpringAnimation {
spring: 3
damping: 0.2
}
}
}
}
ListView {
id: list
width: 480; height: 400
model: folderModel
delegate: Text { id: h; text: fileName }
highlight: highlight
highlightFollowsCurrentItem: false
focus: true
}
FolderListModel
{
id: folderModel
folder: "/home/anisha/"
nameFilters: ["*"]
}
}
This works only when I use keyboard. How to make it work on mouse clicks?
To react on mouse events you need to place MouseArea item.
In the sample below (being an expanded version of the code you provided) I have added a MouseArea to the delegate item that upon being clicked sets the ListView's currentIndex to the delegate's index (a special property visible in the ListView's delegate).
import QtQuick 2.0
import Qt.labs.folderlistmodel 2.0
Item
{
Component {
id: highlight
Rectangle {
id: rooot
width: 180; height: 20
color: ListView.isCurrentItem ? "black" : "red"; radius: 5
y: list.currentItem.y
Behavior on y {
SpringAnimation {
spring: 3
damping: 0.2
}
}
}
}
ListView {
id: list
width: 480; height: 400
model: folderModel
delegate:
Text {
id: h;
text: fileName
MouseArea {
anchors.fill: parent
onClicked: list.currentIndex = index
}
}
highlight: highlight
highlightFollowsCurrentItem: false
focus: true
}
FolderListModel
{
id: folderModel
folder: "/home/anisha/"
nameFilters: ["*"]
}
}
As an alternative approach you might try placing a single MouseArea filling the whole ListView and use ListView's indexAt(int x, int y) method to check which delegate was clicked. However, you would need to care about more edge-conditions in such case.