Use two MouseAreas simultaneously in Android

837 views Asked by At

I am in the need to emulate a control key press on a tablet device that doesn't have a physical keyboard. In order to do so, I have an on-screen floating button which the user can use to achieve that.

The problem however is that there can be only one MouseArea that receives touch events. If one has focus, the rest are ignored. Which is obviously problematic, as that makes it impossible to press and hold the control button and press on some other GUI element.

Note that I don't need multi-touch in the literal context - I only need one touch per mouse area, I just need multiple mouse areas to work simultaneously. This, plus the reason that MultiPointTouchArea's API is tailored specifically for actual multi-touch and lacks the properties, functionality (hover, cursor) and convenience that I actually need is the reason I am really not too eager to use it instead, even if is supposedly backwards compatible with mouse input.

I suspect that this limitation is due to the fact that touch has been shoehorned into MouseArea rather than being a first class citizen and the underlying implementation is one-cursor-to-rule-them-all kinda deal, but still, maybe there is some way to make multiple mouse areas work simultaneously?

OK, here is a trivial example - pressing the left or right side of the screen turns it respectively blue and red, however it is impossible to press one if the other is already pressed.

Window {
  id: main
  visible: true
  width: 600
  height: 300

  Row {
    Rectangle {
      width: main.width * .5
      height: main.height
      color: m1.pressed ? "blue" : "black"
      MouseArea {
        id: m1
        anchors.fill: parent
      }
    }
    Rectangle {
      width: main.width * .5
      height: main.height
      color: m2.pressed ? "red" : "black"
      MouseArea {
        id: m2
        anchors.fill: parent
      }
    }
  }
}
1

There are 1 answers

0
petacreepers23 On

Well, 2 years after and no answers before... I will tell my whole history so nobody tries the same things that we did and loose time. Go below the line (a bit under the image) to skip that.

We had the same problem, even bigger, because we designed all our system with the phrase you said,

even if is supposedly backwards compatible with mouse input

in mind, time after we saw we were in trouble.

After thinking how we could solve the problem we first tried a recursive function to search all mouseareas, and for each touch point, generate a fake mouse event with the flag Qt.MouseEventSynthesizedByApplication active, and the mousearea might be suposed to process that event, and trigger the corresponding callbacks.

Here I put an image of the failed code, so people in a hurry don't copy it... It is unfinished and we still had problens figuring out how to handle multiple touches. Failed code

It seems that when you have more than one touch in the mousearea, there is no behaviour coded for that scenario, wich, makes sense because you should only have one mouse in the system.

Then we searched in web, and found a deleted post of 2009 in webarchive of qt forum of someone with a similar issue, and posted a weird code (that doesn't work on qt5) to trick the mousearea to accept those events. That gave us the idea of generating ourselfes the event that should occur, and call the signal from the corresponding mousearea with the fake mouse event.

That worked except for the pressed(MouseEvent mouse) signal, wich couldnt be triggered. It gave errors.

After that, we tried to make public the signals (by explicit declaration on the mousearea)

property var toques = []
visible: false
property var pressed
onPressed: pressed = true
onReleased: pressed = false

That got us a step closer, but it will require the implementation of each mouse event, and furthermore, thre is an intermitency between all touches (since we may trigger each signal for each touch) and behaviur may be undefined. Also the fact that no order in the touches to keep track of what happend, made us reject the mousearea for this porpuse.


Then what we did is to use MutiPointTouchArea to handle the events. We saw that using multiple MultiPointTouchArea(s) does not give errors, and can handle multiple clicks (from touches) for the applicattion.

The things you'll have to do is to replace MouseAreas with the Multitouch ones, and implement yourself all the behavoiurs you want, concepts like hover doesn't make sense in this context, since we are handling touches. you may use pressure for doing something similar. We put the component Rectangle to keep the variables/signals of our interest, and to be changed from the MultiTouchArea when certain actions occur. Here I will implement your trivial example based on that idea:

Window {
  id: main
  visible: true
  width: 600
  height: 300

  Row {
    Rectangle {
      width: main.width * .5
      height: main.height
      property var lol: false
      color: ispressed ? "blue" : "black"
      MultiPointTouchArea {
        id: m1
        anchors.fill: parent
        onPressed: parent.ispressed= true
        onReleased: parent.ispressed= false
      }
    }
    Rectangle {
      width: main.width * .5
      height: main.height
      x:main.width * .5
      property var ispressed: false
      color: ispressed ? "red" : "black"
      MultiPointTouchArea {
        id: m2
        anchors.fill: parent
        onPressed: parent.ispressed= true
        onReleased: parent.ispressed= false
      }
    }
  }
}

As you see is the Rectangle (your button or whatever) the one that has the properties that matter, in this case ispressed and it gets changed by the child (MultipointToucArea) according to our defined behavoiur, in this case activate when its pressed, and release when it releases, note that in this case, the color goes off only by pulling one finger, because onReleased is triggered.

You may have to decide which is the correct behavoiur for you application in each case. The signals given for programming this were more than enough in our case:

canceled(list<TouchPoint> touchPoints)
gestureStarted(GestureEvent gesture)
pressed(list<TouchPoint> touchPoints)
released(list<TouchPoint> touchPoints)
touchUpdated(list<TouchPoint> touchPoints)
updated(list<TouchPoint> touchPoints)

Also you may see that the pressed only occur in the first area thet is touched, so if you keep holding the finger accross others mouseareas, those won't get the pressed event triggered, so playing with updated() you might be able to get wheter it should activate things or not.

This is the solution we got, and worked for us, we couldn't figure anything better and for clicks/pressing events, this makes the trick.

Hope it helps.