Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2020-04-12 20:54:58 +08:00
parent 7f1f2dffc3
commit eefb3feb44
4 changed files with 84 additions and 25 deletions

25
BUGS.md Normal file
View File

@ -0,0 +1,25 @@
# 已知问题
## 拖拽窗口边框时窗口内容不能及时跟随,能看到窗口的白底
- 复现步骤:运行使用了样式表的*QWidget*程序(不使用样式表则无此问题);运行任意*Qt Quick*程序
- 原因:暂时未知
- 解决方案:对于*QWidget*程序,可以启用`Qt::WA_NoSystemBackground`属性,来禁用窗口默认的白色背景,但窗口背景会变成黑色,且无法改变,如果程序使用深色主题,则无大碍,如果使用浅色主题,体验应该不会太好。*Qt Quick*程序暂时无解。
## 拖拽窗口边框时能看到右上角的三个系统按钮/自绘内容消失(变白)
- 复现步骤:在窗口构造前执行`WinNativeEventFilter::install()`函数
- 原因:暂时未知
- 解决方案:不要全局安装过滤器,应针对每一个窗口,单独调用`addFramelessWindow`函数
## 自绘内容例如D3D错位或不能及时刷新
- 复现步骤在窗口中放置一个完全由Windows API绘制的控件
- 原因:暂时未知
- 解决方案:暂无
## resize后发现尺寸不对
- 复现步骤:在构造函数中调用`resize`函数;在安装过滤器之前调用`resize`函数
- 原因:构造函数中获取的几何位置和大小是不准确的;调用`resize`这一类的函数时Qt默认会考虑边框和标题栏的宽度但安装过滤器后边框和标题栏没了所以尺寸就不对了
- 解决方案:在控件或窗口显示出来以后再去获取或设置它们的几何位置及尺寸;先安装过滤器,再调整尺寸。

View File

@ -28,7 +28,9 @@
#include <QGuiApplication>
#include <QMargins>
#include <QVariant>
#ifdef QT_WIDGETS_LIB
#include <QWidget>
#endif
#include <QWindow>
#include <qpa/qplatformnativeinterface.h>
@ -37,8 +39,10 @@
#else
#include <QEvent>
#include <QMouseEvent>
#ifdef QT_WIDGETS_LIB
#include <QStyle>
#include <QStyleOption>
#endif
#include <QTouchEvent>
#endif
@ -58,6 +62,7 @@ FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {
// is 96. Don't know how to acquire these values on UNIX platforms.
m_borderWidth = 8;
m_borderHeight = 8;
#ifdef QT_WIDGETS_LIB
QWidget widget;
QStyleOption styleOption;
styleOption.initFrom(&widget);
@ -65,6 +70,9 @@ FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {
widget.style()->pixelMetric(QStyle::PixelMetric::PM_TitleBarHeight,
&styleOption) +
m_borderHeight;
#else
m_titlebarHeight = 30 + m_borderHeight;
#endif
qDebug().noquote() << "Window device pixel ratio:"
<< widget.devicePixelRatioF();
qDebug().noquote() << "Window border width:" << m_borderWidth
@ -208,7 +216,9 @@ void FramelessHelper::setFramelessWindows(const QVector<QObject *> &val) {
window->setFlags(flags);
// MouseTracking is always enabled for QWindow.
window->installEventFilter(this);
} else {
}
#ifdef QT_WIDGETS_LIB
else {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
widget->setWindowFlags(flags);
@ -218,6 +228,7 @@ void FramelessHelper::setFramelessWindows(const QVector<QObject *> &val) {
widget->installEventFilter(this);
}
}
#endif
#endif
}
}
@ -230,11 +241,14 @@ void FramelessHelper::setFramelessWindows(const QVector<QObject *> &val) {
bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
const auto isWindowTopLevel = [](QObject *window) -> bool {
if (window) {
if (window->isWidgetType()) {
return qobject_cast<QWidget *>(window)->isTopLevel();
} else if (window->isWindowType()) {
if (window->isWindowType()) {
return qobject_cast<QWindow *>(window)->isTopLevel();
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
return qobject_cast<QWidget *>(window)->isTopLevel();
}
#endif
}
return false;
};
@ -367,7 +381,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
}
window->setCursor(Qt::CursorShape::ArrowCursor);
}
} else if (object->isWidgetType()) {
}
#ifdef QT_WIDGETS_LIB
else if (object->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
if (widget->isFullScreen()) {
@ -381,6 +397,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
widget->setCursor(Qt::CursorShape::ArrowCursor);
}
}
#endif
}
}
break;
@ -406,7 +423,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
getWindowEdges(mouseEvent->localPos(), window->width(),
window->height())));
}
} else {
}
#ifdef QT_WIDGETS_LIB
else {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
if (!widget->isMinimized() && !widget->isMaximized() &&
@ -417,6 +436,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
}
}
}
#endif
}
break;
}
@ -443,12 +463,16 @@ QWindow *FramelessHelper::getWindowHandle(QObject *val) {
};
if (val->isWindowType()) {
return validWindow(qobject_cast<QWindow *>(val));
} else if (val->isWidgetType()) {
}
#ifdef QT_WIDGETS_LIB
else if (val->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(val);
if (widget) {
return validWindow(widget->windowHandle());
}
} else {
}
#endif
else {
qWarning().noquote() << "Can't acquire the window handle: only "
"QWidget and QWindow are accepted.";
}
@ -480,12 +504,15 @@ void *FramelessHelper::getWindowRawHandle(QObject *object) {
if (handle) {
return handle;
}
} else {
}
#ifdef QT_WIDGETS_LIB
else {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
return reinterpret_cast<void *>(widget->winId());
}
}
#endif
}
return nullptr;
}

View File

@ -18,11 +18,14 @@ int main(int argc, char *argv[]) {
#endif
#endif
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
QApplication application(argc, argv);
FramelessHelper helper;
QWidget widget;
widget.setAttribute(Qt::WA_DontCreateNativeAncestors);
helper.setFramelessWindows({&widget});
widget.show();

View File

@ -251,6 +251,20 @@ BOOL IsFullScreened(HWND handle) {
return FALSE;
}
BOOL IsWindowTopLevel(HWND handle) {
if (handle && m_lpIsWindow(handle)) {
if (m_lpGetWindowLongPtrW(handle, GWL_STYLE) & WS_CHILD) {
return FALSE;
}
const HWND parent = m_lpGetAncestor(handle, GA_PARENT);
if (parent && (parent != m_lpGetDesktopWindow())) {
return FALSE;
}
return TRUE;
}
return FALSE;
}
// The thickness of an auto-hide taskbar in pixels.
const int kAutoHideTaskbarThicknessPx = 2;
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
@ -400,13 +414,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
}
if (m_framelessWindows.isEmpty()) {
// Only top level windows can be frameless.
// Try to avoid this case because it will result in strange behavior,
// use addFramelessWindow if possible.
if (m_lpGetWindowLongPtrW(msg->hwnd, GWL_STYLE) & WS_CHILD) {
return false;
}
const HWND parent = m_lpGetAncestor(msg->hwnd, GA_PARENT);
if (parent && (parent != m_lpGetDesktopWindow())) {
if (!IsWindowTopLevel(msg->hwnd)) {
return false;
}
} else if (!m_framelessWindows.contains(msg->hwnd)) {
@ -456,7 +464,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// window. On exit, the structure should contain the screen coordinates
// of the corresponding window client area.
const auto getClientAreaInsets = [](HWND _hWnd) -> RECT {
if (IsMaximized(_hWnd)) {
if (IsMaximized(_hWnd) || IsFullScreened(_hWnd)) {
// Windows automatically adds a standard width border to all
// sides when a window is maximized.
int frameThickness_x = borderWidth(_hWnd);
@ -520,17 +528,13 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
m_lpSHAppBarMessage(ABM_GETAUTOHIDEBAREX, &_abd));
return hTaskbar != nullptr;
};
const bool onTop = hasAutohideTaskbar(ABE_TOP);
const bool onBottom = hasAutohideTaskbar(ABE_BOTTOM);
const bool onLeft = hasAutohideTaskbar(ABE_LEFT);
const bool onRight = hasAutohideTaskbar(ABE_RIGHT);
if (onTop) {
if (hasAutohideTaskbar(ABE_TOP)) {
clientRect->top += kAutoHideTaskbarThicknessPy;
} else if (onBottom) {
} else if (hasAutohideTaskbar(ABE_BOTTOM)) {
clientRect->bottom -= kAutoHideTaskbarThicknessPy;
} else if (onLeft) {
} else if (hasAutohideTaskbar(ABE_LEFT)) {
clientRect->left += kAutoHideTaskbarThicknessPx;
} else if (onRight) {
} else if (hasAutohideTaskbar(ABE_RIGHT)) {
clientRect->right -= kAutoHideTaskbarThicknessPx;
}
}