feat: add framelesshelper

This commit is contained in:
rankun 2020-10-10 20:56:27 +08:00
parent 960fccda8c
commit b0d68c49a8
5 changed files with 315 additions and 28 deletions

View file

@ -2,21 +2,30 @@ import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id: window
id: root
visible: true
flags: Qt.Window |Qt.FramelessWindowHint
flags: Qt.Window | Qt.FramelessWindowHint | Qt.WindowMaximizeButtonHint
width: 800
height: 600
color: "transparent"
// bg
BorderImage {
id: background
property real backgroundRadius: 4
property bool backgroundHasBorder: true
property color backgroundColor: "#2E2F30"
property color backgroundBorderColor: "#555656"
Rectangle {
id: backgroundView
anchors.fill: parent
source: "qrc:/image/mainwindow/bg.png"
border { left: 30; top: 30; right: 30; bottom: 30 }
horizontalTileMode: BorderImage.Stretch
verticalTileMode: BorderImage.Stretch
color: root.backgroundColor
radius: root.backgroundRadius
clip: true
antialiasing: true
border {
color: root.backgroundHasBorder ? root.backgroundBorderColor : "transparent"
width: root.backgroundHasBorder ? 1 : 0
}
}
Rectangle {
@ -28,23 +37,7 @@ Window {
DragHandler {
grabPermissions: TapHandler.CanTakeOverFromAnything
onActiveChanged: if (active) { window.startSystemMove(); }
onActiveChanged: if (active) { root.startSystemMove(); }
}
}
DragHandler {
id: resizeHandler
grabPermissions: TapHandler.TakeOverForbidden
target: null
onActiveChanged:
if (active) {
const p = resizeHandler.centroid.position;
let e = 0;
if (p.x / width < 0.10) { e |= Qt.LeftEdge }
if (p.x / width > 0.90) { e |= Qt.RightEdge }
if (p.y / height > 0.90) { e |= Qt.BottomEdge }
console.log("RESIZING", e);
window.startSystemResize(e);
}
}
}

View file

@ -11,6 +11,7 @@
#include "dialog.h"
#include "mousetap/mousetap.h"
#include "stream.h"
#include "windowframelesshelper.h"
static Dialog *g_mainDlg = Q_NULLPTR;
@ -75,6 +76,10 @@ int main(int argc, char *argv[])
QApplication app(argc, argv);
QQmlApplicationEngine engine("qrc:/MainWindow.qml");
#ifdef Q_OS_WIN32
WindowFramelessHelper::Instance()->Init();
#endif
// windows下通过qmake VERSION变量或者rc设置版本号和应用名称后这里可以直接拿到
// mac下拿到的是CFBundleVersion的值
qDebug() << app.applicationVersion();

View file

@ -2,8 +2,11 @@ FORMS +=
HEADERS += \
$$PWD/keepratiowidget.h \
$$PWD/magneticwidget.h
$$PWD/magneticwidget.h \
$$PWD/windowframelesshelper.h
SOURCES += \
$$PWD/keepratiowidget.cpp \
$$PWD/magneticwidget.cpp
$$PWD/magneticwidget.cpp \
$$PWD/windowframelesshelper.cpp

View file

@ -0,0 +1,254 @@
#include "windowframelesshelper.h"
#if defined(Q_OS_WIN)
#include <QCursor>
#include <QDebug>
#include <QGuiApplication>
#include <windows.h>
#include <windowsx.h>
WindowFramelessHelper::WindowFramelessHelper() {}
WindowFramelessHelper::~WindowFramelessHelper()
{
// do nothing, because this object is static instance
}
WindowFramelessHelper *WindowFramelessHelper::Instance()
{
static WindowFramelessHelper g_windowNativeEventFilter;
return &g_windowNativeEventFilter;
}
void WindowFramelessHelper::Init()
{
if (!m_inited) {
m_inited = true;
QGuiApplication::instance()->installNativeEventFilter(this);
}
}
bool WindowFramelessHelper::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);
MSG *param = static_cast<MSG *>(message);
if (!param) {
return false;
}
switch (param->message) {
case WM_NCHITTEST: {
if (processNcHitTest(message, result)) {
return true;
}
} break;
case WM_NCLBUTTONDOWN: {
if (processNcLButtonDown(message, result)) {
return true;
}
} break;
case WM_SETCURSOR: {
if (processSetCursor(message, result)) {
return true;
}
} break;
default:
break;
}
return false;
}
bool WindowFramelessHelper::processNcHitTest(void *message, long *result)
{
MSG *param = static_cast<MSG *>(message);
if (!param) {
return false;
}
QWindow *window = getWindow((WId)param->hwnd);
if (!window) {
return false;
}
// 没有最大化&全屏按钮,则认为是固定尺寸窗口
if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) {
return false;
}
qreal dpi = window->devicePixelRatio();
if (dpi <= 0.99) {
dpi = 1.0;
}
QPoint ptCursor = QCursor::pos();
int nX = ptCursor.x() - window->geometry().x();
int nY = ptCursor.y() - window->geometry().y();
*result = HTCLIENT;
if (Qt::WindowMaximized == window->windowState()) {
return false;
}
if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) {
return false;
}
qDebug() << "processNcHitTest:" << ptCursor << window->geometry() << "d:" << nX;
int borderWidth = 5;
if ((nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth) && (nY > m_windowMargin.top())
&& (nY < m_windowMargin.top() + borderWidth)) {
*result = HTTOPLEFT;
} else if (
(nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right()) && (nY > m_windowMargin.top())
&& (nY < m_windowMargin.top() + borderWidth)) {
*result = HTTOPRIGHT;
} else if (
(nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth) && (nY > window->height() - m_windowMargin.bottom() - borderWidth)
&& (nY < window->height() - m_windowMargin.bottom())) {
*result = HTBOTTOMLEFT;
} else if (
(nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right())
&& (nY > window->height() - m_windowMargin.bottom() - borderWidth) && (nY < window->height() - m_windowMargin.bottom())) {
*result = HTBOTTOMRIGHT;
} else if ((nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth)) {
*result = HTLEFT;
} else if ((nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right())) {
*result = HTRIGHT;
} else if ((nY > m_windowMargin.top()) && (nY < m_windowMargin.top() + borderWidth)) {
*result = HTTOP;
} else if ((nY > window->height() - m_windowMargin.bottom() - borderWidth) && (nY < window->height() - m_windowMargin.bottom())) {
*result = HTBOTTOM;
}
return true;
}
bool WindowFramelessHelper::processNcLButtonDown(void *message, long *result)
{
Q_UNUSED(result);
MSG *param = static_cast<MSG *>(message);
if (!param) {
return false;
}
QWindow *window = getWindow((WId)param->hwnd);
if (!window) {
return false;
}
if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) {
return false;
}
if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) {
return false;
}
HWND hwnd = param->hwnd;
WPARAM nHitTest = param->wParam;
if (nHitTest == HTTOP) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOP, param->lParam);
} else if (nHitTest == HTBOTTOM) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOM, param->lParam);
} else if (nHitTest == HTLEFT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_LEFT, param->lParam);
} else if (nHitTest == HTRIGHT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_RIGHT, param->lParam);
} else if (nHitTest == HTTOPLEFT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPLEFT, param->lParam);
} else if (nHitTest == HTTOPRIGHT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPRIGHT, param->lParam);
} else if (nHitTest == HTBOTTOMLEFT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMLEFT, param->lParam);
} else if (nHitTest == HTBOTTOMRIGHT) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMRIGHT, param->lParam);
} else if (nHitTest == HTCAPTION) {
::PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE + 1, 0);
}
return false;
}
bool WindowFramelessHelper::processSetCursor(void *message, long *result)
{
Q_UNUSED(result);
MSG *param = static_cast<MSG *>(message);
if (!param) {
return false;
}
QWindow *window = getWindow((WId)param->hwnd);
if (!window) {
return false;
}
if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) {
return false;
}
if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) {
return false;
}
// is invisible window
HWND hwnd = param->hwnd;
if (!::IsWindowVisible(hwnd)) {
return true;
}
// is not enabled window
if (!::IsWindowEnabled(hwnd)) {
return true;
}
// set cursor
HCURSOR hCursor = nullptr;
LPARAM hittest = LOWORD(param->lParam);
switch (hittest) {
case HTRIGHT:
case HTLEFT:
hCursor = LoadCursor(nullptr, IDC_SIZEWE);
break;
case HTTOP:
case HTBOTTOM:
hCursor = LoadCursor(nullptr, IDC_SIZENS);
break;
case HTTOPLEFT:
case HTBOTTOMRIGHT:
hCursor = LoadCursor(nullptr, IDC_SIZENWSE);
break;
case HTTOPRIGHT:
case HTBOTTOMLEFT:
hCursor = LoadCursor(nullptr, IDC_SIZENESW);
break;
default:
break;
}
if (!hCursor) {
return false;
}
::SetCursor(hCursor);
::DestroyCursor(hCursor);
return true;
}
QWindow *WindowFramelessHelper::getWindow(WId wndId)
{
QWindowList windows = QGuiApplication::topLevelWindows();
for (int i = 0; i < windows.size(); ++i) {
if (windows.at(i)->winId() == wndId) {
return windows.at(i);
}
}
return nullptr;
}
#endif //(Q_OS_WIN)

View file

@ -0,0 +1,32 @@
#pragma once
#include <QWindow>
#if defined(Q_OS_WIN)
#include <QAbstractNativeEventFilter>
#include <QMargins>
class WindowFramelessHelper : public QAbstractNativeEventFilter
{
protected:
WindowFramelessHelper();
~WindowFramelessHelper() override;
public:
static WindowFramelessHelper *Instance();
void Init();
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
private:
bool processNcHitTest(void *message, long *result);
bool processNcLButtonDown(void *message, long *result);
bool processSetCursor(void *message, long *result);
QWindow *getWindow(WId wndId);
private:
bool m_inited = false;
QMargins m_windowMargin = QMargins(0, 0, 0, 0);
};
#endif // Q_OS_WIN