最近接手的新客户端项目是用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。