forked from github_mirror/framelesshelper
Minor tweaks.
Amends commit f0ef569b08
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
f0ef569b08
commit
21a563a460
25
BUGS.md
25
BUGS.md
|
@ -1,25 +0,0 @@
|
||||||
# 已知问题
|
|
||||||
|
|
||||||
## 拖拽窗口边框时窗口内容不能及时跟随,能看到窗口的白底
|
|
||||||
|
|
||||||
- 复现步骤:运行使用了样式表的*QWidget*程序(不使用样式表则无此问题);运行任意*Qt Quick*程序
|
|
||||||
- 原因:暂时未知
|
|
||||||
- 解决方案:对于*QWidget*程序,可以启用`Qt::WA_NoSystemBackground`属性,来禁用窗口默认的白色背景,但窗口背景会变成黑色,且无法改变,如果程序使用深色主题,则无大碍,如果使用浅色主题,体验应该不会太好。*Qt Quick*程序暂时无解。
|
|
||||||
|
|
||||||
## 拖拽窗口边框时能看到右上角的三个系统按钮/自绘内容消失(变白)
|
|
||||||
|
|
||||||
- 复现步骤:在窗口构造前执行`WinNativeEventFilter::install()`函数
|
|
||||||
- 原因:暂时未知
|
|
||||||
- 解决方案:不要全局安装过滤器,应针对每一个窗口,单独调用`addFramelessWindow`函数
|
|
||||||
|
|
||||||
## 自绘内容(例如D3D)错位或不能及时刷新
|
|
||||||
|
|
||||||
- 复现步骤:在窗口中放置一个完全由Windows API绘制的控件
|
|
||||||
- 原因:暂时未知
|
|
||||||
- 解决方案:暂无
|
|
||||||
|
|
||||||
## resize后发现尺寸不对
|
|
||||||
|
|
||||||
- 复现步骤:在构造函数中调用`resize`函数;在安装过滤器之前调用`resize`函数
|
|
||||||
- 原因:构造函数中获取的几何位置和大小是不准确的;调用`resize`这一类的函数时,Qt默认会考虑边框和标题栏的宽度,但安装过滤器后,边框和标题栏没了,所以尺寸就不对了
|
|
||||||
- 解决方案:在控件或窗口显示出来以后再去获取或设置它们的几何位置及尺寸;先安装过滤器,再调整尺寸。
|
|
|
@ -50,11 +50,11 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
|
|
||||||
- The `setFramelessWindows` function must not be called after the widget is shown. However, for QWindows, it must be called after they are shown.
|
- The `setFramelessWindows`/`addFramelessWindow` function must not be called after the widget is shown. However, for QWindows, 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.
|
- 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 area will not be resposible because the mouse events are intercepted. Try if `setIgnoreAreas` and `setDraggableAreas` help.
|
- Any widgets (or Qt Quick elements) in the titlebar area will not be resposible because the mouse events are intercepted. Try if `setIgnoreAreas` and `setDraggableAreas` can help.
|
||||||
- Only top level windows can be frameless. Applying this code to child windows or widgets will result in unexpected behavior.
|
- Only top level windows can be frameless. Applying this code to child windows or widgets will result in unexpected behavior.
|
||||||
- If you want to use your own border width, border height, titlebar height or minimum window size, just use the original numbers, no need to scale them according to DPI, this code will do the scaling automatically.
|
- 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
|
## Supported Platforms
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ Notes
|
||||||
## Notes for Windows developers
|
## Notes for Windows developers
|
||||||
|
|
||||||
- The `FramelessHelper` class is just a simple wrapper of the `WinNativeEventFilter` class, you can use the latter directly instead if you encounter with some strange problems.
|
- The `FramelessHelper` class is just a simple wrapper of the `WinNativeEventFilter` class, you can use the latter directly instead if you encounter with some strange problems.
|
||||||
|
- **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.
|
||||||
- If you are using `WinNativeEventFilter` directly, don't forget to call `FramelessHelper::updateQtFrame` everytime after you make a widget or window become frameless, it will make the new frame margins work correctly if `setGeometry` or `frameGeometry` is called.
|
- If you are using `WinNativeEventFilter` directly, don't forget to call `FramelessHelper::updateQtFrame` everytime after you make a widget or window become frameless, it will make the new frame margins work correctly if `setGeometry` or `frameGeometry` is called.
|
||||||
- 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.
|
- 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.
|
- All traditional Win32 APIs are replaced by their DPI-aware ones, if there is one.
|
||||||
|
@ -125,6 +126,8 @@ Notes
|
||||||
|
|
||||||
Thanks **Lucas** for testing this code in many various conditions.
|
Thanks **Lucas** for testing this code in many various conditions.
|
||||||
|
|
||||||
|
Thanks [**Shujaat Ali Khan**](https://github.com/shujaatak) for searching so many useful articles and repositories for me.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
|
@ -569,6 +569,8 @@ RECT GetFrameSizeForWindow(HWND handle, bool includingTitleBar = false) {
|
||||||
RECT rect = {0, 0, 0, 0};
|
RECT rect = {0, 0, 0, 0};
|
||||||
if (handle && m_lpIsWindow(handle)) {
|
if (handle && m_lpIsWindow(handle)) {
|
||||||
const auto style = m_lpGetWindowLongPtrW(handle, GWL_STYLE);
|
const auto style = m_lpGetWindowLongPtrW(handle, GWL_STYLE);
|
||||||
|
// It's the same with using GetSystemMetrics, the returned values
|
||||||
|
// of the two functions are identical.
|
||||||
if (m_lpAdjustWindowRectExForDpi) {
|
if (m_lpAdjustWindowRectExForDpi) {
|
||||||
m_lpAdjustWindowRectExForDpi(
|
m_lpAdjustWindowRectExForDpi(
|
||||||
&rect,
|
&rect,
|
||||||
|
@ -599,9 +601,17 @@ void UpdateFrameMarginsForWindow(HWND handle) {
|
||||||
if (handle && m_lpIsWindow(handle)) {
|
if (handle && m_lpIsWindow(handle)) {
|
||||||
MARGINS margins = {0, 0, 0, 0};
|
MARGINS margins = {0, 0, 0, 0};
|
||||||
if (IsDwmCompositionEnabled()) {
|
if (IsDwmCompositionEnabled()) {
|
||||||
|
// The frame shadow is drawn on the non-client area and thus we have
|
||||||
|
// to make sure the non-client area rendering is enabled first.
|
||||||
const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
|
const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
|
||||||
m_lpDwmSetWindowAttribute(handle, DWMWA_NCRENDERING_POLICY, &ncrp,
|
m_lpDwmSetWindowAttribute(handle, DWMWA_NCRENDERING_POLICY, &ncrp,
|
||||||
sizeof(ncrp));
|
sizeof(ncrp));
|
||||||
|
// Negative margins have special meaning to
|
||||||
|
// DwmExtendFrameIntoClientArea. Negative margins create the "sheet
|
||||||
|
// of glass" effect, where the client area is rendered as a solid
|
||||||
|
// surface with no window border. Use positive margins have similar
|
||||||
|
// appearance, but the window background will be transparent, we
|
||||||
|
// don't want that.
|
||||||
margins = {-1, -1, -1, -1};
|
margins = {-1, -1, -1, -1};
|
||||||
}
|
}
|
||||||
m_lpDwmExtendFrameIntoClientArea(handle, &margins);
|
m_lpDwmExtendFrameIntoClientArea(handle, &margins);
|
||||||
|
@ -684,6 +694,9 @@ QVector<HWND> WinNativeEventFilter::framelessWindows() {
|
||||||
void WinNativeEventFilter::setFramelessWindows(QVector<HWND> windows) {
|
void WinNativeEventFilter::setFramelessWindows(QVector<HWND> windows) {
|
||||||
if (!windows.isEmpty() && (windows != m_framelessWindows)) {
|
if (!windows.isEmpty() && (windows != m_framelessWindows)) {
|
||||||
m_framelessWindows = windows;
|
m_framelessWindows = windows;
|
||||||
|
for (auto &&window : std::as_const(m_framelessWindows)) {
|
||||||
|
createUserData(window);
|
||||||
|
}
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -694,9 +707,7 @@ void WinNativeEventFilter::addFramelessWindow(HWND window,
|
||||||
if (window && m_lpIsWindow(window) &&
|
if (window && m_lpIsWindow(window) &&
|
||||||
!m_framelessWindows.contains(window)) {
|
!m_framelessWindows.contains(window)) {
|
||||||
m_framelessWindows.append(window);
|
m_framelessWindows.append(window);
|
||||||
if (data) {
|
createUserData(window, data);
|
||||||
createUserData(window, data);
|
|
||||||
}
|
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -742,9 +753,21 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
} else if (!m_framelessWindows.contains(msg->hwnd)) {
|
} else if (!m_framelessWindows.contains(msg->hwnd)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
createUserData(msg->hwnd);
|
|
||||||
const auto data = reinterpret_cast<WINDOW *>(
|
const auto data = reinterpret_cast<WINDOW *>(
|
||||||
m_lpGetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
|
m_lpGetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
|
||||||
|
if (!data) {
|
||||||
|
// Work-around a long existing Windows bug.
|
||||||
|
if (msg->message == WM_NCCREATE) {
|
||||||
|
const auto userData =
|
||||||
|
reinterpret_cast<LPCREATESTRUCTW>(msg->lParam)
|
||||||
|
->lpCreateParams;
|
||||||
|
m_lpSetWindowLongPtrW(msg->hwnd, GWLP_USERDATA,
|
||||||
|
reinterpret_cast<LONG_PTR>(userData));
|
||||||
|
}
|
||||||
|
*result = m_lpDefWindowProcW(msg->hwnd, msg->message, msg->wParam,
|
||||||
|
msg->lParam);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!data->initialized) {
|
if (!data->initialized) {
|
||||||
// Avoid initializing a same window twice.
|
// Avoid initializing a same window twice.
|
||||||
data->initialized = TRUE;
|
data->initialized = TRUE;
|
||||||
|
@ -824,10 +847,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
if (IsMaximized(_hWnd) && !IsFullScreen(_hWnd)) {
|
if (IsMaximized(_hWnd) && !IsFullScreen(_hWnd)) {
|
||||||
// Windows automatically adds a standard width border to all
|
// Windows automatically adds a standard width border to all
|
||||||
// sides when a window is maximized.
|
// sides when a window is maximized.
|
||||||
int frameThickness_x =
|
const RECT frame = GetFrameSizeForWindow(_hWnd);
|
||||||
getSystemMetric(_hWnd, SystemMetric::BorderWidth);
|
int frameThickness_x = frame.left;
|
||||||
int frameThickness_y =
|
int frameThickness_y = frame.bottom;
|
||||||
getSystemMetric(_hWnd, SystemMetric::BorderHeight);
|
|
||||||
// The following two lines are two seperate functions in
|
// The following two lines are two seperate functions in
|
||||||
// Chromium, it uses them to judge whether the window
|
// Chromium, it uses them to judge whether the window
|
||||||
// should draw it's own frame or not. But here we will
|
// should draw it's own frame or not. But here we will
|
||||||
|
@ -1056,13 +1078,11 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
mouse.x = GET_X_LPARAM(_lParam);
|
mouse.x = GET_X_LPARAM(_lParam);
|
||||||
mouse.y = GET_Y_LPARAM(_lParam);
|
mouse.y = GET_Y_LPARAM(_lParam);
|
||||||
m_lpScreenToClient(_hWnd, &mouse);
|
m_lpScreenToClient(_hWnd, &mouse);
|
||||||
|
const RECT frame = GetFrameSizeForWindow(_hWnd, true);
|
||||||
// These values are DPI-aware.
|
// These values are DPI-aware.
|
||||||
const LONG bw =
|
const LONG bw = frame.left;
|
||||||
getSystemMetric(_hWnd, SystemMetric::BorderWidth);
|
const LONG bh = frame.bottom;
|
||||||
const LONG bh =
|
const LONG tbh = frame.top;
|
||||||
getSystemMetric(_hWnd, SystemMetric::BorderHeight);
|
|
||||||
const LONG tbh =
|
|
||||||
getSystemMetric(_hWnd, SystemMetric::TitleBarHeight);
|
|
||||||
const qreal dpr = GetDevicePixelRatioForWindow(_hWnd);
|
const qreal dpr = GetDevicePixelRatioForWindow(_hWnd);
|
||||||
const bool isTitlebar = (mouse.y < tbh) &&
|
const bool isTitlebar = (mouse.y < tbh) &&
|
||||||
!isInSpecificAreas(mouse.x, mouse.y,
|
!isInSpecificAreas(mouse.x, mouse.y,
|
||||||
|
|
|
@ -55,14 +55,6 @@ public:
|
||||||
explicit WinNativeEventFilter();
|
explicit WinNativeEventFilter();
|
||||||
~WinNativeEventFilter() override;
|
~WinNativeEventFilter() override;
|
||||||
|
|
||||||
// Make all top level windows become frameless, unconditionally.
|
|
||||||
// Use setFramelessWindows or addFramelessWindow if possible,
|
|
||||||
// because this method will cause strange behavior, currently
|
|
||||||
// don't know why.
|
|
||||||
static void install();
|
|
||||||
// Make all top level windows back to normal.
|
|
||||||
static void uninstall();
|
|
||||||
|
|
||||||
// Frameless windows handle list
|
// Frameless windows handle list
|
||||||
static QVector<HWND> framelessWindows();
|
static QVector<HWND> framelessWindows();
|
||||||
static void setFramelessWindows(QVector<HWND> windows);
|
static void setFramelessWindows(QVector<HWND> windows);
|
||||||
|
@ -91,6 +83,9 @@ public:
|
||||||
static int getSystemMetric(HWND handle, SystemMetric metric,
|
static int getSystemMetric(HWND handle, SystemMetric metric,
|
||||||
bool dpiAware = true);
|
bool dpiAware = true);
|
||||||
|
|
||||||
|
// Use this function to trigger a frame change event or redraw a
|
||||||
|
// specific window. Useful when you want to let some changes
|
||||||
|
// in effect immediately.
|
||||||
static void updateWindow(HWND handle, bool triggerFrameChange = true,
|
static void updateWindow(HWND handle, bool triggerFrameChange = true,
|
||||||
bool redraw = true);
|
bool redraw = true);
|
||||||
|
|
||||||
|
@ -101,4 +96,10 @@ public:
|
||||||
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
||||||
long *result) override;
|
long *result) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Do not call these two functions directly, otherwise strange things
|
||||||
|
// will happen.
|
||||||
|
static void install();
|
||||||
|
static void uninstall();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue