John Boyer
John Boyer

Reputation: 27

Create custom window resize functionality

I'm trying to create a borderless, captionless, and resizeable without using the WS_THICKFRAME, WS_BORDER and WS_SIZEBOX styles.

I don't want to use these styles because they create a border but more importantly, when I use the SetWindowCompositionAttribute function to enable an acrylic blur behind on the window, the blur goes past the window for some reason image.

I checked out this repo and I'm currently using their hit testing logic.

So essentially I believe I need to implement my own resizing ability but I don't know where to really start.

Upvotes: 0

Views: 846

Answers (2)

jwezorek
jwezorek

Reputation: 9545

There is a lot of nuance to exactly replicating all standard window resizing behavior but the main thing you need to do is implement your own handler of WM_NCHITTEST, which basically tells Windows which part of the window a given point is in.

Here's an example that will allow dragging via a custom title bar area and resizing via dragging the left, right, and bottom of the window.

#include <windows.h>
#include <tuple>
#include "windowsx.h"

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
    wc.lpszClassName = L"customwindowresizing";
    if (!RegisterClass(&wc))
        return 1;

    if (!CreateWindow(wc.lpszClassName,
        L"",
        WS_POPUP | WS_VISIBLE,
        0, 0, 640, 480, 0, 0, hInstance, NULL))
        return 2;

    while (GetMessage(&msg, NULL, 0, 0) > 0)
        DispatchMessage(&msg);

    return 0;
}

LRESULT HandleNonclientHitTest(HWND wnd, LPARAM lparam, int title_bar_hgt, int resizing_border_wd)
{
    RECT wnd_rect;
    GetWindowRect(wnd, &wnd_rect);

    int wd = wnd_rect.right - wnd_rect.left;
    int hgt = wnd_rect.bottom - wnd_rect.top;

    RECT title_bar = { 0,0, wd, title_bar_hgt };
    RECT left = { 0, title_bar_hgt , resizing_border_wd , hgt - title_bar_hgt - resizing_border_wd };
    RECT right = {wd - resizing_border_wd , title_bar_hgt , wd, hgt - title_bar_hgt - resizing_border_wd };
    RECT bottom = { 0, hgt - resizing_border_wd, wd, hgt };

    std::tuple<RECT, LRESULT> rects[] = {
        {title_bar, HTCAPTION},
        {left, HTLEFT},
        {right, HTRIGHT},
        {bottom, HTBOTTOM}
    };

    POINT pt = { GET_X_LPARAM(lparam) - wnd_rect.left, GET_Y_LPARAM(lparam) - wnd_rect.top };
    for (const auto& [r, code] : rects) {
        if (PtInRect(&r, pt))
            return code;
    }
    return HTCLIENT;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    case WM_NCHITTEST:
        return HandleNonclientHitTest(hWnd, lParam, 25, 10);
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;

}

Upvotes: 3

catnip
catnip

Reputation: 25398

So essentially I believe I need to implement my own resizing ability

Actually, you don't. If you only want to resize via the bottom-right hand corner of the window, just catch WM_NCHITTEST and return HTGROWBOX when the mouse is in the area of the window that you consider to be your resizing handle.

Other, more complex, return values from WM_NCHITTEST are possible (including making it possible for the user to drag the window around on the screen), but you get the idea.

Upvotes: 1

Related Questions