win32: use a WindowPosChanging hack in SetBounds to prevent Windows to change the LCL position (replaces c5c2329f). Issue

This commit is contained in:
Ondrej Pokorny 2021-11-19 20:49:18 +01:00
parent 89cd2a1045
commit 20009b685b
2 changed files with 47 additions and 14 deletions

View File

@ -134,6 +134,11 @@ procedure WindowCreateInitBuddy(const AWinControl: TWinControl;
// Must be in win32proc but TCreateWindowExParams declared here
procedure SetStdBiDiModeParams(const AWinControl: TWinControl; var Params:TCreateWindowExParams);
var
// WindowPosChanging hack - see comment in TWin32WSWinControl.SetBounds
LockWindowPosChanging: Boolean = False;
LockWindowPosChangingXY: TPoint;
implementation
uses
@ -441,6 +446,8 @@ var
suppressMove: boolean;
Handle: HWND;
WindowPlacement: TWINDOWPLACEMENT;
Mon: HMONITOR;
MonInfo: TMonitorInfo;
begin
IntfLeft := ALeft;
IntfTop := ATop;
@ -459,24 +466,35 @@ begin
begin
Handle := AWinControl.Handle;
WindowPlacement.length := SizeOf(WindowPlacement);
// Windows (at least Win 10) has the feature that SetWindowPos() forces dialogs with parent windows on the same screen
// with the parent window - the position set with Windows.SetWindowPos() is ignored and instead the dialog
// is centered with its parent window.
// To prevent Windows from changing the position defined by the LCL, SetWindowPos() must not be used and
// SetWindowPlacement() must be used instead.
// It looks like GetWindowPlacement/SetWindowPlacement has no negative impact on non-form controls, so it is used directly
// In case of problems with "normal" wincontrols, the GetWindowPlacement/SetWindowPlacement code can be executed only
// for TCustomForm descendands
// To prevent Windows from changing the position defined by the LCL, the LM_WINDOWPOSCHANGING is handled and the
// new coordinates are re-assigned within the message handler with LockWindowPosChanging&LockWindowPosChangingXY
// See issue #39479 for more description and demo application.
if GetWindowPlacement(Handle, @WindowPlacement) then
begin
WindowPlacement.rcNormalPosition := Bounds(IntfLeft, IntfTop, IntfWidth, IntfHeight);
if not IsIconic(Handle) and not Windows.IsWindowVisible(Handle) then // do not show hidden windows prematurely
WindowPlacement.showCmd := SW_HIDE;
SetWindowPlacement(Handle, @WindowPlacement);
end
else
Windows.SetWindowPos(Handle, 0, IntfLeft, IntfTop, IntfWidth, IntfHeight, SWP_NOZORDER or SWP_NOACTIVATE);
LockWindowPosChanging := True;
try
LockWindowPosChangingXY := Point(IntfLeft, IntfTop);
if IsIconic(Handle) and GetWindowPlacement(Handle, @WindowPlacement) then
begin
WindowPlacement.rcNormalPosition := Bounds(IntfLeft, IntfTop, IntfWidth, IntfHeight);
// workarea coordinates must be used for top-level windows without WS_EX_TOOLWINDOW window style
if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOOLWINDOW)=0 then
begin
Mon := MonitorFromRect(@WindowPlacement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY);
MonInfo := Default(TMonitorInfo);
MonInfo.cbSize := SizeOf(TMonitorInfo);
if (Mon<>0) and GetMonitorInfo(Mon, @MonInfo) then
WindowPlacement.rcNormalPosition.Offset(MonInfo.rcMonitor.Left-MonInfo.rcWork.Left, MonInfo.rcMonitor.Top-MonInfo.rcWork.Top);
end;
SetWindowPlacement(Handle, @WindowPlacement);
end
else
Windows.SetWindowPos(Handle, 0, IntfLeft, IntfTop, IntfWidth, IntfHeight, SWP_NOZORDER or SWP_NOACTIVATE);
finally
LockWindowPosChanging := False;
end;
end;
LCLControlSizeNeedsUpdate(AWinControl, True);
// If this control is a child of an MDI form, then we need to update the MDI client bounds in

View File

@ -355,6 +355,19 @@ function CustomFormWndProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; LPar
end;
end;
procedure CallWindowPosChanging;
var
WP: PWindowPos;
begin
if not LockWindowPosChanging then
Exit;
WP := PWindowPos(LParam);
if (WP^.flags and SWP_NOMOVE)<>0 then
Exit;
WP^.x := LockWindowPosChangingXY.X;
WP^.y := LockWindowPosChangingXY.Y;
end;
var
Info: PWin32WindowInfo;
WinControl: TWinControl;
@ -362,6 +375,8 @@ begin
Info := GetWin32WindowInfo(Window);
WinControl := Info^.WinControl;
case Msg of
WM_WINDOWPOSCHANGING:
CallWindowPosChanging;
WM_GETMINMAXINFO:
begin
SetMinMaxInfo(WinControl, PMINMAXINFO(LParam)^);