How to stop window size jumping around when forcin

2019-08-07 04:12发布

Here's my test code:

import QtQuick 2.4
import QtQuick.Window 2.2

Window {
    visible: true
    readonly property int defaultWidth: 700
    readonly property int defaultHeight: 500
    width: defaultWidth
    height: defaultHeight

    readonly property real aspectRatio: defaultWidth / defaultHeight
    readonly property real scaleFactor: width / defaultWidth

    onWidthChanged: {
        var properHeight = Math.round(width / aspectRatio);
        if ( height !== properHeight ) {
            height = properHeight
        }
    }
    onHeightChanged: {
        var properWidth = Math.round(height * aspectRatio);
        if ( width !== properWidth ) {
            width = properWidth
        }
    }

    TextEdit {
        id: textEdit
        text: "Lorem ipsum"
        font.pixelSize: 40 * scaleFactor
        anchors.fill: parent
    }
}

It implements fixed aspect ratio, but it doesn't work very well: there is sort of flicker (window size jumping between the fixed-aspect-ratio size and the mouse-resized size), and it may adjust wrong dimension depending on order of width and height change events. In effect, when size is adjusted programmatically, the window edge may start jumping between mouse cursor position and programmatically set, different position.

What should happen is, the window edge which is dragged should follow mouse cursor (like in any resize), and the other edges should change to maintain aspect ratio:

  • If user drags left/right edge, bottom edge should move to maintain aspect ratio.
  • If user drags top/bottom edge, then right edge should also move to maintain aspect ratio.
  • When user drags a corner, there are two possible sizes to take to keep the aspect ratio, as either height or width could be changed programmatically. How exactly to determine this doesn't matter, as long as it is consistent.
  • It is ok, if aspect ratio is fixed only after resizing stops, though real-time would be better. But how to reliably detect when resizing actually stops?
  • If this can't be done with Qt alone, I'm interested in both Windows and X11/Linux specific solutions.

How to achieve the resize while preserving aspect ratio, using native window resize controls (normal window borders or whatever the platform uses), so that it behaves logically and looks nice ?


Edit, "hint": I had time to do some research, overriding QQuickView::resizeEvent and calling resize from there seems to be able to do the trick, but I didn't have time to get it to work quite right yet.

2条回答
啃猪蹄的小仙女
2楼-- · 2019-08-07 04:16

I've had a problem like that once, I ended up a "chicken or egg" paradox, so I disabled the window decorations altogether and made custom resizing bars. This established a proper value flow from handles to dimensions, without it the flow was dimensions to dimensions, if that makes any sense...

If removing the window frame is not a problem for you, you could go that way, for me it wasn't since I was looking for a custom and platform independent look, so the window frame was actually part of the application:

Window {
    id: main
    visible: true
    width: 500
    height: 250
    flags: Qt.FramelessWindowHint | Qt.Window

    MouseArea {
      width: 20
      height: 20
      anchors.bottom: parent.bottom
      anchors.right: parent.right
      onPositionChanged: {
        var w = mouseX + x, h = mouseY + y
        if (w > (h * 2)) {
          main.width = w
          main.height = w * .5
        } else {
          main.height = h
          main.width = h * 2
        }
      }
      Rectangle {
        anchors.fill: parent
        color: "red"
      }
    }
}

That's just a trivial example, obviously in a complete solution you should do all sides and corners. Aspect ratio is fixed at 2/1.

On my system there is still some flickering for the entire window, but I think that's just the scenegraph, but the "dimensions jumping around" problem is effectively solved.

EDIT:

To clarify, what I mean by the "flow was dimensions to dimensions" is that the handles manipulate the dimensions, and you hook to the changed handlers to... make a change. I suspect that's why it works so flaky. If so, in order to employ the window frame for resizing, the connection between the handles and the window dimensions has to be severed to insert the aspect ratio code between. Unfortunately, filtering out the resize event at QGuiApplication level did not prevent the resizing, but maybe there is another way to do that, for example overriding the class.

查看更多
仙女界的扛把子
3楼-- · 2019-08-07 04:18

Ok, here's one possible workaround. It doesn't try to control the window size, but instead adds extra item inside the window, which then contains the rest of the UI, and keeps the desired aspect ratio: If window aspect ratio is different, then there's empty area either above and below, or left and right, of the content area, drawn with window color ("gray" here). The content area is filled with "yellow" here to show the difference.

import QtQuick 2.4
import QtQuick.Window 2.2

Window {

    readonly property int defaultWidth: 700
    readonly property int defaultHeight: 500
    readonly property real defaultAspectRatio: defaultWidth / defaultHeight

    readonly property real aspectRatio: width / height
    readonly property real scaleFactor: content.width / defaultWidth

    visible: true
    width: defaultWidth
    height: defaultHeight
    color: "gray"

    Item {
        id: content
        anchors.centerIn: parent
        width: (aspectRatio > defaultAspectRatio)
               ? parent.height * defaultAspectRatio
               : parent.width
        height: (aspectRatio < defaultAspectRatio)
                ? parent.width / defaultAspectRatio
                : parent.height

        Rectangle { color: "yellow"; anchors.fill: parent } // just to show the effect

        TextEdit {
            clip: true // text drawing needs to be clipped explicitly
            id: textEdit
            text: "Lorem ipsum"
            font.pixelSize: 40 * scaleFactor
            anchors.fill: parent
        }
    }
}

But this is just a workaround, not a full solution asked for in the question, so the question remains open.

查看更多
登录 后发表回答