由于Qt的原生窗口带有的标题栏无法定制,然而通常情况下我们需要自定义窗体上的关闭、最小化等按钮、背景,甚至需要不需要标题栏。在QtQuick实现去除标题栏,也即无边框很简单,只需要在Qml-Window中设置:
flags: Qt.Window | Qt.FramelessWindowHint | Qt.WindowMinimizeButtonHint
但是这个时候的窗体不能拖拽,也不能在窗体的边缘进行拉伸。所以需要实现窗体的边缘事件进行拉伸,同时在这里也实现了一个带有最小化,最大化和关闭的自定义标题栏
- 首先看自定义标题栏代码TitleBar.qml
import QtQuick 2.7
import QtQuick.Controls 2.13
import QtQuick.Templates 2.12 as T
import QtQuick.Window 2.2
Rectangle{
property bool isMaximized: false
Connections{
target: mainWindow
onVisibilityChanged: {//解决Qt窗口最大化的时候最小化,再恢复窗口变为普通窗口的bug
if(isMaximized && visibility === 2) {
mainWindow.showMaximized()
}
}
}
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.LeftButton //只处理鼠标左键
property bool isDoubleClicked:false
property point clickPos: "0,0"
onPressed:
{
isDoubleClicked = false;
clickPos = Qt.point(mouse.x,mouse.y)
}
onPositionChanged: {
if(!isDoubleClicked && pressed && mainWindow.visibility !== Window.Maximized && mainWindow.visibility !== Window.FullScreen) {
var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
mainWindow.x += delta.x
mainWindow.y += delta.y
}
if(mainWindow.visibility === Window.Maximized && pressed && !isDoubleClicked)
{
isMaximized = false;
mainWindow.showNormal();
normBtnBg.source = "qrc:/res/maximinze_btn.png"
}
}
onDoubleClicked :
{
isDoubleClicked = true; // 这个时候一定不能响应onPositionChanged不然会一直置顶。
if(isMaximized){
isMaximized = false;
mainWindow.showNormal();
normBtnBg.source = "qrc:/res/maximinze_btn.png"
}else{
isMaximized = true;
mainWindow.showMaximized();
normBtnBg.source = "qrc:/res/norm_btn.png"
}
}
}
Button{
id:closeBtn
anchors.right: parent.right
width: parent.height
height: parent.height - 1
hoverEnabled : true
background: Rectangle{
color: closeBtn.hovered ? (closeBtn.pressed ? "#DE0000" : "#F91515") : "transparent"
Image{
id: closeBtnImg
width: parent.height * 0.3; height: parent.height * 0.3
anchors.centerIn: parent
source: "qrc:/res/close_btn.png"
}
}
onClicked: {
mainWindow.close()
}
}
Button{
id: normBtn
anchors.right: closeBtn.left
width: parent.height
height: parent.height - 1
hoverEnabled : true
background: Rectangle{
color: normBtn.hovered ? (normBtn.pressed ? "#4E4E4E" : "#666666") : "transparent"
//color: "black"
Image{
id: normBtnBg
width: parent.height * 0.3; height: parent.height * 0.3
anchors.centerIn: parent
source: "qrc:/res/maximinze_btn.png"
}
}
onClicked:{
if(isMaximized){
isMaximized = false;
mainWindow.showNormal();
normBtnBg.source = "qrc:/res/maximinze_btn.png"
}else{
isMaximized = true;
mainWindow.showMaximized();
normBtnBg.source = "qrc:/res/norm_btn.png"
}
}
}
Button{
id: minBtn
anchors.right: normBtn.left
width: parent.height
height: parent.height - 1
hoverEnabled : true
background: Rectangle{
color: minBtn.hovered ? (minBtn.pressed ? "#4E4E4E" : "#666666") : "transparent"
Image{
width: parent.height * 0.3; height: parent.height * 0.3
anchors.centerIn: parent
source: "qrc:/res/min_btn.png"
}
}
onClicked:{
mainWindow.showMinimized();
}
}
}
标题栏实现起来比较简单,主要有几个功能:最大化,最小化、普通化(normal)以及双击最大化、双击恢复普通窗口。这里用一个两个变量:isMaximized和isDoubleClicked来记录窗体状态和双击状态。然后分别实现其事件逻辑即可。这里需要主要的是Qt的一个bug,也就是在窗口时最大化,然后再恢复窗口,这时窗体变成普通窗口了(正常应该还是最大窗口),这个bug在5.13和5.13.1还没被修复,bug请看这里
在这里我们可以用自己用代码修复,即我们可以判断窗体在最小化恢复时候,如果这个时候窗体是最大化并且是Windowed(值为2)这个时候将其最大化即可。具体代码可看上面onVisibilityChanged这一行代码
- 窗体的边缘拉伸和拖拽主要实现在ResizeItem和ResizeQmlWindow,ResizeQmlWindow类主要实现设置鼠标的指针状态而已,具体检测鼠标的边缘事件在ResizeItem.qml里,如下所示
import QtQuick 2.0
import QtQuick.Window 2.2
Item {
property int enableSize: 4
property bool isPressed: false
property point customPoint
//左上角
Item {
id: leftTop
width: enableSize
height: enableSize
anchors.left: parent.left
anchors.top: parent.top
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(1)
onReleased: release()
onPositionChanged: positionChange(mouse, -1, -1)
}
}
//Top
Item {
id: top
height: enableSize
anchors.left: leftTop.right
anchors.right: rightTop.left
anchors.top: parent.top
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(2)
onReleased: release()
onMouseYChanged: positionChange(Qt.point(customPoint.x, mouseY), 1, -1)
}
}
//右上角
Item {
id: rightTop
width: enableSize
height: enableSize
anchors.right: parent.right
anchors.top: parent.top
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(3)
onReleased: release()
onPositionChanged: positionChange(mouse, 1, -1)
}
}
//Left
Item {
id: left
width: enableSize
anchors.left: parent.left
anchors.top: leftTop.bottom
anchors.bottom: leftBottom.top
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(4)
onReleased: release()
onMouseXChanged: positionChange(Qt.point(mouseX, customPoint.y), -1, 1)
}
}
//Center - 5
Item {
id: center
anchors.left: left.right
anchors.right: right.left
anchors.top: top.bottom
anchors.bottom: bottom.top
MouseArea {
anchors.fill: parent
property point clickPos
onPressed: clickPos = Qt.point(mouse.x,mouse.y)
onPositionChanged: {
if(pressed && mainWindow.visibility !== Window.Maximized && mainWindow.visibility !== Window.FullScreen) {
var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
mainWindow.x += delta.x
mainWindow.y += delta.y
}
}
}
}
//Right
Item {
id: right
width: enableSize
anchors.right: parent.right
anchors.top: rightTop.bottom
anchors.bottom: rightBottom.top
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(6)
onReleased: release()
onMouseXChanged: positionChange(Qt.point(mouseX, customPoint.y), 1, 1)
}
}
//左下角
Item {
id: leftBottom
width: enableSize
height: enableSize
anchors.left: parent.left
anchors.bottom: parent.bottom
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(7)
onReleased: release()
onPositionChanged: positionChange(mouse, -1, 1)
}
}
//bottom
Item {
id: bottom
height: enableSize
anchors.left: leftBottom.right
anchors.right: rightBottom.left
anchors.bottom: parent.bottom
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(8)
onReleased: release()
onMouseYChanged: positionChange(Qt.point(customPoint.x, mouseY), 1, 1)
}
}
//右下角
Item {
id:rightBottom
width: enableSize
height: enableSize
anchors.right: parent.right
anchors.bottom: parent.bottom
z: 1000
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPressed: press(mouse)
onEntered: enter(9)
onReleased: release()
onPositionChanged: positionChange(mouse,1,1)
}
}
function enter(direct) {
Resize.setMyCursor(direct)
}
function press(mouse) {
isPressed = true
customPoint = Qt.point(mouse.x, mouse.y)
}
function release() {
isPressed = false
//customPoint = undefined
}
function positionChange(newPosition, directX/*x轴方向*/, directY/*y轴方向*/) {
if(!isPressed) return
var delta = Qt.point(newPosition.x-customPoint.x, newPosition.y-customPoint.y)
var tmpW,tmpH
if(directX >= 0)
tmpW = mainWindow.width + delta.x
else
tmpW = mainWindow.width - delta.x
if(directY >= 0)
tmpH = mainWindow.height + delta.y
else
tmpH = mainWindow.height - delta.y
if(tmpW < mainWindow.minimumWidth) {
if(directX < 0)
mainWindow.x += (mainWindow.width - mainWindow.minimumWidth)
mainWindow.width = mainWindow.minimumWidth
}
else {
mainWindow.width = tmpW
if(directX < 0)
mainWindow.x += delta.x
}
if(tmpH < mainWindow.minimumHeight) {
if(directY < 0)
mainWindow.y += (mainWindow.height - mainWindow.minimumHeight)
mainWindow.height = mainWindow.minimumHeight
}
else {
mainWindow.height = tmpH
if(directY < 0)
mainWindow.y += delta.y
}
}
}
ResizeQmlWindow.cpp
#include "ResizeQmlWindow.h"
ResizeQmlWindow::ResizeQmlWindow(QObject *parent) : QObject(parent)
{}
void ResizeQmlWindow::setWindow(QWindow *win)
{
m_win = win;
}
void ResizeQmlWindow::setMyCursor(int direct)
{
switch (direct) {
case 1:
m_win->setCursor(QCursor(Qt::SizeFDiagCursor));
break;
case 2:
m_win->setCursor(QCursor(Qt::SizeVerCursor));
break;
case 3:
m_win->setCursor(QCursor(Qt::SizeBDiagCursor));
break;
case 4:
m_win->setCursor(QCursor(Qt::SizeHorCursor));
break;
case 5:
m_win->setCursor(QCursor(Qt::ArrowCursor));
break;
case 6:
m_win->setCursor(QCursor(Qt::SizeHorCursor));
break;
case 7:
m_win->setCursor(QCursor(Qt::SizeBDiagCursor));
break;
case 8:
m_win->setCursor(QCursor(Qt::SizeVerCursor));
break;
case 9:
m_win->setCursor(QCursor(Qt::SizeFDiagCursor));
break;
}
}
因为我们要在C++设置鼠标的状态,所以我们还须将ResizeQmlWindow注册传递到Qml中,如下
QObject * obj = engine.rootObjects().at(0);
QWindow * w = qobject_cast(obj);
ResizeQmlWindow resize;
if(w) {
resize.setWindow(w);
engine.rootContext()->setContextProperty("Resize", &resize);
}
对比效果如图所示
示例工程下载