QML自定义模态窗口

   最近接手的新客户端项目是用QML做的界面。既然是客户端,就需要用到各种弹窗,自然少不了自定义各种样式的弹窗。模态窗口是最常用的弹窗之一,以下是我自定义的两种模态窗口实现。第一种是基于Window,第二种三基于Rectangle,而我的项目代码中不知道什么原因大部分都是用了Rectangle实现。

//基于Window
//WindowTemplate.qml
Window {
    id: rootWin

    property var parent: undefined
    property int winWidth: 500
    property int winHeight: 400
    property string titleText: qsTr("hello world")

    width: winWidth
    height: winHeight
    flags: Qt.FramelessWindowHint
    x: parent.width/2 - rootWin.width/2 + parent.x
    y: parent.height/2 - rootWin.height/2 + parent.y
    modality: Qt.WindowModal


    Rectangle{
        anchors.fill: parent
        radius: 3
        color: "#87C056"

    }

    Rectangle{
        id:titleArea
        color: "#335577"
        anchors.left: parent.left
        anchors.top: parent.top
        width: parent.width
        height: 30
        radius: 3
        z:100
        Text {
            text: titleText
            anchors.horizontalCenter: parent.horizontalCenter
            height: parent.height
            font.pixelSize: 14
            verticalAlignment: Text.AlignVCenter
            color: "#FFFFFF"
        }

        Image {
            id:closeBtn
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.rightMargin: 5
            anchors.verticalCenter: parent.verticalCenter
            source: "qrc:/image/icon_close.png"
            z:200
            MouseArea{
                anchors.fill: parent
                onClicked: {
                      rootWin.close()
                }
            }
        }

        MouseArea{
            anchors.fill: parent
            property point clickPoint: "0,0"
            acceptedButtons: Qt.LeftButton
            onPressed: {
                clickPoint  = Qt.point(mouse.x, mouse.y)
            }
            onPositionChanged: {
                var offset = Qt.point(mouse.x - clickPoint.x, mouse.y - clickPoint.y)
                setDlgPoint(offset.x, offset.y)
            }

        }



    }
    function setDlgPoint(dlgX,dlgY){
        //设置窗口拖拽不能超过父窗口
        if(rootWin.x + dlgX < rootWin.parent.x)
        {
            rootWin.x = rootWin.parent.x
        }
        else if(rootWin.x + dlgX > rootWin.parent.width - rootWin.width + rootWin.parent.x)
        {
            rootWin.x = (rootWin.parent.width - rootWin.width) + rootWin.parent.x
        }
        else
        {
            rootWin.x = rootWin.x + dlgX
        }

        if(rootWin.y + dlgY < rootWin.parent.y)
        {
            rootWin.y = rootWin.parent.y
        }
        else if(rootWin.y + dlgY > rootWin.parent.height - rootWin.height + rootWin.parent.y)
        {
            rootWin.y = (rootWin.parent.height - rootWin.height) + rootWin.parent.y
        }
        else
        {
            rootWin.y = rootWin.y + dlgY
        }
    }



}
//基于Rectangle,
//ModalWindowTemplate.qml
Rectangle{
    id:rootModalWin
    anchors.fill: parent
    width: parent.width
    height: parent.height + 10
    property alias contaners: rootWin
    property int winWidth: 500
    property int winHeight: 400
    property string titleText: qsTr("hello world")
    color: "transparent"
    focus: true
    Keys.enabled: true
     z: 1000
     MouseArea{
         anchors.fill: parent
         hoverEnabled: false
     }

    Rectangle {
        id: rootWin
        width: winWidth
        height: winHeight
        x: parent.width/2 - rootWin.width/2 + parent.x
        y: parent.height/2 - rootWin.height/2 + parent.y

        Rectangle{
            anchors.fill: parent
            radius: 3
            color: "#87C056"
        }

        Rectangle{
            id:titleArea
            color: "#335577"
            anchors.left: parent.left
            anchors.top: parent.top
            width: parent.width
            height: 30
            radius: 3
            z:100
            Text {
                text: titleText
                anchors.horizontalCenter: parent.horizontalCenter
                height: parent.height
                font.pixelSize: 14
                verticalAlignment: Text.AlignVCenter
                color: "#FFFFFF"
            }

            Image {
                id:closeBtn
                anchors.right: parent.right
                anchors.top: parent.top
                anchors.rightMargin: 5
                anchors.verticalCenter: parent.verticalCenter
                source: "qrc:/image/icon_close.png"
                z:200
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        rootModalWin.visible = false
                    }
                }
            }

            MouseArea{
                anchors.fill: parent
                property point clickPoint: "0,0"
                acceptedButtons: Qt.LeftButton
                onPressed: {

                    clickPoint  = Qt.point(mouse.x, mouse.y)
                }
                onPositionChanged: {
                    var offset = Qt.point(mouse.x - clickPoint.x, mouse.y - clickPoint.y)
                    setDlgPoint(offset.x, offset.y)
                }

            }

        }

    }
    function setDlgPoint(dlgX,dlgY){
        //设置窗口拖拽不能超过父窗口
        if(rootWin.x + dlgX < rootWin.parent.x)
        {
            rootWin.x = rootWin.parent.x
        }
        else if(rootWin.x + dlgX > rootWin.parent.width - rootWin.width + rootWin.parent.x)
        {
            rootWin.x = (rootWin.parent.width - rootWin.width) + rootWin.parent.x
        }
        else
        {
            rootWin.x = rootWin.x + dlgX
        }

        if(rootWin.y + dlgY < rootWin.parent.y)
        {
            rootWin.y = rootWin.parent.y
        }
        else if(rootWin.y + dlgY > rootWin.parent.height - rootWin.height + rootWin.parent.y)
        {
            rootWin.y = (rootWin.parent.height - rootWin.height) + rootWin.parent.y
        }
        else
        {
            rootWin.y = rootWin.y + dlgY
        }
    }

}

基于Rectangle的实现,是之程序上增加了一个透明层,使得用户不能操作弹窗后面的界面。这看似满足了模态窗口的要求。但是存在一个问题,就是之定义时设置的focus为true并没有生效。这就是我项目中存在的两个bug,找了好久才弄明白。

下面通过实际应用代码展示bug的存在:由于focus不生效,弹窗中,如果设置输入框的focus,依然没有显示光标闪动;弹窗也不会响应按键事件。

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0

Window {
    id: mainWin
    visible: true
    width: 600
    height: 600
    title: qsTr("Hello World")

    Button{
        text: qsTr("模态窗口1")
        onClicked: {
            childWin.show()
        }
    }

    Button{
        text: qsTr("模态窗口2")
        anchors.centerIn: parent
        onClicked: {
            modalWin.visible = true
        }
    }

    ModalWindowTemplate{
        id:modalWin
        visible: false
        Rectangle{
            id: content
            color: "#FF6677"
            visible: modalWin.visible
            anchors.centerIn: modalWin.contaners
            width: 300
            height: 50
            clip: true
            radius: 3



            TextEdit {
                visible:  modalWin.contaners.visible
                text: qsTr("hello world111")
                focus: true
                width: 100
                height: 30
            }
            //设置focus,但并没有响应按键事件
            focus: true
            Keys.enabled: true

            Keys.onPressed: {
              console.log("key press")
            }
        }
//注释这段代码,弹出讲无法响应按键
//        onVisibleChanged: {
//            if(visible)
//            {
//                content.focus = true
//            }
//        }
    }

    WindowTemplate{
        id: childWin
        visible: false
        parent:mainWin

        Rectangle{
            color: "#FF6677"
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            width: parent.width
            height: parent.height - 30
            clip: true
            radius: 3

            TextEdit {
                text: qsTr("hello world")
//                focus: true
                width: 100
                height: 30
                anchors.centerIn: parent
            }
            //设置focus,就可响应按键事件
            focus: true
            Keys.enabled: true
          Keys.onPressed: {
              console.log("key press")
          }
        }

    }


    Component.onCompleted: {
//        mainWin.showMaximized()
    }
}

所以个人建议还是老老实实用Window吧。省得出各种奇怪的bug。

你可能感兴趣的:(QML,Qt学习笔记)