简体   繁体   中英

Dragging frameless window “jiggles” in qml

I have a frameless ApplicationWindow and I wanted to make it draggable using the answer of this question. However, as someone said in a comment, when I move the window fastly, it jiggles a lot .

I've been trying to improve it but without success.

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("WIP")
    id: mainWindow
    flags: Qt.SubWindow | Qt.Tool | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint
    header: ToolBar{

        MouseArea{
            anchors.fill: parent
            onDoubleClicked: mainWindow.visibility!="2"?mainWindow.showNormal():mainWindow.showMaximized()
            id: maMainWindow
            property variant clickPos: "0,0"

            onPressed: {
                clickPos  = Qt.point(mouse.x,mouse.y)
            }

            onPositionChanged: {
                    var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
                    mainWindow.x += delta.x;
                    mainWindow.y += delta.y;
            }
        }
    }
}

If I add tabs and some elements it makes it worse.

Can C++ improve its performance somehow?

I had the same problem, the performance was ok, but on linux it was jumping all over the screen and "jiggling". I have solved it by writing a helper class in C++ which I have exposed as QML context property. This solution helped me a lot. I have very complex UI and it works perfectly fine, with very good performance. So lets get started. 1) You will need a helper class something like this:

class CursorPosProvider : public QObject
{
    Q_OBJECT
public:
    explicit CursorPosProvider(QObject *parent = nullptr) : QObject(parent)
    {
    }
    virtual ~CursorPosProvider() = default;

    Q_INVOKABLE QPointF cursorPos()
    {
        return QCursor::pos();
    }
};

It is a really simple class which just provides you cursor position from C++ side, it is strange, but when you do the same in QML you get problems(at least on linux). 2) Expose it as context property to QML engine, I have done it in next way:

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView view;

    CursorPosProvider mousePosProvider;

    view.rootContext()->setContextProperty("mousePosition", &mousePosProvider);

    view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

3) Ok, now we are ready to go with QML part. I have a Qt Quick component that implements a TitleBar for frameless window, like this:

Rectangle {
    id: root
    width: parent.width
    color: "#0099d6" // just random one

    property QtObject container

    // extra properties, maybe some signals

    MouseArea {
        id: titleBarMouseRegion
        property var clickPos
        anchors.fill: parent
        onPressed: {
            clickPos = { x: mouse.x, y: mouse.y }
        }
        onPositionChanged: {
            container.x = mousePosition.cursorPos().x - clickPos.x
            container.y = mousePosition.cursorPos().y - clickPos.y
        }
    }
}

4) Now you are ready to use this TitleBar in arbitrary window in next way:

Window {
    id: root
    visible: true

    flags: Qt.FramelessWindowHint

    TitleBar {
        height: 20
        container: root
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

When I was implementing drag&drop for title bar the main problem is in cords that QML provides, this solution fixes that issue. Please provide some feedback if my solution will help you. I'm really interested:)

I don't think there is anything you can do, it is just a side effect of using bindings to build the GUI and binding evaluations are not synchronized with rendering. As a result, as you move or resize the window, everything is wobbling around like elastic until the value "catches up". That's a QML thing, in widgets you don't get that kind of behavior because the UI is not build around bindings. Basically, there is delay involved in the evaluation of every chain of bindings, and as the GUI is draw it catches and displays the delay in propagation of the binding chain, the first objects from the chain are already in their new locations, while those further towards the back can be behind by several steps in the propagation. And naturally, whether your window has frames is totally irrelevant to that issue.

Overcoming this would require control over how signals are being handled by the bindings, and I don't think there is currently such a thing. Basically, the trick is to somehow force drawing to wait until every binding in the chain is evaluated, and draw in between before another series of evaluations is initiated.

Naturally, the more elements you have the longer each change would take to cascade through all of them, making the effect more pronounced. It also depends on how fast your system is - as that determines the delay in evaluating the binding chain, for example my system is fast and your example code does not produce the jiggling. However, while it is understandable that this happens during resizing, it begs the question why it is happening when you are simply moving the window around. After all, that's shouldn't really be changing the relative positions of the UI elements in the window. I suspect this happens because internally those objects are drawn in "absolute screen space", so as you move the window, this causes the actual absolute positions of every element to change even if they remain in the same place relative to the window, resulting in this behavior. Not ideal... Not only does introduce that undesired visual behavior, but also a lot of additional evaluations which seem like unnecessary overhead.

Note that those are just my vague suspicions, I haven't investigated the matter in detail, hopefully someone might offer a quick and easy way to deal with this. For now I am ignoring this issue, hoping that it wouldn't be too annoying, considering that I focus on software which is not about continuous changes in the position and geometry of the window ;) BTW even though I first saw this in QML, the last few years I did notice it in other "modern gui" frameworks as well. It would be understandable as such frameworks move to slower performing languages for the sake of rapid prototyping, combined with "fluid" asynchronous non-blocking rendering.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM