c++ - WinAPI 不妥协的无边框窗口,下方有阴影
问题描述
我想达到什么目标:
我想创建一个行为类似于 a 的窗口WS_OVERLAPPEDWINDOW | WS_SIZEBOX
(可以从系统上下文菜单中最小化、最大化、关闭Alt+Space
,支持Window Snapping,如果它在系统中启用则有阴影Performance Options -> [x] Enable shadow under window
等等)。它也应该是无边界的(没有标题栏、图标、标题栏中的应用程序名称等),只有客户区域,没有其他内容。
所以主要目标是:
- 没有边界;
- 支持默认窗口功能;
- 在下面投下阴影。
不要说这是不可能的。这种窗口最简单的例子是 Telegram Desktop。他们使用 Qt 库。不要说我应该使用它。我想了解他们是如何做到这一点的。他们的窗口是无边界的,没有错误(如下所述)。我知道 Qt 在其窗口中使用 OpenGL。但我也知道应该可以只在没有 OpenGL 的情况下使用 GDI+ 进行绘制。
我已经尝试过的:
github:melak47/BorderlessWindow这个窗口有一个小边框。如果您在窗口的顶部或左侧边缘快速调整它的大小,您可以看到它。我们还可以将边距设置
MARGINS m{0,0,0,1}
为DwmExtendFrameIntoClientArea(hWnd, &m);
. 但是窗口仍将保持相同的边框,如果调整大小可以看到。github:rossy/borderless-window这个方案比较好。它提出了两种变体来解决这个问题。
此处的边框通过启用
WS_EX_LAYERED
具有色度键控的窗口样式(例如洋红色)来隐藏。但很明显,现在我们每次在位图上绘制指定色度的东西时都需要执行检查。如果用户绘制清晰的洋红色RGB(255, 0, 255)
,我们可以将其替换为RGB(254, 0, 255)
以避免内部出现孔洞。但我认为这是一个非常糟糕的解决方案,会导致对位图中的每个像素进行不必要的检查。边界现在被视为客户区的一部分。我们禁用
WM_EX_LAYERED
和颜色键控并使用 GDI+ 在顶部边框上绘制透明颜色。这是您不能在此边框上绘制纯不透明颜色(例如graphics.FillRect(&Gdiplus::SolidBrush{Gdiplus::Color{255, 255, 0, 0}}, Gdiplus::Rect{0, 0, windowWidht, windowHeight})
,不会用红色覆盖顶部边框 - 边框将是白色或其他颜色(我不知道它取决于什么,但我认为它是窗口系统颜色偏好,如Personalize -> Colors -> [x] Title bars and window borders
))。顺便说一句,您可以通过将此颜色的不透明度设置为 254 来覆盖任何您想要的颜色。这将起作用。但是问题仍然存在:如果我们尝试调整窗口大小,猜猜是什么......我们在顶部有这个白色边框(或者你在什么时候设置MARGINS m{0,0,0,1}
这个DwmExtendFrameIntoClientArea(hWnd, &m);
. 还要注意,当你忘记不能绘制纯不透明的颜色并使用 GDI 绘制某些东西时,会很痛苦。所以我想出了下一个解决方案。
我的想法是使用 254 的 alpha 值仅绘制位图最顶部的扫描线。因为我还执行双缓冲,所以可以使用
AlphaBlend(hdc, 0, 0, width, 1, memHdc, 0, 0, width, 1, {AC_SRC_OVER, 0, 254, AC_SRC_ALPHA});
. 但是这个解决方案仍然有它自己的错误。当调整窗口大小时,我们仍然得到这个边框,因为它的绘制速度比我们的窗口内容快。如果我们通过顶部边缘调整窗口大小,我们也会在左侧得到奇怪的像素。最后一个错误是这个 alpha 有时会消失,并且顶部边框不仅在未绘制的客户区域中可见,而且在验证区域的顶部也是可见的。WM_PAINT
程序代码:
LRESULT wmPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps = {};
HDC hdc = BeginPaint(hWnd, &ps);
auto [width, height] = dimensions.normal;
HDC memHdc = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
SelectObject(memHdc, hBitmap);
// Children perform their drawings on "back buffer" - memDc
handleChildrenDraw(memDc);
// Copy top line as transparent and other as always to paint over non-client area as it was client
BLENDFUNCTION bf = {};
bf.BlendOp = AC_SRC_OVER;
bf.SourceConstantAlpha = 254;
bf.AlphaFormat = AC_SRC_ALPHA;
auto alphaSuccess = AlphaBlend(hdc, 0, 0, width, 1, memHdc, 0, 0, width, 1, bf);
expect(alphaSuccess == TRUE);
auto copySuccess = BitBlt(hdc, 1, 0, width, height, memHdc, 1, 0, SRCCOPY);
expect(copySuccess == TRUE);
EndPaint(hWnd, &ps);
DeleteObject(memHdc);
DeleteObject(hBitmap);
return 0;
}
问题:
你能告诉我如何修复这个顶部边框绘制错误或任何更好的解决方案来使用 WinAPI 绘制无边框窗口吗?提前致谢。