Bootstrap

QtQuick自定义无边框窗口

 

在现代桌面应用程序开发中,用户界面的美观性和灵活性显得越来越重要。传统的窗口边框在某些情况下可能会显得过于呆板和限制性,特别是当开发者追求提供无缝和沉浸式用户体验时。Qt Quick (QML) 为开发者提供了强大的工具,以自定义和增强用户界面的外观和感觉。在本篇博客中,我们将探讨如何在Qt QML中创建无边框窗口,并讨论实现该功能的关键步骤及注意事项。

  • 增强的视觉吸引力:无边框窗口可以去除传统窗口边界的干扰,使应用看起来更现代化,更干净。
  • 自定义布局与控制:开发者可以完全控制窗口的外观和布局,包括窗口的大小、形状和行为。
  • 提升用户体验:通过提供定制的窗口控制(如最小化、最大化、关闭按钮),可以更好地融入应用程序的整体设计语言中。

 

//frameless.h

#ifndef FRAMELESS_H
#define FRAMELESS_H


#include <QQuickWindow>

class Frameless : public QQuickWindow {
Q_OBJECT

public:
    explicit Frameless(QWindow *parent = nullptr);

protected:
    bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
};


#endif //FRAMELESS_H

 

//frameless.cpp


#include "btframeless.h"

#ifdef Q_OS_WIN

#pragma comment(lib, "dwmapi")
#pragma comment(lib, "user32.lib")

#include <Windows.h>
#include <windowsx.h>
#include <dwmapi.h>

#endif

Frameless::Frameless(QWindow *parent) : QQuickWindow(parent) {
    setFlags(Qt::Window
             | Qt::WindowMaximizeButtonHint
             | Qt::WindowMinimizeButtonHint
             | Qt::FramelessWindowHint);
    HWND hWnd = (HWND) winId();
    LONG style = GetWindowLongW(hWnd, GWL_STYLE);
    SetWindowLong(
            hWnd,
            GWL_STYLE,
            style
            | WS_MINIMIZEBOX
            | WS_MAXIMIZEBOX
            | WS_CAPTION
            | CS_DBLCLKS
            | WS_THICKFRAME
    );
}

bool Frameless::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
    const static int boundary = 5;
    MSG *msg = static_cast<MSG *>(message);

    switch (msg->message) {
        // 无边框 最大化时增加边距
        case WM_NCCALCSIZE: {
            if (IsZoomed(msg->hwnd)) {
                auto *params = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam);
                int border = GetSystemMetrics(SM_CXSIZEFRAME);
                params->rgrc[0].top += border;
                params->rgrc[0].left += border;
                params->rgrc[0].right -= border;
                params->rgrc[0].bottom -= border;
            }
            *result = HTNOWHERE;
            return true;
        }
            // win11 圆角
        case WM_ACTIVATE: {
            MARGINS margins = {1, 1, 0, 1};
            HRESULT hr = DwmExtendFrameIntoClientArea(msg->hwnd, &margins);
            *result = hr;
            return true;
        }
        case WM_NCHITTEST: {
            POINT mouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
            RECT rc;
            GetWindowRect(msg->hwnd, &rc);
            bool left = (mouse.x >= rc.left && mouse.x < rc.left + boundary);
            bool right = (mouse.x < rc.right && mouse.x >= rc.right - boundary);
            bool top = (mouse.y >= rc.top && mouse.y < rc.top + boundary);
            bool bottom = (mouse.y < rc.bottom && mouse.y >= rc.bottom - boundary);

            if (top && left) {
                *result = HTTOPLEFT;
                return true;
            } else if (top && right) {
                *result = HTTOPRIGHT;
                return true;
            } else if (bottom && left) {
                *result = HTBOTTOMLEFT;
                return true;
            } else if (bottom && right) {
                *result = HTBOTTOMRIGHT;
                return true;
            } else if (top) {
                *result = HTTOP;
                return true;
            } else if (left) {
                *result = HTLEFT;
                return true;
            } else if (right) {
                *result = HTRIGHT;
                return true;
            } else if (bottom) {
                *result = HTBOTTOM;
                return true;
            }
            *result = HTCLIENT;
            return false;
        }
    }
    return false;
}

 如何使用:

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickStyle>
#include "frameless.h"

int main(int argc, char *argv[]) {

#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
    QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
#else
    QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif

    QGuiApplication app(argc, argv);
    QQuickStyle::setStyle("Fusion"); //Fusion  Imagine Universal  Material

    qmlRegisterType<Frameless>("Frameless", 1, 0, "Frameless");

    QQmlApplicationEngine engine;

    QObject::connect(
            &engine,
            &QQmlApplicationEngine::objectCreationFailed,
            &app,
            []() { QCoreApplication::exit(-1); },
            Qt::QueuedConnection);
    engine.load(QUrl("qrc:qml/Main.qml"));

    return QGuiApplication::exec();
}
// main.qml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Frameless
Frameless {
    height: 480
    title: " New UI"
    visible: true
    width: 640

    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        Rectangle {
            id: titleBar
            Layout.fillWidth: true
            height: 30
        }
        Rectangle {
            Layout.fillHeight: true
            Layout.fillWidth: true
        }
    }
}

;