I made a simplified MapImage component which allows to zoom and pan an image with the mouse. This component uses Flickable and MouseArea components. The MapImage component just handles image display, zooming and panning. I want to use another MouseArea in the MapImage instance in main.qml (to be able to place objects using a Canvas but this is not important here). This is not the job of MapImage, so I really need this second MouseArea component.
I need to set the hoverEnabled property to true because I need onPositionChanged and others events... But this property seems to cause problems with mouseX and mouseY values taken from my updateFlickable
function. When I'm zooming with the mouse wheel, zoom does not occur at the mouse position...
I've made a minimal example available here or in a gist.
Any hint to solve this?
main.qml
import QtQml.Models 2.11
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
MapImage {
id: map
height: 600
width: 800
imageSource: "https://images.unsplash.com/photo-1651634099253-720df02a0d50"
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true // this is required to use onPositionChanged
preventStealing: false
onPressed: {
// needed for flickable
mouse.accepted = false;
}
onPositionChanged: {
// do something.
}
}
}
MapImage.qml
import QtQuick 2.11
Item {
id: root
property alias imageSource: image.source
Flickable {
id: flickable
anchors.fill: parent
contentWidth: props.originalImageWidth
contentHeight: props.originalImageHeight
Image {
id: image
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if (status === Image.Ready) {
props.originalImageWidth = sourceSize.width;
props.originalImageHeight = sourceSize.height;
props.changeCurrentScale(1);
}
}
// define the image display size
width: flickable.contentWidth;
height: flickable.contentHeight;
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onWheel: {
wheel.accepted = false;
props.changeCurrentScale(wheel.angleDelta.y);
}
}
}
QtObject {
id: props
// original image size
property int originalImageWidth
property int originalImageHeight
property real scaleStep: 0.2
property real currentScale: 0.1
onCurrentScaleChanged: updateFlickable(currentScale);
function updateFlickable(scale) {
console.log(mouseArea.mouseX, mouseArea.mouseY); // <------ I am no longer able to get mouse x and y coordinates
flickable.resizeContent(originalImageWidth * scale, originalImageHeight * scale, Qt.point(mouseArea.mouseX, mouseArea.mouseY));
flickable.returnToBounds();
}
function changeCurrentScale(wheelDelta) {
if (wheelDelta > 0) currentScale = currentScale * (1 scaleStep);
else currentScale = currentScale / (1 scaleStep);
}
}
}
}
CodePudding user response:
Finally found a solution. I had to add a new property in my MapImage component. This property role is to store the updated position of the mouse in the parent mouse area in the parent coordinate system. After that, I have to use mapToItem to convert in the flickable.contentItem coordinate system.
main.qml
import QtQml.Models 2.11
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
MapImage {
id: map
height: 600
width: 800
imageSource: "https://images.unsplash.com/photo-1651634099253-720df02a0d50"
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true // this is required to use onPositionChanged
preventStealing: false
onPressed: {
// needed for flickable
mouse.accepted = false;
}
onPositionChanged: {
// CHANGE HERE
// the position must be updated on every position change
map.parentMouseAreaPosition = Qt.point(mouse.x, mouse.y);
// TO HERE
}
}
}
MapImage.qml
import QtQuick 2.11
Item {
id: root
property alias imageSource: image.source
// CHANGE HERE
// the current mouse position in the parent mouse area in parent coordinate system
property var parentMouseAreaPosition: Qt.point(0, 0)
// this function maps the parent coordinate system to that of contentItem
function __mapToContentItem(x, y) {
return mapToItem(flickable.contentItem, x, y);
}
// TO HERE
Flickable {
id: flickable
anchors.fill: parent
contentWidth: props.originalImageWidth
contentHeight: props.originalImageHeight
Image {
id: image
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if (status === Image.Ready) {
props.originalImageWidth = sourceSize.width;
props.originalImageHeight = sourceSize.height;
props.changeCurrentScale(1);
}
}
// define the image display size
width: flickable.contentWidth;
height: flickable.contentHeight;
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onWheel: {
wheel.accepted = false;
props.changeCurrentScale(wheel.angleDelta.y);
}
}
}
QtObject {
id: props
// original image size
property int originalImageWidth
property int originalImageHeight
property real scaleStep: 0.2
property real currentScale: 0.1
onCurrentScaleChanged: updateFlickable(currentScale);
function updateFlickable(scale) {
// CHANGE HERE
// get the mapped point
let point = __mapToContentItem(root.parentMouseAreaPosition.x, root.parentMouseAreaPosition.y);
console.log(point.x, point.y);
flickable.resizeContent(originalImageWidth * scale, originalImageHeight * scale, point);
// TO HERE
flickable.returnToBounds();
}
function changeCurrentScale(wheelDelta) {
if (wheelDelta > 0) currentScale = currentScale * (1 scaleStep);
else currentScale = currentScale / (1 scaleStep);
}
}
}
}
I don't know if there is a better solution.