Hi Shawn, I've realized it might be faster to provide some code than try to explain better.
Original.txt - is the code where scaling happens relatively top/left Approach_1.txt - adjusting x/y Approach_2.txt - scaling top level item & scaling non-zoomable to compensate >> anchors.centerIn: parent What I meant is rather anchors.centerIn: child (so that child would free-zoom relatively center, and parent would center itself relatively center of the child) чт, 13 сент. 2018 г. в 16:15, Shawn Rutledge <shawn.rutle...@qt.io>: > > > > On 13 Sep 2018, at 14:41, Alexander Ivash <elder...@gmail.com> wrote: > > > > I need pinch-zoomable item container with non-zoomable item container > > attached to the bottom. Like this: > > > > Item { > > id: item > > Item { > > id: zoomable > > } > > > > Item { > > id: nonzoomable > > } > > } > > > > (anchoring, geometry omitted for simplicity) > > > > I see at least the following solutions: > > > > 1. Change 'scale' of the whole item (zoomable + non-zoomable), then > > change 'scale' of non-zoomable to compensate parent scaling. It feels > > a bit wrong so I'd like to avoid it. Also doing in this way leads to > > some issues with anchoring non-zoomable item to zoomable one properly. > > > > 2. Change 'scale' of the zoomable part only. But in this case I need > > to adjust x/y of 'item', otherwise scaling will always be happening > > relatively top/left (and I need relatively to the center) > > PinchArea only sets the scale property of the zoomable item directly, and > that is relative to the Item transformOrigin which defaults to center. > > PinchHandler zooms relative to the centroid between the touchpoints, which is > more typical behavior like what you get in most non-Qt interfaces (you need > to be able to zoom into a particular geographical area of a map, or an > interesting part of some drawing, for example). It normally does that by > setting the scale and the x and y properties at the same time. But it’s not > overriding anchors.centerIn: parent, so you can still use that to keep your > item centered. > > If yours is always zooming toward the top left, it must be because of your > anchors or bindings, or because of setting transformOrigin to TopLeft. > > You could use another wrapper item to hold the place in the layout, and then > add your zoomable item to that. If it has no bindings or anchors, it can be > free to zoom and move around within that space. If you want to disable > movement, or allow movement only within certain confines, you can try the > minimumX/Y and maximumX/Y properties. > > > Is there better way to do it? > > > > If there would be a way to center parent in child - I could anchor > > 'item' to the center of 'zoomable' and everything would work > > magically. But such kinds of anchors are not supported. > > anchors.centerIn: parent > > What I sometimes wish we had is _bindable_ centerX/Y properties. But at > least we have anchors. >
import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Shapes 1.0 import Qt.labs.handlers 1.0 Item { id: draggableObject property int index: -1 default property alias content: content.children signal grabChanged(var index); signal tapped(var index); width: contentContainer.width onWidthChanged: { console.debug('draggable width: ', width); } height: contentContainer.height onHeightChanged: { console.debug('draggable height: ', height); } Item { id: contentContainer width: content.width * content.scale property int prevWidth: width onWidthChanged: { console.debug('content width: ', width); draggableObject.x -= (width - prevWidth) / 2 prevWidth = width; } height: content.height * content.scale property int prevHeight: height onHeightChanged: { console.debug('content height: ', height); draggableObject.y -= (height - prevHeight) / 2 prevHeight = height; } DragHandler { onGrabChanged: { draggableObject.grabChanged(index); } } PinchHandler { target: content } TapHandler { onTapped: { draggableObject.tapped(index); } } Item { id: content anchors.centerIn: parent clip: true width: childrenRect.width onWidthChanged: { console.debug('content width: ', width); } height: childrenRect.height onHeightChanged: { console.debug('content height: ', height); } onScaleChanged: { } } Shape { anchors.centerIn: parent width: contentContainer.width height: contentContainer.height ShapePath { fillColor: 'transparent' strokeColor: 'green' strokeWidth: 4 strokeStyle: ShapePath.DashLine startX: 0 startY: 0 dashPattern: [ 1, 4 ] PathLine { x: contentContainer.width; y: 0 } PathLine { x: contentContainer.width; y: contentContainer.height } PathLine { x: 0; y: contentContainer.height } PathLine { x: 0; y: 0 } } } MouseArea { id: mouseZoomer anchors.fill: parent hoverEnabled: false onPressed: { mouse.accepted = false } onReleased: { mouse.accepted = false } onWheel: { if(wheel.modifiers & Qt.ControlModifier) { console.debug('onWheel'); if(wheel.angleDelta.y > 1.0) { content.scale *= 1.1 } else { content.scale /= 1.1 } } else { wheel.accepted = false } } } } Button { anchors.top: contentContainer.bottom anchors.right: contentContainer.right width: 50 height: 50 text: 'button: ' + index } }
import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Shapes 1.0 import Qt.labs.handlers 1.0 Item { id: draggableObject property int index: -1 default property alias content: content.children signal grabChanged(var index); signal tapped(var index); width: contentContainer.width onWidthChanged: { console.debug('draggable width: ', width); } height: contentContainer.height onHeightChanged: { console.debug('draggable height: ', height); } Item { id: contentContainer width: content.width * content.scale property int prevWidth: width onWidthChanged: { console.debug('content width: ', width); } height: content.height * content.scale property int prevHeight: height onHeightChanged: { console.debug('content height: ', height); } DragHandler { onGrabChanged: { draggableObject.grabChanged(index); } } PinchHandler { target: draggableObject } TapHandler { onTapped: { draggableObject.tapped(index); } } Item { id: content anchors.centerIn: parent clip: true width: childrenRect.width onWidthChanged: { console.debug('content width: ', width); } height: childrenRect.height onHeightChanged: { console.debug('content height: ', height); } onScaleChanged: { } } Shape { anchors.centerIn: parent width: contentContainer.width height: contentContainer.height ShapePath { fillColor: 'transparent' strokeColor: 'green' strokeWidth: 4 strokeStyle: ShapePath.DashLine startX: 0 startY: 0 dashPattern: [ 1, 4 ] PathLine { x: contentContainer.width; y: 0 } PathLine { x: contentContainer.width; y: contentContainer.height } PathLine { x: 0; y: contentContainer.height } PathLine { x: 0; y: 0 } } } MouseArea { id: mouseZoomer anchors.fill: parent hoverEnabled: false onPressed: { mouse.accepted = false } onReleased: { mouse.accepted = false } onWheel: { if(wheel.modifiers & Qt.ControlModifier) { console.debug('onWheel'); if(wheel.angleDelta.y > 1.0) { draggableObject.scale *= 1.1 } else { draggableObject.scale /= 1.1 } } else { wheel.accepted = false } } } } Button { anchors.top: contentContainer.bottom anchors.right: contentContainer.right scale: 1 / draggableObject.scale width: 50 height: 50 text: 'button: ' + index } }
import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Shapes 1.0 import Qt.labs.handlers 1.0 Item { id: draggableObject property int index: -1 default property alias content: content.children signal grabChanged(var index); signal tapped(var index); width: contentContainer.width onWidthChanged: { console.debug('draggable width: ', width); } height: contentContainer.height onHeightChanged: { console.debug('draggable height: ', height); } Item { id: contentContainer width: content.width * content.scale height: content.height * content.scale DragHandler { onGrabChanged: { draggableObject.grabChanged(index); } } PinchHandler { target: content } TapHandler { onTapped: { draggableObject.tapped(index); } } Item { id: content anchors.centerIn: parent clip: true width: childrenRect.width onWidthChanged: { console.debug('content width: ', width); } height: childrenRect.height onHeightChanged: { console.debug('content height: ', height); } onScaleChanged: { } } Shape { anchors.centerIn: parent width: contentContainer.width height: contentContainer.height ShapePath { fillColor: 'transparent' strokeColor: 'green' strokeWidth: 4 strokeStyle: ShapePath.DashLine startX: 0 startY: 0 dashPattern: [ 1, 4 ] PathLine { x: contentContainer.width; y: 0 } PathLine { x: contentContainer.width; y: contentContainer.height } PathLine { x: 0; y: contentContainer.height } PathLine { x: 0; y: 0 } } } MouseArea { id: mouseZoomer anchors.fill: parent hoverEnabled: false onPressed: { mouse.accepted = false } onReleased: { mouse.accepted = false } onWheel: { if(wheel.modifiers & Qt.ControlModifier) { console.debug('onWheel'); if(wheel.angleDelta.y > 1.0) { content.scale *= 1.1 } else { content.scale /= 1.1 } } else { wheel.accepted = false } } } } Button { anchors.top: contentContainer.bottom anchors.right: contentContainer.right width: 50 height: 50 text: 'button: ' + index } }
_______________________________________________ Interest mailing list Interest@qt-project.org http://lists.qt-project.org/mailman/listinfo/interest