qml实现无边框窗口,超详细教程并且支持跨平台(可拖动与改变大小)

        最底部有完整的代码不想看的可以跳过去

        实现无边框窗口的第一步是先把原有的窗口移除,这算是文章内最重要的一步了,在跟控件设置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(); }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值