Keep cursor shape while pressed as it moves outside its MouseArea
Asked Answered
P

2

6

I am implementing narrow resize handles which give me annoying behavior. The cursor shape is as expected while the mouse is directly over the handle, but once dragging the handle is initiated, the cursor shape becomes inconsistent. There are two causes of this:

  • when the cursor moves fast and goes ahead of the handle until the handle "catches up" (or when "fluid qml" is too fluid) - this is particularly nasty as the cursor shape rapidly changes and blinks

  • when the cursor moves outside of the allowed degree of freedom for the handle

I looked up the doc but it doesn't seem to contain anything about locking the cursor until the press is released.

I did manage to find a hack to fix it - using a dummy overlay MouseArea with acceptedButtons: Qt.NoButton - this actually helps to fake cursor consistency, but comes with an issue of its own. Having that overlay mouse area doesn't allow the cursor to change to a resize shape when it is over the handle, since the handle is under the overlay mouse area it doesn't get to modify the cursor shape at all. So the resize shape kicks in only after the handle is clicked, which is far from ideal. Setting the overlay mouse area to enabled: false doesn't change that - it still keeps blocking cursor shape changes from underlying mouse areas. There is a workaround for that as well, for example setting the overlay mouse area size to 0x0, but it is kind of ugly.

Ideally, the cursor shape should persist until the mouse area is pressed, regardless of whether it is in or outside of its area - after all, the press is not released if you go outside of it, thus the mouse area is still in control and should persist its cursor shape. For example - the window resize handles remain resize shape even if it is moved to resize the window smaller than its minimal size, until the press is released.

To me it seems that there are flaws in the implementation of MouseArea - the cursor shape is not kept while pressed, and the cursor shape is changed even if the mouse area is disabled.

Pyridoxine answered 28/9, 2016 at 16:42 Comment(4)
The fact that the cursor shape is not kept while pressed seems to be a deliberate choice, QWidgets and QQuickItems have both the same behavior about this. Just being curious, what's your use case here ?Yeeyegg
It is described in the first part of the question. Also, I did not imply it was "accidental", just shortsighted, and far from the only such instance in Qt and QML in particular. If an item sets the cursor, that should apply as long as the item has control over the cursor, not just when the cursor is in its area. And disabled items should not interfere with the cursor whatsoever. It is only logical.Pyridoxine
There are many other examples why this behavior makes sense - for example rotating a knob, which is done by moving the mouse either up-down or left-right. You'd want to keep the cursor to preserve its "rotating a knob" shape while you are rotating, not only while the cursor is above the knob. The cursor indicates what you are doing, not strictly and only where you are. You'd want a resize cursor while you are resizing, a drag cursor while you are dragging, a move cursor while you are moving. As simple as that.Pyridoxine
Indeed that's legit :) I glanced over the first part, sorry.Yeeyegg
G
1

I didn't find a way to do this out-of-the-box, it is pretty easy to create a helper for this though. On the qml side you can e.g. have:

CursorChanger {
    cursor: Qt.SizeHorCursor
    active: dragArea.containsMouse || dragArea.drag.active
}

On the C++ side you'll need a helper class like this:

class CursorChanger : public QObject
{
    Q_OBJECT

    Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
    Q_PROPERTY(int cursor READ cursor WRITE setCursor NOTIFY cursorChanged)
    
    // ...
}

In the implementation you can use QGuiApplication::setOverrideCursor and QGuiApplication::restoreOverrideCursor to actually set/reset the cursor. Don't forget to also reset in the CursorChanger destructor if active at that point. If you then register the type:

qmlRegisterType<CursorChanger>(uri, 1, 0, "CursorChanger");`

You can use this type from qml.

Gleeful answered 7/5, 2021 at 15:21 Comment(0)
B
0

I think there are some use cases for the current behavior. For instance, the cursor could convey some relationship between the currently hovered object and the one where the mouse was pressed. Another example, a drag could have its speed intentionally limited, and when the user goes too fast there is a consequence that depends on the item behind.

dtech's demand is more common for sure, and I also would like to see that as an optional feature, but not as a change. The way it is now provides more versatile pieces for apps. I don't like polished components that can be used only exactly as the library writer imagined.

Another QML only solution for a persistent drag cursor is to have a MouseArea behind all itens to hold the persistent shape when needed:

Item
{
    id: scene;    width: 800;  height: 600

    MouseArea
    {  
        id: mouse
        anchors.fill: scene
    }

    Rectangle
    {
        id: draggable;    width: 40;  height: 30;   color: "red"
        MouseArea
        {
            anchors.fill:  draggable
            drag.target :  draggable

            //set and unset a persistent cursor
            onPressed :  mouse.cursorShape =  Qt.DragMoveCursor;
            onReleased:  mouse.cursorShape =  Qt.ArrowCursor;    //QT default cursor

            //let non default scene cursors prevail over the item's
            cursorShape: mouse.cursorShape === Qt.ArrowCursor ?  
                Qt.OpenHandCursor : mouse.cursorShape;
        }
    }
}
Brucebrucellosis answered 12/6, 2021 at 1:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.