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;
}
}
Related
I checked the different mouseArea events in the documentation and there is no chance to execute a js function while a mouseArea is being pressed.
As I am not finding a direct way, so I went for this workaround.
I set a Timer to update my panel in case the added bool property isPressed of my mousearea is set to true.
Mouse area:
MouseArea {
id: myMouseArea
//pos and size removed. Not relevant
property bool isPressed: false
onPressed: {
isPressed = true;
}
onReleased: {
isPressed = false;
}
}
Timer:
Timer {
id: updatePanelTimer
interval: 100
repeat: true
running: true
triggeredOnStart: true
onTriggered: updatePanel()
}
Function:
function updatePanel() {
if (myMouseArea.isPressed) {
//run code
}
}
Is there a more direct way to have a code executed while a mouseArea is being pressed? I would like to know if there is a way to do this whithin the mouseArea itself, mainly if its possible without the need of the timer.
If you want to run code only while mouse is in pressed state :
MouseArea {
id: myMouseArea
//pos and size removed. Not relevant
property bool isPressed: false
onPressed: {
updatePanelTimer.start()
}
onReleased: {
updatePanelTimer.stop()
}
}
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")
}
}
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
I'm trying to implement a subwindow as a pop up that should disappear when the user clicks outside of it. Following the example set by this question, I came up with this:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: win
width: 360
height: 360
color: "black"
Rectangle {
id: block
width: 20
height: 20
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
console.log( "Entered" );
menu.visible = true;
menu.requestActivate();
}
}
Window {
id: menu
width: 100
height: 100
x: win.x + block.width
y: win.y + block.height
flags: Qt.Popup
color: "red"
visible: false
onActiveChanged: {
console.log( "Pop up:", active );
if ( !active ) {
visible = false;
}
}
}
}
onActiveChanged: {
console.log( "Main win:", active );
}
}
However the popup does not disappear when clicked outside of, the debug output is:
// Main win opens
qml: Main win: true
// Green square entered
qml: Entered
qml: Main win: true
qml: Pop up: true
// Clicked outside of the pop up
qml: Pop up: true
qml: Main win: true
As you can see the main window does not lose focus when the pop up becomes active, so when the user clicks outside of it the overall focus does not change. So how is this approach supposed to work!?
The solution I found feels quite hackish, but it works, it's self-contained, and it's scalable.
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: win
width: 360
height: 360
color: "black"
Rectangle {
id: block
width: 20
height: 20
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
menu.visible = true;
}
}
Rectangle {
id: menu
width: 100
height: 100
anchors.top: parent.bottom
anchors.left: parent.right
color: "red"
visible: false
Loader {
id: exitAreaLoader
Component {
id: exitArea
MouseArea {
x: 0
y: 0
parent: menu.getRoot()
width: if ( parent !== null ) { return parent.width }
height: if ( parent !== null ) { return parent.height }
onPressed: {
var pnt = Qt.point( mouse.x - menu.parent.width,
mouse.y - menu.parent.height );
if ( !menu.contains( pnt ) )
{
console.log( "Closing", pnt );
menu.visible = false;
exitAreaLoader.sourceComponent = undefined;
} else {
mouse.accepted = false;
}
}
}
}
}
onVisibleChanged: {
if ( visible ) {
console.log( "Opening" );
exitAreaLoader.sourceComponent = exitArea;
}
}
function getRoot() {
var par = parent;
while ( par.parent !== null ) {
par = par.parent;
}
return par;
}
}
}
}
The general principle is that when the menu/popup/drop down is made visible a Loader creates a MouseArea as the most descendent child in the entire QObject tree (this guarantees that all mouse events within the window will be received by it). However this means that mouse events that are destined for whatever the popup contains are also captured by the MouseArea, so we have to explicitly test for that and reject the event.
It's a real shame that there's not an official object for this, one was requested years ago and it seems to have adopted by Ubuntu, but that's not very useful for cross-platform development.
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.