QML对话框坏了吗?

时间:2015-06-09 14:51:02

标签: qml qt5 qtquick2 qtquickcontrols

我有这段代码:

import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2

Dialog {
    standardButtons: StandardButton.Ok | StandardButton.Cancel

    width: layout.implicitWidth
    height: layout.implicitHeight

    RowLayout {
        id: layout
        anchors.fill: parent

        Item {
            width: 10
            height: 1
        }

        GridLayout {
            columns: 2
            rowSpacing: 10

            Layout.fillHeight: true
            Layout.fillWidth: true

            Text {
                text: "Hello world? "
            }
            Text {
                text: "Hello world!"
            }

            Text {
                text: "Goodbye world? "
            }
            Text {
                text: "Goodbye world!"
            }

        }

        Item {
            width: 10
            height: 1
        }
    }
}

当你运行它时,它看起来像这样,并且对话框可以调整为任何大小。另外,RowLayout实际上 不会填充其父级,如您所见。

damn pls

如何才能使对话框无法在布局的最小尺寸以下调整大小,以便布局填充对话框?

2 个答案:

答案 0 :(得分:6)

不幸的是,这是Qt中的一个错误。目前,该文档具有误导性,Dialog 考虑这个工作示例,我基于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" }
        }
    }
}

这完全按预期工作,但当然你没有按钮。

如果您只是将其更改为Dialog并取消注释standardButtons,则它会停止工作 - 您可以调整对话框的大小以剪切其内容(至少是宽度方式),内容是不扩展到对话框大小。

当我们查看Dialog的源代码(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被硬编码为Screen.pixelDensity * 50 !!从来没有任何希望它能与对话内容相匹配。 minimumHeight确实效果更好(虽然不完美,我相信因为没有考虑间距)。

我不确定为什么defaultContentItem无法正确扩展,但无论如何。 目前看来唯一的解决方案是使用AbstractDialog并实施按钮和accepted() / rejected() /等。发出自己的信号。有点痛苦。

编辑/解决方案

我做了一些进一步的调查。

  1. defaultContentItem未展开的原因是因为它的底部锚点没有绑定到按钮行的顶部:

    Item {
        id: defaultContentItem
        anchors {
            left: parent.left
            right: parent.right
            top: parent.top
            margins: content.outerSpacing
        }
        implicitHeight: childrenRect.height
    }
    
  2. 最小尺寸不适用于基于锚点的布局。它们使用基于GridLayout的布局。

  3. 不幸的是,childrenRect没有implicitWidth / Height,所以我们必须让子项进入ColumnLayout,而不是 ColumnLayout。

  4. ...

    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()
    }
    

    你必须使用它与普通Dialog略有不同。想象一下它是ColumnLayout(这与原始问题略有不同):

    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!"
        }
    }
    

    顺便说一句,您可以将ColumnLayout更改为GridLayout,并根据需要公开columns属性。这可能更有意义。

    一个小问题

    事实证明,QWindow的最小宽度和高度仅确保对话框不会主动调整为小于其内容。它不会确保对话框永远不会小于其内容,因为内容可以在创建对话框后增长(例如添加额外的项目)。要解决此问题,我将此功能添加到ColumnLayoutDialog

    // The minimumWidth/Height values of content are accessed by the C++ class, but they
    // only ensure that the window isn't resized to be smaller than its content. They
    // don't ensure that if the content grows the window grows with it.
    function ensureMinimumSize()
    {
        if (root.width < content.minimumWidth)
            root.width = content.minimumWidth;
        if (root.height < content.minimumHeight)
            root.height = content.minimumHeight;
    }
    

    更改对话框内容时必须手动调用。或者自动执行此操作,您可以将其添加到content矩形:

        onMinimumHeightChanged: {
            if (root.height < content.minimumHeight)
                root.height = content.minimumHeight;
        }
        onMinimumWidthChanged: {
            if (root.width < content.minimumWidth)
                root.width = content.minimumWidth;
        }
    

答案 1 :(得分:1)

这是QT版本5.6.0中的错误。最有可能是bug number 49058。该问题的代码在QT 5.6.1和5.7.0中按预期工作。

旧版本的部分解决方法是删除行

width: layout.implicitWidth
height: layout.implicitHeight

并替换

anchors.fill: parent

anchors.right: parent.right
anchors.left: parent.left

对话框然后尊重最小高度,内容水平展开。

这也是一个完整的解决方法,但它依赖于Dialog的未记录的实现细节,因此应谨慎使用。它适用于5.5.1,5.6.0,5.6.1和5.7.0。另请注意,第二个项目更改为红色矩形,以使行为更明显。

import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2

Dialog {
    visible: true
    standardButtons: StandardButton.Ok | StandardButton.Cancel

    RowLayout {
        id: layout

        // In the horizontal direction, expansion and shrinking can be achieved with anchors.
        anchors.left: parent.left
        anchors.right: parent.right

        // Used only for guessing the height of the Dialog's standard buttons.
        Button {
            id: hiddenButton
            visible: false
        }

        // Repeats until the relevant parts of the dialog (parent of the parent of the RowLayout)
        // are complete, then overwrites the minimum width and implicit height and stops repeating.
        Timer {
            id: timer
            interval: 50; running: true; repeat: true;
            onTriggered: {
                if(layout.parent.parent) {
                    var lp = layout.parent
                    var lpp = layout.parent.parent
                    lpp.minimumWidth = layout.implicitWidth + 2 * lpp.outerSpacing
                    layout.buttonHeight = 2 * lpp.outerSpacing + hiddenButton.implicitHeight + lpp.spacing
                    lp.implicitHeight = layout.implicitHeight + 2 * lpp.outerSpacing
                    running = false
                }
            }
        }

        // The guessed space needed for the Dialog's buttons.
        property int buttonHeight: 80

        // Expand and shrink vertically when the dialog is resized.
        height: parent.parent ? Math.max(parent.parent.height-buttonHeight, implicitHeight) : implicitHeight

        Item {
            width: 10
            height: 1
        }

        GridLayout {
            columns: 2
            rowSpacing: 10

            Layout.fillHeight: true
            Layout.fillWidth: true

            Text {
                text: "Hello world? "
            }
            Text {
                text: "Hello world!"
            }

            Text {
                text: "Goodbye world? "
            }
            Text {
                text: "Goodbye world!"
            }

        }

        Rectangle {
            Layout.fillHeight: true
            color: 'red'
            width: 10
        }
    }
}