Search code examples
windowswindowadobechromium-embedded

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:

enter image description here

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?


Solution

  • 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;
    }