From 6b6dbf27a1b509487cfdc304ea441ac6c640b1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Mon, 11 Dec 2023 23:47:03 +0800 Subject: [PATCH] update --- src/FluFrameless.cpp | 288 +++++++++++++++++++++++++++++++++++++++++++ src/FluFrameless.h | 47 +++++++ 2 files changed, 335 insertions(+) create mode 100644 src/FluFrameless.cpp create mode 100644 src/FluFrameless.h diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp new file mode 100644 index 00000000..51d942c5 --- /dev/null +++ b/src/FluFrameless.cpp @@ -0,0 +1,288 @@ +#include "FluFrameless.h" + +#include "FluFrameless.h" + +#include +#include + +#ifdef Q_OS_WIN +#pragma comment(lib, "dwmapi.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "shcore.lib") +#pragma comment(lib, "Gdi32.lib") +#include +#include +#include +#include +#include + +static inline QByteArray qtNativeEventType() +{ + static const auto result = "windows_generic_MSG"; + return result; +} + +int getSystemMetrics2(const WId windowId, const int index, const bool scaled){ + Q_ASSERT(windowId); + if (!windowId) { + return 0; + } + const UINT realDpi = GetDpiForWindow(reinterpret_cast(windowId)); + { + const UINT dpi = (scaled ? realDpi : USER_DEFAULT_SCREEN_DPI); + if (const int result = GetSystemMetricsForDpi(index, dpi); result > 0) { + return result; + } + } + const qreal dpr = (scaled ? qreal(1) : (qreal(realDpi) / qreal(USER_DEFAULT_SCREEN_DPI))); + return std::round(qreal(::GetSystemMetrics(index)) / dpr); +} + +quint32 getResizeBorderThickness(const WId windowId, const bool scaled){ + if (!windowId) { + return 0; + } + return getSystemMetrics2(windowId, SM_CXSIZEFRAME, scaled) + getSystemMetrics2(windowId, SM_CXPADDEDBORDER, scaled); +} + +#endif + + +FramelessEventFilter::FramelessEventFilter(QQuickWindow* window){ + _window = window; + _current = window->winId(); +} + +bool FramelessEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result){ +#ifdef Q_OS_WIN + if ((eventType != qtNativeEventType()) || !message || !result || !_window) { + return false; + } + const auto msg = static_cast(message); + const HWND hWnd = msg->hwnd; + if (!hWnd) { + return false; + } + const qint64 wid = reinterpret_cast(hWnd); + if(wid != _current){ + return false; + } + const UINT uMsg = msg->message; + if (!msg || !msg->hwnd) + { + return false; + } + const LPARAM lParam = msg->lParam; + const int borderPadding = getResizeBorderThickness(_window->winId(), true); + if (uMsg == WM_NCCALCSIZE) { + if (_window->visibility() == QWindow::FullScreen) { + NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); + sz->rgrc[0].left += borderPadding; + sz->rgrc[0].top += borderPadding; + sz->rgrc[0].right -= borderPadding; + sz->rgrc[0].bottom -= borderPadding; + *result = WVR_REDRAW; + return true; + } + NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); + if ( _window->visibility() == QWindow::Maximized) { + sz->rgrc[0].top += borderPadding; + } else { + sz->rgrc[0].top += 0; + } + sz->rgrc[0].right -= borderPadding; + sz->rgrc[0].bottom -= borderPadding; + sz->rgrc[0].left -= -borderPadding; + *result = WVR_REDRAW; + return true; + }else if (uMsg == WM_NCHITTEST){ + const bool isResizable = !(_window->height()==_window->maximumHeight()&&_window->height()==_window->minimumHeight()&&_window->width()==_window->maximumWidth()&&_window->width()==_window->minimumWidth()); + RECT winrect; + GetWindowRect(msg->hwnd, &winrect); + long x = GET_X_LPARAM(msg->lParam); + long y = GET_Y_LPARAM(msg->lParam); + if (x >= winrect.left && x < winrect.left + borderPadding && + y < winrect.bottom && y >= winrect.bottom - borderPadding && isResizable) { + *result = HTBOTTOMLEFT; + return true; + } + if (x < winrect.right && x >= winrect.right - borderPadding && + y < winrect.bottom && y >= winrect.bottom - borderPadding && isResizable) { + *result = HTBOTTOMRIGHT; + return true; + } + if (x >= winrect.left && x < winrect.left + borderPadding && + y >= winrect.top && y < winrect.top + borderPadding && isResizable) { + *result = HTTOPLEFT; + return true; + } + if (x < winrect.right && x >= winrect.right - borderPadding && + y >= winrect.top && y < winrect.top + borderPadding && isResizable) { + *result = HTTOPRIGHT; + return true; + } + if (x >= winrect.left && x < winrect.left + borderPadding && isResizable) { + *result = HTLEFT; + return true; + } + if (x < winrect.right && x >= winrect.right - borderPadding && isResizable) { + *result = HTRIGHT; + return true; + } + if (y < winrect.bottom && y >= winrect.bottom - borderPadding && isResizable) { + *result = HTBOTTOM; + return true; + } + if (y >= winrect.top && y < winrect.top + borderPadding && isResizable) { + *result = HTTOP; + return true; + } + return false; + }else if(uMsg == WM_COMMAND){ + SendMessage(msg->hwnd, WM_SYSCOMMAND, msg->wParam, msg->lParam); + *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + return true; + }else if(uMsg == WM_WINDOWPOSCHANGING){ + WINDOWPOS* wp = reinterpret_cast(msg->lParam); + if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + { + wp->flags |= SWP_NOCOPYBITS; + *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + return true; + } + return false; + } + return false; +#endif + return false; +} + + +FluFrameless::FluFrameless(QObject *parent) + : QObject{parent} +{ +} + +void FluFrameless::classBegin(){ +} + +void FluFrameless::refresLayout(){ +#ifdef Q_OS_WIN + if(!_window.isNull()){ + HWND hWnd = reinterpret_cast(_window->winId()); + RECT rect; + GetWindowRect(hWnd, &rect); + SetWindowPos(hWnd, nullptr, rect.left, rect.top, rect.right - rect.left,rect.bottom - rect.top,SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE |SWP_FRAMECHANGED); + } +#endif +} + +void FluFrameless::updateCursor(Qt::Edges edges){ + switch (edges) { + case 0: + _window->setCursor(Qt::ArrowCursor); + break; + case Qt::LeftEdge: + case Qt::RightEdge: + _window->setCursor(Qt::SizeHorCursor); + break; + case Qt::TopEdge: + case Qt::BottomEdge: + _window->setCursor(Qt::SizeVerCursor); + break; + case Qt::LeftEdge | Qt::TopEdge: + case Qt::RightEdge | Qt::BottomEdge: + _window->setCursor(Qt::SizeFDiagCursor); + break; + case Qt::RightEdge | Qt::TopEdge: + case Qt::LeftEdge | Qt::BottomEdge: + _window->setCursor(Qt::SizeBDiagCursor); + break; + } +} + +bool FluFrameless::eventFilter(QObject *obj, QEvent *ev){ + if (_window->flags()& Qt::FramelessWindowHint) { + static Qt::Edges edges = Qt::Edges(); + const int margin = 8; + switch (ev->type()) { + case QEvent::MouseButtonPress: + updateCursor(edges); + _window->startSystemResize(edges); + break; + case QEvent::MouseButtonRelease: + qDebug() << Q_FUNC_INFO << ev; + edges = Qt::Edges(); + updateCursor(edges); + break; + case QEvent::MouseMove: { + edges = Qt::Edges(); + QMouseEvent *event = static_cast(ev); + QPoint p = +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + event->pos(); +#else + event->position().toPoint(); +#endif + if ( p.x() < margin ) { + edges |= Qt::LeftEdge; + } + if ( p.x() > (_window->width() - margin) ) { + edges |= Qt::RightEdge; + } + if ( p.y() < margin ) { + edges |= Qt::TopEdge; + } + if ( p.y() > (_window->height() - margin) ) { + edges |= Qt::BottomEdge; + } + updateCursor(edges); + break; + } + default: + break; + } + } + return QObject::eventFilter(obj, ev); +} + +void FluFrameless::componentComplete(){ + auto o = parent(); + while (nullptr != o) { + _window = (QQuickWindow*)o; + o = o->parent(); + } +#ifdef Q_OS_WIN + if(!_window.isNull()){ + _nativeEvent =new FramelessEventFilter(_window); + qApp->installNativeEventFilter(_nativeEvent); + MARGINS margins[2]{{0, 0, 0, 0}, {0, 0, 1, 0}}; + HWND hWnd = reinterpret_cast(_window->winId()); + DwmExtendFrameIntoClientArea(hWnd, &margins[false]); + refresLayout(); + connect(_window,&QWindow::visibilityChanged,this,[=](QWindow::Visibility visibility){ refresLayout(); }); + } +#endif +#ifdef Q_OS_LINUX + if(!_window.isNull()){ + _window->setFlag(Qt::FramelessWindowHint,true); + _window->installEventFilter(this); + } +#endif +} + +FluFrameless::~FluFrameless(){ +#ifdef Q_OS_WIN + if (_nativeEvent) { + delete _nativeEvent; + _nativeEvent = nullptr; + refresLayout(); + } +#endif +#ifdef Q_OS_LINUX + if (!_window.isNull()) { + _window->removeEventFilter(this); + _window->setFlag(Qt::FramelessWindowHint,false); + } +#endif +} diff --git a/src/FluFrameless.h b/src/FluFrameless.h new file mode 100644 index 00000000..ca05e42a --- /dev/null +++ b/src/FluFrameless.h @@ -0,0 +1,47 @@ +#ifndef FLUFRAMELESS_H +#define FLUFRAMELESS_H + +#include +#include +#include +#include +#include "stdafx.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; +using QT_ENTER_EVENT_TYPE = QEnterEvent; +#else +using QT_NATIVE_EVENT_RESULT_TYPE = long; +using QT_ENTER_EVENT_TYPE = QEvent; +#endif + +class FramelessEventFilter : public QAbstractNativeEventFilter +{ +public: + FramelessEventFilter(QQuickWindow* window); + bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; +public: + QQuickWindow* _window = nullptr; + qint64 _current = 0; +}; + +class FluFrameless : public QObject, public QQmlParserStatus +{ + Q_OBJECT + QML_NAMED_ELEMENT(FluFrameless) +public: + explicit FluFrameless(QObject *parent = nullptr); + ~FluFrameless(); + void classBegin() override; + void componentComplete() override; + Q_INVOKABLE void refresLayout(); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +private: + void updateCursor(Qt::Edges edges); +private: + QPointer _window = nullptr; + FramelessEventFilter* _nativeEvent = nullptr; +}; + +#endif // FLUFRAMELESS_H