I'm creating a window and I'm defining the height as 768:
RECT windowRect;
windowRect.left = (long)0;
windowRect.right = 1366;
windowRect.top = (long)0;
windowRect.bottom = 768;
...and the following styles:
dwExStyle = WS_EX_APPWINDOW | WS_EX_CLIENTEDGE;
dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
...and finally doing this:
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
//windowRect.left = -11;
//windowRect.right = 1377;
//windowRect.top = -38;
//windowRect.bottom = 779;
And then I'm creating another window trying to fill the whole screen, like this:
hTest = CreateWindow(L"STATIC", L"Testing", WS_CHILD | WS_VISIBLE | SS_BITMAP, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, hWnd, NULL, hInstance, 0);
With width of 1366 and height of 768. My computer's resolution is 1920 x 1080.
The width is perfectly aligned, exactly 1366.
The height is weird though, at 768 there's about a 10px margin below. If I use values up to 779, nothing changes, the same 10px margin in the bottom is still there. Then if I change to 780px, it's fully covered.
Why does this happen and what's the best way to deal with this? I'd like to have a window menu with exactly the same client height available.
EDIT: I've tried:
RECT r;
GetClientRect(hWnd, &r);
//r.right = 1366
//r.bottom = 768
I don't know why this weird area in bottom is showing, even though the numbers say it's fine...
It's the black border below (the white area is the new child window I'm creating):
Although it's in a bad quality (for size), there's exactly 1366 x 768 pixels of client area, which is what I requested in the CreateWindow function.
Creating a child window for this main window with 768 pixels of height causes the image above.
Adding a padding at top to check if the top 0 is actually 0 (and it isn't using the title border) shows that it is actually 0, that is, if I add any positionX to the child window I see the black background above it.
Related
I'm trying to place dialog window adjacent to desktop's top or bottom border.
Code:
CRect MonitorWorkingArea(HMONITOR monitor)
{
MONITORINFOEX monInfo;
zeroVar(monInfo);
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(monitor, &monInfo);
return monInfo.rcWork;
}
CRect MonitorWorkingArea_Wnd(HWND hwnd)
{
return MonitorWorkingArea(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST));
}
CRect MonitorWorkArea_Wnd(CWnd& wnd)
{
return MonitorWorkingArea_Wnd(wnd.GetSafeHwnd());
}
void CMyDialog::SetDefaultWndPos(bool toBottom)
{
// Here we can be sure, that
// a) parent of this window is not null, is visible, not minimized
// b) this window smaller than working area of desktop
// c) it has WS_POPUP style
CRect rcThis;
GetWindowRect(&rcThis);
// Shift our rectangle to most upper or most bottom position
const CRect rcDesktop = MonitorWorkingArea_Wnd(*this);
rcThis.MoveToY(toBottom ? rcDesktop.bottom - rcThis.Height() : 0);
// Move window
SetWindowPos(NULL, rcThis.left, rcThis.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
Real code is bit more complex, I omit some checks and features for simplicity.
I have two monitors, both are 1920×1080 and in virtual coordinate space they are laying on:
Primary: (0,0) — (1920,1080)
Secondary: (1920,0) — (3840, 1080)
Code above works fine when window placed on primary monitor, but on secondary one it's works unexpectedly: it centers window based on it's parent.
For instance I'm calling SetDefaultWndPos(false) and rcThis being calculated as {left:2710, top:0, right:3423, bottom:550}. So SetWindowPos is called with zero y:
SetWindowPos(NULL, 2710, 0, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
But in handler of WM_WINDOWPOSCHANGING
void CMyDialog::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
__super::OnWindowPosChanging(lpwndpos);
}
I see in debug x = 2710 (unchanged) and y = 475 (unexpected). Next I check dialog's y-position in Spy utility, it's 475. Dialog window looks like it was centered on it's parent window.
All it looks like an OS errorneously detected requested window position as out-of-desktop and used some default positioning.
Only workarund I found is to decrease desktop working area on 1 pixel from each side:
CRect MonitorWorkingArea(HMONITOR monitor)
{
MONITORINFOEX monInfo;
zeroVar(monInfo);
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(monitor, &monInfo);
CRect rc = monInfo.rcWork;
if( !(monInfo.dwFlags & MONITORINFOF_PRIMARY) )
{
++rc.left;
++rc.top;
--rc.right;
--rc.bottom;
}
return rc;
}
It works but looks as strange ugly hack.
Can anybody explain what happens here and help me with good decision?
Can it be OS version dependent? (I use Windows 7 Professional SP1).
Having a bit of a problem in my dx9 application.
When I switch back from Full screen to windowed mode using the code below the client area is not the right size, it's smaller.
The AdjustWindowRect function is doing its task correctly but SetWindowPos does not set the right window size. Perhaps I'm missing something. Any ideas .
if (d3dpp.Windowed && resChoice == resolutionchoice) return false; // already in window mode and same resolution
//MessageBox(NULL, L"switching to window", L"ERROR", MB_OK);
resChoice = resolutionchoice;
screenWidth = resolutionwidth[resChoice];
screenHeight = resolutionheight[resChoice];
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // set the back buffer format to 32-bit
d3dpp.BackBufferWidth = screenWidth; // set the width of the buffer
d3dpp.BackBufferHeight = screenHeight; // set the height of the buffer
d3dpp.Windowed = true;
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); // WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_MINIMIZEBOX );
// need to call SetWindowPos as well
string values;
RECT r = { 0,0,screenWidth,screenHeight };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
values = std::to_string(r.left);
OutputDebugStringA("Adjust area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
SetWindowPos(hWnd, HWND_TOP, r.left, r.top, r.right - r.left , r.bottom - r.top, SWP_NOZORDER | SWP_SHOWWINDOW | SWP_FRAMECHANGED);//
//screenWidth = SCREEN_WIDTH;
//screenHeight = SCREEN_HEIGHT;
//windowXscale = 1;
//windowYscale = 1;
GetClientRect(hWnd, &r);
values = std::to_string(r.left);
OutputDebugStringA("Client area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
}
After some more searching discovered that if you use SetWindowPos and attempt to make a window that is bigger than the desktop or when returning from Full screen in this case, windows will send a message and the window size will be adjusted automatically to make it no bigger than the current desktop size.
Adding SWP_NOSENDCHANGING flag in the SetWindowPos api call stops this happening and the client size will be what you wanted.
void Initialize_Window(void)
{
RECT rConsole;
GetWindowRect(GetConsoleWindow(), &rConsole);
SetWindowPos(GetConsoleWindow(), NULL, 0, 0, 800, 700, 0);
SetWindowLong(GetConsoleWindow(), GWL_STYLE, GetWindowLong(GetConsoleWindow(), GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX));
SetWindowPos(GetConsoleWindow(), NULL, (GetSystemMetrics(SM_CXSCREEN) - rConsole.right - rConsole.left) / 2, (GetSystemMetrics(SM_CYSCREEN) - rConsole.bottom - rConsole.top) / 2, 0, 0, SWP_NOSIZE);
}
I'm trying to center my console window by using the code above, but seems like the window just moved to a random position on my screen every time I execute the program, any idea how to fix it?
You need (GetSystemMetrics(SM_CXSCREEN) - (rConsole.right - rConsole.left))/2 to get center.
Side note: you can use one SetWindowPos instead of two (and do not need to get window Rect)
const int width = 800;
const int height = 700;
//SetWindowLong()...
SetWindowPos(GetConsoleWindow(), NULL,
GetSystemMetrics(SM_CXSCREEN)/2 - width/2,
GetSystemMetrics(SM_CYSCREEN)/2 - height/2,
width, height, SWP_SHOWWINDOW);
Don't use GetSystemMetrics() for this because it only returns the metrics of the primary monitor. Multi monitor setups are quite common these days so users will be rightly upset if you ignore that.
In addition, a window normally should not be aligned to the physical monitor surface, but to the work area which excludes the taskbar(s). Yes, there can be multiple taskbars (called "appbars" in Windows slang) on either side of the screen. An exception where you would actually use the full physical surface are full screen windows.
To cover both aspects we can use MonitorFromWindow() and GetMonitorInfo().
First we get the "nearest" monitor from the window handle. This is the monitor that either shows the window completely or that has the biggest area of the window on it:
HWND hConsoleWnd = ::GetConsoleWindow();
HMONITOR hMonitor = ::MonitorFromWindow( hConsoleWnd, MONITOR_DEFAULTTONEAREST );
Then we get the work area rectangle of that monitor and center the window relative to that:
if( hMonitor )
{
MONITORINFO info{ sizeof(info) }; // set cbSize member and fill the rest with zero
if( ::GetMonitorInfo( hMonitor, &info ) )
{
int width = 800;
int height = 700;
int x = ( info.rcWork.left + info.rcWork.right ) / 2 - width / 2;
int y = ( info.rcWork.top + info.rcWork.bottom ) / 2 - height / 2;
::SetWindowPos( hConsoleWnd, nullptr, x, y, width, height,
SWP_NOZORDER | SWP_NOOWNERZORDER );
}
}
That's it. In a real-world application you should of course not hardcode the window size because it is a user preference. For first launch a default size can be reasonable but even that should not be hardcoded but scaled according to the Windows DPI settings.
INTRODUCTION AND RELEVANT INFORMATION:
I am trying to implement listview control with editable items and subitems. Instead of regular listview look, items and subitems should have edit control, checkbox or combo box.
I am using raw WinAPI and C++. I am targeting Windows XP onwards.
MY EFFORTS TO SOLVE THE PROBLEM:
After researching here and on the Internet, I was able to only find examples in MFC. They all use LVN_BEGINLABELEDIT technique to implement this behavior.
Unfortunately I do not understand entirely this concept so I have decided to start from scratch ( I consider this also to be the best approach for improving ones programming skills ).
MY CONCEPT:
I have decided to catch NM_DBLCLK for listview and to get coordinates from there using ListView_GetItemRect or ListView_GetSubItemRect macro.
Then I would simply move the combobox/checkbox/edit control over corresponding item/subitem ( combobox/edit control/checkbox would be created as separate, hidden windows ).
After user finishes with input ( by pressing enter or changing focus ) I would simply hide the combobox/checkbox/edit control.
MY CURRENT RESULTS:
At the moment, I am stuck with the dimensions of combobox/edit control/checkbox not being the same as item/subitem dimensions, when moved above the item/subitem.
QUESTION:
Can my code example submitted below be improved to properly adjust combobox/edit control/checkbox window size to the size of the item/subitem? For now, I will only focus on this part of the problem, to keep this question as short as possible.
Here is the instruction for creating small application that illustrates the problem. Notice that I have tried to keep things as minimal as I could:
1.) Create default Win32 project in Visual Studio ( I use VS 2008 ).
2.) Add the following WM_CREATE handler to main window's procedure:
case WM_CREATE:
{
HWND hEdit = CreateWindowEx( 0,WC_EDIT, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER | ES_AUTOHSCROLL,
250, 10, 100, 20, hWnd, (HMENU)1500, hInst, 0 );
HWND hComboBox = CreateWindowEx( 0,WC_COMBOBOX, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST,
100, 10, 100, 20, hWnd, (HMENU)1600, hInst, 0 );
HWND hwndLV = CreateWindowEx( 0, WC_LISTVIEW,
L"Editable Subitems",
WS_CHILD | WS_VISIBLE | WS_BORDER |
LVS_REPORT | LVS_SINGLESEL,
150, 100, 250, 150, hWnd, (HMENU)2000, hInst, 0 );
// set extended listview styles
ListView_SetExtendedListViewStyle( GetDlgItem( hWnd, 2000 ),
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER );
// add some columns
LVCOLUMN lvc = {0};
lvc.iSubItem = 0;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Column %d", nIndex + 1 );
lvc.iSubItem = nIndex;
lvc.cx = 60;
lvc.pszText = txt;
ListView_InsertColumn( GetDlgItem( hWnd,2000 ), nIndex, &lvc );
}
// add some items
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = 0;
for( lvi.iItem = 0; lvi.iItem < 10; lvi.iItem++ )
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Item %d%d", lvi.iItem + 1, nIndex + 1 );
lvi.iSubItem = nIndex;
lvi.pszText = txt;
if( ! nIndex ) // item
SendDlgItemMessage( hWnd, 2000,
LVM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
else // sub-item
SendDlgItemMessage( hWnd, 2000,
LVM_SETITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
}
}
return 0L;
3.) Add the following handler for WM_NOTIFY in main window's procedure:
case WM_NOTIFY:
{
if( ((LPNMHDR)lParam)->code == NM_DBLCLK )
{
switch( ((LPNMHDR)lParam)->idFrom )
{
case 2000: // remember, this was our listview's ID
{
LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
// SHIFT/ALT/CTRL/their combination, must not be pressed
if( ( lpnmia->uKeyFlags || 0 ) == 0 )
{
// this is where we store item/subitem rectangle
RECT rc = { 0, 0, 0, 0 };
if( (lpnmia->iSubItem) <= 0 ) // this is item so we must call ListView_GetItemRect
{
// this rectangle holds proper left coordinate
// since ListView_GetItemRect with LVIR_LABEL flag
// messes up rectangle's left cordinate
RECT rcHelp = { 0, 0, 0, 0 };
// this call gets the length of entire row
// but holds proper left coordinate
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rcHelp, LVIR_BOUNDS );
// this call gets proper rectangle except for the left side
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rc, LVIR_LABEL );
// now we can correct the left coordinate
rc.left = rcHelp.left;
}
else // it is subitem, so we must call ListView_GetSubItemRect
{
ListView_GetSubItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, lpnmia->iSubItem,
LVIR_BOUNDS, &rc );
}
// convert listview client coordinates to parent coordinates
// so edit control can be properly moved
POINT p;
p.x = rc.left;
p.y = rc.top;
ClientToScreen( lpnmia->hdr.hwndFrom, &p );
ScreenToClient( hWnd, &p );
MoveWindow( GetDlgItem( hWnd, 1500 ),
p.x, p.y,
rc.right - rc.left,
rc.bottom - rc.top, TRUE );
// set focus to our edit control
HWND previousWnd = SetFocus( GetDlgItem( hWnd, 1500 ) );
}
}
break;
default:
break;
}
}
}
break;
And this is the result I get:
You can clearly see that top and bottom border of the edit control are not drawn properly. As for combobox, the width is properly adjusted, but height remains the same.
I have tried substituting MoveWindow call with SetWindowPos but the result was the same.
After further tampering, I have found out that NMITEMACTIVATE bugs when returning the rectangle of a subitem, if listview doesn't have LVS_EX_FULLROWSELECT style set. You can see this by simply commenting out the part in my WM_CREATE handler where I set this style. Maybe I am doing something wrong and this "bug" may be caused by my code, but I don't see the problem.
EDITED on September, 17th 2014:
After testing the values for iItem and iSubItem members of NMITEMACTIVATE structure when listview doesn't have LVS_EX_FULLROWSELECT I can verify that the bug is not in my code. It always returns iItem to be 0, no matter which subitem I click. This explains the faulty behavior I got when removing this style.
If any further info is required please leave a comment and I will act as soon as possible.
Thank you for your time and efforts to help.
The issue you're facing is multi-faceted.
Firstly, the default font of the edit control is larger (higher) than that of the list-view. You can fix this one quite trivially, by first getting the font from the list-view and then setting it to the edit control. Doing this will then make the bottom border of the control visible.
The next issue is that the caret of the edit control needs a pixel above and below it, to ensure that the control doesn't have its borders interfered with. In addition to this 1 pixel of 'space' you then need another pixel for the border.
Added to this second point, the dimensions calculated by rc.right - rc.left and rc.bottom - rc.top are 1 pixel too small. Think of a rect that starts at 1,1 and extends to 2,2 - this is a rect of 4 pixels - 2 wide and 2 high. Simply subtracting the top/left from the bottom/right would give you a width/height of only 1 pixel each. To fix this, you need to add 1 to each of these subtractions.
Finally, since the caret is exactly the height of the 'client-area' of each item/sub-item, you need to make the edit control 2 pixels taller than the item/sub-item, and start 1 2 pixels higher than it does currently.
Here's the output I get when making the suggested changes:
And here's the changes/additions I made.
1. Get/Set the font. (inserted after creating the list-view and before setting its extended style)
HFONT lvFont = (HFONT)SendDlgItemMessage(hWnd, 2000, WM_GETFONT, 0, 0);
SendDlgItemMessage(hWnd, 1500, WM_SETFONT, (WPARAM)lvFont, TRUE);
2. Set the window position/size
MoveWindow( GetDlgItem( hWnd, 1500 ),
p.x, p.y-2,
1+ rc.right - rc.left,
1+ 2 + rc.bottom - rc.top, TRUE );
Finally, contrast this against the original output from your code:
UPDATE:
Here's a snapshot of the appearance when the built-in label editing functionality is used (LVS_EDITLABELS style)
I'm wondering if it's possible to toggle back and forth between fullscreen mode and windowed mode in an OpenGL window(I'm writing for Windows using C++ and win32), without destroying the OpenGL context, and thus having to reload assets(Textures, VBOs, etc) in the process?
This is undesirable because it introduces a delay in switching between fullscreen and windowed mode, potentially a long one, as well as making it easier to screw things up by forgetting to reinitialize something.
As a followup to that, are there certain visual effects that are broken by managing to do this?
I've done a fair bit of searching and reading for the past few days, and despite a lot of flaming of SDL and other frameworks for having the same problem(I'm not using them anyway, but...), the best I've managed to find is a possible lead on opening a 1x1 window in the background to retain the context while a secondary window is destroyed or created at whim. And that's seeming unreliable from the comments I found regarding it, and seems very kludgey regardless.
Is there a proper way to do this, or is the proper way the often-given-as-an-example method of destroying your window, and recreating it, including destroying your OpenGL context and recreating it?
Basically it's just resizing the window and specifying flags that the border is invisible.
SetWindowLongPtr(hWnd, GWL_STYLE,
WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
MoveWindow(hWnd, 0, 0, width, height, TRUE);
to set it back:
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
or for a not-resizable window:
SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
and then just resize your OpenGL viewport settings.
If you want to set the display mode too, use this:
// change display mode if destination mode is fullscreen
if (fullscreen) {
DEVMODE dm;
dm.dmSize = sizeof(DEVMODE);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmBitsPerPel = bitsPerPixel;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
success = ChangeDisplaySettings(&dm, 0) == DISP_CHANGE_SUCCESSFUL;
}
// reset display mode if destination mode is windowed
if (!fullscreen)
success = ChangeDisplaySettings(0, 0) == DISP_CHANGE_SUCCESSFUL;
Here's the code I use, which uses SetWindowPos() rather than MoveWindow(), as discussed in the comments of the other answer.
void enter_fullscreen(application* App)
{
POINT Point = {0};
HMONITOR Monitor = MonitorFromPoint(Point, MONITOR_DEFAULTTONEAREST);
MONITORINFO MonitorInfo = { sizeof(MonitorInfo) };
if (GetMonitorInfo(Monitor, &MonitorInfo)) {
DWORD Style = WS_POPUP | WS_VISIBLE;
SetWindowLongPtr(App->Window, GWL_STYLE, Style);
SetWindowPos(App->Window, 0, MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW);
}
App->IsFullscreen = true;
}
void exit_fullscreen(application* App)
{
bool WasMaximized = App->IsMaximized;
DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
if (WasMaximized) {
Style = Style | WS_MAXIMIZE;
}
ivec2 WindowPosition = WasMaximized ? App->WindowPosition : App->NormalWindowPosition;
ivec2 WindowSize = WasMaximized ? App->WindowSize : App->NormalWindowSize;
SetWindowLongPtr(App->Window, GWL_STYLE, Style);
SetWindowPos(App->Window, 0,
WindowPosition.X, WindowPosition.Y, WindowSize.X, WindowSize.Y,
SWP_FRAMECHANGED | SWP_SHOWWINDOW);
App->IsFullscreen = false;
}
I call it on F11, but also on WM_ACTIVATE. Otherwise the window would sometimes keep rendering on top on Windows 7, even if another application would receive all messages, including mouse and keyboard.