WinAPI CreateWindow WS_POPUP resize flickering - winapi

I'm creating a window with no borders (WS_POPUP in CreateWindow) and need to resize it when mouse is over and change back when mouse is out of the application. It works OK, but it flickers when I change the position using SetWindowPos. I need to decrease y by 30 pixels and increase height, so the bottom of the window remains in the same position.
Below is the code:
WNDCLASS windowClass;
ZeroMemory(&windowClass, sizeof(WNDCLASS));
windowClass.style = 0;// CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = (WNDPROC) WindowProc;
windowClass.hInstance = hInst;
windowClass.lpszClassName = PROCESS_WINDOW_CLASS;
windowClass.hIcon = NULL;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
windowHandle = CreateWindowEx(
0,
PROCESS_WINDOW_CLASS,
L"",
WS_POPUP | WS_CLIPSIBLINGS,
x, //x
y, //y
width, //width
height, //height
NULL,
NULL,
hInst,
NULL);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_MOUSEMOVE: {
if (!mouseOver) {
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
if (TrackMouseEvent(&tme)) {
RECT rect;
GetWindowRect(hwnd, &rect);
int y = rect.top;
int height = rect.bottom - y;
y -= BUTTONS_AREA_HEIGHT;
height += BUTTONS_AREA_HEIGHT;
//tried with MoveWindow, DeferWindowPos - same thing
//MoveWindow(hwnd, rect.left, y, rect.right - rect.left, height, TRUE);
//HDWP winPos = BeginDeferWindowPos(1);
//DeferWindowPos(winPos, hwnd, NULL, rect.left, y, rect.right - rect.left, height, 0);
//EndDeferWindowPos(winPos);
SetWindowPos(hwnd, NULL, rect.left, y, rect.right - rect.left, height,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
rect.top = y;
rect.bottom = height - y;
InvalidateRect(hwnd, &rect, FALSE);
//0);
RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_NOERASE);
mouseOver = TRUE;
}
}
}
break;
case WM_MOUSELEAVE: {
RECT rect;
GetWindowRect(hwnd, &rect);
int y = rect.top;
int height = rect.bottom - y;
y += BUTTONS_AREA_HEIGHT;
height -= BUTTONS_AREA_HEIGHT;
MoveWindow(hwnd, rect.left, y, rect.right - rect.left, height, TRUE);
//SetWindowPos(hwnd, NULL, rect.left, y, rect.right - rect.left, height,
// SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
//InvalidateRect(hwnd, &rect, FALSE);
//SetWindowPos(hwnd, NULL, rect.left, y, rect.right - rect.left, height, 0);
mouseOver = FALSE;
}
case WM_CREATE: {
LONG lExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
lExStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE |
WS_SYSMENU | WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, lExStyle | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
return 0;
case WM_DESTROY:
PostQuitMessage(EXIT_SUCCESS);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
I tried all combinations of SetWindowPos, MoveWindow etc but when mouse is over the window, it seems that window is first settings Y position, then it increases the height after a few milliseconds and this is visible.
Does anyone know a solution for this?

I fixed the problem by using the SetWindowRgn function.
This still doesn't answer the question why flickering happens, but it fixed the problem for me as I was able to achieve the same thing. Code is below:
HRGN region = CreateRectRgn(0, BUTTONS_AREA_HEIGHT, width, height);
SetWindowRgn(windowHandle, region, TRUE);
DeleteObject(region);

Related

C++ Transparent image using windows.h / WinAPI

I'm trying to make a custom title bar/border and I want it to be half transparent. When I load the image it works fine, but as soon, as I make even just 1 pixel from 255 opacity to 254, it just shows a white image.
Help would really be appreciated!
I use the windows.h library.
I use a resource ( .rc ) file to get the location of the images.
Don't think about where im doing things right, it's just for testing. I know it's not very efficient.
The Code!
( or at least the most of it / the important )
#include "../resource/resource.h"
#include "../header/Window.h"
#pragma warning(disable : 4244)
/////////////////////////
// Window
//
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND m_hWnd;
POINT wndSize = { 900, 900 };
Window::Window() : m_hInstance(GetModuleHandle(nullptr))
{
const wchar_t * className = L"Main Window";
WNDCLASS wc = {};
wc.lpszClassName = className;
wc.hInstance = m_hInstance;
wc.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_ICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpfnWndProc = WindowProc;
wc.hbrBackground = CreatePatternBrush(LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_BG)));
RegisterClass(&wc);
DWORD style = NULL;
RECT rect;
rect.left = GetSystemMetrics(SM_CXSCREEN) * .5 - (wndSize.x * .5);
rect.top = GetSystemMetrics(SM_CYSCREEN) * .5 - (wndSize.y * .5);
rect.right = rect.left + wndSize.x;
rect.bottom = rect.top + wndSize.y;
AdjustWindowRect(&rect, style, false);
m_hWnd = CreateWindowEx(
0,
className,
L"Chess",
style,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
NULL,
NULL,
m_hInstance,
NULL
);
SetWindowLongPtr(m_hWnd, GWL_STYLE, NULL); // Remove the default borders
ShowWindow(m_hWnd, SW_SHOW);
}
Window::~Window()
{
const wchar_t * className = L"Main Window";
UnregisterClass(className, m_hInstance);
}
bool Window::ProcessMessage()
{
MSG msg = {};
RECT rectBorder;
HBRUSH colorBorder = CreatePatternBrush(LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TOP)));
//BitBlt(GetWindowDC(m_hWnd), 0, 0, 900, 30, GetWindowDC(m_hWnd), 0, 0, SRCINVERT);
//BitBlt(GetWindowDC(m_hWnd), 0, 0, 900, 30, GetWindowDC(m_hWnd), 0, 0, SRCAND);
//BitBlt(GetWindowDC(m_hWnd), 0, 0, 900, 30, GetWindowDC(m_hWnd), 0, 0, SRCINVERT);
while(PeekMessage(&msg, nullptr, 0u, 0u, PM_REMOVE))
{
switch(msg.message)
{
case WM_MOUSEMOVE: // On mouse motion
Window::updateWndEvents();
break;
case WM_PAINT: // On draw request
rectBorder = { 0, 0, wndSize.x, 30 };
FillRect(GetWindowDC(m_hWnd), &rectBorder, colorBorder); // Draw the top border
break;
case WM_QUIT: // On quit
return false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
}
/////////////////////////
// Window Functions
//
void Window::resizeWindow()
{
}
void Window::moveWindow()
{
}
void Window::updateWndEvents()
{
}
The alpha channel of a PNG image cannot be loaded using LoadBitmap.
It is recommended to use LockBits with PixelFormat32bppARGB in GDI+.

How to properly grow label automatically according to text's size?

I did create a new rectangle in the WM_PAINT but in order to change the text, I also SetWindowText(), I need a InvalidateRect() so it seems quite hacky to me. What's the proper way to do that? I'm not sure if I'm calculating the proper height and width properly either. I've tried grow using SetWindowPos() from WM_PAINT but it failed so I switch to draw it using Rectangle(). I did that because I wanted the text o vertical center but static control doesn't support that, so I went to do the paiting myself so I can use DrawText() with DT_CENTER | DT_SINGLELINE | DT_VCENTER flags. Here's how I'm handling VM_PAINT:
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rt = {0};
GetClientRect(hwnd, &rt);
int height = rt.right - rt.left;
int width = rt.bottom - rt.top;
int len = GetWindowTextLength(hwnd);
wchar_t s[len+1];
GetWindowText(hwnd, s, len+1);
int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
HDC dc = BeginPaint(hwnd, &ps);
// calculate the width and height
DrawText(dc, s, -1, &rt, DT_CALCRECT | flags);
width += rt.right;
height += rt.bottom;
// update width and height
rt.right = width;
rt.bottom = height;
Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);
// this prevent from painting the border.
InflateRect(&rt, -1, -1);
FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
SetBkMode(dc, TRANSPARENT);
DrawText(dc, s, -1, &rt, flags);
EndPaint(hwnd, &ps);
return 0;
}
full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
WNDPROC oldButtonProc;
HINSTANCE ghInstance;
HWND hTab;
HFONT hdDfaultFont;
HWND btn;
enum
{
BTN_ID = 10,
BTN2_ID,
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow)
{
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
//InitComControls();
if(!RegisterClass(&wc)) {
return -1;
}
int width = 540;
int height = 460;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int cx = (screenWidth - width) / 2;
int cy = (screenHeight - height) / 2;
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
cx, cy, width, height, NULL, NULL,
hInstance, NULL);
ghInstance = hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DeleteObject(hdDfaultFont);
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
btn =
CreateWindow(L"static", L"init text",
SS_NOTIFY |
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
0, 0, 20, 30,
hwnd, (HMENU) BTN_ID, NULL, NULL);
oldButtonProc = (WNDPROC) SetWindowLongPtr(btn,
GWLP_WNDPROC,
(LONG_PTR) ButtonProc);
HWND btn2 =
CreateWindow(L"Button", L"Click me!",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
10, 50, 70, 25,
hwnd, (HMENU) BTN2_ID,
NULL, NULL);
SetDefaultFont(btn2);
}
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case BTN2_ID:
{
SetWindowText(btn, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0");
InvalidateRect(btn, NULL, TRUE);
}
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rt = {0};
GetClientRect(hwnd, &rt);
int height = rt.right - rt.left;
int width = rt.bottom - rt.top;
int len = GetWindowTextLength(hwnd);
wchar_t s[len+1];
GetWindowText(hwnd, s, len+1);
int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
HDC dc = BeginPaint(hwnd, &ps);
// calculate the width and height
DrawText(dc, s, -1, &rt, DT_CALCRECT | flags);
width += rt.right;
height += rt.bottom;
// update width and height
rt.right = width;
rt.bottom = height;
Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);
InflateRect(&rt, -1, -1);
FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
SetBkMode(dc, TRANSPARENT);
DrawText(dc, s, -1, &rt, flags);
EndPaint(hwnd, &ps);
return 0;
}
break;
}
return CallWindowProc(oldButtonProc, hwnd, msg, wParam, lParam);
}
You can use GetTextExtentPoint32 to find the string length and then you don't need to do calculations.
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rt = { 0 };
GetClientRect(hwnd, &rt);
int len = GetWindowTextLength(hwnd);
wchar_t s[len + 1];
GetWindowText(hwnd, s, len + 1);
int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
HDC dc = BeginPaint(hwnd, &ps);
SIZE sz;
GetTextExtentPoint32(dc, s, len, &sz);
rt.right = sz.cx + 10; //I added 10 to make the display less crowded.
rt.bottom = sz.cy;
Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);
// this prevent from painting the border.
InflateRect(&rt, -1, -1);
FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
SetBkMode(dc, TRANSPARENT);
DrawText(dc, s, -1, &rt, flags);
EndPaint(hwnd, &ps);
return 0;
}
To answer the X part of the XY problem, DT_VCENTER | DT_SINGLELINE implies single-line text, and in that case a static control with the SS_CENTERIMAGE style will center the text vertically.
SS_CENTERIMAGE -
A bitmap is centered in the static control that contains it. The control is not resized, so that a bitmap too large for the control will be clipped. If the static control contains a single line of text, the text is centered vertically in the client area of the control.

How to paint over white line between menu bar and client area of window?

I tried to color customize menu items (pure WinAPI). But there is a line in the menu bar which does not draw with MenuInfo.hbrBack color. If the mouse cursor hover above items a part of this line is redrawn. But if I resize the window the line will return. And in the area of menu bar where no items the line drawn constantly. How can I draw over this annoying line?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
struct
{
COLORREF text = RGB(200, 200, 250);
COLORREF clientBorder = RGB(120, 0, 0);
COLORREF clientBackground = RGB(100, 100, 100);
COLORREF itemBorder = RGB(0, 0, 255);
COLORREF itemBackground = RGB(0, 120, 0);
COLORREF pink = RGB(255, 0, 255);
} colorTheme;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWindow";
wc.cbWndExtra = NULL;
wc.cbClsExtra = NULL;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(colorTheme.clientBackground);
wc.hInstance = hInst;
RegisterClassEx(&wc);
HWND hMainWnd = CreateWindow(
"MainWindow",
"MainWindow",
WS_OVERLAPPEDWINDOW,
100, 100, 450, 120,
(HWND)NULL, NULL, HINSTANCE(hInst), NULL);
HMENU hMenu = CreateMenu();
HMENU hMenuSub1 = CreatePopupMenu();
HMENU hMenuSub2 = CreatePopupMenu();
HMENU hMenuSub3 = CreatePopupMenu();
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub1, "SubMenu1");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item01");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item02");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item03");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item04");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item05");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub2, "SubMenu2");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub3, "SubMenu3");
MENUINFO menuInfo;
menuInfo.cbSize = sizeof(menuInfo);
menuInfo.fMask = MIM_BACKGROUND;
menuInfo.hbrBack = CreateSolidBrush(colorTheme.pink);
SetMenuInfo(hMenu, &menuInfo);
SetMenu(hMainWnd, hMenu);
ShowWindow(hMainWnd, nCmdShow);
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
HFONT hApplicationFont;
LOGFONT applicationFont;
applicationFont.lfHeight = 16;
applicationFont.lfWidth = 6;
applicationFont.lfEscapement = 0;
applicationFont.lfOrientation = 0;
applicationFont.lfWeight = FW_NORMAL;
applicationFont.lfItalic = FALSE;
applicationFont.lfUnderline = FALSE;
applicationFont.lfStrikeOut = FALSE;
applicationFont.lfCharSet = DEFAULT_CHARSET;
applicationFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
applicationFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
applicationFont.lfQuality = ANTIALIASED_QUALITY;
applicationFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy_s(applicationFont.lfFaceName, "Arial");
hApplicationFont = CreateFontIndirectA(&applicationFont);
SelectObject(hDC, hApplicationFont);
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.clientBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.clientBackground);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
Rectangle(hDC, 0, 0, clientRect.right, clientRect.bottom);
EndPaint(hWnd, &ps);
break;
}
case WM_MEASUREITEM:
{
LPMEASUREITEMSTRUCT itemStruct = (LPMEASUREITEMSTRUCT)lParam;
const char* str = (const char*)(itemStruct->itemData);
SIZE strSize;
HDC hDC = GetDC(hWnd);
GetTextExtentPoint32(hDC, str, lstrlen(str), &strSize);
itemStruct->itemWidth = strSize.cx;
itemStruct->itemHeight = 30;
ReleaseDC(hWnd, hDC);
return TRUE;
break;
}
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT itemStruct = (LPDRAWITEMSTRUCT)lParam;
HDC hDC = itemStruct->hDC;
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.itemBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.itemBackground);
SetTextColor(hDC, colorTheme.text);
SetBkMode(hDC, TRANSPARENT);
Rectangle(hDC, itemStruct->rcItem.left,
itemStruct->rcItem.top,
itemStruct->rcItem.right,
itemStruct->rcItem.bottom + 1);
DrawText(hDC, (const char*)(itemStruct->itemData), -1, &(itemStruct->rcItem), DT_SINGLELINE | DT_CENTER | DT_VCENTER);
break;
}
case WM_DESTROY:
{
PostQuitMessage(NULL);
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return NULL;
}
It seems to be part of the non-client area of the window. If that's the case then to paint there you need to handle WM_NCPAINT.
It is a single pixel line above the window's client area, so for example if I add the following code to your program I can paint it in red.
// ... in the WNDPROC
case WM_NCPAINT:
{
auto result = DefWindowProc(hWnd, WM_NCPAINT, wParam, lParam);
HDC hdc = GetWindowDC(hWnd);
RECT r = GetNonclientMenuBorderRect(hWnd);
HBRUSH red = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &r, red);
DeleteObject(red);
ReleaseDC(hWnd, hdc);
return result;
}
// ... elsewhere
RECT MapRectFromClientToWndCoords(HWND hwnd, const RECT& r)
{
RECT wnd_coords = r;
// map to screen
MapWindowPoints(hwnd, NULL, reinterpret_cast<POINT*>(&wnd_coords), 2);
RECT scr_coords;
GetWindowRect(hwnd, &scr_coords);
// map to window coords by substracting the window coord origin in
// screen coords.
OffsetRect(&wnd_coords, -scr_coords.left, -scr_coords.top);
return wnd_coords;
}
RECT GetNonclientMenuBorderRect(HWND hwnd)
{
RECT r;
GetClientRect(hwnd, &r);
r = MapRectFromClientToWndCoords(hwnd, r);
int y = r.top - 1;
return {
r.left,
y,
r.right,
y+1
};
}
Now an issue with the above code is that it is over-painting the rectangle after the default non-client painting is done. In theory this could flicker; in practice I don't notice a flicker. If it did flicker, however, a safer way to do this would be to modify the WPARAM you pass to DefWindowProc(hWnd, WM_NCPAINT, ... ) such that it is the handle to a region that is the region passed to WM_NCPAINT minus the rectangle you want to paint. This doesnt seem necessary to me, for whatever reason.
If you are using themes / visual styles, which pretty much everything is nowadays, you can't override a lot of the menu styling without using a workaround like https://github.com/adzm/win32-custom-menubar-aero-theme which also uses the same approach to get rid of the white line. Note that you will need to handle this in WM_NCPAINT and WM_NCACTIVATE.

Make parent window transparent and child window opaque with CEF

On Windows, I'm trying to use CEF (Chromium Embedded Framework) to create a window application with parent window to be transparent and its child window to be opaque (I want to have a rounded corner and an arrow pointing to the status bar in the child window). Something similar to:
I tried to use SetLayeredWindowAttributes to make the parent window transparent but it also makes the child window transparent. Is there a way to make this happen on Windows?
SetLayeredWindowAttributes needs a color for transparency. Make sure the transparency color is not used by the child window. You can pick a random color, for example RGB(255, 0, 254) and assume the child window is not using it.
If you have no control over the child window, and you can't be sure what colors it might use, then SetWindowRgn is another option to create non-rectangular windows.
The example below shows how to set the region such that the corners are round with a triangle on top.
You can use GDI+ to gain more flexibility for drawing the region, and for anti-aliasing effect so that the borders look more smooth.
#include <Windows.h>
int triangle_height = 30;
int corner_size = 20;
int caption_height = 60;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
{
//create a child button for testing
CreateWindowW(L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE,
10, caption_height + 10, 100, 30,
hwnd, HMENU(100), GetModuleHandle(NULL), NULL);
RECT rc;
GetClientRect(hwnd, &rc);
//create a triangle region
int w = rc.right;
int h = rc.bottom;
int z = triangle_height;
POINT pt[3];
pt[0] = { w / 2, 0 };
pt[1] = { w / 2 - z, z };
pt[2] = { w / 2 + z, z };
HRGN htri = CreatePolygonRgn(pt, 3, WINDING);
//create a round rectangle region
HRGN hrgn = CreateRoundRectRgn(0, z, w, h - z, corner_size, corner_size);
//combine the triangle with round rectangle
CombineRgn(hrgn, htri, hrgn, RGN_OR);
//set the new region
SetWindowRgn(hwnd, hrgn, TRUE);
DeleteObject(htri);
DeleteObject(hrgn);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//we don't have a standard title bar, paint one here:
rc.bottom = caption_height;
SetDCBrushColor(hdc, RGB(80, 80, 80));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//paint the background
rc = ps.rcPaint;
rc.top = caption_height;
SetDCBrushColor(hdc, RGB(240, 240, 240));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//use FrameRgn to paint a border around the region
HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hrgn);
SetDCBrushColor(hdc, RGB(128, 128, 128));
FrameRgn(hdc, hrgn, (HBRUSH)GetStockObject(DC_BRUSH), 1, 1);
DeleteObject(hrgn);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST:
{
//we don't have a standard title-bar
//respond to our custome title-bar manually:
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
if(pt.y < caption_height)
return HTCAPTION;
break;
}
case WM_COMMAND:
if(HIWORD(wparam) == BN_CLICKED)
if(LOWORD(wparam) == 100)
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEXW wcex = { sizeof(wcex) };
wcex.style = CS_DROPSHADOW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszClassName = L"classname";
RegisterClassExW(&wcex);
CreateWindowW(wcex.lpszClassName, L"Test", WS_VISIBLE | WS_POPUP,
200, 200, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}

Visual studio wont work with "MoveTo" (win32api)

Here's the code below, VS said that on
line 109 ( MoveTo(hdc, x, y);
"MoveTo" is undefined.
Can someone explain me what's wrong ?
This is a win32api application.
// Win32Project.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Win32Project4.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int pNum = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
const WCHAR szAppName[] = L"Third_LAB";
const WCHAR szChildClass[] = L"Child";
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_CROSS);
wndclass.hbrBackground = (HBRUSH)(NULL_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassEx(&wndclass);
wndclass.lpfnWndProc = ChildWndProc;
wndclass.cbWndExtra = sizeof(WORD);
wndclass.hIcon = NULL;
wndclass.lpszClassName = szChildClass;
wndclass.hIconSm = NULL;
RegisterClassEx(&wndclass);
hwnd = CreateWindowEx(
0,
szAppName, // window class name
L"Third_LAB", // window caption
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, // window style
200, // initial x position
150, // initial y position
800, // initial x size
500, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL
); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, 0, 0, 0))
DispatchMessage(&msg);
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
{
const WCHAR szChildClass[] = L"Child";
static int cxBlock, cyBlock;
static HDC hdc;
static HPEN hPen;
static BOOL track = FALSE;
static HWND hwndCH;
static int x, y;
switch (iMsg) {
case WM_CREATE:
hdc = GetDC(hwnd);
hwndCH = CreateWindow(szChildClass, NULL,
WS_CHILDWINDOW | WS_VISIBLE,
0, 0, cxBlock / 2 + 3, cyBlock + 3,
hwnd, NULL,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
MoveWindow(hwndCH, 0, 0, cxBlock / 2 + 3, cyBlock + 3, NULL);
return 0;
case WM_SIZE:
cxBlock = LOWORD(lParam);
cyBlock = HIWORD(lParam);
MoveWindow(hwndCH, 0, 0, cxBlock / 2 + 3, cyBlock + 3, NULL);
return 0;
case WM_KEYDOWN:
switch (wParam)
{
case (0x51):
PostQuitMessage(0);
break;
}
return 0;
case WM_LBUTTONDOWN:
track = TRUE;
x = LOWORD(lParam);
y = HIWORD(lParam);
MoveTo(hdc, x, y);
switch (pNum)
{
case (1):
hPen = CreatePen(PS_SOLID, 5, 0);
SelectObject(hdc, hPen); break;
case (2):
hPen = CreatePen(PS_SOLID, 15, 0);
SelectObject(hdc, hPen); break;
case (3):
hPen = CreatePen(PS_SOLID, 25, 0);
SelectObject(hdc, hPen); break;
}
return 0;
case WM_LBUTTONUP:
track = FALSE;
DeleteObject(hPen);
return 0;
case WM_MOUSEMOVE:
if (track)
{
x = LOWORD(lParam);
y = HIWORD(lParam);
LineTo(hdc, x, y);
MoveTo(hdc, x, y);
}
return 0;
case WM_DESTROY:
ReleaseDC(hwnd, hdc);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
}
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
PAINTSTRUCT ps;
static int cxBlock, cyBlock;
static int x, y;
static HPEN hPen;
switch (iMsg)
{
case WM_SIZE:
hdc = GetDC(hwnd);
cxBlock = LOWORD(lParam);
cyBlock = HIWORD(lParam);
InvalidateRect(hwnd, NULL, TRUE);
ReleaseDC(hwnd, hdc);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetROP2(hdc, R2_BLACK);
Rectangle(hdc, 0, cyBlock / 2 - 100, cxBlock, cyBlock / 2 - 105);
Rectangle(hdc, 0, cyBlock / 2, cxBlock, cyBlock / 2 - 15);
Rectangle(hdc, 0, cyBlock / 2 + 100, cxBlock, cyBlock / 2 + 125);
InvalidateRect(hwnd, NULL, FALSE);
break;
case WM_RBUTTONDOWN:
x = LOWORD(lParam);
y = HIWORD(lParam);
if ((y <= (cyBlock / 2 - 100)) && (y >= (cyBlock / 2 - 105)))
pNum = 1;
else if ((y <= (cyBlock / 2)) && (y >= (cyBlock / 2 - 15)))
pNum = 2;
else if ((y >= (cyBlock / 2 + 100)) && (y <= (cyBlock / 2 + 125)))
pNum = 3;
return 0;
case WM_DESTROY:
DeleteObject(hPen);
ReleaseDC(hwnd, hdc);
DestroyWindow(hwnd);
return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
Waiting for a fast response, I would be very glad.
MoveTo() is a Windows 3.x function that is no longer supported. Use MoveToEx() instead, setting the last argument to NULL. You can define your own MoveTo() to do that, eg:
#define MoveTo(hdc, x, y) ((void)MoveToEx(hdc, x, y, NULL))
Or:
inline void MoveTo(HDC hdc, int X, int Y) { MoveToEx(hdc, X, Y, NULL); }

Resources