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.
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:
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 atQGuiApplication
level did not prevent the resizing, but maybe there is another way to do that, for example overriding the class.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.
But this is just a workaround, not a full solution asked for in the question, so the question remains open.