最底部有完整的代码不想看的可以跳过去
实现无边框窗口的第一步是先把原有的窗口移除,这算是文章内最重要的一步了,在跟控件设置flags属性为Qt.FramelessWindowHint即可
ApplicationWindow {
id: window
visible: true
flags: Qt.FramelessWindowHint // 无边框窗口
}
接下来需要实现的是窗口的两个基本功能,窗口拖动与窗口大小改变,我们先实现窗口拖动下面我直接放代码
// 切换最大化/还原
function toggleMaximized() {
if (window.visibility === Window.Maximized) {
window.showNormal();
} else {
window.showMaximized();
}
}
ToolBar {
Rectangle {
color: "transparent" // 设置 ToolBar 背景为透明
}
Item {
anchors.fill: parent
// 双击最大化/还原
TapHandler {
onTapped: if (tapCount === 2) toggleMaximized()
gesturePolicy: TapHandler.DragThreshold
}
// 拖动窗口
DragHandler {
grabPermissions: TapHandler.CanTakeOverFromAnything
onActiveChanged: if (active) { window.startSystemMove(); }
}
}
}
TapHandler是一个处理点击或按钮事件的手势处理组件,能够处理点击,按压和释放事件,在点击时会触发onTapped 函数,tapCount 属性来判断在一定时间内用户点击的次数,gesturePolicy手势策略属性可以设置不同的策略来改变onTapped的触发条件,TapHandler.DragThreshold策略是允许用户在轻微拖动后仍触发点击
DragHandler处理拖动事件的手势处理组件,grabPermissions属性用于设置事件权限,TapHandler.CanTakeOverFromAnything表示当拖动开始后,强制接管所有同级与子级的事件处理,保证了当拖动区域内有按钮等控件时也可以正常触发,onActiveChanged函数会在拖动开始与结束时成为,active是系统提供的变量用于确定当前是否处于拖动中。
当我的开始拖动时触发onActiveChanged函数,这是active置为true从而调用startSystemMove()这是Qt框架的底层API,调用后Qt 会与操作系统通信将窗口的移动控制权完全交给操作系统,操作系统会完成所有的操作包括当松开鼠标时停止移动,这相较于我们手动移动窗口要高效的多
好的拖动窗口已经介绍的差不多了,接下了再介绍如何改变窗口的大小,代码如下
// 鼠标区域,用于设置光标形状
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: {
const p = Qt.point(mouseX, mouseY);
const b = borderWidth + 10; // 增加边角区域的大小
if (p.x < b && p.y < b) return Qt.SizeFDiagCursor;
if (p.x >= width - b && p.y >= height - b) return Qt.SizeFDiagCursor;
if (p.x >= width - b && p.y < b) return Qt.SizeBDiagCursor;
if (p.x < b && p.y >= height - b) return Qt.SizeBDiagCursor;
if (p.x < b || p.x >= width - b) return Qt.SizeHorCursor;
if (p.y < b || p.y >= height - b) return Qt.SizeVerCursor;
return Qt.ArrowCursor;
}
acceptedButtons: Qt.NoButton // 不处理实际事件
}
这一部分代码的作用是改变鼠标的形状,MouseArea是qml中一个很全面的鼠标事件处理组件,上面介绍过的功能它都可以干,但能用TapHandler或者DragHandler就使用这两个把,因为在功能性和效率上都高于MouseArea
hoverEnabled属性置为true表示启用鼠标悬停事件的监听,默认为false,acceptedButtons: Qt.NoButton这设置了当前MouseArea不处理如何鼠标按钮事件,只负责根据鼠标位置改变光标形状,cursorShape这里使用了属性绑定来动态的改变鼠标样式,qml的属性绑定会自动追踪依赖项(如 mouseX, mouseY, width, height, borderWidth),任一值变化时触发重新计算。
// 调整窗口大小
DragHandler {
id: resizeHandler
grabPermissions: TapHandler.TakeOverForbidden
target: null
onActiveChanged: if (active) {
const p = resizeHandler.centroid.position;
const b = borderWidth + 10; // 增加边角区域的大小
let edges = 0;
if (p.x < b) { edges |= Qt.LeftEdge }
if (p.x >= width - b) { edges |= Qt.RightEdge }
if (p.y < b) { edges |= Qt.TopEdge }
if (p.y >= height - b) { edges |= Qt.BottomEdge }
window.startSystemResize(edges);
}
}
大小调整继续用到了DragHandler组件,这里的权限我们设置为了TapHandler.TakeOverForbidden它不会接管其他事件处理器,确保拖动操作不会影响其他事件,target属性用于指定拖动事件的目标,这里设置为null表示不会影响某个特定的可视化元素,edges是标志变量,根据当前鼠标位置与b向其添加不同的标志位,最后调用startSystemResize函数将大小改变操作交给操作系统
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
ApplicationWindow {
id: window
visible: true
flags: Qt.FramelessWindowHint // 无边框窗口
color: "transparent" // 窗口背景透明
// 边框宽度
property int borderWidth: 3
// 切换最大化/还原
function toggleMaximized() {
if (window.visibility === Window.Maximized) {
window.showNormal();
} else {
window.showMaximized();
}
}
// 鼠标区域,用于设置光标形状
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: {
const p = Qt.point(mouseX, mouseY);
const b = borderWidth + 10; // 增加边角区域的大小
if (p.x < b && p.y < b) return Qt.SizeFDiagCursor;
if (p.x >= width - b && p.y >= height - b) return Qt.SizeFDiagCursor;
if (p.x >= width - b && p.y < b) return Qt.SizeBDiagCursor;
if (p.x < b && p.y >= height - b) return Qt.SizeBDiagCursor;
if (p.x < b || p.x >= width - b) return Qt.SizeHorCursor;
if (p.y < b || p.y >= height - b) return Qt.SizeVerCursor;
return Qt.ArrowCursor;
}
acceptedButtons: Qt.NoButton // 不处理实际事件
}
// 调整窗口大小
DragHandler {
id: resizeHandler
grabPermissions: TapHandler.TakeOverForbidden
target: null
onActiveChanged: if (active) {
const p = resizeHandler.centroid.position;
const b = borderWidth + 10; // 增加边角区域的大小
let edges = 0;
if (p.x < b) { edges |= Qt.LeftEdge }
if (p.x >= width - b) { edges |= Qt.RightEdge }
if (p.y < b) { edges |= Qt.TopEdge }
if (p.y >= height - b) { edges |= Qt.BottomEdge }
window.startSystemResize(edges);
}
}
// 工具栏(用于拖动窗口)
ToolBar {
id: toolBar
width: parent.width - 2 * borderWidth
height: 80
x: borderWidth; y: borderWidth
background: Rectangle {
color: "transparent" // 设置 ToolBar 背景为透明
}
Item {
anchors.fill: parent
// 双击最大化/还原
TapHandler {
onTapped: if (tapCount === 2) toggleMaximized()
gesturePolicy: TapHandler.DragThreshold
}
// 拖动窗口
DragHandler {
grabPermissions: TapHandler.CanTakeOverFromAnything
onActiveChanged: if (active) { window.startSystemMove(); }
}
}
}
}