What do I want to achive:
I want to create a window that behaves like a WS_OVERLAPPEDWINDOW | WS_SIZEBOX
(can be minimized, maximized, closed from system context menu on Alt+Space
, supports Window Snapping, has shadow if it is enabled in system Performance Options -> [x] Enable shadow under window
and so on). It also should be borderless (without title bar, icon, application name in title bar and so on), just client region and nothing else.
So main goals are:
- Has no border;
- Supports default window functionality;
- Drops shadow underneath.
Don't say that it is impossible. The most simple example of such a window is Telegram Desktop. They use Qt library. Don't say I should use it. I want to learn how they did this trick. Their window is borderless, without bugs (that are described below). I know that Qt uses OpenGL inside for their window. But I also know that it should be possible to draw with GDI+ only without OpenGL.
What I have already tried:
github:melak47/BorderlessWindow This window has a tiny border around. You can see it if you resize it really quickly holding by the top or left edge of the window. We can also set margins to be like
MARGINS m{0,0,0,1}
forDwmExtendFrameIntoClientArea(hWnd, &m);
. But window will still remain the same border which could be seen if resize.github:rossy/borderless-window This solution is better. It propose two variants to struggle with this problem.
The border here is hidden by enabling
WS_EX_LAYERED
window style with chroma keying by color (e.g. magenta). But it is obvious that now we need to perform checks every time we draw something on bitmap for a specified chroma color. If user draws clear magentaRGB(255, 0, 255)
we can then replace it withRGB(254, 0, 255)
to avoid holes inside. But I think it is a very bad solution that cause unneccessary checks for each pixel in bitmap.The border is now considered as a part of client area. We disable
WM_EX_LAYERED
and color keying and using GDI+ to draw transparent colors on top border. The this is that you can't draw pure opaque color on this border (e.g.graphics.FillRect(&Gdiplus::SolidBrush{Gdiplus::Color{255, 255, 0, 0}}, Gdiplus::Rect{0, 0, windowWidht, windowHeight})
will not cover top border by red color - the border will be white or another color (I don't know what it depends on but I think it is window system color preference, likePersonalize -> Colors -> [x] Title bars and window borders
)). By the way you can cover it any color you want just by setting opacity of this color to 254. This will work. But the problem is still there: if we'll try to resize the window guess what.. we have this white border on top (or what you set this whenMARGINS m{0,0,0,1}
forDwmExtendFrameIntoClientArea(hWnd, &m);
. And also notice that it will be a lot of pain when you will forget that you can't draw pure opaque color and draw something with GDI. So I come up to the next solution.
My idea is to draw with alpha value of 254 only the top most scanline of bitmap. Becase I also perform double buffering I can do it with
AlphaBlend(hdc, 0, 0, width, 1, memHdc, 0, 0, width, 1, {AC_SRC_OVER, 0, 254, AC_SRC_ALPHA});
. But this solution is still got it's own bug. When resize window we still got this border because it's been drawn faster than our window content. Also we got weird pixels on the left side if we resize the window by the top edge. And the last bug is that this alpha dissapears sometimes to and the top border is visible not only in undrawn client regions but also on top of validated regions.The code of
WM_PAINT
procedure:
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;
}
Sreenshot: window rendered with a white srtipe on top (border has been rendered for some reasons)
Question:
Can you please tell me how to fix this top border drawing bug or any better solution to draw borderless window with WinAPI? Thanks in advance.