How to get window original size and position (wsNormal vs wsMaximized) - windows

I'm looking to store user personalization of window size and position to use it when the application is reopened. That's actually very simple and I have the following code working:
OnCreate:
Width := wIni.ReadInteger('FORM', 'FORMW', 980);
Height := wIni.ReadInteger('FORM', 'FORMH', 500);
PnlXMLI.Width := wIni.ReadInteger('FORM', 'PNLXMLIW', 500);
WindowState := TWindowState(GetEnumValue(TypeInfo(TWindowState), wIni.ReadString('FORM', 'WINDOWSTATE', 'wsNormal')));
OnDestroy:
wIni.WriteInteger('FORM', 'FORMW', Width);
wIni.WriteInteger('FORM', 'FORMH', Height);
wIni.WriteInteger('FORM', 'PNLXMLIW', PnlXMLI.Width);
wIni.WriteString('FORM', 'WINDOWSTATE', GetEnumName(TypeInfo(TWindowState), Ord(WindowState)));
The problem is that when the user maximizes the window, and then restore it, it goes back to the size it was before maximizing. But if he maximizes, and then close and reopen the application and restore it, the application will not go back to original size before maximizing. It will be at the size of screen, because Width and Height properties gives the maximized size when read.
Question is: how can I get the original size of the window, i.e., the one it will go back to when the user restore the window? I tried setting WindowState to wsNormal right before reading the Width and Height, but it didn't work (maybe because the form is being destroyed?)... and I'd rather use a solution that doesn't do unnecessary GUI operations (for source code aesthetic reasons).
Thanks in advance.

The functions that you need are GetWindowPlacement and SetWindowPlacement. These operate on the WINDOWPLACEMENT struct. That struct has the rcNormalPosition member which is documented like this:
The window's coordinates when the window is in the restored position.
When you save away the window position and size you need to save away the values found in rcNormalPosition. In the opposite direction, when restoring the window position and size you must call SetWindowPlacement passing as rcNormalPosition the value stored in the user preference in the INI file.

Related

PTVISIBLE always returns true when relevant point obscured by another window

I am trying to ascertain whether the mouse cursor is over a specific window and whether there are any other windows obscuring that window at that specific point. The relevant point is obtained in screen coordinates by way of a mouse hook. I then use the ptVisible function to determine this. My code is:
DC := GetDC(wnd);
try
Result := PtVisible(DC, pt.X, pt.Y);
finally
ReleaseDC(wnd, DC);
end;
This always returns false even when there is nothing obscuring the window represented by the wnd handle.
I found very little on the web as to proper use of ptVisible. Can anyone advise if I am using it incorrectly?
You are not using it correctly. From the docs page, PtVisible() "determines whether the specified point is within the clipping region of a device context." This has nothing to do with mouse location or location of a pixel within a window. This has specific uses to check whether the point is within the clipping region of a graphics device context.
If you want to check whether the mouse is within a certain window, you might want to try checking if your mouse coordinate is within GetWindowRect(). And then to check whether or not any windows overlap, you'd want to EnumWindows() and then IntersectRect()

Managing window size with respect to the taskbar

How can I resize my application's window when the taskbar's size has changed?
For example, when the taskbar has been reduced in size my window should increase in size to fill up the hole that was created. My window should never overlap the taskbar.
I was able to create the window in the correct place by calling CreateWindowEx with a position derived from calling SystemParametersInfo(SPI_GETWORKAREA, 0, &rectWorkArea, 0);
Now, when I increase the taskbar's size my window's size decreases automatically
without any code. But when I "go back", my window remains in its current position. How can I fix this?
Not judging if it is good or wrong idea (as standard applications just don't do it), I think it can be tracked by handling WM_SETTINGCHANGE in any top-level window.

How do I change the viewport of a window in win32?

I have a window with child windows inside in it. The child windows take up about 1000 pixels of vertical space. However, our users don't always have 1000 pixels of vertical space available - they might have as little as 500 or 600 pixels.
I want to be able to display this window at a size of 500 pixels high, and have the user "scroll" up and down the window to see the full contents. The window should always be 500 pixels high, but the view within it should change.
Assume I can add a scroll bar somewhere so the user can choose which part of the window he wants to see. Windows will normally paint the window contents from height 0 to height 500; how do I tell it instead to "paint from height 250 to height 750", for example?
I know that I can set the viewport with functions like SetViewportOrgEx etc, but those functions require a device context - when do I call them if I want them to be "permanent"? Do I call them when I get the WM_PAINT message from windows? Or at some other time? And which functions from that family do I want to use?
Edit to add: I don't want to actually change the position of the child windows - they should stay at the same position, and the only thing that should change is the view into the window.
Thanks.
If (when you get messages about the scroll bars changing) you call ScrollWindowEx with the SW_SCROLLCHILDREN flag, the child windows should be told to scroll along with everything else. This ought to put them in the right position.

Getting the size of a minimized window

I never knew this, but apparently:
By default, the system reduces a
minimized window to the size of its
taskbar button and moves the minimized
window to the taskbar. A restored
window is a window that has been
returned to its previous size and
position, that is, the size it was
before it was minimized or maximized.
In an application, we want to save the position/size of various windows at exit. This leads to a problem for minimized windows. Our solution is to restore all windows before running the save-state logic, but that just seems hacky. Is there a better way?
How about using GetWindowPlacement?
That returns a WINDOWPLACEMENT structure that contains information about the window's coordinates in the restored position.
Remember that (as Leo Davidson points out in the comments) that you must respect the difference between workspace and screen coordinates. As the WINDOWPLACEMENT documentation explains:
The coordinates used in a
WINDOWPLACEMENT structure should be
used only by the GetWindowPlacement
and SetWindowPlacement functions.
Passing workspace coordinates to
functions which expect screen
coordinates (such as SetWindowPos)
will result in the window appearing in
the wrong location. For example, if
the taskbar is at the top of the
screen, saving window coordinates
using GetWindowPlacement and restoring
them using SetWindowPos causes the
window to appear to "creep" up the
screen.
Or, the simpler solution that I've no doubt used before is just to check if the window is minimized before saving the state, and if it is, skip saving any state information.
As far as explaining why a window changes its size when it gets minimized, Raymond Chen's blog entry (and the linked entry as well) on the subject is mandatory reading. They don't really change to their taskbar button's size, but rather to a pre-defined size of 160x31. He explains that you can see this by minimizing a MDI child window into its parent—that's really its size.
Handle WM_SIZE message. If wParam is not SIZE_MAXIMIZED or SIZE_MINIMIZED, keep window size and position in some varibles. Use these variables when window is closed.

win32: detect if start menu is auto-hidden?

I want to position a window at the bottom of the screen. If the start menu is present, I want it to lie along the top of the start menu. If it is not (or it is auto-hidden), I still want it to be in the same position as it would be if the start menu was there, meaning there will be a gap of a few pixels.
Currently I get the monitor work area, position the window at the bottom, and always offset by 20 pixels or so. If the start menu isn't there, this works well. If it is, however, the work area also shrinks (as it should), and I end up double-offsetting.
How would I fix the issue?
To get the work area of the screen not obscured by the system taskbar or by application desktop toolbars, you can use SystemParametersInfo() with SPI_GETWORKAREA as uiAction parameter. The pvParam parameter must point to a RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. For example:
RECT rectWorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rectWorkArea, 0);
As you said in the comment, to get the bounding rectangle of the taskbar, we can call SHAppBarMessage(ABM_GETTASKBARPOS, ...)
To determine the position of the taskbar (whether it is currently at the bottom, top, left, or right of the screen), you could use the following calculation:
type
TTaskBarPos = (_TOP, _BOTTOM, _LEFT, _RIGHT);
var
iScrW, iScrH: Longint;
iScrW := GetSystemMetrics(SM_CXSCREEN);
iScrH := GetSystemMetrics(SM_CXSCREEN);
if (rectTaskbar.Top > iScrH div 2) and (rectTaskbar.Right >= iScrW) then
Result := _BOTTOM
else if (rectTaskbar.Top < iScrH div 2) and (rectTaskbar.Bottom <= iScrW div 2) then
Result := _TOP
else if (rectTaskbar.Left < iScrW div 2) and (rectTaskbar.Top <= 0) then
Result := _LEFT
else
Result := _RIGHT;
They should be enough to solve your current problem. However, if you need to know (for another reason) the current taskbar settings of the autohide and always-on-top states, you can use SHAppBarMessage(ABM_GETSTATE, ...).
If you need to be notified that that the taskbar's autohide or always-on-top state has changed, you have to intercept ABN_STATECHANGE message.
Are you are using or have access to .NET in your project?
If so, you can use the Screen.PrimaryScreen.WorkingArea.Height property to determine the bottom of the screen excluding the task bar.
You can also grab the total screen height by getting the Screen.PrimaryScreen.Bounds.Height property (which includes the task bar in the total height value).
Comparing these values, if they're the same, the task bar isn't present. Otherwise, the task bar is and you can adjust accordingly.

Resources