I have a fullscreen map, on which I added a mouse area:
Map {
id: map
anchors.fill: parent
plugin: osm
zoomLevel: 16
minimumZoomLevel: 13
maximumZoomLevel: 20
gesture.enabled: true
Rectangle {
id: infoPanel
// ...
Button {
// ...
}
}
MouseArea {
anchors.fill: parent
onPressed: {
infoPanel.visible = false
}
}
The infoPanel rect will ocasionally be made visible, overlaying the map with some information, and a button to trigger a certain action.
Now I have added the mouse area to the map in order to hide the info panel whenever the map is clicked, which works fine.
However, the info panel is also dismissed when the rectangle of the info panel itself is clicked.
How can I prevent the mouse area from the map to interfere with anything which is inside infoPanel?
you have to way:
you must set z value of info panel to map.z + 1 and define mouse area for info panel to capture mouse event. like below code
Map {
id: map
anchors.fill: parent
plugin: osm
zoomLevel: 16
minimumZoomLevel: 13
maximumZoomLevel: 20
gesture.enabled: true
Rectangle {
id: infoPanel
width: 100
height: 100
color: "red"
z: map.z + 1
MouseArea {
anchors.fill: parent
onClicked: {
print("info panel")
}
}
Button {
width: parent.width / 2
height: width
anchors.centerIn: parent
text: "button"
onClicked: {
print("button")
}
}
}
MouseArea {
anchors.fill: parent
onPressed: {
infoPanel.visible = false
print("map")
}
}
}
or just move your mouse area to up of info panel
Map {
id: map
anchors.fill: parent
plugin: osm
zoomLevel: 16
minimumZoomLevel: 13
maximumZoomLevel: 20
gesture.enabled: true
MouseArea {
anchors.fill: parent
onPressed: {
infoPanel.visible = false
print("map")
}
}
Rectangle {
id: infoPanel
width: 100
height: 100
color: "red"
MouseArea {
anchors.fill: parent
onClicked: {
print("info panel")
}
}
Button {
width: parent.width / 2
height: width
anchors.centerIn: parent
text: "button"
onClicked: {
print("button")
}
}
}
}
Related
I have a custom button that shows a white overlay over the content and background area when pressed. The user of the button can specify how to align the content (label + text) by defining the contentItem.
The current implementation works, but I would like to control the press state inside the button. How can the button display the white overlay on top of the contentItem from within the button?
I prefer not to add a bunch of properties to the button to define the layout of the text/icon. Is there another way to accomplish this?
Working code so far:
ButtonOverlay {
id: btnOverlay
width: 100
height: 50
state: ""
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.state === "pressed"
}
}
onPressed: {
btnOverlay.state = "pressed"
}
onReleased: {
btnOverlay.state = ""
}
}
ButtonOverlay.qml
Button {
id: root
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: root.state === "pressed"
color: "white"
opacity: 0.5
}
}
}
I've removed the overlay in the contentItem and moved the overlay from the background into the Button. Furthermore I've used the z property to change the drawing order.
component ButtonOverlay: Button {
id: btn
background: Rectangle {
radius: 10
color: "gray"
}
Rectangle {
id: overlay
anchors.fill: parent
radius: btn.background.radius
color: "white"
opacity: 0.5
visible: btn.pressed
z: 1
}
}
ButtonOverlay {
width: 100
height: 50
contentItem: Rectangle {
color: "red"
Label { text: "button" }
}
}
Perhaps you could use Frame for your ButtonOverlay. Frame works by automatically sizing to your inner contents but allows you to easily replace the borders via the background property. To use it, you will have to move the entire Button out:
import QtQuick
import QtQuick.Controls
Page {
ButtonOverlay {
id: btnOverlay
pressed: btn.pressed
Button {
id: btn
width: 100
height: 50
implicitWidth: width
implicitHeight: height
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.pressed
}
}
}
}
}
// ButtonOverlay.qml
import QtQuick
import QtQuick.Controls
Frame {
id: root
property bool pressed: false
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: pressed
color: "white"
opacity: 0.5
}
}
}
You can Try it Online!
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")
}
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
}
}
}
}
Here's a code snippet with current form of the code
Rectangle
{
id: menu
GridLayout
{
id: layout
columns: 4
rows: 3
Repeater
{
model: ListModel {}
ToolButton {}
}
Rectangle
{
x: -3
y: -33
width: menu.width - 2
height: menu.height + 33
border.color: "red"
border.width: 3
color: "blue"
MouseArea
{
x: mapToItem(menu, -5, -35).x
y: mapToItem(menu, -5, -35).y
width: menu.width
height: menu.height + 35
hoverEnabled: true
preventStealing: true
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
}
}
}
}
The MouseArea hover event is propagated down to the ToolButtons in the layout. I don't get why. Hence, the onEntered and onExited events do not work as expected, because onExited happen inside the MouseArea when the ToolButtons are 'hovered' and tooltips are shown. In the end I need the MouseArea to be a bit wider and longer than its parent Rectangle so that once onExited is emitted the menu gets invisible. After the test with Rectangle is successfull it will make sense to make C++ type Polygon.
In your example, onExited must emits when entering ToolButton. According to MouseArea.exited():
Rectangle {
width: 400; height: 400
MouseArea {
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
}
MouseArea {
id: mouseArea2
width: 100; height: 100
anchors.centerIn: parent
hoverEnabled: true
}
}
Moving the mouse into mouseArea2 from mouseArea1 will cause mouseArea1 to emit the exited signal.
If you do not want the exited signal to be emitted,
If instead you give the two MouseAreas a parent-child relationship, moving the mouse into mouseArea2 from mouseArea1 will not cause mouseArea1 to emit exited. Instead, they will both be considered to be simultaneously hovered.
That is, place ToolButton (and all related components) within the MouseArea. For example,
Rectangle {
id: menu
Rectangle {
//some properties
MouseArea {
hoverEnabled: true
//some properties
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
GridLayout {
id: layout
Repeater {
ToolButton {}
}
}
}
}
}
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