From d1cf1fc92d726e6049d74e1d222455aad3c83b22 Mon Sep 17 00:00:00 2001 From: jared Date: Sat, 26 Dec 2020 23:00:30 +0800 Subject: [PATCH] update FrameLess --- .../TaoCommon/Frameless/TaoFrameLessView.h | 10 +- .../Frameless/Unix/TaoFrameLessView_unix.cpp | 168 ++------------- .../Frameless/Win/TaoFrameLessView_win.cpp | 201 +++++++++++++----- .../Frameless/Win/WindowsContext.cpp | 0 .../TaoCommon/Frameless/Win/WindowsContext.h | 3 - examples/TaoQuickShow/Qml/Pane/RightPane.qml | 2 +- 6 files changed, 180 insertions(+), 204 deletions(-) delete mode 100644 3rdparty/TaoCommon/Frameless/Win/WindowsContext.cpp delete mode 100644 3rdparty/TaoCommon/Frameless/Win/WindowsContext.h diff --git a/3rdparty/TaoCommon/Frameless/TaoFrameLessView.h b/3rdparty/TaoCommon/Frameless/TaoFrameLessView.h index 665279d..e0338fa 100644 --- a/3rdparty/TaoCommon/Frameless/TaoFrameLessView.h +++ b/3rdparty/TaoCommon/Frameless/TaoFrameLessView.h @@ -1,7 +1,8 @@ #pragma once #include "TaoCommonGlobal.h" #include -//无边框窗口,支持拖动和改变大小,支持Windows平台Aero效果 +//无边框窗口,实现自定义标题栏。支持拖动和改变大小,支持Windows平台Aero效果 +class TaoFrameLessViewPrivate; class TAO_API TaoFrameLessView : public QQuickView { Q_OBJECT @@ -11,8 +12,8 @@ public: explicit TaoFrameLessView(QWindow *parent = nullptr); ~TaoFrameLessView(); void moveToScreenCenter(); - bool isMax() const { return m_isMax; } - QQuickItem *titleItem() const { return m_titleItem; } + bool isMax() const; + QQuickItem *titleItem() const; static QRect calcCenterGeo(const QRect &screenGeo, const QSize &normalSize); public slots: @@ -30,6 +31,5 @@ protected: # endif #endif private: - bool m_isMax; - QQuickItem *m_titleItem = nullptr; + TaoFrameLessViewPrivate *d; }; diff --git a/3rdparty/TaoCommon/Frameless/Unix/TaoFrameLessView_unix.cpp b/3rdparty/TaoCommon/Frameless/Unix/TaoFrameLessView_unix.cpp index 7d0209e..1ccfef5 100644 --- a/3rdparty/TaoCommon/Frameless/Unix/TaoFrameLessView_unix.cpp +++ b/3rdparty/TaoCommon/Frameless/Unix/TaoFrameLessView_unix.cpp @@ -1,83 +1,19 @@ -#include "TaoFrameLessView.h" - +#include "Frameless/TaoFrameLessView.h" #include #include #include #include -#if WIN32 -# include -# include -# include // Fixes error C2504: 'IUnknown' : base class undefined -# include -# include -# pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved - // external symbol __imp__DwmExtendFrameIntoClientArea -# pragma comment(lib, "user32.lib") - -#endif - -#ifdef WIN32 -static long hitTest(RECT winrect, long x, long y, int borderWidth) +class TaoFrameLessViewPrivate { - // 鼠标区域位于窗体边框,进行缩放 - if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) - && (y < winrect.top + borderWidth)) { - return HTTOPLEFT; - } else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top - && y < winrect.top + borderWidth) { - return HTTOPRIGHT; - } else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom - && y >= winrect.bottom - borderWidth) { - return HTBOTTOMLEFT; - } else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom - && y >= winrect.bottom - borderWidth) { - return HTBOTTOMRIGHT; - } else if (x >= winrect.left && x < winrect.left + borderWidth) { - return HTLEFT; - } else if (x < winrect.right && x >= winrect.right - borderWidth) { - return HTRIGHT; - } else if (y >= winrect.top && y < winrect.top + borderWidth) { - return HTTOP; - } else if (y < winrect.bottom && y >= winrect.bottom - borderWidth) { - return HTBOTTOM; - } else { - return 0; - } -} -#endif - -static bool isMaxWin(QWindow *win) +public: + bool m_isMax; + QQuickItem *m_titleItem = nullptr; +}; +TaoFrameLessView::TaoFrameLessView(QWindow *parent) : Super(parent), d(new TaoFrameLessViewPrivate) { } +TaoFrameLessView::~TaoFrameLessView() { - return win->windowState() == Qt::WindowMaximized; -} -static bool isFullWin(QQuickView *win) -{ - return win->windowState() == Qt::WindowFullScreen; -} - -TaoFrameLessView::TaoFrameLessView(QWindow *parent) : QQuickView(parent) -{ - setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint - | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); - setResizeMode(SizeRootObjectToView); - - // WS_THICKFRAME 带回Areo效果 -#if WIN32 - DWORD style = ::GetWindowLong(HWND(winId()), GWL_STYLE); - style |= WS_THICKFRAME; - ::SetWindowLong(HWND(winId()), GWL_STYLE, style); -#endif - - setIsMax(isMaxWin(this)); - connect(this, &QWindow::windowStateChanged, this, - [&](Qt::WindowState state) { setIsMax(state == Qt::WindowMaximized); }); -} - -TaoFrameLessView::~TaoFrameLessView() { } -void TaoFrameLessView::setTitleItem(QQuickItem *item) -{ - m_titleItem = item; + delete d; } QRect TaoFrameLessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize) { @@ -105,83 +41,23 @@ void TaoFrameLessView::moveToScreenCenter() setGeometry(geo); update(); } - +bool TaoFrameLessView::isMax() const +{ + return d->m_isMax; +} +QQuickItem *TaoFrameLessView::titleItem() const +{ + return d->m_titleItem; +} void TaoFrameLessView::setIsMax(bool isMax) { - if (m_isMax == isMax) + if (d->m_isMax == isMax) return; - m_isMax = isMax; - emit isMaxChanged(m_isMax); + d->m_isMax = isMax; + emit isMaxChanged(d->m_isMax); } - -#if WIN32 -const long border_width = 6; -# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -bool TaoFrameLessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) -# else -bool TaoFrameLessView::nativeEvent(const QByteArray &eventType, void *message, long *result) -# endif +void TaoFrameLessView::setTitleItem(QQuickItem *item) { - if (!result) { - return false; - } - MSG *msg = reinterpret_cast(message); - if (!msg || !msg->hwnd) { - return false; - } - switch (msg->message) { - case WM_NCCALCSIZE: { - const auto mode = static_cast(msg->wParam); - *result = mode ? WVR_REDRAW : 0; - - const auto clientRect = mode - ? &(reinterpret_cast(msg->lParam)->rgrc[0]) - : reinterpret_cast(msg->lParam); - //规避 拖动border进行resize时界面闪烁 - if (!isMaxWin(this) && !isFullWin(this)) { - if (clientRect->top != 0) { - clientRect->top -= 0.1; - } - } else { - if (clientRect->top != 0) { - clientRect->top += 0.1; - } - } - return true; - } - case WM_NCHITTEST: { - *result = 0; - - RECT winrect; - GetWindowRect(HWND(winId()), &winrect); - - long x = GET_X_LPARAM(msg->lParam); - long y = GET_Y_LPARAM(msg->lParam); - - *result = 0; - if (!isMaxWin(this) && !isFullWin(this)) { //非最大化、非全屏时,进行命中测试,处理边框拖拽 - *result = hitTest(winrect, x, y, border_width); - if (0 != *result) { - return true; - } - } - - if (m_titleItem) { - auto titlePos = m_titleItem->mapToGlobal(m_titleItem->position()); - titlePos = mapFromGlobal(titlePos.toPoint()); - auto titleRect = - QRect(titlePos.x(), titlePos.y(), m_titleItem->width(), m_titleItem->height()); - double dpr = qApp->devicePixelRatio(); - QPoint pos = mapFromGlobal(QPoint(x / dpr, y / dpr)); - if (titleRect.contains(pos)) { - *result = HTCAPTION; - return true; - } - } - return false; - } // end case WM_NCHITTEST - } - return QQuickView::nativeEvent(eventType, message, result); + d->m_titleItem = item; } -#endif diff --git a/3rdparty/TaoCommon/Frameless/Win/TaoFrameLessView_win.cpp b/3rdparty/TaoCommon/Frameless/Win/TaoFrameLessView_win.cpp index 67a5dc1..5194b1c 100644 --- a/3rdparty/TaoCommon/Frameless/Win/TaoFrameLessView_win.cpp +++ b/3rdparty/TaoCommon/Frameless/Win/TaoFrameLessView_win.cpp @@ -10,10 +10,36 @@ #include // Fixes error C2504: 'IUnknown' : base class undefined #include #include -#pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved \ - // external symbol __imp__DwmExtendFrameIntoClientArea -#pragma comment(lib, "user32.lib") +#pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved +#pragma comment(lib, "User32.lib") +// we cannot just use WS_POPUP style +// WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work +// WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item) +// WS_CAPTION: enables aero minimize animation/transition +// WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize +enum class Style : DWORD { + windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, + aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, + basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX +}; +static bool isCompositionEnabled() +{ + BOOL composition_enabled = FALSE; + bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK; + return composition_enabled && success; +} +static Style selectBorderLessStyle() +{ + return isCompositionEnabled() ? Style::aero_borderless : Style::basic_borderless; +} +static void setShadow(HWND handle, bool enabled) +{ + if (isCompositionEnabled()) { + static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } }; + ::DwmExtendFrameIntoClientArea(handle, &shadow_state[enabled]); + } +} static long hitTest(RECT winrect, long x, long y, int borderWidth) { // 鼠标区域位于窗体边框,进行缩放 @@ -47,24 +73,67 @@ static bool isFullWin(QQuickView *win) return win->windowState() == Qt::WindowFullScreen; } -TaoFrameLessView::TaoFrameLessView(QWindow *parent) : QQuickView(parent) + +class TaoFrameLessViewPrivate +{ +public: + bool m_isMax; + QQuickItem *m_titleItem = nullptr; + bool borderless = true; // is the window currently borderless + bool borderless_resize = true; // should the window allow resizing by dragging the borders while borderless + bool borderless_drag = true; // should the window allow moving my dragging the client area + bool borderless_shadow = true; // should the window display a native aero shadow while borderless + void setBorderLess(HWND handle, bool enabled) + { + auto newStyle = enabled ? selectBorderLessStyle() : Style::windowed; + auto oldStyle = static_cast