diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 0000000..3eb6c0d --- /dev/null +++ b/BUGS.md @@ -0,0 +1,25 @@ +# 已知问题 + +## 拖拽窗口边框时窗口内容不能及时跟随,能看到窗口的白底 + +- 复现步骤:运行使用了样式表的*QWidget*程序(不使用样式表则无此问题);运行任意*Qt Quick*程序 +- 原因:暂时未知 +- 解决方案:对于*QWidget*程序,可以启用`Qt::WA_NoSystemBackground`属性,来禁用窗口默认的白色背景,但窗口背景会变成黑色,且无法改变,如果程序使用深色主题,则无大碍,如果使用浅色主题,体验应该不会太好。*Qt Quick*程序暂时无解。 + +## 拖拽窗口边框时能看到右上角的三个系统按钮/自绘内容消失(变白) + +- 复现步骤:在窗口构造前执行`WinNativeEventFilter::install()`函数 +- 原因:暂时未知 +- 解决方案:不要全局安装过滤器,应针对每一个窗口,单独调用`addFramelessWindow`函数 + +## 自绘内容(例如D3D)错位或不能及时刷新 + +- 复现步骤:在窗口中放置一个完全由Windows API绘制的控件 +- 原因:暂时未知 +- 解决方案:暂无 + +## resize后发现尺寸不对 + +- 复现步骤:在构造函数中调用`resize`函数;在安装过滤器之前调用`resize`函数 +- 原因:构造函数中获取的几何位置和大小是不准确的;调用`resize`这一类的函数时,Qt默认会考虑边框和标题栏的宽度,但安装过滤器后,边框和标题栏没了,所以尺寸就不对了 +- 解决方案:在控件或窗口显示出来以后再去获取或设置它们的几何位置及尺寸;先安装过滤器,再调整尺寸。 diff --git a/framelesshelper.cpp b/framelesshelper.cpp index 2cd8084..c0fd8b0 100644 --- a/framelesshelper.cpp +++ b/framelesshelper.cpp @@ -28,7 +28,9 @@ #include #include #include +#ifdef QT_WIDGETS_LIB #include +#endif #include #include @@ -37,8 +39,10 @@ #else #include #include +#ifdef QT_WIDGETS_LIB #include #include +#endif #include #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 &val) { window->setFlags(flags); // MouseTracking is always enabled for QWindow. window->installEventFilter(this); - } else { + } +#ifdef QT_WIDGETS_LIB + else { const auto widget = qobject_cast(object); if (widget) { widget->setWindowFlags(flags); @@ -218,6 +228,7 @@ void FramelessHelper::setFramelessWindows(const QVector &val) { widget->installEventFilter(this); } } +#endif #endif } } @@ -230,11 +241,14 @@ void FramelessHelper::setFramelessWindows(const QVector &val) { bool FramelessHelper::eventFilter(QObject *object, QEvent *event) { const auto isWindowTopLevel = [](QObject *window) -> bool { if (window) { - if (window->isWidgetType()) { - return qobject_cast(window)->isTopLevel(); - } else if (window->isWindowType()) { + if (window->isWindowType()) { return qobject_cast(window)->isTopLevel(); } +#ifdef QT_WIDGETS_LIB + else if (window->isWidgetType()) { + return qobject_cast(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(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(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(val)); - } else if (val->isWidgetType()) { + } +#ifdef QT_WIDGETS_LIB + else if (val->isWidgetType()) { const auto widget = qobject_cast(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(object); if (widget) { return reinterpret_cast(widget->winId()); } } +#endif } return nullptr; } diff --git a/main.cpp b/main.cpp index 354007d..d4a7579 100644 --- a/main.cpp +++ b/main.cpp @@ -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(); diff --git a/winnativeeventfilter.cpp b/winnativeeventfilter.cpp index e0a917b..371d7b7 100644 --- a/winnativeeventfilter.cpp +++ b/winnativeeventfilter.cpp @@ -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; } }