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

Support system menu when clicking on top left corner of frameless window on Windows #24

Open
jiakuan opened this issue Sep 24, 2018 · 4 comments

Comments

@jiakuan
Copy link

jiakuan commented Sep 24, 2018

So glad to see this project solving the problem of customising window title bar using native ways for both macOS and Windows! And, from a Chinese friend :) 这个项目太棒了!

I had a quick test on Windows and found all worked well except one feature seemed missing. When we click the top left corner of a standard application window, it normally shows a system menu like this:
screen shot 2018-09-24 at 12 11 52 pm
And this:
screen shot 2018-09-24 at 12 11 01 pm
As shown above, Adobe Illustrator for Windows uses a custom window title bar and has the system menu.

Just wondering if it's possible to add the same support in this project?

@Bringer-of-Light
Copy link
Owner

@jiakuan I believe you can show these system menu using shortcut "Alt+Space". But I‘m not sure how to add the same support using mouse right click.

@pzhlkj6612
Copy link

Hi there!

When we click the top left corner of a standard application window, it normally shows a system menu like this:
...

... But I‘m not sure how to add the same support using mouse right click.

I think the OP was talking about the menu displayed when we left click the top left corner.

@LochlanPerry-Scott
Copy link

LochlanPerry-Scott commented Oct 5, 2022

Howdy All.
@jiakuan @Bringer-of-Light

Here is my findings on this one.
Looking at the General WinAPI For Custom Framing, there looks to be a message case called WM_NCRBUTTONDOWN.
using this allows us to receive a Right click when working in the window.

As a simple example of using this could be

// End case WM_NCHITTEST
case WM_NCRBUTTONDOWN:
{
	// Get Mouse pos
	long x = GET_X_LPARAM(msg->lParam);
	long y = GET_Y_LPARAM(msg->lParam);
	
	// Support highdpi
	double dpr = this->devicePixelRatioF();
	QPoint pos = m_titlebar->mapFromGlobal(QPoint(x / dpr, y / dpr));
	
	// Is the mouse within the titlebar pos?
	if (!m_titlebar->rect().contains(pos))
	    return false;
	
	// Get Win Handle
	HWND pHndl = HWND(winId());
	// Get SysMenu handle
	HMENU pSysMenu = ::GetSystemMenu(pHndl, FALSE);
	
	// If it exists
	if (pSysMenu)
	{
		// Open the system menu, Aligning menu to the top left corner, at mouse pos.
		TrackPopupMenu(pSysMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, x, y, NULL, pHndl, 0);
	}
	return false;
}

This is a simple Case and does not support the MenuControls such as 'Close' or other functions.
This also has a bug that allows the user to display the menu even when in resize area.

Fixing this bug should be a simple fix but requires more editing of the already implamented functions.
Adding in a bool m_bInBorder and setting this bool to true when the Cursor changes,
Then setting the bool back to false when not changing the Cursor can fix this issue.

i.e.

//top border
if (y >= winrect.top && y < winrect.top + border_width)
{
	m_bInBorder = true;
	*result = HTTOP;
}

with this we can now check to see if the cursor is 'm_bInBorder'.
And if it is NOT we can continue to process the above, like so:

//end case WM_NCHITTEST
case WM_NCRBUTTONDOWN:
{
	// Check if the mouse is NOT within the Resize area.
	if (!m_bInBorder)
	{
		// Get Mouse pos
		long x = GET_X_LPARAM(msg->lParam);
		long y = GET_Y_LPARAM(msg->lParam);
		
		// Support highdpi - Needed for determining the pos of mouse, reletive to the dpi/monitor.
		double dpr = this->devicePixelRatioF();
		QPoint pos = m_titlebar->mapFromGlobal(QPoint(x / dpr, y / dpr));
	
		// Is the mouse within the titlebar pos?
		if (!m_titlebar->rect().contains(pos))
			return false;
	
		// Get Win Handle
		HWND pHndl = HWND(winId());
		// Get SysMenu handle
		HMENU pSysMenu = ::GetSystemMenu(pHndl, FALSE);
	
		// If it exists
		if (pSysMenu)
		{
			// Open the system menu, Aligning menu to the top left corner, at mouse pos (dpi not included here).
			TrackPopupMenu(pSysMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, x, y, NULL, pHndl, 0);
		}
		return false;
	}
}

Now all that is needed is to connect the Functions of the Menu..
This is a bit of a tougher case.

It looks like I was able to find a Solution for the SysMenu buttons too, after a bit of digging.
Following along with the WinAPI it looks like the message Case WM_COMMAND is the message used to receive the menuInputs.
Filtering the wParam of this message will determine the case used.
For Example, adding the below under the above code will allow you to 'Close' the Popup Dialog and Debug print to console.

//end case WM_NCRBUTTONDOWN
case WM_COMMAND:     // "Sent when the user selects a command item from a menu" - https://learn.microsoft.com/en-us/windows/win32/menurc/wm-command
{
	// Check if the wParam is the Close Param
	if (msg->wParam == SC_CLOSE)
	{
		// If so Debug and Close Popup.
		qDebug() << "Test";
		::EndDialog(HWND(winId()), TRUE);
		return false;
	}
}

From here you can simply close the Widget Window itself.
There may be a need to do further troubleshooting on this as I have not fully tested the menu Functions.
Like for instance it seems the 'Restore' button has issues with detecting if fullscreen or not, but this can be fixed by troubleshooting.

More info can be found via:
https://learn.microsoft.com/en-us/windows/win32/menurc/wm-syscommand
https://learn.microsoft.com/en-us/windows/win32/dwm/customframe#appendix-c-hittestnca-function

I hope this helps anyone that comes across this :)

@LochlanPerry-Scott
Copy link

@jiakuan
If you wish to make the prompt appear when selecting an Icon or Button then just place the before mentioned code into a button signal.
Just make sure this is OS dependent.

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

No branches or pull requests

4 participants