Remove resize border and drawn in the occupied area? - c++

When I remove the sizing border of a window, it leaves black areas around it.
Is it possible to somehow tell the window to occupy/paint these areas, as there's no sizing border anymore?
A reproducible example: launch a instance of chrome browser with:
chrome.exe -app=https://www.google.com
Remove the sizing border:
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_THICKFRAME | WS_DLGFRAME);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
WS_THICKFRAME
0x00040000L
The window has a sizing border. Same as the WS_SIZEBOX style.
WS_DLGFRAME
0x00400000L
The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.
The black areas im talking about: left, right, bottom.
As suggested, I use SetWindowPos() with SWP_FRAMECHANGED, but it didn't result in any visible change.
int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
SetWindowPos(hWnd, 0, 0, 0, 0, 0, flags);
Code:
#include <iostream>
#include <windows.h>
int main()
{
HWND hwnd = FindWindowW( L"Chrome_WidgetWin_1", nullptr );
//HWND hwnd = FindWindowW( L"Notepad", nullptr );
std::cout << "hwnd: " << hwnd << "\n";
LONG lStyle = GetWindowLong( hwnd, GWL_STYLE );
lStyle &= ~( WS_THICKFRAME | WS_DLGFRAME );
SetWindowLong( hwnd, GWL_STYLE, lStyle );
int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
auto swp = SetWindowPos( hwnd, 0, 0, 0, 0, 0, flags );
std::cout << "swp: " << swp << "\n";
}

TL;DR
Considering that it works for notepad, it must be a specialty of Google Chrome. The difference should be that Chrome is drawing into the non-client area of the window (i.e. the tabs appear in the title bar, which is kind of special). That means that Chrome is doing a lot of drawing and computations by itself. Apparently, the code does not handle the case that the window has no sizing borders (because Chrome by itself does not support this). Instead, they hardcoded that the client rectangle is always smaller by the sizing border width, even if there is no sizing border.
A workaround would be to start Chrome with --disable-dwm-composition. But I do not really know if there are any unintended side effects when using this option.
Details
Digging through the source code of Chromium, one can see:
First note that the WinAPI function DwmExtendFrameIntoClientArea() is called, which means that Chromium has indeed the special drawing logic to draw the window frame by itself.
To remove the standard frame, the WM_NCCALCSIZE message is handled. Therein, we can find a call to GetClientAreaInsets(), which returns the "insets", i.e. 4 values (for left, top, bottom and right) by which the client rectangle becomes smaller.
Searching for GetClientAreaInsets(), we see that its implementations essentially calls ui::GetFrameThickness() and uses its return value as the inset value. But only if the window is not in fullscreen; indeed, the black border effect does not appear in fullscreen.
ui::GetFrameThickness() calls (indirectly) the WinAPI function GetSystemMetricsForDpi() to get the values of SM_CXSIZEFRAME and SM_CXPADDEDBORDER, i.e. the size of the sizing border. Their sum is returned.
The SM_CXSIZEFRAME and SM_CXPADDEDBORDER apparently map to the registry values BorderWidth and PaddedBorderWidth in HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics. I.e. they are system settings. Changing these values (to a notable more negative value) and restarting the PC does not make the drawn border of windows larger on Windows 10 (although it did in earlier Windows versions). But they do change the invisible "capture" area of the sizing borders. And, in our case here, the black borders shown by Chrome also become larger.
I think the former sizing border areas are drawn black after removing them because of the BLACK_BRUSH in HWNDMessageHandler::OnPaint.
So, in a nutshell, Chrome is simply making the client area smaller by the sizing borders, regardless if the sizing borders actually exist or not. And then draws everything in there. If Chrome were supporting removed sizing borders officially, it could be considered as a bug. But I don't think they do.
How do we work around this behavior from the "outside"?
Considering that SM_CXSIZEFRAME and SM_CXPADDEDBORDER are global settings, changing them (by means of the registry or the SystemParametersInfo(SPI_GETBORDER,...) function is probably out of the question for practical uses.
But looking through the source code, we note that in HWNDMessageHandler::UpdateDwmFrame() the function ui::win::IsAeroGlassEnabled() is called, which in turn returns false if the disable-dwm-composition commandline flag is specified. Hence, starting Chrome with the --disable-dwm-composition flag and then running the code in the OP (removing WS_THICKFRAME | WS_DLGFRAME) does the trick for me. The invisible sizing borders are truly gone, i.e. clicks onto the space where they were are no longer registered by Chrome but by the window below it (e.g. the Desktop).

Related

c++ MFC Window(Frame) Manipulation

I'm working on an Windows Application which has to show an overlaying fixed positioned window ("PopUp") in the left corner of the MainFrame which will receive some Information if a user missed some input or if certain actions have been successfully.
The "PopUp" Titlebar shall have an Icon next to the Title (e.g. ->Icon<- "Error") and the standard X - Close-Button. The ClientArea will have an descriptive text of the occurred Message.
Additionally the standard Border of the PopUp shall be set to 1px(smaller than the default windows border)
The "PopUp" is derived from CWnd and created with WS_VISLBE | WS_CLIPSIBLINGS | WS_CHILD | WS_CAPTION in the OnCreate-Method of the Applications MainFrame Window
Now I need to set/shrink the default Border of my PopUp and add the Icon to the Titlebar of the PopUp.
Can someone give me some example code of how i can solve my issues?
I'm pretty new to c++ and MFC so far my research brought me to https://msdn.microsoft.com/en-us/library/windows/desktop/bb688195(v=vs.85).aspx
but i dont know where and how to use DwmExtendFrameIntoClientArea() but so far I've read I assume Dwm is the way to go to be able to solve both problems or is there another/totally different way? Am I on the right track?
Finally I was able to shrinkthe default Windows Border by overriding the handling of WM_NCCALCSIZE.
I will update this answer as soon as I solved how to put my Icon in the Titlebar.
As of now I'll explain how I shrink the windows border:
Add ON_WM_NCCALCSIZE() to your MessageMap of the desired Window and Implement OnNcCalcSize() (Class Wizard will help to set this up) as followed:
void YourCWndClass::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
if (bCalcValidRects){
CRect rcClient, rcWind;
GetClientRect(&rcClient);
GetWindowRect(&rcWind);
int border = (rcWind.right - rcWind.left - rcClient.right) / 2 - 1;
//-1: leaves 1px of the Windows Default Border Width erase to have no border
lpncsp->rgrc->left -= border;
lpncsp->rgrc->right += border;
lpncsp->rgrc->bottom += border;
}
CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
}
The WM_NCCALCSIZE Message is sent up on the Window Creation (when you call Create()/CreateEx() ) but at this point of time GetClientRect() and GetWindowRect() will not return the proper values therefore you need to check the Bool Parameter!!!
To trigger another WM_NCCALCSIZE to be able to work with the proper Window Rectangles call SetWindowPos() right after the window creation
if (!m_MessagePopOver->Create(NULL, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CAPTION, rect, this, NULL, NULL)){
TRACE0("failed to create MessagePopOver");
}
m_MessagePopOver->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
This will result in a window like this:

Creating window frame without maximize button and without resizing options?

I need to create a window that acts like "normal" one, but without maximize button, and sizing border.
Searching through Internet, and studying MSDN, I have learned that natively achieving both is impossible.
There is no window style that does both ( I can disable maximize button, but that is not my aim; as for removing resizing options, I have found suitable window styles in the documentation ).
The closest description would be the dialogbox frame behavior ( no sizing border ), but with extra minimize button.
QUESTION:
Is there a way to achieve my goal some other way?
If yes, can you please provide links to tutorials or code examples? This would be the first time for me to do such a thing and could use all the help I could get?
An important note: I have found this example while searching for a solution, but it will not help me because I target Windows XP onwards.
Creating a window as below will give you a non-sizeable window with a title bar, a minimize button and an exit button.
dwStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX;
hWnd = CreateWindow(szAppName, szTitle, dwStyle,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
See http://msdn.microsoft.com/en-us/library/windows/desktop/ms632679%28v=vs.85%29.aspx
and http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600%28v=vs.85%29.aspx
I suppose you are creating the window using CreateWindowEx. Then, if you omit both WS_MAXIMIZEBOX and WS_MINIMIZEBOX flags (the dwStyle parameter), the window will have only the close button (no minimize/maximize) buttons. If you ommit just WS_MAXIMIZEBOX, Windows draw the maximize box disabled to keep the graphics layout consistent for all windows. There is no way to change this behavior, and it can change in different versions of Windows (Win3.1, for instance, didn't draw the maximize button at all when the flags were set as mentioned.)
Resizable border is disabled by setting other frame than WS_THICKFRAME (ie. WS_BORDER or WS_EX_DLGMODALFRAME in the dwExStyle parameter).
You can also control the user sizing/moving of your window by intercepting messages WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_SIZING and WM_MOVING.
In theory, you can also completly change the appearance of the non-client area of the window, but it's hardly worth the effort, and it's questionable whether it's a good idea to fight with the default graphic layout of the operating system when all the developers and user are used to it and content with it. (In other words: if you don't want your window to be maximized, just omit the WS_MAXIMIZEBOX flag and leave it on the operating system how to realize this particular decision.)
I'm pretty sure it is documented on MSDN that the window style you want to OMIT is WS_THICKFRAME, since the Window Styles page says that a thick frame is a sizing frame.

Groupbox resizing issue with radio buttons on top

I'm not sure what I'm doing wrong here. I'm trying to implement a resizing dialog window using MFC. The code is pretty straightforward. I override the following sizing notification:
void CMyDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
//...
//First move the groupbox, pGroupbox is of type CWnd
pGroupbox->MoveWindow(rcGroupbox);
//And then move all radio buttons in it
//Each is moved the exact same way
//pEachRadioButton is of type CWnd
pEachRadioButton->MoveWindow(rcEachRadioButton);
}
But what I get as a result is this.
First here's the initial groupbox:
It happens only when I start dragging the bottom of the main window frame down. I get this artifact:
Note that the radio button positions themselves are correct. If I move the mouse over either of them, it redraws itself correctly (like this "shut-down" button):
Here's the layout of the dialog itself:
IDD_MY_DIALOG DIALOGEX 0, 0, 437, 190
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "My dialog"
MENU IDR_MENU_MAIN
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
PUSHBUTTON "&Cancel",IDCANCEL,381,169,50,14
GROUPBOX "When Tasks Are Completed",IDC_STATIC_WHEN_COMPLETED,7,113,423,36
CONTROL "Close the pro&gram",IDC_RADIO_CLOSE_PROGRAM,"Button",BS_AUTORADIOBUTTON | WS_GROUP,26,129,73,8
CONTROL "Put computer to sleep",IDC_RADIO_SLEEP,"Button",BS_AUTORADIOBUTTON,122,129,84,10
CONTROL "Hibernate computer",IDC_RADIO_HIBERNATE,"Button",BS_AUTORADIOBUTTON,229,129,78,10
CONTROL "Shut down computer",IDC_RADIO_SHUT_DOWN,"Button",BS_AUTORADIOBUTTON,330,129,81,10
DEFPUSHBUTTON "&OK",IDC_BUTTON_SET,311,161,67,22
END
I did some search and found this article, but unfortunately setting those styles did not fix the bug.
Any idea how to fix this?
PS. I'm testing it on Windows Vista, 7, or 8 with visual themes enabled.
When you move a window, the window manager will move the current image of the window as it exists. Unfortunately because you moved the frame first, all those windows got clipped. Flipping them around wouldn't help, because then the tops would get clipped.
The easy way to fix it would be to call InvalidateRect on each control after moving it.
The better way would be to call BeginDeferWindowPos before you start moving anything, then EndDeferWindowPos when you're done so that all the windows move together.
P.S. Windows prefers for the group box to come after the radio buttons in the tab order, that might make a difference too.

How to implement a re-sizable border + making it invisible using MFC?

I was wondering, how do I create a re-sizable border in MFC without showing the border itself?
IDD_GADGETTRANSLUCENTDIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_SETFONT | DS_FIXEDSYS | WS_SYSMENU | WS_THICKFRAME
The "WS_THICKFRAME" style provides the functionality of resizing, but I don't want the border to be visible. How would I go about doing this?
Or handle WM_NCPAINT and draw the borders (and caption) yourself...
I created a MCF application that has the borders (and frame) as optional. When in the mode of no border, I still wanted it resizable and movable, so essentially in OnMouseMove if I was within a few pixels of the edge of the window, I set the appropriate cursor (e.g. IDC_SIZENESW for the top right corner) then if nFlags & MK_LBUTTON did a SendMessage using WM_SYSCOMMAND and the appropriate size command (e.g. SC_SIZE + WMSZ_TOPRIGHT) and let the CWnd::OnSysCommand default handler process it. Also, same trick works with SC_MOVE...

WinAPI: Create resizable window without title bar, but with minimize/maximize/close buttons (as Firefox/Chrome/Opera)

If you look at the windows of the browsers Firefox, Chrome or Opera, you'll notice that their windows
have minimize/maximize/close buttons
are resizable
but have no title bar
I'm interested: how can I create such a window?
What I have already tried:
I looked around on StackOverflow (and googled, too), and found this: opening a window that has no title bar with win32
Unluckily, this didn't help completely:
The first step was to extend the solution proposed on opening a window that has no title bar with win32
hWnd = CreateWindow(szWindowClass, szTitle, WS_BORDER,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
SetWindowLong(hWnd, GWL_STYLE, WS_SIZEBOX);
// See remarks on http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545.aspx
SetWindowPos(hWnd, 0,
0, 0, 0, 0, // Position + Size
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
Of course, this delivers no minimize/maximize buttons, but on the other hand, if I want minimize/maximize buttons, I have to do:
SetWindowLong(hWnd, GWL_STYLE, WS_SIZEBOX | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION);
Why does this combination seem to be necessary? First I probably want WS_MAXIMIZEBOX | WS_MINIMIZEBOX since I want these buttons.
But http://msdn.microsoft.com/en-us/library/ms632600.aspx says that if I set one of WS_MAXIMIZEBOX and WS_MINIMIZEBOX, I also have to set WS_SYSMENU. And when I set WS_SYSMENU, I also have to set WS_CAPTION but this is not what I want, because I wanted to avoid the title bar (indeed: if WS_CAPTION is not set, no minimize/maximize buttons are shown).
So what is to do?
The programs remove the non-client area (the title bar) and have a bunch of custom handling for reproducing the window buttons, icons, system menu etc. The benefit of this is that they can draw to the new "title bar", which is actually part of the standard client area, adding tabs or other custom controls.
The following two articles will show you how to do this on Vista and above (using the DWM):
Setting up a custom title bar on Windows Vista / 7
Setting up a custom title bar - reprise This one has a demo app showing the result of a number of variations / options.
This is very complex to do and get right, so the above two articles are invaluable. The author must have put a lot of work into them! Both links have example code written in Delphi, but it should be easy enough to translate it to C++ - the concepts are identical, it's just syntax.
You might also be interested in general resources on Glass and DWM, since it's all closely related. You'll spot the above two links included in that list :)
You can create a window with or without caption - whatever is more appropriate from the point of view of desired customization (that is "without" is you want to do it "without title bar" as you say), and the important wart is that you take over painting non-client area - this is the key thing.
At this point, there is no one to paint your mimimize/maximize buttons already. It does not however mean that you have to do the painting right from scratch and mimic standard UI. There is DrawFrameControl and friends API where you can use DFCS_CAPTIONMIN argument and have minimize button painted for you. You will also want to respond to other non-client area messages, e.g. handle WM_NCHITTEST to tell Windows where your new window buttons are.
You might also want to check Visual Styles Reference to leverage theme-enabled drawing API such as DrawThemeBackground.
A simple example of this activity is putting an additional button onto caption, such as described in detail here: CCaptionButton (buttons for the titlebar).
I believe they create a normal window and then paint over the title bar with their custom widgets/tabs. This is evident in Firefox, as when it hangs you can see the normal Windows title bar appear over the tabs.