diff --git a/QtScrcpy/MainWindow.qml b/QtScrcpy/MainWindow.qml index 1c8b0ab..b7f8dac 100644 --- a/QtScrcpy/MainWindow.qml +++ b/QtScrcpy/MainWindow.qml @@ -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); - } - } } diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index d4d48cb..993168a 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -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(); diff --git a/QtScrcpy/uibase/uibase.pri b/QtScrcpy/uibase/uibase.pri index cac526a..1a29b2d 100644 --- a/QtScrcpy/uibase/uibase.pri +++ b/QtScrcpy/uibase/uibase.pri @@ -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 diff --git a/QtScrcpy/uibase/windowframelesshelper.cpp b/QtScrcpy/uibase/windowframelesshelper.cpp new file mode 100644 index 0000000..70dfb6d --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper.cpp @@ -0,0 +1,254 @@ +#include "windowframelesshelper.h" + +#if defined(Q_OS_WIN) +#include +#include +#include +#include +#include + +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(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(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(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(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) diff --git a/QtScrcpy/uibase/windowframelesshelper.h b/QtScrcpy/uibase/windowframelesshelper.h new file mode 100644 index 0000000..0b93228 --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper.h @@ -0,0 +1,32 @@ +#pragma once +#include + +#if defined(Q_OS_WIN) +#include +#include + +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