mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-30 17:50:19 +02:00
win32: fix form bounds for windows with multi-line main menus and/or scrollbars
This commit is contained in:
parent
0c34cad9a0
commit
877d933344
@ -280,8 +280,8 @@ function AdjustWindowRectExForDpi(const lpRect: LPRECT; dwStyle: DWORD; bMenu: B
|
|||||||
function GetDpiForMonitor(hmonitor: HMONITOR; dpiType: TMonitorDpiType; out dpiX: UINT; out dpiY: UINT): HRESULT;
|
function GetDpiForMonitor(hmonitor: HMONITOR; dpiType: TMonitorDpiType; out dpiX: UINT; out dpiY: UINT): HRESULT;
|
||||||
function LoadIconWithScaleDown(hinst:HINST; pszName:LPCWStr;cx:cint;cy:cint;var phico: HICON ):HRESULT;
|
function LoadIconWithScaleDown(hinst:HINST; pszName:LPCWStr;cx:cint;cy:cint;var phico: HICON ):HRESULT;
|
||||||
|
|
||||||
procedure AdjustFormBounds(const AHandle: HANDLE; var ioSizeRect: TRect); overload;
|
procedure AdjustFormClientToWindowSize(const AHandle: HANDLE; var ioSize: TSize); overload;
|
||||||
procedure AdjustFormBounds(aHasMenu: Boolean; dwStyle, dwExStyle: DWORD; dpi: UINT; var ioSizeRect: TRect); overload;
|
procedure AdjustFormClientToWindowSize(aHasMenu: Boolean; dwStyle, dwExStyle: DWORD; dpi: UINT; var ioSize: TSize); overload;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -439,44 +439,68 @@ begin
|
|||||||
Result := S_FALSE;
|
Result := S_FALSE;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure AdjustFormBounds(const AHandle: HANDLE; var ioSizeRect: TRect);
|
procedure AdjustFormClientToWindowSize(const AHandle: HANDLE; var ioSize: TSize);
|
||||||
{$IFNDEF LCLRealFormBounds}
|
{$IFNDEF LCLRealFormBounds}
|
||||||
var
|
var
|
||||||
xClientRect, xWindowRect: TRect;
|
xClientRect, xWindowRect, xSR: TRect;
|
||||||
xNonClientDPI: UINT;
|
xNonClientDPI: UINT;
|
||||||
Info: tagWINDOWINFO;
|
xInfo: tagWINDOWINFO;
|
||||||
|
xTopLeft: TPoint;
|
||||||
|
xHasMenu: Boolean;
|
||||||
|
xReplaceTop: LongInt;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
begin
|
begin
|
||||||
{$IFNDEF LCLRealFormBounds}
|
{$IFNDEF LCLRealFormBounds}
|
||||||
xClientRect := Default(TRect);
|
// convert form client size to window size
|
||||||
xWindowRect := Default(TRect);
|
// the difference between Windows.GetClientRect(AHandle, xClientRect) and Windows.GetWindowRect(AHandle, xWindowRect)
|
||||||
if (AHandle<>0)
|
// must not be used because it fails when the form has visible scrollbars (and can be scrolled)
|
||||||
and GetClientRect(AHandle, xClientRect) and not xClientRect.IsEmpty
|
|
||||||
and (GetWindowRect(AHandle, xWindowRect)<>0) and not xWindowRect.IsEmpty then
|
|
||||||
begin
|
|
||||||
Inc(ioSizeRect.Right, xWindowRect.Width-xClientRect.Width);
|
|
||||||
Inc(ioSizeRect.Bottom, xWindowRect.Height-xClientRect.Height);
|
|
||||||
Exit; // the sizes could be obtained from window-client -> exit
|
|
||||||
end;
|
|
||||||
|
|
||||||
// the sizes could not be obtained from window-client (e.g. the window is minimized) -> calculate default
|
if AreDpiAwarenessContextsEqual(GetThreadDpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) then
|
||||||
if (AHandle<>0) and AreDpiAwarenessContextsEqual(GetThreadDpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) then
|
|
||||||
xNonClientDPI := GetDpiForWindow(AHandle)
|
xNonClientDPI := GetDpiForWindow(AHandle)
|
||||||
else
|
else
|
||||||
xNonClientDPI := ScreenInfo.PixelsPerInchX;
|
xNonClientDPI := ScreenInfo.PixelsPerInchX;
|
||||||
|
|
||||||
Info := Default(tagWINDOWINFO);
|
xInfo := Default(tagWINDOWINFO);
|
||||||
Info.cbSize := SizeOf(Info);
|
xInfo.cbSize := SizeOf(xInfo);
|
||||||
if GetWindowInfo(AHandle, @Info) then
|
if GetWindowInfo(AHandle, @xInfo) then
|
||||||
AdjustWindowRectExForDpi(@ioSizeRect, Info.dwStyle, GetMenu(AHandle)<>0, Info.dwExStyle, xNonClientDPI);
|
begin
|
||||||
|
xHasMenu := GetMenu(AHandle)<>0;
|
||||||
|
xTopLeft := Point(0, 0);
|
||||||
|
xClientRect := Default(TRect);
|
||||||
|
xWindowRect := Default(TRect);
|
||||||
|
|
||||||
|
// AdjustWindowRectExForDpi calculates only 1 menu line but on Win32 the menu can have multiple lines
|
||||||
|
// therefore get the top coordinate from ClientToScreen difference if possible (that is correct also when scrollbars are shown)
|
||||||
|
if xHasMenu
|
||||||
|
and Windows.GetClientRect(AHandle, xClientRect) and not xClientRect.IsEmpty // just to check that there is some client rect and ClientToScreen will return some sane results
|
||||||
|
and Windows.GetWindowRect(AHandle, xWindowRect) and not xWindowRect.IsEmpty
|
||||||
|
and Windows.ClientToScreen(AHandle, xTopLeft) then
|
||||||
|
begin
|
||||||
|
xReplaceTop := xTopLeft.Y-xWindowRect.Top;
|
||||||
|
xHasMenu := False;
|
||||||
|
end else
|
||||||
|
xReplaceTop := 0;
|
||||||
|
|
||||||
|
xSR := Rect(0, 0, 0, 0);
|
||||||
|
AdjustWindowRectExForDpi(@xSR, xInfo.dwStyle, xHasMenu, xInfo.dwExStyle, xNonClientDPI);
|
||||||
|
if xReplaceTop>0 then
|
||||||
|
xSR.Top := -xReplaceTop;
|
||||||
|
Inc(ioSize.cx, xSR.Width);
|
||||||
|
Inc(ioSize.cy, xSR.Height);
|
||||||
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure AdjustFormBounds(aHasMenu: Boolean; dwStyle, dwExStyle: DWORD; dpi: UINT; var ioSizeRect: TRect);
|
procedure AdjustFormClientToWindowSize(aHasMenu: Boolean; dwStyle, dwExStyle: DWORD; dpi: UINT; var ioSize: TSize);
|
||||||
|
var
|
||||||
|
SizeRect: TRect;
|
||||||
begin
|
begin
|
||||||
{$IFNDEF LCLRealFormBounds}
|
{$IFNDEF LCLRealFormBounds}
|
||||||
// no known handle -> default (1 menu line)
|
// no known handle -> default (1 menu line)
|
||||||
AdjustWindowRectExForDpi(@ioSizeRect, dwStyle, aHasMenu, dwExStyle, dpi);
|
SizeRect := Rect(0, 0, ioSize.Width, ioSize.Height);
|
||||||
|
AdjustWindowRectExForDpi(@SizeRect, dwStyle, aHasMenu, dwExStyle, dpi);
|
||||||
|
ioSize.Width := SizeRect.Width;
|
||||||
|
ioSize.Height := SizeRect.Height;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -2418,14 +2418,14 @@ function TWin32WidgetSet.GetWindowSize(Handle : hwnd;
|
|||||||
|
|
||||||
procedure ExcludeBorder(dwStyle, dwExStyle: DWORD);
|
procedure ExcludeBorder(dwStyle, dwExStyle: DWORD);
|
||||||
var
|
var
|
||||||
xRect: Windows.RECT;
|
xSize: Windows.SIZE;
|
||||||
begin
|
begin
|
||||||
if (Height<>0) and (Width<>0) then
|
if (Height<>0) and (Width<>0) then
|
||||||
begin
|
begin
|
||||||
xRect := Rect(0, 0, 0, 0);
|
xSize := TSize.Create(0, 0);
|
||||||
AdjustFormBounds(GetMenu(Handle)<>0, dwStyle, dwExStyle, GetDpiForWindow(Handle), xRect);
|
AdjustFormClientToWindowSize(Handle, xSize);
|
||||||
Dec(Height, xRect.Height);
|
Dec(Height, xSize.Height);
|
||||||
Dec(Width, xRect.Width);
|
Dec(Width, xSize.Width);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2457,13 +2457,12 @@ begin
|
|||||||
if Result then
|
if Result then
|
||||||
begin
|
begin
|
||||||
// for minimized window use normal position, in other case use rcWindow of WindowInfo
|
// for minimized window use normal position, in other case use rcWindow of WindowInfo
|
||||||
if (WP.showCmd<>SW_MINIMIZE) and (WP.showCmd<>SW_SHOWMINIMIZED) and not Info.rcClient.IsEmpty then
|
// DO NOT USE rcClient here because it is wrong if the form has scrollbars
|
||||||
SetWidthHeightFromRect(Info.rcClient)
|
if (WP.showCmd<>SW_MINIMIZE) and (WP.showCmd<>SW_SHOWMINIMIZED) then
|
||||||
|
SetWidthHeightFromRect(Info.rcWindow)
|
||||||
else
|
else
|
||||||
begin
|
|
||||||
SetWidthHeightFromRect(WP.rcNormalPosition);
|
SetWidthHeightFromRect(WP.rcNormalPosition);
|
||||||
ExcludeBorder(Info.dwStyle, Info.dwExStyle); // rcClient is not available -> get ExcludeBorder
|
ExcludeBorder(Info.dwStyle, Info.dwExStyle);
|
||||||
end;
|
|
||||||
//WriteLn('W = ', Width, ' H = ', Height);
|
//WriteLn('W = ', Width, ' H = ', Height);
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
@ -122,7 +122,7 @@ type
|
|||||||
published
|
published
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure AdjustFormBounds(const AForm: TCustomForm; var ioSizeRect: TRect); overload;
|
procedure AdjustFormClientToWindowSize(const AForm: TCustomForm; var ioSize: TSize); overload;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -305,29 +305,29 @@ begin
|
|||||||
FlagsEx := FlagsEx or CalcBorderIconsFlagsEx(AForm);
|
FlagsEx := FlagsEx or CalcBorderIconsFlagsEx(AForm);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure AdjustFormBounds(const AForm: TCustomForm; var ioSizeRect: TRect); overload;
|
procedure AdjustFormClientToWindowSize(const AForm: TCustomForm; var ioSize: TSize);
|
||||||
var
|
var
|
||||||
xNonClientDPI: LCLType.UINT;
|
xNonClientDPI: LCLType.UINT;
|
||||||
begin
|
begin
|
||||||
if AForm.HandleAllocated then
|
if AForm.HandleAllocated then
|
||||||
AdjustFormBounds(AForm.Handle, ioSizeRect)
|
AdjustFormClientToWindowSize(AForm.Handle, ioSize)
|
||||||
else // default handling
|
else // default handling
|
||||||
AdjustFormBounds(AForm.Menu<>nil,
|
AdjustFormClientToWindowSize(AForm.Menu<>nil,
|
||||||
CalcBorderStyleFlags(AForm) or CalcBorderIconsFlags(AForm),
|
CalcBorderStyleFlags(AForm) or CalcBorderIconsFlags(AForm),
|
||||||
CalcBorderStyleFlagsEx(AForm) or CalcBorderIconsFlagsEx(AForm),
|
CalcBorderStyleFlagsEx(AForm) or CalcBorderIconsFlagsEx(AForm),
|
||||||
ScreenInfo.PixelsPerInchX, ioSizeRect);
|
ScreenInfo.PixelsPerInchX, ioSize);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function CustomFormWndProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; stdcall;
|
function CustomFormWndProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; stdcall;
|
||||||
|
|
||||||
procedure LCLFormSizeToWin32Size(AForm: TCustomForm; var AWidth, AHeight: Integer);
|
procedure LCLFormSizeToWin32Size(AForm: TCustomForm; var AWidth, AHeight: Integer);
|
||||||
var
|
var
|
||||||
SizeRect: Windows.RECT;
|
Size: TSize;
|
||||||
begin
|
begin
|
||||||
SizeRect := Classes.Rect(0, 0, AWidth, AHeight);
|
Size := TSize.Create(AWidth, AHeight);
|
||||||
AdjustFormBounds(AForm, SizeRect);
|
AdjustFormClientToWindowSize(AForm, Size);
|
||||||
AWidth := SizeRect.Width;
|
AWidth := Size.Width;
|
||||||
AHeight := SizeRect.Height;
|
AHeight := Size.Height;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure SetMinMaxInfo(WinControl: TWinControl; var MinMaxInfo: TMINMAXINFO);
|
procedure SetMinMaxInfo(WinControl: TWinControl; var MinMaxInfo: TMINMAXINFO);
|
||||||
@ -420,6 +420,7 @@ var
|
|||||||
Bounds: TRect;
|
Bounds: TRect;
|
||||||
SystemMenu: HMenu;
|
SystemMenu: HMenu;
|
||||||
MaximizeForm: Boolean = False;
|
MaximizeForm: Boolean = False;
|
||||||
|
lSize: TSize;
|
||||||
begin
|
begin
|
||||||
// general initialization of Params
|
// general initialization of Params
|
||||||
PrepareCreateWindow(AWinControl, AParams, Params);
|
PrepareCreateWindow(AWinControl, AParams, Params);
|
||||||
@ -471,7 +472,9 @@ begin
|
|||||||
pClassName := @ClsName[0];
|
pClassName := @ClsName[0];
|
||||||
WindowTitle := StrCaption;
|
WindowTitle := StrCaption;
|
||||||
Bounds := lForm.BoundsRect;
|
Bounds := lForm.BoundsRect;
|
||||||
AdjustFormBounds(lForm, Bounds);
|
lSize := Bounds.Size;
|
||||||
|
AdjustFormClientToWindowSize(lForm, lSize);
|
||||||
|
Bounds.Size := lSize;
|
||||||
if (lForm.Position in [poDefault, poDefaultPosOnly]) and not (csDesigning in lForm.ComponentState) then
|
if (lForm.Position in [poDefault, poDefaultPosOnly]) and not (csDesigning in lForm.ComponentState) then
|
||||||
begin
|
begin
|
||||||
Left := CW_USEDEFAULT;
|
Left := CW_USEDEFAULT;
|
||||||
@ -643,18 +646,26 @@ class procedure TWin32WSCustomForm.SetBounds(const AWinControl: TWinControl;
|
|||||||
var
|
var
|
||||||
AForm: TCustomForm absolute AWinControl;
|
AForm: TCustomForm absolute AWinControl;
|
||||||
CurRect: Windows.RECT = (Left: 0; Top: 0; Right: 0; Bottom: 0);
|
CurRect: Windows.RECT = (Left: 0; Top: 0; Right: 0; Bottom: 0);
|
||||||
SizeRect: Windows.RECT;
|
lSize: Windows.SIZE;
|
||||||
L, T, W, H: Integer;
|
L, T, W, H: Integer;
|
||||||
|
Attempt: 0..1; // 2 attempts
|
||||||
|
begin
|
||||||
|
// Problem:
|
||||||
|
// When setting the ClientRect, the main menu may change height (the menu lines may change).
|
||||||
|
// After the first attempt to set bounds, they can be wrong because the number of the lines changed and
|
||||||
|
// it is not possible to determine the menu line count for the target rectangle/
|
||||||
|
// Therefore a second attempt is needed to get the correct height.
|
||||||
|
for Attempt := Low(Attempt) to High(Attempt) do
|
||||||
begin
|
begin
|
||||||
// the LCL defines the size of a form without border, win32 with.
|
// the LCL defines the size of a form without border, win32 with.
|
||||||
// -> adjust size according to BorderStyle
|
// -> adjust size according to BorderStyle
|
||||||
SizeRect := Bounds(ALeft, ATop, AWidth, AHeight);
|
lSize := TSize.Create(AWidth, AHeight);
|
||||||
|
|
||||||
AdjustFormBounds(AForm, SizeRect);
|
AdjustFormClientToWindowSize(AForm, lSize);
|
||||||
L := ALeft;
|
L := ALeft;
|
||||||
T := ATop;
|
T := ATop;
|
||||||
W := SizeRect.Right - SizeRect.Left;
|
W := lSize.Width;
|
||||||
H := SizeRect.Bottom - SizeRect.Top;
|
H := lSize.Height;
|
||||||
|
|
||||||
// we are calling setbounds in TWinControl.Initialize
|
// we are calling setbounds in TWinControl.Initialize
|
||||||
// if position is default it will be changed to designed. We do not want this.
|
// if position is default it will be changed to designed. We do not want this.
|
||||||
@ -678,6 +689,11 @@ begin
|
|||||||
|
|
||||||
// rect adjusted, pass to inherited to do real work
|
// rect adjusted, pass to inherited to do real work
|
||||||
TWin32WSWinControl.SetBounds(AWinControl, L, T, W, H);
|
TWin32WSWinControl.SetBounds(AWinControl, L, T, W, H);
|
||||||
|
if (Attempt=High(Attempt)) // last one, no need to call GetClientRect
|
||||||
|
or not GetClientRect(AWinControl, CurRect) // not available
|
||||||
|
or ((CurRect.Width=AWidth) and (CurRect.Height=AHeight)) then // or correct size -> break
|
||||||
|
break;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class procedure TWin32WSCustomForm.SetIcon(const AForm: TCustomForm; const Small, Big: HICON);
|
class procedure TWin32WSCustomForm.SetIcon(const AForm: TCustomForm; const Small, Big: HICON);
|
||||||
|
Loading…
Reference in New Issue
Block a user