How to set pressed=false for mouse area QML - qt

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

Related

QML SplitView auto collapse on handlebar mouse release

I have a QML Controls 2 SplitView and a redefined handle, which works well, but I want detect mouse release event on the handler, so I could collapse the SplitView under a certain threshold of width. Adding a MouseArea on top of the existing handle will absorb drag events, so I'm unable to move the handlebar. Any idea how could I gather the mouse release event, or any other solution which solves this problem?
Alright, I have created an example application. As you can see in this example, my MouseArea is marked with yellow and collapses the right view programmatically when double clicked, which is nice, but I also want to drag the handlebar and upon mouse release under a certain width threshold I want to collapse the view as well. The black part of the handlebar where my MouseArea is not covering the handlebar, responds to drag, but since there is no signal I can gather from it, the width threshold already set shouldCollapse boolean property, so the view won't update. Probably I could solve this issue with a timer, but I need a more sophisticated solution.
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 800
height: 400
visible: true
SplitView {
id: splitView
anchors.fill: parent
orientation: Qt.Horizontal
function toggleCollapse() { collapsibleRect.shouldCollapse = !collapsibleRect.shouldCollapse }
handle: Rectangle {
implicitWidth: 20
implicitHeight: 20
color: "black"
MouseArea {
anchors.centerIn: parent
width: parent.width
height: parent.height / 2
onDoubleClicked: splitView.toggleCollapse()
Rectangle {
anchors.fill: parent
color: "yellow"
Text {
anchors.centerIn: parent
text: "Double click to collapse"
rotation: 90
}
}
}
}
Rectangle {
id: mainRect
color: "green"
SplitView.fillWidth: true
Text {
anchors.centerIn: parent
font.pixelSize: 24
text: "Main scene"
}
}
Rectangle {
id: collapsibleRect
property bool shouldCollapse: false
SplitView.preferredWidth: shouldCollapse ? 0 : 300
color: "purple"
clip: true
onWidthChanged: {
if(width < 200) shouldCollapse = true
else shouldCollapse = false
}
Text {
anchors.centerIn: parent
rotation: parent.shouldCollapse ? 90 : 0
font.pixelSize: 24
text: parent.shouldCollapse ? "SHOULD BE COLLAPSED" : "NOT COLLAPSED"
Behavior on rotation { NumberAnimation { duration: 100 } }
}
}
}
}
I had a similar problem and was able to solve it thanks to the hint of #Ponzifex that the SplitView's resizing property will be set to true as soon as the handle is clicked. Using a Timer I managed to detect whether the handle was quickly pressed twice in a row.
SplitView {
id: view
...
handle: Rectangle {
...
}
//============================================================
// double click behavior
Timer {
id: doubleClickTimer
interval: 300 // number of ms between clicks that should be considered a double click
}
property bool doubleClicked: false
// `resizing` will be set to true even if the handle is just pressed
onResizingChanged: {
if (view.resizing) {
if (!doubleClickTimer.running) {
doubleClickTimer.start();
return;
}
view.doubleClicked = true;
} else {
if (view.doubleClicked) {
// do any manual resizing in here
view.doubleClicked = false;
}
}
}
}
It is important to note, however, that it is only possible to resize the contents of a SplitView when resizing is false. That's why I need to have the doubleClicked helper property.
Add this to MouseArea:
onPressed: {
mouse.accepted = (mouse.flags & Qt.MouseEventCreatedDoubleClick);
}
propagateComposedEvents: true
cursorShape: Qt.SplitHCursor

QML Virtual Keyboard not deactivated when I press "Hide" button

I've created a Virtual Keyboard control on a QML screen. However, while I can show and hide the keyboard in my code fine, when I press the "Dismiss" button, it doesn't hide.
I don't think this is a duplicate of QML Virtual keyboard Hide button not working because that question seems to be about the visibility not being bound to the active status whereas in my case, the active status is not being updated at all from what I can tell, and my setup is different. Also I'm not getting any error message while that poster did.
Here is my Keyboard input panel (Keyboard.qml):
import QtQuick 2.0
import QtQuick.VirtualKeyboard 2.1
InputPanel {
id: keyboard
z: 99
y: parent.height
anchors.left: parent.left
anchors.right: parent.right
onActiveChanged: {
console.info("keyboard active changed"); // this doesn't get hit when I press the hide button
}
states: State {
name: "visible"
when: keyboard.active
PropertyChanges {
target: keyboard
y: parent.height - keyboard.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
Binding {
target: InputContext
property: "animating"
value: inputPanelTransition.running
}
}
Then on my QML screen I have this:
TextInput {
id: myField
anchors.fill: parent
anchors.margins: vitalStyle.marginSmall
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignBottom
clip: true
onTextChanged: {
if (model.fieldType === SettingsManager.WLAN_PASSKEY) {
resultData = text;
dataUpdated++;
}
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onPressed:{
keyboard.active = true; // works fine - makes keyboard pop up.
mouse.accepted = false;
if( (otherScreen.visible) ) {
keyboard.active = false; // works fine - hides keyboard if otherScreen is visible.
}
}
onActiveFocusChanged: {
console.info("active focus changed (basic)"); // doesn't get hit if I press the dismiss button.
if( (focus) ) {
cursorPosition = text.length;
clearInputMethod();
console.info("activating keyboard"); // doesn't get hit if I press the dismiss button.
keyboard.active = true;
}
else{
clearInputFocus.enable();
}
EnterKeyAction.enabled: false
}
}
.
.
.
.
function clearInputMethod() {
// sets 'visible' to false of a number of other controls.
keyboard.active = false;
}
TextInput {
id: clearInputFocus
readOnly: true
function enable() {
forceActiveFocus();
clearInputMethod();
}
}
Keyboard {
id: keyboard
}
I was afraid that pressing the 'Hide' button might cause the focus to go back to the text field and then maybe it was just popping it back up immeadiately because of the code in onActiveFocusChanged however those console.log statements aren't displaying anything when I press the Hide button so I don't think that's the case. Neither is the onActiveChanged event on the InputPanel itself being hit when I press the button. Though both are being hit when I first show the keyboard by clicking on the field.
The button itself does dim slightly as I press it, as all of the buttons do, so the button press is "occurring" if you get me.
I ended up having to use an "eventFilter" which detects when the region that the Hide button occupies is pressed. Not very elegant I know...
bool contentLoader::eventFilter( QObject *object, QEvent *event )
{
if( (m_keyboardActive) &&
(event->type() == QEvent::TouchEnd) )
{
QTouchEvent *touch = static_cast<QTouchEvent *>(event);
if( (touch->touchPoints().count()) )
{
QTouchEvent::TouchPoint lastPoint = touch->touchPoints().last();
//Normalised location of the Hide Keyboard Button
QRectF hideButton_normalised( 0.8725, 0.90, 1.0, 1.0 ); //fullsize keyboard
if( ((m_inputHint & Qt::ImhDigitsOnly) == Qt::ImhDigitsOnly) )
{
hideButton_normalised.setCoords( 0.55, 0.85, 0.87, 0.90 ); //numpad
}
if( (hideButton_normalised.contains( lastPoint.normalizedPos() )) )
{
hideKeyboard();
}
}
}
and in the QML...
Connections {
target: contentHandler
onHideKeyboard: {
keyboard.active = false;
}
}

QML Button - How to check that button is pressed with Control modifier?

How we can detect the QML button is clicked (checked) with "Control" key button pressed simultaniously?
AFAIK there's no way to check if the CTRL key is pressed while you're in an onPressed handler of a button, but you could work around that:
Let QML inform you when the CTRL key is pressed/released. Save that status in a property and use it in the onPressed handler of the button.
// Example code
Item {
id: rootItem
anchors.fill: parent
focus: true
property bool ctrlPressed: false
Keys.onPressed: {
if (event.key === Qt.Key_Control) {
ctrlPressed = true
}
}
Keys.onReleased: {
if (event.key === Qt.Key_Control) {
ctrlPressed = false
}
}
Button {
anchors.centerIn: parent
text: "Press me"
onPressed: {
if (rootItem.ctrlPressed)
console.log("Click with CTRL")
else
console.log("Click without CTRL")
}
}
}
This is just a 'workaround' and has some issues:
The rootItem has to have the focus
when the Application loses focus (ALT+TAB or minimize) while the CTRL button is pressed you might get unexpected behaviour
Another option is simply adding your own MouseArea on top of the button. You could use propogateComposedEvents to allow the Button's default mouse handling to still occur, or you could manually regenerate things like the clicked signal yourself. Neither option is ideal, but maybe one works for you.
Button {
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ControlModifier)) {
doSomething();
}
}
}
}
I used #ParkerHalo's answer. The issue he noticed when the app loses focus with ctrl pressed can be avoided by placing this in the top level Window:
onActiveChanged: if( ! active ) rootItem.ctrlPressed = false;
One more solution is to use a TapHandler (a relatively new feature, see blog post).
There's an example in the docs for a new TreeViewDelegate which can be modified for your case with Button (they both inherit AbstractButton).
This way you won't have to remember any state or propagate events to underlying internal mouse area of the button:
Button {
text: "Test"
TapHandler {
acceptedModifiers: Qt.ControlModifier
onTapped: console.log("tapped with Qt.ControlModifier")
}
TapHandler {
acceptedModifiers: Qt.NoModifier
onTapped: console.log("tapped with Qt.NoModifier")
}
}

Drag after long press

I want to drag my custom buttons QML after a long press over them. I've implemented that behaviour, however the problem is that after enabling drag, I need to press button once again to actually start dragging. How should I implement this mechanism if I want to move buttons without releasing after long press?
Here is my button code (onReleased and onLongPressed are my own signals):
ButtonWidget.SoftButtonUI
{
id:softButtonDelegate2
x:500
y:300
labelText: "button"
iconImageSource: path
isGrayedOut: false
Drag.active: dragArea2.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
onReleased:
{
console.log("onClicked")
}
onLongPressed:
{
console.log("onLongPressed")
dragArea2.enabled = true
}
MouseArea {
id: dragArea2
enabled: false
anchors.fill: parent
drag.target: parent
onReleased: parent.Drag.drop()
onClicked: {
console.log("MouseArea onClicked")
}
onPressAndHold: {
console.log("MouseArea onPressAndHold")
}
}
}
Any idea?
Generally speaking you can connect different signals and concatenate operations as discussed in this page. You should have a look at it since it is full of nice and useful information.
However, when it comes to mouse events, an interesting approach to events concatenation is given by MouseEvents acceptation. Documentation says about MouseEvent::accepted:
Setting accepted to true prevents the mouse event from being
propagated to items below this item. Generally, if the item acts on
the mouse event then it should be accepted so that items lower in the
stacking order do not also respond to the same event.
In this case we can take the opposite approach by not accepting the event. This way the pressed event can be used to both activate the drag and actually perform it. Then the MouseEvent can be accepted (implicitly) in the release event, occurring at the end of the drag.
Here is a simple example following this approach. As the mouse is pressed and hold the drag.target is set and drag can start, whereas when the mouse is released the drag.target is removed, removing the dragging behaviour. To test it, just press and hold the mouse over the rectangle and when it changes color just drag it.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
width: 300
height: 300
visible: true
Rectangle {
id: item
border.width: 2
x: 100
y: 100
width: 100
height: 100
state: "BASE"
states: [
State {
name: "BASE"
PropertyChanges { target: mouseArea; drag.target: undefined}
PropertyChanges { target: item; color: "steelblue"}
},
State {
name: "DRAGGABLE"
PropertyChanges { target: mouseArea; drag.target: item}
PropertyChanges { target: item; color: "darkblue"}
}
]
MouseArea {
id: mouseArea
anchors.fill: parent
drag{
// target: NOT SET HERE
minimumX: 0
minimumY: 0
maximumX: parent.parent.width - parent.width
maximumY: parent.parent.height - parent.height
smoothed: true
}
onPressAndHold: {
item.state = "DRAGGABLE"
mouse.accepted = false // mouse event is USED but NOT CONSUMED...
}
onReleased: {
item.state = "BASE" // mouse event acceptation occurs here!
}
}
}
}
This simple approach should work perfectly also with your custom signals.

How can I switch the focus for the pop-up window?

I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.

Resources