Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adjust_maximized_client_rect fails when restore maximized window from taskbar #21

Open
KennyProgrammer opened this issue May 3, 2023 · 3 comments

Comments

@KennyProgrammer
Copy link

KennyProgrammer commented May 3, 2023

adjust_maximized_client_rect for me works fine when i minimize/maximize and restore window within my window, but problem appears when i hide (minimize) window using taskbar, and then again restore it with taskbar, size of the window still not fits correctly (it not fills hole monitor but a make size of my window a little larger ,and shift window down).

  1. Not maximized window (normal)
    image

  2. Maximized window (by double clicking on nc area or my maximize button).
    image

  3. But when window maximized and minimized by taskbar and restore it fails.
    image

And that happens on all sides of the window (i.e it little bigger that usual).

But when i resize taskbar, window again fits currently.
image

I try everything, send message WM_NCCALCSIZE, when message WM_SHOWWINDOW process, or manually move and change size of the window, but result still the same.

NOTE: In my window i use the same example as BordelessWindow only i change a little the HitTest, but that not causing this problem i've checked.

LRESULT HitTest(POINT p, HWND hwnd, bool handleResize) {
		// identify borders and corners to allow resizing the window.
		// Note: On Windows 10, windows behave differently and
		// allow resizing outside the visible window frame.
		// This implementation does not replicate that behavior.
		const POINT border{
			::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER),
			::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)
		};
		RECT window;
		if (!::GetWindowRect(hwnd, &window)) {
			return HTNOWHERE;
		}

		bool handleDragForClient = FE_FALSE;
		bool handleDrag = FE_TRUE;

		const auto drag = handleDrag ? HTCAPTION : HTCLIENT;
		const auto dragClient = handleDragForClient ? HTCAPTION : HTCLIENT;

		enum region_mask {
			client = 0b0000,
			left = 0b0001,
			right = 0b0010,
			top = 0b0100,
			bottom = 0b1000,
		};

		const auto result = left * (p.x < (window.left + border.x)) |
			right * (p.x >= (window.right - border.x)) |
			top * (p.y < (window.top + border.y)) |
			bottom * (p.y >= (window.bottom - border.y));

		switch (result) {
		case left:           return handleResize ? HTLEFT : drag;
		case right:          return handleResize ? HTRIGHT : drag;
		case top:            return handleResize ? HTTOP : drag;
		case bottom:         return handleResize ? HTBOTTOM : drag;
		case top | left:  return handleResize ? HTTOPLEFT : drag;
		case top | right: return handleResize ? HTTOPRIGHT : drag;
		case bottom | left:  return handleResize ? HTBOTTOMLEFT : drag;
		case bottom | right: return handleResize ? HTBOTTOMRIGHT : drag;
		case client: {
			// Define the No Client Area Where we can drag. In this case is:
			// 0 -- window.right - 117 
			// |                     |
			// |                     |
			// window.top + 40 -------
			// 
			// NOTE: Client Area implement here using in Force Editor as PanelNonClientArea.
			if (
				// First rect.
				p.x >= 0 && p.x <= window.right - 117 &&
				p.y >= 0 && p.y <= window.top + 32 ||

				// Second rect.
				p.x >= (window.right / 2) + 100 && p.x <= window.right &&
				p.y >= window.top + 32 && p.y <= window.top + 64
				)
				return HTCAPTION;

			return dragClient;
		}
		default:             return HTNOWHERE;
		}
	}

Edit 1: I look up to the #14 issue, it shoud fix my problem, but it didn't.

@KennyProgrammer
Copy link
Author

KennyProgrammer commented May 4, 2023

After few hours digging out Win API, i finally found fix for this problem. It was in adjust_maximized_client_rect, in MonitorFromWindow , where its flag was set to MONITOR_DEFAULTTONULL, witch retrieve monitor only if window is intersect that monitor, but when window was minimized by task bar it seems to not intersect with monitor and returns NULL. So to fix this need just set MONITOR_DEFAULTTOPRIMARY instead, in that case that invalid NULL handle to monitor will be retrieved always as primary monitor.

@melak47
Copy link
Owner

melak47 commented May 11, 2023

That's curious.
I did some debugging, and I see the same behavior.
I guess when WM_NCCALCSIZE gets sent, the system is giving us the proposed window rect,
meaning it's not the actual window rect yet.
Calling GetWindowRect from WM_NCCALCSIZE seems to confirm that:

Code_DnkIM0p1aG

So it makes sense that MonitorFromWindow can't determine which monitor this window intersects.

Using MONITOR_DEFAULTTOPRIMARY kind of works, but only if the window was maximized on the primary monitor. Otherwise, the monitor work rect and window rect might not even overlap, and you have effectively no client area.

However, we do know the rectangle the window is about to occupy (which is the maximized rect the window is being restored to), so we can use that to determine the right monitor:

- auto monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
+ auto monitor = ::MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);

@z4none
Copy link

z4none commented Dec 27, 2023

Maybe this code can solve the problem

/* Adjust client rect to not spill over monitor edges when maximized.
 * rect(in/out): in: proposed window rect, out: calculated client rect
 * Does nothing if the window is not maximized.
 */
auto adjust_maximized_client_rect(HWND window, RECT& rect) -> void {
    if (!maximized(window)) {
        return;
    }

    // get the screen that the window is supposed to be on
    WINDOWPLACEMENT wp{};
    if (!::GetWindowPlacement(window, &wp)) {
        return;
    }

    POINT center = {
        rect.left + (rect.right - rect.left) / 2,
        rect.top + (rect.bottom - rect.top) / 2
    };

    auto monitor = ::MonitorFromPoint(center, MONITOR_DEFAULTTONULL);
    if (!monitor) {
        return;
    }

    MONITORINFO monitor_info{};
    monitor_info.cbSize = sizeof(monitor_info);
    if (!::GetMonitorInfoW(monitor, &monitor_info)) {
        return;
    }

    // when maximized, make the client area fill just the monitor (without task bar) rect,
    // not the whole window rect which extends beyond the monitor.
    rect = monitor_info.rcWork;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants