[Development] Gesture-scoped vs. "persistent" properties in pointer handlers; handlers in 3D

Shawn Rutledge Shawn.Rutledge at qt.io
Sat Jun 5 00:02:53 CEST 2021

(The TLDR for this is https://bugreports.qt.io/browse/QTBUG-94168)

When working on pointer handlers a few years ago, I was mainly thinking of the primary use cases where the target property points to an Item to be manipulated, and when the handler is active, it changes the appropriate properties for you.  (I.e. you can just declare a DragHandler inside an Item, and it becomes draggable.  If you want to drag something else instead, you can set the target.)  But there are some use cases where the target is or should be null, and you use ordinary bindings to make the handler manipulate arbitrary numeric properties.  QTBUG-68941 was the first (if memory serves) to point out that it’s an API mistake to have the main "value property", like PinchHandler.scale, resetting to the default value (1) when the gesture ends: the result is whatever you bound it to will jump back; so you need conditional bindings, or imperative onValueChanged and/or onActiveChanged code blocks to work around that.  So we made PinchHandler.scale “persistent” or “accumulated” so that you can bind it directly to an Item’s scale, and the item will scale smoothly when you do a sequence of pinches.  And then I thought "But I suppose we need to do the same with rotation.” But didn’t do it.  A couple of years went by… and now we are afraid to repeat that sort of behavior change, for example in 6.2, for fear that too many users might be using those existing properties, and will see breakage when they port their Qt 5 apps to Qt 6.  I’m thinking of PinchHandler.rotation, PinchHandler.translation and DragHandler.translation (I wonder if I missed any more).

Also, DragHandler and PinchHandler have the centroid property.  The gesture-scoped translation should be the same as centroid.scenePosition - centroid.scenePressPosition I think.  Binding to centroid properties can be useful, but they also change abruptly between gestures.

Working with Qt Quick 3D reminded me of this problem.  I want to make pointer handlers work in 3D.  (Not start over again with all-new objects for event handling: maybe that’s been done enough times now.)  But a pointer handler’s target property is a QQuickItem*, so it cannot be a QQuick3DModel* (so far… but it’s still C++-private, so maybe we can still change it to QObject*, as long as it doesn’t affect the QML API for the 2D use cases; I didn’t try that yet).  I have patches that make it possible to declare the handler in the Model, and do event delivery so that it becomes interactive; but DragHandler cannot of its own accord know exactly how to drag a 3D model, because the units are different, and you probably want to drag on a 2D plane, which there is no API to define.  So it seems that the value properties like DragHandler.translation are going to be more important, because you will more likely need to make bindings to them:

     Model {
        source: "#Sphere"
        pickable: true
        materials: DefaultMaterial { diffuseColor: "beige" }
        x: dh.translation.x
        y: -dh.translation.y
        z: 400
        DragHandler { id: dh }

The translation should “pick up where it left off” when you start dragging the second time, to make that work.  But currently it has the same problem as PinchHandler.scale did: it’s just the distance you have dragged within one drag gesture, and jumps back to 0,0 on release.  A workaround could be like this:

    DragHandler {
property point startDrag
onActiveChanged: {
   if (active)
startDrag = Qt.point(sphere.x, sphere.y)
onTranslationChanged: {
            // replace bindings with imperative code, yuck
    sphere.x = startDrag.x + translation.x
    sphere.y = startDrag.y - translation.y

(It would be nice if DragHandler “just works”: declare it in a Model and it’s draggable.  But how?  We’d maybe need to teach DragHandler to use some sort of callback to transform pixel deltas to 3D deltas, where the callback is defined by some Qt Quick 3D API which we haven’t put any thought into yet; and then perhaps use QMetaProperty’s to set the position of the 3D object, because Qt Quick can’t link directly to Qt Quick 3D.  Using metaproperties is a good idea anyway, for another reason: so that property interceptors like Behavior and BoundaryRule will work.  (Every time a handler calls setSomething() on an Item directly, it means those cannot intercept it.)  I don’t know if we will succeed in making binding-free DragHandler possible in 3D or not; but making plain old bindings is simple enough, and ought to work.)

So I want to make translation persistent, but that’s likely to break someone else’s QML code.  I don’t know how widely-used that property is.

So now we are going towards the convention that there is activeTranslation and persistentTranslation, and the same for the other “value properties” like that, in pointer handlers.  That makes translation redundant, so it should be deprecated.  In order to avoid breaking some use cases unexpectedly, we will cause intentional trouble for more of the users (replacing deprecated properties), if we go ahead and repeat that pattern on the rest of the handlers.  It implies that even PinchHandler.scale should be deprecated, because we should call it persistentScale now.  But this is what we do in Qt: we always worry about backwards compatibility, and it’s often a hassle.  In the end I’d rather have the shorter property names, actually.  What do you think?

Should we stick to the rule that we always deprecate and replace, rather than taking a risk?

Any other ideas?

If only I’d thought of doing this a few months ago for 6.0, or 5.12 even…

Anyway https://codereview.qt-project.org/c/qt/qtdeclarative/+/352432 went in for 6.2 today.  Changing the other properties would be past the feature freeze now, so I guess it will be later.

A couple of grinding follow-up patches are https://codereview.qt-project.org/c/qt/qtdeclarative/+/352811 and https://codereview.qt-project.org/c/qt/qtdeclarative/+/352812

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20210604/24e8928b/attachment-0001.html>

More information about the Development mailing list