Unfortunately, this is a bug in Qt. The documentation is currently misleading, and Dialog does not match the correct size of the content. Consider this working example, which I based on DefaultFontDialog :
AbstractDialog { title: "Hello" id: root // standardButtons: StandardButton.Ok | StandardButton.Cancel modality: Qt.NonModal Rectangle { id: content implicitWidth: mainLayout.implicitWidth + outerSpacing * 2 implicitHeight: mainLayout.implicitHeight + outerSpacing * 2 property real spacing: 6 property real outerSpacing: 12 color: "white" GridLayout { id: mainLayout anchors { fill: parent; margins: content.outerSpacing } rowSpacing: content.spacing columnSpacing: content.spacing columns: 5 Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } } } }
This works exactly as expected, although of course you don't get the buttons.
If you just change it to Dialog and uncomment standardButtons , then it will stop working - you can resize the dialog box to copy its contents (at least in width) and the content will not expand to the size of the dialog box.
The reason that the minimum width does not work becomes clear when we look at the source code for Dialog (in qtquickcontrols/src/dialogs/DefaultDialogWrapper.qml ):
AbstractDialog { id: root default property alias data: defaultContentItem.data onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus() Rectangle { id: content property real spacing: 6 property real outerSpacing: 12 property real buttonsRowImplicitWidth: minimumWidth property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth property real minimumHeight: implicitHeight property real minimumWidth: Screen.pixelDensity * 50 implicitHeight: defaultContentItem.implicitHeight + spacing + outerSpacing * 2 + buttonsRight.implicitHeight implicitWidth: Math.min(root.__maximumDimension, Math.max( defaultContentItem.implicitWidth, buttonsRowImplicitWidth, Screen.pixelDensity * 50) + outerSpacing * 2);
minimumWidth hardcoded to Screen.pixelDensity * 50 !! There was no hope that this would be consistent with the contents of the dialogue. minimumHeight works better (although not perfect, I think because distance is not taken into account).
I'm not sure why defaultContentItem is not distributed correctly, but anyway. It seems that the only solution at the moment is to use AbstractDialog and implement the buttons and accepted() / rejected() / etc. signals itself. A bit of pain.
Change / Decision
I did some further research.
The reason the defaultContentItem is not expanding is because the bottom anchor is not anchored to the top of the button row:
Item { id: defaultContentItem anchors { left: parent.left right: parent.right top: parent.top margins: content.outerSpacing } implicitHeight: childrenRect.height }
Minimum sizes just don't work well with snap based layouts. They work with GridLayout based layouts.
Unfortunately, childrenRect does not have implicitWidth / Height, so we should actually have children in ColumnLayout, not ColumnLayout.
...
import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 // A Dialog that resizes properly. The defualt dialog doesn't work very well for this purpose. AbstractDialog { id: root default property alias data: defaultContentItem.data onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus() Rectangle { id: content property real spacing: 6 property real outerSpacing: 12 property real buttonsRowImplicitWidth: minimumWidth property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth property real minimumHeight: implicitHeight property real minimumWidth: implicitWidth // Don't hard-code this. implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitWidth + outerSpacing * 2)) implicitHeight: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitHeight + outerSpacing * 2)) color: palette.window Keys.onPressed: { event.accepted = true switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: reject() break case Qt.Key_Enter: case Qt.Key_Return: accept() break default: event.accepted = false } } SystemPalette { id: palette } // We use layouts rather than anchors because there are no minimum widths/heights // with the anchor system. ColumnLayout { id: mainLayout anchors { fill: parent; margins: content.outerSpacing } spacing: content.spacing // We have to embed another item so that children don't go after the buttons. ColumnLayout { id: defaultContentItem Layout.fillWidth: true Layout.fillHeight: true } Flow { Layout.fillWidth: true id: buttonsLeft spacing: content.spacing Repeater { id: buttonsLeftRepeater Button { text: (buttonsLeftRepeater.model && buttonsLeftRepeater.model[index] ? buttonsLeftRepeater.model[index].text : index) onClicked: root.click(buttonsLeftRepeater.model[index].standardButton) } } Button { id: moreButton text: qsTr("Show Details...") visible: false } } Flow { Layout.fillWidth: true id: buttonsRight spacing: content.spacing layoutDirection: Qt.RightToLeft Repeater { id: buttonsRightRepeater // TODO maybe: insert gaps if the button requires it (destructive buttons only) Button { text: (buttonsRightRepeater.model && buttonsRightRepeater.model[index] ? buttonsRightRepeater.model[index].text : index) onClicked: root.click(buttonsRightRepeater.model[index].standardButton) } } } } } function setupButtons() { buttonsLeftRepeater.model = root.__standardButtonsLeftModel() buttonsRightRepeater.model = root.__standardButtonsRightModel() if (!buttonsRightRepeater.model || buttonsRightRepeater.model.length < 2) return; var calcWidth = 0; function calculateForButton(i, b) { var buttonWidth = b.implicitWidth; if (buttonWidth > 0) { if (i > 0) buttonWidth += content.spacing calcWidth += buttonWidth } } for (var i = 0; i < buttonsRight.visibleChildren.length; ++i) calculateForButton(i, buttonsRight.visibleChildren[i]) content.minimumWidth = calcWidth + content.outerSpacing * 2 for (i = 0; i < buttonsLeft.visibleChildren.length; ++i) calculateForButton(i, buttonsLeft.visibleChildren[i]) content.buttonsRowImplicitWidth = calcWidth + content.spacing } onStandardButtonsChanged: setupButtons() Component.onCompleted: setupButtons() }
You should use it a little differently than regular Dialog . Imagine this is a ColumnLayout (this is a slightly different example of the original question):
ColumnLayoutDialog { id: dialog1 standardButtons: StandardButton.Ok | StandardButton.Cancel Text { text: "Hello world? " } Text { text: "Hello world!" } // Spacer. Item { Layout.fillHeight: true; } Text { text: "Goodbye world? " } Text { text: "Goodbye world!" } }
By the way, you can change ColumnLayout to GridLayout and set the columns property, if you want. That might make sense.
little problem
It turns out that the minimum width and height of a QWindow only ensures that the dialog will not be actively resized to be smaller than its contents. This does not guarantee that the dialogue will never be smaller than its contents, because the content can grow after the dialogue is created (for example, additional elements added). To get around this, I added this function to my ColumnLayoutDialog :
It must be called up manually when changing the contents of the dialog box. Or do it automatically, you can add this to the content rectangle:
onMinimumHeightChanged: { if (root.height < content.minimumHeight) root.height = content.minimumHeight; } onMinimumWidthChanged: { if (root.width < content.minimumWidth) root.width = content.minimumWidth; }