From 7753161f9f3dc395a66393065a50c1c46dcbad92 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Fri, 8 May 2020 14:19:41 +0800 Subject: [PATCH] Add Qt Quick example. Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- README.md | 60 +-- framelesshelper.cpp | 522 --------------------- framelesshelper.h | 103 ---- framelesshelper.pro | 25 +- main.cpp | 148 +++++- resources.qrc | 18 + resources.rc | 10 +- resources/images/button_close_black.svg | 6 + resources/images/button_close_white.svg | 6 + resources/images/button_maximize_black.svg | 3 + resources/images/button_maximize_white.svg | 3 + resources/images/button_minimize_black.svg | 3 + resources/images/button_minimize_white.svg | 3 + resources/images/button_restore_black.svg | 11 + resources/images/button_restore_white.svg | 11 + resources/qml/CloseButton.qml | 24 + resources/qml/MaximizeButton.qml | 25 + resources/qml/MinimizeButton.qml | 23 + resources/qml/main.qml | 64 +++ winnativeeventfilter.cpp | 67 +-- winnativeeventfilter.h | 6 +- 21 files changed, 414 insertions(+), 727 deletions(-) delete mode 100644 framelesshelper.cpp delete mode 100644 framelesshelper.h create mode 100644 resources.qrc create mode 100644 resources/images/button_close_black.svg create mode 100644 resources/images/button_close_white.svg create mode 100644 resources/images/button_maximize_black.svg create mode 100644 resources/images/button_maximize_white.svg create mode 100644 resources/images/button_minimize_black.svg create mode 100644 resources/images/button_minimize_white.svg create mode 100644 resources/images/button_restore_black.svg create mode 100644 resources/images/button_restore_white.svg create mode 100644 resources/qml/CloseButton.qml create mode 100644 resources/qml/MaximizeButton.qml create mode 100644 resources/qml/MinimizeButton.qml create mode 100644 resources/qml/main.qml diff --git a/README.md b/README.md index b4a1cac..89427ff 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,19 @@ ## Features -- Frameless but have frame shadow (especially on Windows). +- Frameless but have frame shadow. - Draggable and resizeable. - Automatically high DPI scaling. - Multi-monitor support (different resolution and DPI). -- Have animations when minimizing and maximizing (Windows). -- Tiled/Stack windows by DWM (Windows). -- Won't cover the task bar when maximized (Windows). -- Won't block the auto-hide task bar when maximized (Windows). -- No flickers when resizing (Windows). -- Load all APIs at run-time, no need to link to any system libraries directly (Windows). +- Have animations when minimizing and maximizing. +- Tiled/Stack windows by DWM. +- Won't cover the task bar when maximized. +- Won't block the auto-hide task bar when maximized. +- No flickers when resizing. +- Load all APIs at run-time, no need to link to any system libraries directly. ## Usage -### Windows - ```cpp // include other files ... #include "winnativeeventfilter.h" @@ -37,7 +35,9 @@ int main(int argc, char *argv[]) { } ``` -#### Ignore areas and etc +Please refer to [**main.cpp**](/main.cpp) for more detailed information. + +### Ignore areas and etc ```cpp WinNativeEventFilter::WINDOWDATA data; @@ -73,43 +73,15 @@ data.borderHeight = 5; data.titlebarHeight = 30; ``` -### Linux and macOS - -```cpp -// include other files ... -#include "framelesshelper.h" -// include other files ... - -// Anywhere you like, we use the main function here. -int main(int argc, char *argv[]) { - // ... - QWidget widget; - FramelessHelper helper; - // Do this before the widget is shown. - // Only QWidget and QWindow are supported. - helper.setFramelessWindows({&widget}); - widget.show(); - // ... -} -``` - -Notes - -- The `setFramelessWindows`/`addFramelessWindow` function must not be called after the widget is shown. However, for `QWindow`, it must be called after they are shown. -- I use `startSystemMove` and `startSystemResize` which are only available since Qt 5.15 for moving and resizing frameless windows on UNIX platforms, so if your Qt version is below that, you can't compile this code. I'm sorry for it but using the two functions is the easiest way to achieve this. -- Any widgets (or Qt Quick elements) in the titlebar or resize area will not respond to any mouse events because they are intercepted by my code. If you want to add somewhere or something to the ignore list, try change the window data's `ignoreAreas`, `draggableAreas`, `ignoreObjects` and `draggableObjects`. -- Only top level windows can be frameless. Applying this code to child windows or widgets or even something that is not a window will result in unexpected behavior. Don't try to do this! -- If you want to use your own border width, border height, titlebar height or maximum/minimum window size, just use the original numbers, no need to scale them according to DPI, this code will do the scaling automatically. - ## Supported Platforms -- Windows 7 ~ 10 -- Should work on X11, Wayland and macOS, but not tested. +Windows 7 ~ 10, 32 bit & 64 bit -## Notes for Windows developers +The code itself should be able to work on Windows Vista in theory, but Qt has drop Vista support long time ago. -- The `FramelessHelper` class is just a simple wrapper of the `WinNativeEventFilter` class and the former is mainly designed to work on UNIX platforms. So if you are developing Windows applications, you should use the latter instead. -- **As you may have found, if you use this code, the resize areas will be inside the frameless window, however, a normal Win32 window can be resized outside of it.** Here is the reason: the `WS_THICKFRAME` window style will cause a window has three transparent areas beside the window's left, right and bottom edge. Their width/height is 8px if the window is not scaled. In most cases, they are totally invisible. It's DWM's responsibility to draw and control them. They exist to let the user resize the window, visually outside of it. They are in the window area, but not the client area, so they are in the non-client area actually. But we have turned the whole window area into client area in `WM_NCCALCSIZE`, so the three transparent resize areas also become a part of the client area and thus they become visible. When we resize the window, it looks like we are resizing inside of it, however, that's because the transparent resize areas are visible now, we ARE resizing outside of the window actually. But I don't know how to make them become transparent again without breaking the frame shadow drawn by DWM. If you really want to solve it, you can try to embed your window into a larger transparent window and draw the frame shadow yourself. [See the discussions here](https://github.com/wangwenx190/framelesshelper/issues/3) for more detailed information. +## Notes for developers + +- As you may have found, if you use this code, the resize areas will be inside the frameless window, however, a normal Win32 window can be resized outside of it. Here is the reason: the `WS_THICKFRAME` window style will cause a window has three transparent areas beside the window's left, right and bottom edge. Their width/height is 8px if the window is not scaled. In most cases, they are totally invisible. It's DWM's responsibility to draw and control them. They exist to let the user resize the window, visually outside of it. They are in the window area, but not the client area, so they are in the non-client area actually. But we have turned the whole window area into client area in `WM_NCCALCSIZE`, so the three transparent resize areas also become a part of the client area and thus they become visible. When we resize the window, it looks like we are resizing inside of it, however, that's because the transparent resize areas are visible now, we ARE resizing outside of the window actually. But I don't know how to make them become transparent again without breaking the frame shadow drawn by DWM. If you really want to solve it, you can try to embed your window into a larger transparent window and draw the frame shadow yourself. [See the discussions here](https://github.com/wangwenx190/framelesshelper/issues/3) for more detailed information. - Don't change the window flags (for example, enable the Qt::FramelessWindowHint flag) because it will break the functionality of this code. I'll get rid of the window frame (including the titlebar of course) in Win32 native events. - All traditional Win32 APIs are replaced by their DPI-aware ones, if there is one. - Starting from Windows 10, normal windows usually have a one pixel width border line. After many times of trying, I still can't preserve it if I want to remove the window frame. I don't know how to solve this currently. If you really need it, you have to draw one manually by yourself. [See the discussions here](https://github.com/wangwenx190/framelesshelper/issues/3) for more detailed information. @@ -118,7 +90,7 @@ Notes - The border width (8 if not scaled), border height (8 if not scaled) and titlebar height (30 if not scaled) are acquired by Win32 APIs and are the same with other standard windows, and thus you should not modify them. Only modify them when you really have a good reason to do so. - You can also copy all the code to `[virtual protected] bool QWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)` or `[virtual protected] bool QWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)`, it's the same with install a native event filter to the application. -## References for Windows developers +## References for developers ### Microsoft Docs diff --git a/framelesshelper.cpp b/framelesshelper.cpp deleted file mode 100644 index 588dca6..0000000 --- a/framelesshelper.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2020 by wangwenx190 (Yuhang Zhao) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "framelesshelper.h" - -#include -#include -#include -#include -#ifdef QT_WIDGETS_LIB -#include -#endif -#include -#include - -#ifdef Q_OS_WINDOWS -#include "winnativeeventfilter.h" -#else -#include -#include -#ifdef QT_WIDGETS_LIB -#include -#include -#endif -#include -#endif - -Q_DECLARE_METATYPE(QMargins) - -FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) { - connect(this, &FramelessHelper::titlebarHeightChanged, this, - &FramelessHelper::updateQtFrame_internal); - connect(this, &FramelessHelper::framelessWindowsChanged, - [this]() { updateQtFrame_internal(m_titlebarHeight); }); -#ifdef Q_OS_WINDOWS - m_borderWidth = WinNativeEventFilter::getSystemMetric( - nullptr, WinNativeEventFilter::SystemMetric::BorderWidth, false); - m_borderHeight = WinNativeEventFilter::getSystemMetric( - nullptr, WinNativeEventFilter::SystemMetric::BorderHeight, false); - m_titlebarHeight = WinNativeEventFilter::getSystemMetric( - nullptr, WinNativeEventFilter::SystemMetric::TitleBarHeight, false); -#else - // TODO: The default border width and height on Windows is 8 pixels if DPI - // 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); - m_titlebarHeight = - 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 - << "Window border height:" << m_borderHeight - << "Window titlebar height:" << m_titlebarHeight; -#endif - updateQtFrame_internal(m_titlebarHeight); -} - -void FramelessHelper::updateQtFrame(QWindow *window, int titlebarHeight) { - if (window && (titlebarHeight > 0)) { - // Reduce top frame to zero since we paint it ourselves. Use - // device pixel to avoid rounding errors. - const QMargins margins = {0, -titlebarHeight, 0, 0}; - const QVariant marginsVar = QVariant::fromValue(margins); - // The dynamic property takes effect when creating the platform - // window. - window->setProperty("_q_windowsCustomMargins", marginsVar); - // If a platform window exists, change via native interface. - QPlatformWindow *platformWindow = window->handle(); - if (platformWindow) { - QGuiApplication::platformNativeInterface()->setWindowProperty( - platformWindow, QString::fromUtf8("WindowsCustomMargins"), - marginsVar); - } - } -} - -int FramelessHelper::borderWidth() const { return m_borderWidth; } - -void FramelessHelper::setBorderWidth(int val) { - if (m_borderWidth != val) { - m_borderWidth = val; -#ifdef Q_OS_WINDOWS - WinNativeEventFilter::setBorderWidth(val); -#endif - Q_EMIT borderWidthChanged(val); - } -} - -int FramelessHelper::borderHeight() const { return m_borderHeight; } - -void FramelessHelper::setBorderHeight(int val) { - if (m_borderHeight != val) { - m_borderHeight = val; -#ifdef Q_OS_WINDOWS - WinNativeEventFilter::setBorderHeight(val); -#endif - Q_EMIT borderHeightChanged(val); - } -} - -int FramelessHelper::titlebarHeight() const { return m_titlebarHeight; } - -void FramelessHelper::setTitlebarHeight(int val) { - if (m_titlebarHeight != val) { - m_titlebarHeight = val; -#ifdef Q_OS_WINDOWS - WinNativeEventFilter::setTitlebarHeight(val); -#endif - Q_EMIT titlebarHeightChanged(val); - } -} - -FramelessHelper::Areas FramelessHelper::ignoreAreas() const { - return m_ignoreAreas; -} - -void FramelessHelper::setIgnoreAreas(const Areas &val) { - if (m_ignoreAreas != val) { - m_ignoreAreas = val; -#ifdef Q_OS_WINDOWS - auto iter = val.cbegin(); - while (iter != val.cend()) { - if (iter.key()) { - const auto hwnd = - static_cast(getWindowRawHandle(iter.key())); - if (hwnd) { - const auto data = WinNativeEventFilter::windowData(hwnd); - data->ignoreAreas = iter.value(); - } - } - ++iter; - } -#endif - Q_EMIT ignoreAreasChanged(val); - } -} - -FramelessHelper::Areas FramelessHelper::draggableAreas() const { - return m_draggableAreas; -} - -void FramelessHelper::setDraggableAreas(const Areas &val) { - if (m_draggableAreas != val) { - m_draggableAreas = val; -#ifdef Q_OS_WINDOWS - auto iter = val.cbegin(); - while (iter != val.cend()) { - if (iter.key()) { - const auto hwnd = - static_cast(getWindowRawHandle(iter.key())); - if (hwnd) { - const auto data = WinNativeEventFilter::windowData(hwnd); - data->draggableAreas = iter.value(); - } - } - ++iter; - } -#endif - Q_EMIT draggableAreasChanged(val); - } -} - -QVector FramelessHelper::framelessWindows() const { - return m_framelessWindows; -} - -void FramelessHelper::setFramelessWindows(const QVector &val) { - if (m_framelessWindows != val) { - m_framelessWindows = val; - if (!val.isEmpty()) { - for (auto &&object : qAsConst(val)) { - if (object) { -#ifdef Q_OS_WINDOWS - const auto hwnd = - static_cast(getWindowRawHandle(object)); - if (hwnd) { - WinNativeEventFilter::addFramelessWindow(hwnd); - } else { - qWarning().noquote() - << "Can't make the window frameless: failed to " - "acquire the window handle."; - } -#else - // Don't miss the Qt::Window flag. - const Qt::WindowFlags flags = - Qt::Window | Qt::FramelessWindowHint; - QWindow *window = getWindowHandle(object); - if (window) { - window->setFlags(flags); - // MouseTracking is always enabled for QWindow. - window->installEventFilter(this); - } -#ifdef QT_WIDGETS_LIB - else { - const auto widget = qobject_cast(object); - if (widget) { - widget->setWindowFlags(flags); - // We can't get MouseMove events if MouseTracking is - // disabled. - widget->setMouseTracking(true); - widget->installEventFilter(this); - } - } -#endif -#endif - } - } - } - Q_EMIT framelessWindowsChanged(val); - } -} - -#ifndef Q_OS_WINDOWS -bool FramelessHelper::eventFilter(QObject *object, QEvent *event) { - const auto isWindowTopLevel = [](QObject *window) -> bool { - if (window) { - if (window->isWindowType()) { - return qobject_cast(window)->isTopLevel(); - } -#ifdef QT_WIDGETS_LIB - else if (window->isWidgetType()) { - return qobject_cast(window)->isTopLevel(); - } -#endif - } - return false; - }; - if (!object || !isWindowTopLevel(object)) { - event->ignore(); - return false; - } - const auto getWindowEdges = [this](const QPointF &point, int ww, - int wh) -> Qt::Edges { - if (point.y() < m_borderHeight) { - if (point.x() < m_borderWidth) { - return Qt::Edge::TopEdge | Qt::Edge::LeftEdge; - } - if (point.x() > (ww - m_borderWidth)) { - return Qt::Edge::TopEdge | Qt::Edge::RightEdge; - } - return Qt::Edge::TopEdge; - } - if (point.y() > (wh - m_borderHeight)) { - if (point.x() < m_borderWidth) { - return Qt::Edge::BottomEdge | Qt::Edge::LeftEdge; - } - if (point.x() > (ww - m_borderWidth)) { - return Qt::Edge::BottomEdge | Qt::Edge::RightEdge; - } - return Qt::Edge::BottomEdge; - } - if (point.x() < m_borderWidth) { - return Qt::Edge::LeftEdge; - } - if (point.x() > (ww - m_borderWidth)) { - return Qt::Edge::RightEdge; - } - return {}; - }; - const auto getCursorShape = [](Qt::Edges edges) -> Qt::CursorShape { - if ((edges.testFlag(Qt::Edge::TopEdge) && - edges.testFlag(Qt::Edge::LeftEdge)) || - (edges.testFlag(Qt::Edge::BottomEdge) && - edges.testFlag(Qt::Edge::RightEdge))) { - return Qt::CursorShape::SizeFDiagCursor; - } - if ((edges.testFlag(Qt::Edge::TopEdge) && - edges.testFlag(Qt::Edge::RightEdge)) || - (edges.testFlag(Qt::Edge::BottomEdge) && - edges.testFlag(Qt::Edge::LeftEdge))) { - return Qt::CursorShape::SizeBDiagCursor; - } - if (edges.testFlag(Qt::Edge::TopEdge) || - edges.testFlag(Qt::Edge::BottomEdge)) { - return Qt::CursorShape::SizeVerCursor; - } - if (edges.testFlag(Qt::Edge::LeftEdge) || - edges.testFlag(Qt::Edge::RightEdge)) { - return Qt::CursorShape::SizeHorCursor; - } - return Qt::CursorShape::ArrowCursor; - }; - const auto isInSpecificAreas = [](int x, int y, - const QVector &areas) -> bool { - for (auto &&area : qAsConst(areas)) { - if (area.contains(x, y, true)) { - return true; - } - } - return false; - }; - const auto isInTitlebarArea = - [this, &isInSpecificAreas](const QPointF &point, - QObject *window) -> bool { - if (window) { - return (point.y() < m_titlebarHeight) && - !isInSpecificAreas(point.x(), point.y(), - m_ignoreAreas.value(window)) && - (m_draggableAreas.isEmpty() - ? true - : isInSpecificAreas(point.x(), point.y(), - m_draggableAreas.value(window))); - } - return false; - }; - const auto moveOrResize = [this, &getWindowEdges, &isInTitlebarArea]( - const QPointF &point, QObject *object) { - QWindow *window = getWindowHandle(object); - if (window) { - const Qt::Edges edges = - getWindowEdges(point, window->width(), window->height()); - if (edges == Qt::Edges{}) { - if (isInTitlebarArea(point, object)) { - window->startSystemMove(); - } - } else { - if (window->windowStates().testFlag( - Qt::WindowState::WindowNoState)) { - window->startSystemResize(edges); - } - } - } else { - qWarning().noquote() << "Can't move or resize the window: failed " - "to acquire the window handle."; - } - }; - switch (event->type()) { - case QEvent::MouseButtonDblClick: { - const auto mouseEvent = static_cast(event); - if (mouseEvent) { - if (mouseEvent->button() != Qt::MouseButton::LeftButton) { - break; - } - if (isInTitlebarArea(mouseEvent->localPos(), object)) { - // FIXME: If the current object is a QWidget, we can use - // getWindowHandle(object) to get the window handle, but if we - // call showMaximized() of that window, it will not be - // maximized, it will be moved to the top-left edge of the - // screen without changing it's size instead. Why? Convert the - // object to QWidget and call showMaximized() doesn't have this - // issue. - if (object->isWindowType()) { - const auto window = qobject_cast(object); - if (window) { - if (window->windowStates().testFlag( - Qt::WindowState::WindowFullScreen)) { - break; - } - if (window->windowStates().testFlag( - Qt::WindowState::WindowMaximized)) { - window->showNormal(); - } else { - window->showMaximized(); - } - window->setCursor(Qt::CursorShape::ArrowCursor); - } - } -#ifdef QT_WIDGETS_LIB - else if (object->isWidgetType()) { - const auto widget = qobject_cast(object); - if (widget) { - if (widget->isFullScreen()) { - break; - } - if (widget->isMaximized()) { - widget->showNormal(); - } else { - widget->showMaximized(); - } - widget->setCursor(Qt::CursorShape::ArrowCursor); - } - } -#endif - } - } - break; - } - case QEvent::MouseButtonPress: { - const auto mouseEvent = static_cast(event); - if (mouseEvent) { - if (mouseEvent->button() != Qt::MouseButton::LeftButton) { - break; - } - moveOrResize(mouseEvent->localPos(), object); - } - break; - } - case QEvent::MouseMove: { - const auto mouseEvent = static_cast(event); - if (mouseEvent) { - QWindow *window = getWindowHandle(object); - if (window) { - if (window->windowStates().testFlag( - Qt::WindowState::WindowNoState)) { - window->setCursor(getCursorShape( - getWindowEdges(mouseEvent->localPos(), window->width(), - window->height()))); - } - } -#ifdef QT_WIDGETS_LIB - else { - const auto widget = qobject_cast(object); - if (widget) { - if (!widget->isMinimized() && !widget->isMaximized() && - !widget->isFullScreen()) { - widget->setCursor(getCursorShape( - getWindowEdges(mouseEvent->localPos(), - widget->width(), widget->height()))); - } - } - } -#endif - } - break; - } - case QEvent::TouchBegin: - case QEvent::TouchUpdate: { - moveOrResize( - static_cast(event)->touchPoints().first().pos(), - object); - break; - } - default: { - break; - } - } - event->ignore(); - return false; -} -#endif - -QWindow *FramelessHelper::getWindowHandle(QObject *val) { - if (val) { - const auto validWindow = [](QWindow *window) -> QWindow * { - return (window && window->handle()) ? window : nullptr; - }; - if (val->isWindowType()) { - return validWindow(qobject_cast(val)); - } -#ifdef QT_WIDGETS_LIB - else if (val->isWidgetType()) { - const auto widget = qobject_cast(val); - if (widget) { - return validWindow(widget->windowHandle()); - } - } -#endif - else { - qWarning().noquote() << "Can't acquire the window handle: only " - "QWidget and QWindow are accepted."; - } - } - return nullptr; -} - -void FramelessHelper::updateQtFrame_internal(int val) { - if (!m_framelessWindows.isEmpty()) { - for (auto &&object : qAsConst(m_framelessWindows)) { - QWindow *window = getWindowHandle(object); - if (window) { - updateQtFrame(window, val); - } else { - qWarning().noquote() << "Can't modify the window frame: failed " - "to acquire the window handle."; - } - } - } -} - -#ifdef Q_OS_WINDOWS -void *FramelessHelper::getWindowRawHandle(QObject *object) { - if (object) { - QWindow *window = getWindowHandle(object); - if (window) { - const auto handle = QGuiApplication::platformNativeInterface() - ->nativeResourceForWindow("handle", window); - if (handle) { - return handle; - } - } -#ifdef QT_WIDGETS_LIB - else { - const auto widget = qobject_cast(object); - if (widget) { - return reinterpret_cast(widget->winId()); - } - } -#endif - } - return nullptr; -} -#endif diff --git a/framelesshelper.h b/framelesshelper.h deleted file mode 100644 index b995482..0000000 --- a/framelesshelper.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2020 by wangwenx190 (Yuhang Zhao) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -QT_FORWARD_DECLARE_CLASS(QWindow) -QT_END_NAMESPACE - -class FramelessHelper : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(FramelessHelper) - - Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY - borderWidthChanged) - Q_PROPERTY(int borderHeight READ borderHeight WRITE setBorderHeight NOTIFY - borderHeightChanged) - Q_PROPERTY(int titlebarHeight READ titlebarHeight WRITE setTitlebarHeight - NOTIFY titlebarHeightChanged) - Q_PROPERTY(Areas ignoreAreas READ ignoreAreas WRITE setIgnoreAreas NOTIFY - ignoreAreasChanged) - Q_PROPERTY(Areas draggableAreas READ draggableAreas WRITE setDraggableAreas - NOTIFY draggableAreasChanged) - Q_PROPERTY(QVector framelessWindows READ framelessWindows WRITE - setFramelessWindows NOTIFY framelessWindowsChanged) - -public: - using Areas = QHash>; - - explicit FramelessHelper(QObject *parent = nullptr); - ~FramelessHelper() override = default; - - static void updateQtFrame(QWindow *window, int titlebarHeight); - - int borderWidth() const; - void setBorderWidth(int val); - - int borderHeight() const; - void setBorderHeight(int val); - - int titlebarHeight() const; - void setTitlebarHeight(int val); - - Areas ignoreAreas() const; - void setIgnoreAreas(const Areas &val); - - Areas draggableAreas() const; - void setDraggableAreas(const Areas &val); - - QVector framelessWindows() const; - void setFramelessWindows(const QVector &val); - -protected: -#ifndef Q_OS_WINDOWS - bool eventFilter(QObject *object, QEvent *event) override; -#endif - -private: - QWindow *getWindowHandle(QObject *val); -#ifdef Q_OS_WINDOWS - void *getWindowRawHandle(QObject *object); -#endif - void updateQtFrame_internal(int val); - -Q_SIGNALS: - void borderWidthChanged(int); - void borderHeightChanged(int); - void titlebarHeightChanged(int); - void ignoreAreasChanged(const Areas &); - void draggableAreasChanged(const Areas &); - void framelessWindowsChanged(const QVector &); - -private: - int m_borderWidth = -1, m_borderHeight = -1, m_titlebarHeight = -1; - Areas m_ignoreAreas, m_draggableAreas; - QVector m_framelessWindows; -}; diff --git a/framelesshelper.pro b/framelesshelper.pro index 669ded9..1288778 100644 --- a/framelesshelper.pro +++ b/framelesshelper.pro @@ -1,16 +1,11 @@ -TARGET = framelesswidget +TARGET = framelessapplication TEMPLATE = app -QT += gui-private widgets -CONFIG += c++17 strict_c++ utf8_source warn_on -win32 { - DEFINES += WIN32_LEAN_AND_MEAN - CONFIG += windeployqt - CONFIG -= embed_manifest_exe - RC_FILE = resources.rc - HEADERS += winnativeeventfilter.h - SOURCES += winnativeeventfilter.cpp - OTHER_FILES += manifest.xml -} -DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII -HEADERS += framelesshelper.h -SOURCES += framelesshelper.cpp main.cpp +QT += gui-private widgets quick +CONFIG += c++17 strict_c++ utf8_source warn_on windeployqt +DEFINES += WIN32_LEAN_AND_MEAN QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII +CONFIG -= embed_manifest_exe +RC_FILE = resources.rc +HEADERS += winnativeeventfilter.h +SOURCES += winnativeeventfilter.cpp main.cpp +RESOURCES += resources.qrc +OTHER_FILES += manifest.xml diff --git a/main.cpp b/main.cpp index 68a89ec..931b2fc 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,59 @@ +#include "winnativeeventfilter.h" #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include -#include "framelesshelper.h" +Q_DECLARE_METATYPE(QMargins) + +static const int m_defaultTitleBarHeight = 30; +static const int m_defaultButtonWidth = 45; + +static void updateQtFrame(QWindow *const window, const int titleBarHeight) { + if (window && (titleBarHeight > 0)) { + // Reduce top frame to zero since we paint it ourselves. Use + // device pixel to avoid rounding errors. + const QMargins margins = {0, -titleBarHeight, 0, 0}; + const QVariant marginsVar = QVariant::fromValue(margins); + // The dynamic property takes effect when creating the platform + // window. + window->setProperty("_q_windowsCustomMargins", marginsVar); + // If a platform window exists, change via native interface. + QPlatformWindow *platformWindow = window->handle(); + if (platformWindow) { + QGuiApplication::platformNativeInterface()->setWindowProperty( + platformWindow, QString::fromUtf8("WindowsCustomMargins"), + marginsVar); + } + } +} + +class MyQuickView : public QQuickView { + Q_OBJECT + Q_DISABLE_COPY_MOVE(MyQuickView) + +public: + explicit MyQuickView(QWindow *parent = nullptr) : QQuickView(parent) { + setResizeMode(QQuickView::ResizeMode::SizeRootObjectToView); + } + ~MyQuickView() override = default; + +protected: + void resizeEvent(QResizeEvent *event) override { + QQuickView::resizeEvent(event); + Q_EMIT windowSizeChanged(event->size()); + } + +Q_SIGNALS: + void windowSizeChanged(const QSize &); +}; int main(int argc, char *argv[]) { // High DPI scaling is enabled by default from Qt 6 @@ -32,11 +84,99 @@ int main(int argc, char *argv[]) { QApplication application(argc, argv); - FramelessHelper helper; - + // Qt Widgets example: QWidget widget; - helper.setFramelessWindows({&widget}); + widget.setContentsMargins(0, 0, 0, 0); + QLabel label; + label.setText(QObject::tr("Hello, World!")); + QObject::connect(&widget, &QWidget::windowTitleChanged, &label, + &QLabel::setText); + QPushButton minimizeButton; + minimizeButton.setText(QObject::tr("Minimize")); + QObject::connect(&minimizeButton, &QPushButton::clicked, &widget, + &QWidget::showMinimized); + QPushButton maximizeButton; + maximizeButton.setText(QObject::tr("Maximize")); + QObject::connect(&maximizeButton, &QPushButton::clicked, + [&widget, &maximizeButton]() { + if (widget.isMaximized()) { + widget.showNormal(); + maximizeButton.setText(QObject::tr("Maximize")); + } else { + widget.showMaximized(); + maximizeButton.setText(QObject::tr("Restore")); + } + }); + QPushButton closeButton; + closeButton.setText(QObject::tr("Close")); + QObject::connect(&closeButton, &QPushButton::clicked, &widget, + &QWidget::close); + QHBoxLayout tbLayout; + tbLayout.setContentsMargins(0, 0, 0, 0); + tbLayout.setSpacing(0); + tbLayout.addSpacing(15); + tbLayout.addWidget(&label); + tbLayout.addStretch(); + tbLayout.addWidget(&minimizeButton); + tbLayout.addWidget(&maximizeButton); + tbLayout.addWidget(&closeButton); + QVBoxLayout mainLayout; + mainLayout.setContentsMargins(0, 0, 0, 0); + mainLayout.setSpacing(0); + mainLayout.addLayout(&tbLayout); + mainLayout.addStretch(); + widget.setLayout(&mainLayout); + WinNativeEventFilter::WINDOWDATA data_widget; + data_widget.ignoreObjects << &minimizeButton << &maximizeButton + << &closeButton; + const auto hWnd_widget = reinterpret_cast(widget.winId()); + const int tbh_widget = WinNativeEventFilter::getSystemMetric( + hWnd_widget, WinNativeEventFilter::SystemMetric::TitleBarHeight, false); + updateQtFrame(widget.windowHandle(), + (tbh_widget > 0 ? tbh_widget : m_defaultTitleBarHeight)); + widget.resize(800, 600); + WinNativeEventFilter::addFramelessWindow(hWnd_widget, &data_widget, true); widget.show(); + // Qt Quick example: + MyQuickView view; + const auto hWnd_qml = reinterpret_cast(view.winId()); + const int tbh_qml_sys = WinNativeEventFilter::getSystemMetric( + hWnd_qml, WinNativeEventFilter::SystemMetric::TitleBarHeight, false); + const int tbh_qml = tbh_qml_sys > 0 ? tbh_qml_sys : m_defaultTitleBarHeight; + updateQtFrame(&view, tbh_qml); + view.rootContext()->setContextProperty(QString::fromUtf8("$TitleBarHeight"), + tbh_qml); + view.setSource(QUrl(QString::fromUtf8("qrc:///qml/main.qml"))); + QObject::connect( + &view, &MyQuickView::windowSizeChanged, [hWnd_qml](const QSize &size) { + const auto data = WinNativeEventFilter::windowData(hWnd_qml); + if (data) { + const int tbh_qml = WinNativeEventFilter::getSystemMetric( + hWnd_qml, + WinNativeEventFilter::SystemMetric::TitleBarHeight, false); + data->draggableAreas = { + {0, 0, (size.width() - (m_defaultButtonWidth * 3)), + tbh_qml}}; + } + }); + const QQuickItem *const rootObject = view.rootObject(); + Q_ASSERT(rootObject); + // We can't use the Qt5 syntax here because we can't get the function + // pointers of the signals written in QML. + QObject::connect(rootObject, SIGNAL(minimizeButtonClicked()), &view, + SLOT(showMinimized())); + QObject::connect(rootObject, SIGNAL(maximizeButtonClicked()), &view, + SLOT(showMaximized())); + QObject::connect(rootObject, SIGNAL(restoreButtonClicked()), &view, + SLOT(showNormal())); + QObject::connect(rootObject, SIGNAL(closeButtonClicked()), &view, + SLOT(close())); + view.resize(800, 600); + WinNativeEventFilter::addFramelessWindow(hWnd_qml, nullptr, true); + view.show(); + return QApplication::exec(); } + +#include "main.moc" diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..c8da852 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,18 @@ + + + resources/qml/main.qml + resources/qml/MinimizeButton.qml + resources/qml/MaximizeButton.qml + resources/qml/CloseButton.qml + + + resources/images/button_minimize_black.svg + resources/images/button_minimize_white.svg + resources/images/button_maximize_black.svg + resources/images/button_maximize_white.svg + resources/images/button_restore_black.svg + resources/images/button_restore_white.svg + resources/images/button_close_black.svg + resources/images/button_close_white.svg + + diff --git a/resources.rc b/resources.rc index db45f73..3bd0ec2 100644 --- a/resources.rc +++ b/resources.rc @@ -21,16 +21,16 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "Built by Qt Toolkit." + VALUE "Comments", "Built by the Qt Toolkit." VALUE "CompanyName", "wangwenx190" - VALUE "FileDescription", "A frameless widget based Qt application." + VALUE "FileDescription", "A framelesshelper based Qt application." VALUE "FileVersion", "1.0.0.0" - VALUE "InternalName", "flw-test-app" + VALUE "InternalName", "flh-test-app" VALUE "LegalCopyright", "MIT License" #ifdef _DEBUG - VALUE "OriginalFilename", "framelesswidgetd.exe" + VALUE "OriginalFilename", "framelessapplicationd.exe" #else - VALUE "OriginalFilename", "framelesswidget.exe" + VALUE "OriginalFilename", "framelessapplication.exe" #endif VALUE "ProductName", "Test Application" VALUE "ProductVersion", "1.0.0.0" diff --git a/resources/images/button_close_black.svg b/resources/images/button_close_black.svg new file mode 100644 index 0000000..e73db96 --- /dev/null +++ b/resources/images/button_close_black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/button_close_white.svg b/resources/images/button_close_white.svg new file mode 100644 index 0000000..dc9cf3e --- /dev/null +++ b/resources/images/button_close_white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/button_maximize_black.svg b/resources/images/button_maximize_black.svg new file mode 100644 index 0000000..f9c7aa9 --- /dev/null +++ b/resources/images/button_maximize_black.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/button_maximize_white.svg b/resources/images/button_maximize_white.svg new file mode 100644 index 0000000..4a60724 --- /dev/null +++ b/resources/images/button_maximize_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/button_minimize_black.svg b/resources/images/button_minimize_black.svg new file mode 100644 index 0000000..e0d19ab --- /dev/null +++ b/resources/images/button_minimize_black.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/button_minimize_white.svg b/resources/images/button_minimize_white.svg new file mode 100644 index 0000000..f7a2698 --- /dev/null +++ b/resources/images/button_minimize_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/button_restore_black.svg b/resources/images/button_restore_black.svg new file mode 100644 index 0000000..5521964 --- /dev/null +++ b/resources/images/button_restore_black.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/button_restore_white.svg b/resources/images/button_restore_white.svg new file mode 100644 index 0000000..de337f6 --- /dev/null +++ b/resources/images/button_restore_white.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/qml/CloseButton.qml b/resources/qml/CloseButton.qml new file mode 100644 index 0000000..2983632 --- /dev/null +++ b/resources/qml/CloseButton.qml @@ -0,0 +1,24 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Button { + id: button + + width: 45 + height: 30 + + ToolTip.visible: hovered && !down + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: qsTr("Close") + + contentItem: Image { + anchors.fill: parent + source: button.down + || button.hovered ? "qrc:/images/button_close_white.svg" : "qrc:/images/button_close_black.svg" + } + + background: Rectangle { + visible: button.down || button.hovered + color: button.down ? "#8c0a15" : (button.hovered ? "#e81123" : "transparent") + } +} diff --git a/resources/qml/MaximizeButton.qml b/resources/qml/MaximizeButton.qml new file mode 100644 index 0000000..820f98b --- /dev/null +++ b/resources/qml/MaximizeButton.qml @@ -0,0 +1,25 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Button { + id: button + + width: 45 + height: 30 + + property bool maximized: false + + ToolTip.visible: hovered && !down + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize") + + contentItem: Image { + anchors.fill: parent + source: maximized ? "qrc:/images/button_restore_black.svg" : "qrc:/images/button_maximize_black.svg" + } + + background: Rectangle { + visible: button.down || button.hovered + color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") + } +} diff --git a/resources/qml/MinimizeButton.qml b/resources/qml/MinimizeButton.qml new file mode 100644 index 0000000..0700856 --- /dev/null +++ b/resources/qml/MinimizeButton.qml @@ -0,0 +1,23 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Button { + id: button + + width: 45 + height: 30 + + ToolTip.visible: hovered && !down + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: qsTr("Minimize") + + contentItem: Image { + anchors.fill: parent + source: "qrc:/images/button_minimize_black.svg" + } + + background: Rectangle { + visible: button.down || button.hovered + color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") + } +} diff --git a/resources/qml/main.qml b/resources/qml/main.qml new file mode 100644 index 0000000..88ef828 --- /dev/null +++ b/resources/qml/main.qml @@ -0,0 +1,64 @@ +import QtQuick 2.15 + +Item { + id: root + + signal minimizeButtonClicked + signal maximizeButtonClicked + signal restoreButtonClicked + signal closeButtonClicked + + Rectangle { + id: titleBar + height: $TitleBarHeight + color: "white" + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Text { + id: titleBarText + text: qsTr("Hello, World!") + font.family: "Noto Sans CJK SC" + font.pointSize: 15 + color: "black" + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.verticalCenter: parent.verticalCenter + } + + Row { + anchors.top: parent.top + anchors.right: parent.right + + MinimizeButton { + onClicked: root.minimizeButtonClicked() + } + + MaximizeButton { + onClicked: { + if (maximized) { + root.restoreButtonClicked() + maximized = false + } else { + root.maximizeButtonClicked() + maximized = true + } + } + } + + CloseButton { + onClicked: root.closeButtonClicked() + } + } + } + + Rectangle { + id: content + color: "#f0f0f0" + anchors.top: titleBar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + } +} diff --git a/winnativeeventfilter.cpp b/winnativeeventfilter.cpp index 2dfdbd4..fd1b31f 100644 --- a/winnativeeventfilter.cpp +++ b/winnativeeventfilter.cpp @@ -183,7 +183,7 @@ const UINT m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI; const qreal m_defaultDevicePixelRatio = 1.0; -int m_borderWidth = -1, m_borderHeight = -1, m_titlebarHeight = -1; +int m_borderWidth = -1, m_borderHeight = -1, m_titleBarHeight = -1; using HPAINTBUFFER = HANDLE; @@ -1117,24 +1117,32 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, case WM_NCPAINT: { // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 - if (IsDwmCompositionEnabled()) { - break; - } else { + if (!IsDwmCompositionEnabled()) { // Only block WM_NCPAINT when DWM composition is disabled. If // it's blocked when DWM composition is enabled, the frame // shadow won't be drawn. *result = 0; return true; } + break; } case WM_NCACTIVATE: { - // DefWindowProc won't repaint the window border if lParam (normally - // a HRGN) is -1. See the following link's "lParam" section: - // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate - // Don't use "*result = 0" otherwise the window won't respond to - // the window active state change. - *result = - m_lpDefWindowProcW(msg->hwnd, msg->message, msg->wParam, -1); + if (IsDwmCompositionEnabled()) { + // DefWindowProc won't repaint the window border if lParam + // (normally a HRGN) is -1. See the following link's "lParam" + // section: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate + // Don't use "*result = 0" otherwise the window won't respond to + // the window active state change. + *result = m_lpDefWindowProcW(msg->hwnd, msg->message, + msg->wParam, -1); + } else { + if (static_cast(msg->wParam)) { + *result = FALSE; + } else { + *result = TRUE; + } + } return true; } case WM_NCHITTEST: { @@ -1280,13 +1288,13 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, mouse.x = GET_X_LPARAM(_lParam); mouse.y = GET_Y_LPARAM(_lParam); m_lpScreenToClient(_hWnd, &mouse); - const RECT frame = GetFrameSizeForWindow(_hWnd, true); // These values are DPI-aware. - const LONG bw = frame.left; // identical to right - // identical to top, if the latter doesn't include the title bar - // height - const LONG bh = frame.bottom; - const LONG tbh = frame.top; + const LONG bw = + getSystemMetric(_hWnd, SystemMetric::BorderWidth); + const LONG bh = + getSystemMetric(_hWnd, SystemMetric::BorderHeight); + const LONG tbh = + getSystemMetric(_hWnd, SystemMetric::TitleBarHeight); const qreal dpr = GetDevicePixelRatioForWindow(_hWnd); const bool isInIgnoreAreas = isInSpecificAreas( mouse.x, mouse.y, _data->windowData.ignoreAreas, dpr); @@ -1311,10 +1319,11 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, const bool isInIgnoreObjects = false; const bool isInDraggableObjects = true; #endif - const bool isResizePermitted = !isInIgnoreAreas && - isInDraggableAreas && !isInIgnoreObjects && - isInDraggableObjects; - const bool isTitlebar = (mouse.y <= tbh) && isResizePermitted; + const bool isResizePermitted = + !isInIgnoreAreas && !isInIgnoreObjects; + const bool isTitlebar = (mouse.y <= tbh) && + isInDraggableAreas && isInDraggableObjects && + isResizePermitted; if (IsMaximized(_hWnd)) { if (isTitlebar) { return HTCAPTION; @@ -1488,8 +1497,8 @@ void WinNativeEventFilter::setBorderWidth(int bw) { m_borderWidth = bw; } void WinNativeEventFilter::setBorderHeight(int bh) { m_borderHeight = bh; } -void WinNativeEventFilter::setTitlebarHeight(int tbh) { - m_titlebarHeight = tbh; +void WinNativeEventFilter::setTitleBarHeight(int tbh) { + m_titleBarHeight = tbh; } void WinNativeEventFilter::updateWindow(HWND handle, bool triggerFrameChange, @@ -1545,16 +1554,12 @@ int WinNativeEventFilter::getSystemMetric(HWND handle, SystemMetric metric, } } case SystemMetric::TitleBarHeight: { - const int tbh = userData->windowData.titlebarHeight; + const int tbh = userData->windowData.titleBarHeight; if (tbh > 0) { return qRound(tbh * dpr); } else { - const int result = m_lpGetSystemMetrics(SM_CYSIZEFRAME) + - m_lpGetSystemMetrics(SM_CXPADDEDBORDER) + - m_lpGetSystemMetrics(SM_CYCAPTION); + const int result = m_lpGetSystemMetrics(SM_CYCAPTION); const int result_dpi = - GetSystemMetricsForWindow(handle, SM_CYSIZEFRAME) + - GetSystemMetricsForWindow(handle, SM_CXPADDEDBORDER) + GetSystemMetricsForWindow(handle, SM_CYCAPTION); return dpiAware ? result_dpi : result; } @@ -1575,8 +1580,8 @@ int WinNativeEventFilter::getSystemMetric(HWND handle, SystemMetric metric, break; } case SystemMetric::TitleBarHeight: { - if (m_titlebarHeight > 0) { - return qRound(m_titlebarHeight * dpr); + if (m_titleBarHeight > 0) { + return qRound(m_titleBarHeight * dpr); } break; } diff --git a/winnativeeventfilter.h b/winnativeeventfilter.h index 11fa045..be7b7ab 100644 --- a/winnativeeventfilter.h +++ b/winnativeeventfilter.h @@ -41,7 +41,7 @@ public: using WINDOWDATA = struct _WINDOWDATA { BOOL fixedSize = FALSE, mouseTransparent = FALSE, notLayeredWindow = FALSE; - int borderWidth = -1, borderHeight = -1, titlebarHeight = -1; + int borderWidth = -1, borderHeight = -1, titleBarHeight = -1; QVector ignoreAreas = {}, draggableAreas = {}; QVector> ignoreObjects = {}, draggableObjects = {}; QSize maximumSize = {-1, -1}, minimumSize = {-1, -1}; @@ -72,7 +72,7 @@ public: static void removeFramelessWindow(HWND window); static void clearFramelessWindows(); - // Set borderWidth, borderHeight or titlebarHeight to a negative value to + // Set borderWidth, borderHeight or titleBarHeight to a negative value to // restore default behavior. // Note that it can only affect one specific window. // If you want to change these values globally, use setBorderWidth instead. @@ -86,7 +86,7 @@ public: // them yourself. Just pass the original value. static void setBorderWidth(int bw); static void setBorderHeight(int bh); - static void setTitlebarHeight(int tbh); + static void setTitleBarHeight(int tbh); // System metric value of the given window (if the pointer is null, // return the system's standard value).