I have two monitors that are running at different resolutions. Left monitor is 1920x1200. Right monitor (the primary monitor) is 1920x1080.
I want to use SetWindowPos to make a window take up the full vertical height of the left hand monitor.
Here's what I do:
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
hMonitor = monitorFromPoint(x, 0, MONITOR_DEFAULTTONEAREST);
MONITORINFO moninfo;
moninfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, moninfo);
height = moninfo.rcWork.bottom - moninfo.rcWork.top;
SetWindowPos(hwnd, 0, moninfo.rcWork.left, moninfo.rcWord.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
I have confirmed that height is computing to 1200 (expected b/c that is the target monitor's vertical resolution).
However, after the call to SetWindowPos, the window rectangle doesn't fill the entire height of the screen (it is actually 1080 high).
I even tried this in VBA just for giggles:
Public Sub testSWP()
Dim hwnd As Long
hwnd = &H1D2F2C
SetWindowPos &H1D2F2C, 0, -1900, 0, 150, 1200, SWP_NOZORDER Or SWP_NOACTIVATE
Dim r As RECT
GetWindowRect hwnd, r
' at this point, r.bottom = 1080
End Sub
This is well and good (GetWindowRect documentation says coordinates will be in Client space, and I'm assuming that win32 is translating between the resolution of my primary and secondary monitor.
I'm getting ready to inflate the vertical dimension by the ratio of the heights of the target and primary monitor. I'm pretty sure this is going to work, but it seems like a lot of hoops to have to jump through - am I maybe just not aware of a better way of determining the screen dimensions in 'client coordinates'?
The issue isn't with coordinate transformation. It is that windows isn't allowing SetWindowPos to adjust the window so it is larger than the screen. Of course, it is basing this on the primary monitor size.
See: Can a window be resized past the screen size/offscreen?
Do you want a normal window (with titlebar etc) or do you want a fullscreen window (like youtube fullscreen video playback or games).
I think you want the latter i.e. make a fullscreen window which covers the entire screen. For that, in the call to CreateWindow, pass WS_POPUP as the window style (see dwStyle param). This will create the window without the titlebar and it will cover the entire screen.
Also, I don't think the way you're getting the left monitor is correct. You should be using EnumMonitors to iterate through all the monitors, get the left most monitor and then use GetMonitorInfo to retrieve the monitor's rects if you want to make this a generic application.
Related
First, if this isn't a great way to do this, PLEASE suggest better. I can't seem to find an exact solution to what must be a common desire (see post title).
My approach was to trap the WM_PAINT message and check whether the window position is maximized. If it is, then run this code:
SetWindowLong(hCDGWnd, GWL_STYLE, 0); // remove all styling
SetWindowPos(hCDGWnd, HWND_TOP, 0, 0, 1280, 720, SWP_SHOWWINDOW); // full screen is 1280 x 720
StretchDIBits(hdc, 0, 0, 1280, 720, CDG_XOFFSET, CDG_YOFFSET, CDG_RENDER_WIDTH, CDG_RENDER_HEIGHT,
bmBits, (LPBITMAPINFO)&bmInfo, DIB_RGB_COLORS, SRCCOPY);
What I want to see is my BITMAP stretched to fill the entire client area, which should just fill the entire screen. Actually it is a section taken out of a larger bitmap. It seems like the window is in fact full screen, but the bitmap is its normal size and not stretched. Curiously, when I was fiddling with the window positioning stuff I had all kinds of attempts leaving the styling but trying to position the title bar and frame offscreen (see below) - the bitmap was appearing stretched just fine during those close but failed attempts. Now I've got the window right, suddenly the bitmap no longer stretches. Is there something about removing the styling that would screw up the StretchDIBits function?
Also, when I attempt leaving the style in place, and use AdjustWindowRect() to have my client size be fullscreen, it returns {-3, -26, 1283, 723 } which makes sense - 3 pixel border plus 23 more for title bar on top. But, just to explore things, when I don't even test for maximized state, and just make the window have x = -3, y = -26, cx = 1286, cy = 749, then almost everything is fine except the bottom of the window is shy of fullscreen by about 4 pixels. When I make the window height much bigger - say 760 - IT STAYS THE SAME HEIGHT!? I so confused. If try this maneuver only when maximized, it seems like windows ignores my attempts to have the title bar off the top of the screen.
I use the handle of a window from another process to resize and reposition it from an MFC based app. The handle comes from a window search for Title.
Repositioning works fine, resizing too - if not larger then the screen resolution.
But that's the problem - I want it to be bigger then the actual screen.
Even if set partially outside the visible screen area with negative coordinates in SetWindowPos it does not scale above the values returned by GetSystemMetrics(SM_CXSCREEN) and GetSystemMetrics(SM_CYSCREEN)
Any ideas or hints?
Say you have a form that you can expand to the left to show additional controls:
Collapsed:
Expanded:
The simplest way to achieve this in Delphi is to use alRight as the primary anchor for all controls (instead of alLeft) and then simply adjust the width and X coordinate of the form. Either you can set the Width and Left properties individually, or you can use a function that sets them simultaneously, like
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0)
The problem is that there is quite noticeable flickering in the always-visible part of the form (in this example, the buttons) while expanding or collapsing. Try it yourself!
It is possible for the operating system to resize the form to the left without any flickering at all -- just grab the left edge of the form using the mouse and drag the mouse to the left or right -- but I am unable to find any function in the Windows API that exposes this kind of resizing.
I have tried to use several different Windows API functions to resize and reposition the form, tried their various parameters (for instance, the SWP_* flags), tried LockWindowUpdate, WM_SETREDRAW, TForm.DoubleBuffered etc. to no avail. I also examined the possibility to use the WM_SYSCOMMAND SC_SIZE approach.
I am not yet sure if the problem lies at the OS level or the VCL level.
Any suggestions?
Edit: I am very surprised to see that this Q received close votes. Let me try to clarify:
Create a new VCL forms application.
Add a few buttons to the right side of the main form and a memo to the left. Set Anchors to [alTop, alRight] on all controls. On the OnClick handler of the buttons, add the following code:
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0);
FCollapsed := not FCollapsed;
where FCollapsed is private boolean field of the form (initialized to false).
Now, click the buttons repeatedly. (Or give one of them keyboard focus and hold the Enter key for a few seconds.) You will probably notice that the region with the buttons on your monitor will not display a perfect still image, but will flicker. In addition, you might actually see 'ghosts' of the buttons to the left of the actual column of buttons.
I am unable to capture this millisecond flickering using screen capture, so instead I used a digital camera to record my screen:
https://privat.rejbrand.se/VCLFormExpandFlicker.mp4
In this video clip, it is apparent that the column of buttons isn't a static image on the screen; instead, for a few milliseconds each time the form is resized, this region is something else than it should be. It is equally apparent that there is a 'ghost' column of buttons to the left.
My question is if there is any reasonably simple way to get rid of these visual artefacts (that at least to me are very visible even if you expand/collapse the form a single time).
On my Windows 10/Delphi 10.1 computer at work, the form is resized in a perfect manner when I drag its left-most edge using the mouse: the unaffected client area of the form is perfectly static on the monitor. However, on my Windows 7/Delphi 2009 PC at home, I do see that there is a lot of repositioning going on when I do this.
I can provide some insight about why you see ghost images of the other half of your UI and possibly a way to stop it. The ghost image indicates that someone is copying your client area pixels (and copying them to the wrong place, always flush-left in your window) before you have a chance to redraw them with the correct pixels.
There are likely two different, overlapping sources of these ghost pixels.
The first layer applies to all Windows OSes and comes from a BitBlt inside SetWindowPos. You can get rid of that BitBlt in several ways. You can create your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
However, Windows 8/10 aero adds another, more troublesome layer. Apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.
For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, please see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Since you have multiple child windows, the situation is even a little more complicated. The BitBlt type operations I mentioned above happen on your whole top-level window as a whole (they treat the window as one set of pixels regardless of how many windows are underneath, and regardless of CLIPCHILDREN). But you need to have windows move atomically so that on the next redraw they are all positioned correctly. You may find BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos useful for that (but only go there if the above tricks do not work).
I'm working on a project that requires me to draw a thin line horizontally across the screen and drag it vertically. To add a bit of flair I'm trying to add the DWM glass effect to the window so that it will match window boders in Windows 7 and 8 (though I know I won't get transparency in Windows 8).
I've got the line drawn by creating a child window with the non client area extended into the client area using the sheet of glass trick with DwmExtendFrameIntoClientArea like this:
MARGINS margins = { -1, -1, -1, -1 };
DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
DwmExtendFrameIntoClientArea(DIV_HWND, &margins);
And I create the window like this:
HWND DIV_HWND = CreateWindow(DIV_NAME,
NULL,
WS_VISIBLE,
0, 0, mon_info.rcWork.right, mon_info.rcWork.top + 3,
top->hwnd, NULL,
hInstance, NULL);
And the window class like this:
const wchar_t DIV_NAME[] = L"DIV";
WNDCLASS DIV = {};
DIV.lpfnWndProc = DIV_PROC;
DIV.hInstance = hInstance;
DIV.lpszClassName = DIV_NAME;
DIV.hCursor = LoadCursor(NULL, IDC_SIZEALL);
RegisterClass(&DIV);
I've gotten the window down to a width of 16 pixels with all the nice borders and drop shadow effects I expect to see around a windows border by handling the WM_GETMINMAXINFO message and returning 2 as the ptMinTrackSize.x and y but I can't seem to get it smaller than that.
I can get a "glassy" window by using DwmEnableBlurBehindWindow on a region I set with SetWindowRgn but the color is that of a background window even when it is in focus. Additionally I miss the slight drop shadow and border.
It seems like I might be hitting the size restriction due to border sizes since each border is 8 pixels wide.
Any help is greatly appreciated!
I've found my answer so I'll leave it here on the off chance someone else needs it. All I had to do was call DwmSetWindowAttribute(hwmd, DWMWA_NCRENDERING_POLICY, DWMNCRP_ENABLED, sizeof(int)); to ensure the DWM rendering policy was not based on the window style. This allowed me to use a WS_POPUP window to get the size I wanted with a nicely glass filled interior.
There are a few bugs here:
There is a strange shadow along the top and bottom if the Y dimension of the window is smaller than 16px.
On Vista\7 the rounded window edges give the window a strange oval-like shape.
i've created a layered window by adding the the WS_EX_LAYERED extended style:
wndClass.ExStyle = wndClass.ExStyle | WS_EX_LAYERED;
Windows will use black as the chroma key color value. i'm going to leave a large border of black to make the problem obvious:
After the window is constructed, i tell it to use black as a chroma-key color:
SetLayeredWindowAttributes(hwnd, 0x00000000, 255, LWA_COLORKEY);
Now the popup layered window appears partially transparent:
The problem is the final step. i want to use CS_DROPSHADOW class style, available since Windows XP, to create a drop-shadow:
wndClass.Style = wndClass.Style | CS_DROPSHADOW;
The drop shadow appears, but the shadow surrounds the original rectangular window, and doesn't take into account the window's transparency provided by the layered window:
Does anyone know what magical option i've missed somewhere that will make the drop shadow honor the non-rectangular layered window?
Another example of where this issue appears is when you don't include the 6px padding/margin. The hint window as drawn by Windows® themes is non-rectangular. This leaves a small visible gap where the window is transparent, but the drop shadow does not appear:
Microsoft has managed to make it work, as you can see from this hint from Internet Explorer:
Looking closer at a Windows tooltips class hint window. Using SpyXX - i can get its window rect, and class styles:
SpyXX says:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
So everything points to the window itself being 104x20 pixels, with the drop shadow outside the window itself. (Which is consistent with CS_DROPSHADOW.)
Next i can look at the styles of the tooltips window class:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
Interestingly, it doesn't use CS_SAVEBITS (0x800); which is useful for small, short-lived, windows.
Nor does it use CS_DROPSHADOW (0x20000). So now i wonder how is it drawing outside its own window?
Note: Transparent layered windows is documented as the preferred technique over regions.
Edit: Layered Windows have been around with Windows 2000. CS_DropShadow was added with XP.
Transparent layered windows is documented as the preferred technique over regions.
However, CS_DROPSHADOW does pay attention to regions. If you crop or otherwise shape your window using a region, the drop-shadow will follow the new outline.
Fortunately, you can use regions with layered windows, and by combining the two get the effect you're looking for.
BTW: tooltips_class32 does use CS_DROPSHADOW - you won't see it in the window styles because it's a class style, not a window style.
Why don't you use LWA_ALPHA and build the shadow into the image?
Edit in reponse to your comment:
A) Doesn't stop you using an alpha channeled PNG for a shadow only. Blt the 2 images together and use as one single image.
B) Its not hard to generate a drop shadow. In the image you posted its black with 3 different alpha values.
C) But it doesn't work does it? ie Time to get creative.
D) Then don't try and get windows to do something it won't do for you.
E) Is entirely irrelevant. Layered windows handle that for you.
I strongly recommend you learn more about layered windows because they CAN help you to your goal.
Edit2: You have the bitmap. Its fairly easy to scan over the image and find which bits will be colour keyed (by identifying the black yourself) and hen modify that to have an alpha of 0 where everything else will have an alpha of 255 (Not: You may have to convert the image to a 32-bit image from a lower colour format, you can do this by creating a new DC and copying the image). This will give you the same effect with LWA_ALPHA as with LWA_COLORKEY. From there its fairly easy to identify the pixel at the edge, where the color changes to (R = 0, G = 0, B = 0, A = 0). You then change that first pixel to have a n alpha of 192, the one blow it to 128 and the one below to 64. You now have an alpha'd gradation below the image that will look like the shadow. You can adjust the alpha to get the effect you want.
CS_DROPSHADOW only works with standard rectangular windows. WS_EX_LAYERED windows don't support everything. They are more of a low-level, self-service method to draw exactly what you want.
To get a drop shadow, you'll have to generate the drop-shadow yourself from the alpha channel in the image.