1
0
mirror of https://github.com/jaredtao/TaoQuick.git synced 2025-01-17 20:12:54 +08:00

update FrameLess

This commit is contained in:
jared 2020-12-26 23:00:30 +08:00
parent e6f9ff25bf
commit d1cf1fc92d
6 changed files with 180 additions and 204 deletions

View File

@ -1,7 +1,8 @@
#pragma once
#include "TaoCommonGlobal.h"
#include <QQuickView>
//无边框窗口支持拖动和改变大小支持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;
};

View File

@ -1,83 +1,19 @@
#include "TaoFrameLessView.h"
#include "Frameless/TaoFrameLessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#if WIN32
# include <WinUser.h>
# include <dwmapi.h>
# include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
# include <windows.h>
# include <windowsx.h>
# 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<MSG *>(message);
if (!msg || !msg->hwnd) {
return false;
}
switch (msg->message) {
case WM_NCCALCSIZE: {
const auto mode = static_cast<BOOL>(msg->wParam);
*result = mode ? WVR_REDRAW : 0;
const auto clientRect = mode
? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0])
: reinterpret_cast<LPRECT>(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

View File

@ -10,10 +10,36 @@
#include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
#include <windows.h>
#include <windowsx.h>
#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<Style>(::GetWindowLongPtrW(handle, GWL_STYLE));
if (oldStyle != newStyle) {
borderless = enabled;
::SetWindowLongPtrW(handle, GWL_STYLE, static_cast<LONG>(newStyle));
// when switching between borderless and windowed, restore appropriate shadow state
setShadow(handle, borderless_shadow && (newStyle != Style::windowed));
// redraw frame
::SetWindowPos(handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
::ShowWindow(handle, SW_SHOW);
}
}
void setBorderLessShadow(HWND handle, bool enabled)
{
if (borderless) {
borderless_shadow = enabled;
setShadow(handle, enabled);
}
}
};
TaoFrameLessView::TaoFrameLessView(QWindow *parent) : QQuickView(parent), d(new TaoFrameLessViewPrivate)
{
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView);
// WS_THICKFRAME 带回Areo效果, 在NC_CALCSIZE中再去掉
DWORD style = ::GetWindowLong(HWND(winId()), GWL_STYLE);
style |= WS_THICKFRAME;
::SetWindowLong(HWND(winId()), GWL_STYLE, style);
d->setBorderLess((HWND)(winId()), d->borderless);
d->setBorderLessShadow((HWND)(winId()), d->borderless_shadow);
setIsMax(isMaxWin(this));
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) { setIsMax(state == Qt::WindowMaximized); });
}
TaoFrameLessView::~TaoFrameLessView() { }
TaoFrameLessView::~TaoFrameLessView()
{
delete d;
}
bool TaoFrameLessView::isMax() const
{
return d->m_isMax;
}
QQuickItem *TaoFrameLessView::titleItem() const
{
return d->m_titleItem;
}
void TaoFrameLessView::setTitleItem(QQuickItem *item)
{
m_titleItem = item;
d->m_titleItem = item;
}
QRect TaoFrameLessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize)
{
@ -95,82 +164,116 @@ void TaoFrameLessView::moveToScreenCenter()
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);
}
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
{
const long border_width = 6;
if (!result) {
//防御式编程
//一般不会发生这种情况win7一些特殊情况会传空指针进来。解决方案是升级驱动、切换到basic主题。
//一般不会发生这种情况win7一些极端情况会传空指针进来。解决方案是升级驱动、切换到basic主题。
return false;
}
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
// Work-around a bug caused by typo which only exists in Qt 5.11.1
const auto msg = *reinterpret_cast<MSG **>(message);
#else
const auto msg = static_cast<LPMSG>(message);
#endif
if (!msg || !msg->hwnd) {
return false;
}
switch (msg->message) {
case WM_NCCALCSIZE: {
const auto mode = static_cast<BOOL>(msg->wParam);
*result = mode ? WVR_REDRAW : 0;
const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(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;
if (mode == TRUE && d->borderless) {
*result = WVR_REDRAW;
//规避 拖动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;
}
return true;
break;
}
case WM_NCACTIVATE: {
if (!isCompositionEnabled()) {
// Prevents window frame reappearing on window activation
// in "basic" theme, where no aero shadow is present.
*result = 1;
return true;
}
break;
}
// case WM_NCCALCSIZE: {
// const auto mode = static_cast<BOOL>(msg->wParam);
// *result = mode ? WVR_REDRAW : 0;
// const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(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;
if (d->borderless) {
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
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;
*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;
if (d->m_titleItem) {
auto titlePos = d->m_titleItem->mapToGlobal(d->m_titleItem->position());
titlePos = mapFromGlobal(titlePos.toPoint());
auto titleRect = QRect(titlePos.x(), titlePos.y(), d->m_titleItem->width(), d->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;
}
return false;
break;
} // end case WM_NCHITTEST
}
return QQuickView::nativeEvent(eventType, message, result);
return Super::nativeEvent(eventType, message, result);
}

View File

@ -1,3 +0,0 @@
#pragma once
#include <QtCore/qt_windows.h>

View File

@ -6,7 +6,7 @@ Loader {
property string homeUrl: qmlPath + "Page/Home.qml"
source: homeUrl
clip: true
Column {
spacing: 10
anchors {