Qml 中的那些坑(七)---ComboBox嵌入Popup時,滾動內容超過其可見區域不會關閉ComboBox彈窗

梦起丶發表於2024-11-11

【寫在前面】

最近在寫資訊提交 ( 表單 ) 的視窗時發現一個奇怪的 BUG:

其程式碼如下:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Button{
        text: "open"
        onClicked: popup.open();
    }

    Popup {
        id: popup
        width: 400
        height: 200
        anchors.centerIn: parent
        clip: true
        closePolicy: Popup.CloseOnPressOutside
        background: Rectangle { color: "#80800000" }
        contentItem: Flickable {
            id: flickable
            clip: true
            topMargin: 10
            contentWidth: implicitWidth
            contentHeight: 500
            ScrollBar.vertical: ScrollBar { width: 14 }
            /*onMovementStarted: {
                for (let key in contentItem.children) {
                    let item = contentItem.children[key];
                    if (item.objectName === "__ComboBox__")
                        item.popup.close();
                }
            }*/

            ComboBox {
                width: 160
                height: 40
                objectName: "__ComboBox__"
                model: ["aaaaaa", "bbbbbb", "cccccc", "dddddd"]
            }
        }
    }
}

可以看到,當 ComboBox 嵌入 Popup 時,點開 ComboBox,然後滾動內容超過其可見區域並不會關閉 ComboBox 彈窗,並且會超出其 父 Popup 範圍。


【正文開始】

實際上,這是幾乎存在在 Qt 所有版本 ( Qt5 ~ Qt6 ) 的 BUG,猜測其主要原因為彈窗無法對內部巢狀彈窗進行裁剪,因為此彈窗 ( Popup ) 並非真正的視窗 ( Window )

該 BUG 我已報告給官方:https://bugreports.qt.io/browse/QTBUG-130960?filter=-2

不過,在官方修復的版本出來之前,我實現的改動較小的修復辦法為:

  • Qt5 中為:
Flickable {
    ...
    onMovementStarted: {
        for (let key in contentItem.children) {
            let item = contentItem.children[key];
            if (item.objectName === "__ComboBox__")
                item.popup.close();
        }
    }

    ComboBox {
        ...
        objectName: "__ComboBox__"
    }
}
  • Qt6 中為:
Flickable {
    ...
    onMovementStarted: {
        for (let item of contentItem.children) {
            if (item.objectName === "__ComboBox__")
                item.popup.close();
        }
    }

    ComboBox {
        ...
        objectName: "__ComboBox__"
    }
}

只需要在當檢視由於使用者互動或生成的 flick() 而開始移動時,關閉掉 ComboBox 的彈窗即可。

修復後的效果如下:

image


【結語】

最後,要說明並非只有本文中的例子會有該 BUG,所有形如下面的程式碼都可能出現。

Popup {
    Popup {
        ...
    }
}

而修復思路也大致相似。

相關文章