Mouse hover events not working in the header of the TableView - qt

Similar question is asked here
How to get Clicked or Pressed event on QML TableView header?
I want to highlight the header when hovered over. But the behavior in headerDelegate is weird.
headerDelegate: Rectangle {
height:30
MouseArea{
hoverEnabled: true
anchors.fill: parent
onPressed: {
console.debug("This will not be printed")
}
onEntered: {
console.debug("Will print on pressed event")
}
onExited: {
console.debug("Will print on released event")
}
}
}
Hovering over will not trigger any events at all.

The hover and other states are handled by the styleData component in the header delegate.
If you want to know when the hover changes, connect the different signals (onPressed, onContainsMouseChanged, etc.) of styleData to a function.
For example:
headerDelegate: Rectangle {
height:30
color: "red"
Connections {
target: styleData
onPressedChanged: {
console.debug("PRESSED: "+ styleData.column + " " + styleData.pressed)
}
onContainsMouseChanged: {
if (styleData.containsMouse)
console.debug("The mouse is hover the header of column " + styleData.column)
else
console.debug("The mouse is leaving the header of column " + styleData.column)
}
}
}

Related

Create conditional button background color in onClicked - QT(qml)

My goal is that when the button is clicked, the button color changes to orange and stays orange until the result of the if condition is declared. Then I want the button color to be green if the condition is met and red if not. However, when I click the button, the button color does not change until the condition result is clear, and when the condition is certain, the button color becomes either green or red. So the button color never turns orange.
I have shown my button usage below;
Button
{
...
...
onClicked: {
background.color = "orange"
if(some_condition))
background.color = "green"
else
background.color = "red"
}
}
NOTE: I am sending data to a device in an if condition and waiting for its response(this takes about 1-2 seconds). I expect the button to be orange until the answer comes, green if my condition is true after the answer, and red if it is false. But the color of the button does not turn orange.
Do you have any suggestions?
To achieve this, property bindings must be used.
In this example, I'll use Image to demonstrate your loading thing.
For this example, I am providing two methods.
Method one
Thus, the plan is as follows.
I press the Button.
The image's source changes, and it goes to the loading state.
And the Button turns orange as well.
The Button then turns green if Image enters the ready state.
Or else, the Button turns red.
Grid {
columns: 1
Image {
id: img
width: 200; height: 200
fillMode: Image.PreserveAspectFit
}
Button {
palette.button: img.status == Image.Loading ? "orange" :
img.status == Image.Ready ? "green" :
img.status == Image.Error ? "red" : "#259bd7"
onClicked: img.source = "https://thispersondoesnotexist.com/image";
}
}
Method two
Similar to method one in most ways, but not declaratively.
I press the Button, and the Button turns green or red anytime the image status changes.
Grid {
columns: 1
Image {
id: img
width: 200; height: 200
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if(status == Image.Ready) btn.palette.button = "green";
else if(img.status == Image.Error) btn.palette.button = "red";
}
}
Button {
id: btn
palette.button: "#259bd7"
onClicked: {
palette.button = "orange"
img.source = "https://thispersondoesnotexist.com/image";
}
}
}
Now, if for some weird reason, you want to do everything in the button, you can put a boolean property and change the color as it changes.
Like:
Image {
...
onStatusChanged: {
// some code and calculation
btn.condition = // true or false
}
}
Button {
...
property bool condition
onConditionChanged: {
if(condition) palette.button = "green";
else palette.button = "red";
}
...
}
Example.qml
Okay, so we now know what the answer is, well let's improve it (In the case that a lazy programmer wants to copy :D).
Image.status is an Enum with values ranging from 0 to 3.
Image {
id: img
width: 200; height: 200
fillMode: Image.PreserveAspectFit
}
Button {
id: btn
implicitHeight: 30
property list<Item> content: [
Item {},
Text { text: " ✓" },
BusyIndicator { running: true },
Text { text: " ✖" }
]
contentItem: Item {
width: parent.implicitWidth
Grid {
x: (parent.width - width)/2
spacing: 6
Text { text: "button" }
Control { contentItem: btn.content[img.status] }
Behavior on x { SmoothedAnimation {}}
}
}
onClicked: img.source = "https://thispersondoesnotexist.com/image";
}
Preview
BusyIndicator
The BusyIndicator in the example: https://github.com/SMR76/qml-snow-white/blob/master/SnowWhite/BusyIndicator.qml
https://github.com/Furkanzmc/QML-Loaders
In this answer, I manipulate Button background by setting it to a Rectangle and I control the Rectangle's color to iterate between (1) default button color, (2) orange, (3) red and (4) green. Hence, I identified there are 4 unique states as detailed as follows:
button hasn't been clicked yet, the button background color is the default color
button has been clicked, but the user hasn't typed anything, the button background changes to an "orange" color
the text has been entered, but the answer is wrong, the button color changes to "red" color
the text has been entered, and the answer is correct. the button background changes to "green" color
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
property bool started: false
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 10
Label {
visible: started
text: qsTr("What is 1+1?")
}
Frame {
Layout.fillWidth: true
visible: started
TextEdit {
id: answer
width: parent.width
}
}
Button {
background: Rectangle {
color: started
? answer.text
? answer.text === "2"
? "green"
: "red"
: "orange"
: palette.button
}
text: qsTr("Start")
onClicked: {
if (!started) {
started = true;
answer.text = "";
} else {
started = false;
}
}
}
}
}
You can Try it Online!
I found a tricky and simple solution as the Pressed state occurs before the Clicked state;
Button {
...
...
onPressed: {
background.color = "orange"
}
onClicked: {
if(some_condition))
background.color = "green"
else
background.color = "red"
}
}
Thus, as soon as I press the button, the button color becomes orange and after the condition result is clear, the button color becomes red or green depending on the condition.

QML: Mouse wheel event propagation in ListView

Have strange situation with ListView scrolling on mouse wheel. Have Items structure similar to this:
MainAppWindow {
// Some zoomable map item
Map {
anchors.fill: parent
}
PopupMenu { // Simple Rectangle item
anchors.top: parent.top
width: 200
height: parent.height / 2
z: parent.z + 1
ListView {
anchors.fill: parent
clip: true
...
delegate: Item {
...
MouseArea {
anchors.fill: parent
onClick: {
someHandler()
}
}
}
}
}
}
ListView with vertical scroll works and scrolls just fine until it stops at bounds (top or bottom - whatever) and after this mouse event starts to propagate to underlying layer and ZoomableMap starts to zoom which is not we want: should be propagated there only if PopupMenu is not visible. Adding
onWheel: wheel.accepted = true
into MouseArea inside ListView delegate could partially solve the problem - it disables wheel and allows scrolling only by dragging the content. However better allow scrolling by the wheel as well. MouseArea in PopupMenu blocks wheel and dragging in the ListView completely as well - not helps also.
So what is problem here, how to fix? Or we doing something wrong here?
Need to add another MouseArea into PopupMenu which blocks all mouse events and is disabled by default and enable it only if popup is visible (optional):
enabled: popupMenu.visible
MainAppWindow {
// Some zoomable map item
Map {
id: map
anchors.fill: parent
}
PopupMenu { // Simple Rectangle item
id: popupMenu
anchors.top: parent.top
width: 200
height: parent.height / 2
z: parent.z + 1
MouseArea {
id: mapMouseArea
anchors.fill: parent
enabled: popupMenu.visible
preventStealing:true
hoverEnabled: true
onWheel: { wheel.accepted = true; }
onPressed: { mouse.accepted = true; }
onReleased: { mouse.accepted = true; }
}
ListView {
anchors.fill: parent
clip: true
...
delegate: Item {
...
MouseArea {
anchors.fill: parent
onClick: {
someHandler()
}
}
}
}
}
}
Note: however this solution does not work if ListView (or any other control) is a Map descendant item: item dragging causes map panning. To make it work need to make it at least sibling.

QML Popup: know how it was closed

I've a QML Popup that I can close by clicking outside of it or by pressing escape (hence default closing policy is fine).
However, I need to do different things if the popup is closed with escape (cancel a few things) or by clicking outside (commit the changes). I can't use Dialog, we don't want explicit buttons.
How can I reliably detect it ? So far, I've used a MouseArea to detect if the mouse is hover the Popup when it closes. The only issue is that is doesn't work if the user presses escape and it's mouse is outside the popup.
Popup
{
onAboutToHide: {
if(!ma.containsMouse)
{
}
}
contentItem: Text{
text: "hello"
}
MouseArea{
z:-1
id: ma
anchors.fill: parent
hoverEnabled:true
}
}
I tried to add
Keys.onEscapePressed:{
console.log('esc !')
}
to the popup, but then QML complains it's not an item.
you could change the default close policy and handle the Esc key press separately. The code will then look something like this:
Popup {
id: popup
onAboutToHide: {
if(!ma.containsMouse) {
console.log("click outside: commit the changes")
}
}
contentItem: Text {
text: "hello"
}
MouseArea {
z:-1
id: ma
anchors.fill: parent
hoverEnabled:true
}
closePolicy: Popup.CloseOnPressOutside
Shortcut {
sequence: "Esc"
onActivated: {
console.log("Esc: cancel a few things")
popup.close()
}
}
}

Iterate over buttons in ButtonGroup QML

I have a bunch on Button in my QML application.
Since there are many buttons on the same page it might be difficult for the user to see which button was pressed last!
So I would like to change the last Button font to indicate it was pressed, but later when another button gets pressed I would like to restore its font again.
I can not find a good way to do this.
I tried connecting all buttons to a ButtonGroup, but I need to iterate through all the buttons in order to set underline to false but however I try to do this I get different type of error.
Please see the example below:
Button {
text: qsTr("Open")
ButtonGroup.group: buttonGroup
font.pointSize: 20; font.family: "Courier"
onPressed: { /* some action here*/ }
onReleased: { /* some action here*/ }
}
Button {
text: qsTr("Close")
ButtonGroup.group: buttonGroup
font.pointSize: 20; font.family: "Courier"
onPressed: { /* some action here*/ }
onReleased: { /* some action here*/ }
}
ButtonGroup {
id: buttonGroup
function underlineButtons(underlined) {
for (var i = 0; i < buttons.length; ++i) {
children[i].font.underline = underlined;
}
}
onClicked: {
console.log(" -------------- buttonGroup Clicked: ", button.text)
underlineButtons(false) // restore old buttons
button.font.underline = true // mark the last button clicked
}
}
I am getting the following error what ever I do to try to access the children element.
qrc:/main.qml:65 ((null)): qrc:/main.qml:65: ReferenceError: children is not defined
Short answer
You can either
iterate on the children of the parent of your buttons
or iterate on the buttons of your ButtonGroup.
Long answer
Every QML Item has a children property, containing all Item-inherited children.
It means that you can iterate over the children of an item, like any other list of elements.
Item {
id: container
function setUnderlineOnChildren() {
for (var i = 0; i < children.length; ++i) {
children[i].font.underline = true;
}
}
Button {}
Button {}
Button {
onClicked: {
container.setUnderlineOnChildren();
}
}
}
ButtonGroup is used to create mutually exclusive checkable buttons (like radio buttons) as explained in the documentation. But contrary to most QML elements, because it does not represent a graphical element, it inherits QtObject instead of Item. So it won't have the children property of Items. But you should be able to do the same, relying on the buttons property by adding to the above
ButtonGroup {
id: myGroup
buttons: container.children
function underlineButtons(underlined) {
for (var i = 0; i < buttons.length; ++i) {
buttons[i].font.underline = underlined;
}
}
}
And then calling myGroup.underlineButtons(true).
You can also try using the activeFocus property and then in the font you can query whether or not the button is in active focus.
Button {
text: qsTr("Open")
font.pointSize: activeFocus ? 30 : 20
font.family: "Courier"
onPressed: {
activeFocus = true
/* some action here*/ }
onReleased: {/* some action here*/ }
}
Button {
text: qsTr("Close")
font.pointSize: activeFocus ? 30 : 20
font.family: "Courier"
onPressed: {
activeFocus = true
/* some action here*/ }
onReleased: {/* some action here*/ }
}

How to set pressed=false for mouse area QML

I've faced with next situation:
I have an Item with MouseArea. This is my button on the screen - keyboard.
I persorm long press for button on keyboard.
I don't release this button (holding it).
In onPressed{} signal handler I'm opening another screen.
My buton from keyboard doesn't receive anymore Release signal.
When I come back to the previous keyboard - my button still pressed.
I've tried to set "pressed"=false, but this is readonly property.
I've tried to emit released() signal, but it doesn't clear the "pressed' flag.
But I think that I did it in wrong way. Function release takes parameter- mause - mouseEvent. The mouse parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held. And I didn't find how to set it correct.
I've tried to update MouseArea, it doesn't help.
I don't know what else I have to try to unsed the "pressed" flag.
My mouse area is simple:
MouseArea {
id: mouseArea
property bool haveToRelease: false
onHaveToReleaseChanged: {
if(haveToRelease)
{
console.log("BaseButton.qml: call canceled()")
released()
haveToRelease = false
}
}
anchors.fill: parent
hoverEnabled: true
onReleased: {
console.log("BaseButton.qml: onReleased")
}
onPressedChanged: {
console.log("BaseButton.qml: onPressedChanged, pressed = ", pressed)
}
}
In my button I have the next handler:
onVisibleChanged: {
if(config.isToyota && !visible && pressed) {
console.log("Key.qml :: config.isToyota && !visible && pressed")
releaseButton = true
}
}
I will be vary glad if somebody help me to solve this issue!
Thanks a lot!
as for me that works as expected:
Window {
visible: true
width: 640
height: 480
Rectangle {
width: 100
height: 100
anchors.centerIn: parent
color: "orange"
MouseArea {
anchors.fill: parent
onPressed: {
console.log("pressed");
wnd.show();
released(mouse);
}
onReleased: {
console.log("released");
}
onPressedChanged: console.log("pressed: " + pressed );
}
}
Window {
id: wnd
width: 200
height: 200
}
}
The output:
qml: pressed
qml: released
qml: pressed: true
qml: pressed: false

Resources