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

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

Related

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

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